mirror of
				https://github.com/mcMMO-Dev/mcMMO.git
				synced 2025-11-04 11:03:43 +01:00 
			
		
		
		
	Move legacy Serializable usage into a subclass.
This commit is contained in:
		@@ -3,33 +3,35 @@ package com.gmail.nossr50.util.blockmeta;
 | 
			
		||||
import org.bukkit.Bukkit;
 | 
			
		||||
import org.bukkit.World;
 | 
			
		||||
import org.jetbrains.annotations.NotNull;
 | 
			
		||||
import org.jetbrains.annotations.Nullable;
 | 
			
		||||
 | 
			
		||||
import java.io.*;
 | 
			
		||||
import java.util.BitSet;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
public class BitSetChunkStore implements ChunkStore, Serializable {
 | 
			
		||||
    private static final long serialVersionUID = -1L;
 | 
			
		||||
    transient private boolean dirty = false;
 | 
			
		||||
    // Bitset store conforms to a "bottom-up" bit ordering consisting of a stack of {worldHeight} Y planes, each Y plane consists of 16 Z rows of 16 X bits.
 | 
			
		||||
    private BitSet store;
 | 
			
		||||
public class BitSetChunkStore implements ChunkStore {
 | 
			
		||||
    private static final int CURRENT_VERSION = 8;
 | 
			
		||||
    private static final int MAGIC_NUMBER = 0xEA5EDEBB;
 | 
			
		||||
    private int cx;
 | 
			
		||||
    private int cz;
 | 
			
		||||
    private int worldHeight;
 | 
			
		||||
    private UUID worldUid;
 | 
			
		||||
 | 
			
		||||
    private final int cx;
 | 
			
		||||
    private final int cz;
 | 
			
		||||
    private final int worldHeight;
 | 
			
		||||
    private final UUID worldUid;
 | 
			
		||||
    // Bitset store conforms to a "bottom-up" bit ordering consisting of a stack of {worldHeight} Y planes, each Y plane consists of 16 Z rows of 16 X bits.
 | 
			
		||||
    private final BitSet store;
 | 
			
		||||
 | 
			
		||||
    private transient boolean dirty = false;
 | 
			
		||||
 | 
			
		||||
    public BitSetChunkStore(@NotNull World world, int cx, int cz) {
 | 
			
		||||
        this.cx = cx;
 | 
			
		||||
        this.cz = cz;
 | 
			
		||||
        this.worldUid = world.getUID();
 | 
			
		||||
        this.worldHeight = world.getMaxHeight();
 | 
			
		||||
        this.store = new BitSet(16 * 16 * worldHeight);
 | 
			
		||||
        this(world.getUID(), world.getMaxHeight(), cx, cz);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private BitSetChunkStore() {}
 | 
			
		||||
    private BitSetChunkStore(@NotNull UUID worldUid, int worldHeight, int cx, int cz) {
 | 
			
		||||
        this.cx = cx;
 | 
			
		||||
        this.cz = cz;
 | 
			
		||||
        this.worldUid = worldUid;
 | 
			
		||||
        this.worldHeight = worldHeight;
 | 
			
		||||
        this.store = new BitSet(16 * 16 * worldHeight);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean isDirty() {
 | 
			
		||||
@@ -83,58 +85,24 @@ public class BitSetChunkStore implements ChunkStore, Serializable {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private int coordToIndex(int x, int y, int z) {
 | 
			
		||||
        return coordToIndex(x, y, z, worldHeight);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static int coordToIndex(int x, int y, int z, int worldHeight) {
 | 
			
		||||
        if (x < 0 || x >= 16 || y < 0 || y >= worldHeight || z < 0 || z >= 16)
 | 
			
		||||
            throw new IndexOutOfBoundsException(String.format("x: %d y: %d z: %d World Height: %d", x, y, z, worldHeight));
 | 
			
		||||
        return (z * 16 + x) + (256 * y);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void fixWorldHeight() {
 | 
			
		||||
    private static int getWorldHeight(UUID worldUid, int storedWorldHeight)
 | 
			
		||||
    {
 | 
			
		||||
        World world = Bukkit.getWorld(worldUid);
 | 
			
		||||
 | 
			
		||||
        // Not sure how this case could come up, but might as well handle it gracefully.  Loading a chunkstore for an unloaded world?
 | 
			
		||||
        if (world == null)
 | 
			
		||||
            return;
 | 
			
		||||
            return storedWorldHeight;
 | 
			
		||||
 | 
			
		||||
        // Lop off any extra data if the world height has shrunk
 | 
			
		||||
        int currentWorldHeight = world.getMaxHeight();
 | 
			
		||||
        if (currentWorldHeight < worldHeight)
 | 
			
		||||
        {
 | 
			
		||||
            store.clear(coordToIndex(16, currentWorldHeight, 16), store.length());
 | 
			
		||||
            worldHeight = currentWorldHeight;
 | 
			
		||||
            dirty = true;
 | 
			
		||||
        }
 | 
			
		||||
        // If the world height has grown, update the worldHeight variable, but don't bother marking it dirty as unless something else changes we don't need to force a file write;
 | 
			
		||||
        else if (currentWorldHeight > worldHeight)
 | 
			
		||||
            worldHeight = currentWorldHeight;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    private void writeObject(ObjectOutputStream out) throws IOException {
 | 
			
		||||
        throw new UnsupportedOperationException("Serializable support should only be used for legacy deserialization");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
 | 
			
		||||
        in.readInt(); // Magic number
 | 
			
		||||
        in.readInt(); // Format version
 | 
			
		||||
        long lsb = in.readLong();
 | 
			
		||||
        long msb = in.readLong();
 | 
			
		||||
        worldUid = new UUID(msb, lsb);
 | 
			
		||||
        cx = in.readInt();
 | 
			
		||||
        cz = in.readInt();
 | 
			
		||||
 | 
			
		||||
        boolean[][][] oldStore = (boolean[][][]) in.readObject();
 | 
			
		||||
        worldHeight = oldStore[0][0].length;
 | 
			
		||||
        store = new BitSet(16 * 16 * worldHeight / 8);
 | 
			
		||||
        for (int x = 0; x < 16; x++) {
 | 
			
		||||
            for (int z = 0; z < 16; z++) {
 | 
			
		||||
                for (int y = 0; y < worldHeight; y++) {
 | 
			
		||||
                    store.set(coordToIndex(x, y, z), oldStore[x][z][y]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        dirty = true;
 | 
			
		||||
        fixWorldHeight();
 | 
			
		||||
        return world.getMaxHeight();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void serialize(DataOutputStream out) throws IOException {
 | 
			
		||||
@@ -163,26 +131,34 @@ public class BitSetChunkStore implements ChunkStore, Serializable {
 | 
			
		||||
        if (magic != MAGIC_NUMBER || fileVersionNumber != CURRENT_VERSION)
 | 
			
		||||
            throw new IOException();
 | 
			
		||||
 | 
			
		||||
        BitSetChunkStore chunkStore = new BitSetChunkStore();
 | 
			
		||||
 | 
			
		||||
        long lsb = in.readLong();
 | 
			
		||||
        long msb = in.readLong();
 | 
			
		||||
        chunkStore.worldUid = new UUID(msb, lsb);
 | 
			
		||||
        chunkStore.cx = in.readInt();
 | 
			
		||||
        chunkStore.cz = in.readInt();
 | 
			
		||||
        UUID worldUid = new UUID(msb, lsb);
 | 
			
		||||
        int cx = in.readInt();
 | 
			
		||||
        int cz = in.readInt();
 | 
			
		||||
 | 
			
		||||
        chunkStore.worldHeight = in.readInt();
 | 
			
		||||
        int worldHeight = in.readInt();
 | 
			
		||||
        byte[] temp = new byte[in.readInt()];
 | 
			
		||||
        in.readFully(temp);
 | 
			
		||||
        chunkStore.store = BitSet.valueOf(temp);
 | 
			
		||||
        BitSet stored = BitSet.valueOf(temp);
 | 
			
		||||
 | 
			
		||||
        int currentWorldHeight = getWorldHeight(worldUid, worldHeight);
 | 
			
		||||
 | 
			
		||||
        boolean worldHeightShrunk = currentWorldHeight < worldHeight;
 | 
			
		||||
        // Lop off extra data if world height has shrunk
 | 
			
		||||
        if (worldHeightShrunk)
 | 
			
		||||
            stored.clear(coordToIndex(16, currentWorldHeight, 16, worldHeight), stored.length());
 | 
			
		||||
 | 
			
		||||
        BitSetChunkStore chunkStore = new BitSetChunkStore(worldUid, currentWorldHeight, cx, cz);
 | 
			
		||||
        chunkStore.store.or(stored);
 | 
			
		||||
        chunkStore.dirty = worldHeightShrunk; // In the expanded case there is no reason to re-write it unless the data changes
 | 
			
		||||
 | 
			
		||||
        chunkStore.fixWorldHeight();
 | 
			
		||||
        return chunkStore;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class Serialization {
 | 
			
		||||
 | 
			
		||||
        public static final short STREAM_MAGIC = (short)0xACDC;
 | 
			
		||||
        public static final short STREAM_MAGIC = (short)0xACDC; // Rock on
 | 
			
		||||
 | 
			
		||||
        public static @NotNull ChunkStore readChunkStore(DataInputStream inputStream) throws IOException {
 | 
			
		||||
            if (inputStream.markSupported())
 | 
			
		||||
@@ -198,7 +174,7 @@ public class BitSetChunkStore implements ChunkStore, Serializable {
 | 
			
		||||
                {
 | 
			
		||||
                    // Creates a new stream with the two magic number bytes and then the rest of the original stream...   Java is so dumb.  I just wanted to look at two bytes.
 | 
			
		||||
                    PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream, 2);
 | 
			
		||||
                    pushbackInputStream.unread((magicNumber >>> 0) & 0xFF);
 | 
			
		||||
                    pushbackInputStream.unread((magicNumber) & 0xFF);
 | 
			
		||||
                    pushbackInputStream.unread((magicNumber >>> 8) & 0xFF);
 | 
			
		||||
                    inputStream = new DataInputStream(pushbackInputStream);
 | 
			
		||||
                }
 | 
			
		||||
@@ -218,8 +194,61 @@ public class BitSetChunkStore implements ChunkStore, Serializable {
 | 
			
		||||
            ((BitSetChunkStore)chunkStore).serialize(outputStream);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Handles loading the old serialized classes even though we have changed name/package
 | 
			
		||||
        // Handles loading the old serialized class
 | 
			
		||||
        private static class LegacyDeserializationInputStream extends ObjectInputStream {
 | 
			
		||||
            private static class LegacyChunkStoreDeserializer implements Serializable
 | 
			
		||||
            {
 | 
			
		||||
                private static final long serialVersionUID = -1L;
 | 
			
		||||
 | 
			
		||||
                private int cx;
 | 
			
		||||
                private int cz;
 | 
			
		||||
                private int worldHeight;
 | 
			
		||||
                private UUID worldUid;
 | 
			
		||||
                private boolean[][][] store;
 | 
			
		||||
 | 
			
		||||
                private LegacyChunkStoreDeserializer() {}
 | 
			
		||||
 | 
			
		||||
                @Deprecated
 | 
			
		||||
                private void writeObject(ObjectOutputStream out) throws IOException {
 | 
			
		||||
                    throw new UnsupportedOperationException("You goofed.");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                @Deprecated
 | 
			
		||||
                private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
 | 
			
		||||
                    in.readInt(); // Magic number
 | 
			
		||||
                    in.readInt(); // Format version
 | 
			
		||||
                    long lsb = in.readLong();
 | 
			
		||||
                    long msb = in.readLong();
 | 
			
		||||
 | 
			
		||||
                    worldUid = new UUID(msb, lsb);
 | 
			
		||||
                    cx = in.readInt();
 | 
			
		||||
                    cz = in.readInt();
 | 
			
		||||
 | 
			
		||||
                    store = (boolean[][][]) in.readObject();
 | 
			
		||||
                    worldHeight = store[0][0].length;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                public BitSetChunkStore convert()
 | 
			
		||||
                {
 | 
			
		||||
                    int currentWorldHeight = getWorldHeight(worldUid, worldHeight);
 | 
			
		||||
 | 
			
		||||
                    BitSetChunkStore converted = new BitSetChunkStore(worldUid, currentWorldHeight, cx, cz);
 | 
			
		||||
 | 
			
		||||
                    // Read old data into new chunkstore
 | 
			
		||||
                    for (int x = 0; x < 16; x++) {
 | 
			
		||||
                        for (int z = 0; z < 16; z++) {
 | 
			
		||||
                            for (int y = 0; y < worldHeight && y < currentWorldHeight; y++) {
 | 
			
		||||
                                converted.store.set(converted.coordToIndex(x, y, z), store[x][z][y]);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    // Mark dirty so it will be re-written in new format on close
 | 
			
		||||
                    converted.dirty = true;
 | 
			
		||||
                    return converted;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            public LegacyDeserializationInputStream(InputStream in) throws IOException {
 | 
			
		||||
                super(in);
 | 
			
		||||
                enableResolveObject(true);
 | 
			
		||||
@@ -229,13 +258,14 @@ public class BitSetChunkStore implements ChunkStore, Serializable {
 | 
			
		||||
            protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
 | 
			
		||||
                ObjectStreamClass read = super.readClassDescriptor();
 | 
			
		||||
                if (read.getName().contentEquals("com.gmail.nossr50.util.blockmeta.chunkmeta.PrimitiveChunkStore"))
 | 
			
		||||
                    return ObjectStreamClass.lookup(BitSetChunkStore.class);
 | 
			
		||||
                    return ObjectStreamClass.lookup(LegacyChunkStoreDeserializer.class);
 | 
			
		||||
                return read;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public ChunkStore readLegacyChunkStore(){
 | 
			
		||||
                try {
 | 
			
		||||
                    return (ChunkStore) readObject();
 | 
			
		||||
                    LegacyChunkStoreDeserializer deserializer = (LegacyChunkStoreDeserializer)readObject();
 | 
			
		||||
                    return deserializer.convert();
 | 
			
		||||
                } catch (IOException | ClassNotFoundException e) {
 | 
			
		||||
                    return null;
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user