mirror of
				https://github.com/mcMMO-Dev/mcMMO.git
				synced 2025-10-30 16:53:43 +01:00 
			
		
		
		
	Merge branch 'master' of github.com:mcMMO-Dev/mcMMO
This commit is contained in:
		
							
								
								
									
										4
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								pom.xml
									
									
									
									
									
								
							| @@ -121,6 +121,10 @@ | ||||
|                     </artifactSet> | ||||
| <!--                    <dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation>--> | ||||
|                     <relocations> | ||||
|                         <relocation> | ||||
|                             <pattern>net.kyori.examination</pattern> | ||||
|                             <shadedPattern>com.gmail.nossr50.kyori.examination</shadedPattern> | ||||
|                         </relocation> | ||||
|                         <relocation> | ||||
|                             <pattern>net.kyori.adventure</pattern> | ||||
|                             <shadedPattern>com.gmail.nossr50.mcmmo.kyori.adventure</shadedPattern> | ||||
|   | ||||
| @@ -253,12 +253,18 @@ public class BlockListener implements Listener { | ||||
|     @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) | ||||
|     public void onBlockGrow(BlockGrowEvent event) | ||||
|     { | ||||
|         Block block = event.getBlock(); | ||||
|         World world = block.getWorld(); | ||||
|  | ||||
|         /* WORLD BLACKLIST CHECK */ | ||||
|         if(WorldBlacklist.isWorldBlacklisted(event.getBlock().getWorld())) | ||||
|         if(WorldBlacklist.isWorldBlacklisted(world)) | ||||
|             return; | ||||
|  | ||||
|         BlockState blockState = event.getBlock().getState(); | ||||
|         mcMMO.getPlaceStore().setFalse(blockState); | ||||
|         // Minecraft is dumb, the events still throw when a plant "grows" higher than the max block height.  Even though no new block is created | ||||
|         if (block.getY() >= world.getMaxHeight()) | ||||
|             return; | ||||
|  | ||||
|         mcMMO.getPlaceStore().setFalse(block); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -345,7 +345,6 @@ public class mcMMO extends JavaPlugin { | ||||
|  | ||||
|             formulaManager.saveFormula(); | ||||
|             holidayManager.saveAnniversaryFiles(); | ||||
|             placeStore.cleanUp();       // Cleanup empty metadata stores | ||||
|             placeStore.closeAll(); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -2,32 +2,36 @@ package com.gmail.nossr50.util.blockmeta; | ||||
|  | ||||
| import org.bukkit.Bukkit; | ||||
| import org.bukkit.World; | ||||
| import org.jetbrains.annotations.NotNull; | ||||
|  | ||||
| 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; | ||||
|  | ||||
|     public BitSetChunkStore(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); | ||||
|     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(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() { | ||||
| @@ -50,7 +54,7 @@ public class BitSetChunkStore implements ChunkStore, Serializable { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public UUID getWorldId() { | ||||
|     public @NotNull UUID getWorldId() { | ||||
|         return worldUid; | ||||
|     } | ||||
|  | ||||
| @@ -81,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(); | ||||
|             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 { | ||||
| @@ -153,7 +123,7 @@ public class BitSetChunkStore implements ChunkStore, Serializable { | ||||
|         dirty = false; | ||||
|     } | ||||
|  | ||||
|     private static BitSetChunkStore deserialize(DataInputStream in) throws IOException { | ||||
|     private static BitSetChunkStore deserialize(@NotNull DataInputStream in) throws IOException { | ||||
|         int magic = in.readInt(); | ||||
|         // Can be used to determine the format of the file | ||||
|         int fileVersionNumber = in.readInt(); | ||||
| @@ -161,28 +131,36 @@ 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 ChunkStore readChunkStore(DataInputStream inputStream) throws IOException { | ||||
|         public static @NotNull ChunkStore readChunkStore(DataInputStream inputStream) throws IOException { | ||||
|             if (inputStream.markSupported()) | ||||
|                 inputStream.mark(2); | ||||
|             short magicNumber = inputStream.readShort(); | ||||
| @@ -196,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); | ||||
|                 } | ||||
| @@ -216,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); | ||||
| @@ -227,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; | ||||
|                 } | ||||
|   | ||||
| @@ -1,126 +1,10 @@ | ||||
| package com.gmail.nossr50.util.blockmeta; | ||||
|  | ||||
| import org.bukkit.World; | ||||
| import org.bukkit.block.Block; | ||||
| import org.bukkit.block.BlockState; | ||||
| import org.jetbrains.annotations.NotNull; | ||||
|  | ||||
| public interface ChunkManager { | ||||
| public interface ChunkManager extends UserBlockTracker { | ||||
|     void closeAll(); | ||||
|  | ||||
|     /** | ||||
|      * Saves a given Chunk's Chunklet data | ||||
|      * | ||||
|      * @param cx Chunk X coordinate that is to be saved | ||||
|      * @param cz Chunk Z coordinate that is to be saved | ||||
|      * @param world World that the Chunk is in | ||||
|      */ | ||||
|     void saveChunk(int cx, int cz, World world); | ||||
|  | ||||
|     /** | ||||
|      * Informs the ChunkletManager a chunk is unloaded | ||||
|      * | ||||
|      * @param cx Chunk X coordinate that is unloaded | ||||
|      * @param cz Chunk Z coordinate that is unloaded | ||||
|      * @param world World that the chunk was unloaded in | ||||
|      */ | ||||
|     void chunkUnloaded(int cx, int cz, World world); | ||||
|  | ||||
|     /** | ||||
|      * Save all ChunkletStores related to the given world | ||||
|      * | ||||
|      * @param world World to save | ||||
|      */ | ||||
|     void saveWorld(World world); | ||||
|  | ||||
|     /** | ||||
|      * Unload all ChunkletStores from memory related to the given world after saving them | ||||
|      * | ||||
|      * @param world World to unload | ||||
|      */ | ||||
|     void unloadWorld(World world); | ||||
|  | ||||
|     /** | ||||
|      * Save all ChunkletStores | ||||
|      */ | ||||
|     void saveAll(); | ||||
|  | ||||
|     /** | ||||
|      * Check to see if a given location is set to true | ||||
|      * | ||||
|      * @param x X coordinate to check | ||||
|      * @param y Y coordinate to check | ||||
|      * @param z Z coordinate to check | ||||
|      * @param world World to check in | ||||
|      * @return true if the given location is set to true, false if otherwise | ||||
|      */ | ||||
|     boolean isTrue(int x, int y, int z, World world); | ||||
|  | ||||
|     /** | ||||
|      * Check to see if a given block location is set to true | ||||
|      * | ||||
|      * @param block Block location to check | ||||
|      * @return true if the given block location is set to true, false if otherwise | ||||
|      */ | ||||
|     boolean isTrue(Block block); | ||||
|  | ||||
|     /** | ||||
|      * Check to see if a given BlockState location is set to true | ||||
|      * | ||||
|      * @param blockState BlockState to check | ||||
|      * @return true if the given BlockState location is set to true, false if otherwise | ||||
|      */ | ||||
|     boolean isTrue(BlockState blockState); | ||||
|  | ||||
|     /** | ||||
|      * Set a given location to true, should create stores as necessary if the location does not exist | ||||
|      * | ||||
|      * @param x X coordinate to set | ||||
|      * @param y Y coordinate to set | ||||
|      * @param z Z coordinate to set | ||||
|      * @param world World to set in | ||||
|      */ | ||||
|     void setTrue(int x, int y, int z, World world); | ||||
|  | ||||
|     /** | ||||
|      * Set a given block location to true, should create stores as necessary if the location does not exist | ||||
|      * | ||||
|      * @param block Block location to set | ||||
|      */ | ||||
|     void setTrue(Block block); | ||||
|  | ||||
|     /** | ||||
|      * Set a given BlockState location to true, should create stores as necessary if the location does not exist | ||||
|      * | ||||
|      * @param blockState BlockState location to set | ||||
|      */ | ||||
|     void setTrue(BlockState blockState); | ||||
|  | ||||
|     /** | ||||
|      * Set a given location to false, should not create stores if one does not exist for the given location | ||||
|      * | ||||
|      * @param x X coordinate to set | ||||
|      * @param y Y coordinate to set | ||||
|      * @param z Z coordinate to set | ||||
|      * @param world World to set in | ||||
|      */ | ||||
|     void setFalse(int x, int y, int z, World world); | ||||
|  | ||||
|     /** | ||||
|      * Set a given block location to false, should not create stores if one does not exist for the given location | ||||
|      * | ||||
|      * @param block Block location to set | ||||
|      */ | ||||
|     void setFalse(Block block); | ||||
|  | ||||
|     /** | ||||
|      * Set a given BlockState location to false, should not create stores if one does not exist for the given location | ||||
|      * | ||||
|      * @param blockState BlockState location to set | ||||
|      */ | ||||
|     void setFalse(BlockState blockState); | ||||
|  | ||||
|     /** | ||||
|      * Delete any ChunkletStores that are empty | ||||
|      */ | ||||
|     void cleanUp(); | ||||
|     void chunkUnloaded(int cx, int cz, @NotNull World world); | ||||
|     void unloadWorld(@NotNull World world); | ||||
| } | ||||
|   | ||||
| @@ -1,9 +1,10 @@ | ||||
| package com.gmail.nossr50.util.blockmeta; | ||||
|  | ||||
| import com.gmail.nossr50.config.HiddenConfig; | ||||
| import org.jetbrains.annotations.NotNull; | ||||
|  | ||||
| public class ChunkManagerFactory { | ||||
|     public static ChunkManager getChunkManager() { | ||||
|     public static @NotNull ChunkManager getChunkManager() { | ||||
|         HiddenConfig hConfig = HiddenConfig.getInstance(); | ||||
|  | ||||
|         if (hConfig.getChunkletsEnabled()) { | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| package com.gmail.nossr50.util.blockmeta; | ||||
|  | ||||
| import org.bukkit.World; | ||||
| import org.jetbrains.annotations.NotNull; | ||||
|  | ||||
| import java.util.UUID; | ||||
|  | ||||
| @@ -36,7 +36,7 @@ public interface ChunkStore { | ||||
|      */ | ||||
|     int getChunkZ(); | ||||
|  | ||||
|     UUID getWorldId(); | ||||
|     @NotNull UUID getWorldId(); | ||||
|  | ||||
|     /** | ||||
|      * Checks the value at the given coordinates | ||||
|   | ||||
| @@ -1,12 +1,16 @@ | ||||
| package com.gmail.nossr50.util.blockmeta; | ||||
|  | ||||
| import com.gmail.nossr50.mcMMO; | ||||
| import org.bukkit.Bukkit; | ||||
| import org.bukkit.World; | ||||
| import org.bukkit.block.Block; | ||||
| import org.bukkit.block.BlockState; | ||||
| import org.jetbrains.annotations.NotNull; | ||||
| import org.jetbrains.annotations.Nullable; | ||||
|  | ||||
| import java.io.*; | ||||
| import java.io.DataInputStream; | ||||
| import java.io.DataOutputStream; | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.util.*; | ||||
|  | ||||
| public class HashChunkManager implements ChunkManager { | ||||
| @@ -21,7 +25,10 @@ public class HashChunkManager implements ChunkManager { | ||||
|         { | ||||
|             if (!chunkStore.isDirty()) | ||||
|                 continue; | ||||
|             writeChunkStore(Bukkit.getWorld(chunkStore.getWorldId()), chunkStore); | ||||
|             World world = Bukkit.getWorld(chunkStore.getWorldId()); | ||||
|             if (world == null) | ||||
|                 continue; // Oh well | ||||
|             writeChunkStore(world, chunkStore); | ||||
|         } | ||||
|         // Clear in memory chunks | ||||
|         chunkMap.clear(); | ||||
| @@ -32,7 +39,7 @@ public class HashChunkManager implements ChunkManager { | ||||
|         regionMap.clear(); | ||||
|     } | ||||
|  | ||||
|     private synchronized ChunkStore readChunkStore(World world, int cx, int cz) throws IOException { | ||||
|     private synchronized @Nullable ChunkStore readChunkStore(@NotNull World world, int cx, int cz) throws IOException { | ||||
|         McMMOSimpleRegionFile rf = getSimpleRegionFile(world, cx, cz, false); | ||||
|         if (rf == null) | ||||
|             return null; // If there is no region file, there can't be a chunk | ||||
| @@ -43,7 +50,7 @@ public class HashChunkManager implements ChunkManager { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private synchronized void writeChunkStore(World world, ChunkStore data) { | ||||
|     private synchronized void writeChunkStore(@NotNull World world, @NotNull ChunkStore data) { | ||||
|         if (!data.isDirty()) | ||||
|             return; // Don't save unchanged data | ||||
|         try { | ||||
| @@ -58,7 +65,7 @@ public class HashChunkManager implements ChunkManager { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private synchronized McMMOSimpleRegionFile getSimpleRegionFile(World world, int cx, int cz, boolean createIfAbsent) { | ||||
|     private synchronized @Nullable McMMOSimpleRegionFile getSimpleRegionFile(World world, int cx, int cz, boolean createIfAbsent) { | ||||
|         CoordinateKey regionKey = toRegionKey(world.getUID(), cx, cz); | ||||
|  | ||||
|         return regionMap.computeIfAbsent(regionKey, k -> { | ||||
| @@ -73,7 +80,7 @@ public class HashChunkManager implements ChunkManager { | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     private ChunkStore loadChunk(int cx, int cz, World world) { | ||||
|     private @Nullable ChunkStore loadChunk(int cx, int cz, World world) { | ||||
|         try { | ||||
|             return readChunkStore(world, cx, cz); | ||||
|         } | ||||
| @@ -82,7 +89,7 @@ public class HashChunkManager implements ChunkManager { | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     private void unloadChunk(int cx, int cz, World world) { | ||||
|     private void unloadChunk(int cx, int cz, @NotNull World world) { | ||||
|         CoordinateKey chunkKey = toChunkKey(world.getUID(), cx, cz); | ||||
|         ChunkStore chunkStore = chunkMap.remove(chunkKey); // Remove from chunk map | ||||
|         if (chunkStore == null) | ||||
| @@ -102,56 +109,12 @@ public class HashChunkManager implements ChunkManager { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public synchronized void saveChunk(int cx, int cz, World world) { | ||||
|         if (world == null) | ||||
|             return; | ||||
|  | ||||
|         CoordinateKey chunkKey = toChunkKey(world.getUID(), cx, cz); | ||||
|  | ||||
|         ChunkStore out = chunkMap.get(chunkKey); | ||||
|  | ||||
|         if (out == null) | ||||
|             return; | ||||
|  | ||||
|         if (!out.isDirty()) | ||||
|             return; | ||||
|  | ||||
|         writeChunkStore(world, out); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public synchronized void chunkUnloaded(int cx, int cz, World world) { | ||||
|         if (world == null) | ||||
|             return; | ||||
|  | ||||
|     public synchronized void chunkUnloaded(int cx, int cz, @NotNull World world) { | ||||
|         unloadChunk(cx, cz, world); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public synchronized void saveWorld(World world) { | ||||
|         if (world == null) | ||||
|             return; | ||||
|  | ||||
|         UUID wID = world.getUID(); | ||||
|  | ||||
|         // Save all teh chunks | ||||
|         for (ChunkStore chunkStore : chunkMap.values()) { | ||||
|             if (!chunkStore.isDirty()) | ||||
|                 continue; | ||||
|             if (!wID.equals(chunkStore.getWorldId())) | ||||
|                 continue; | ||||
|             try { | ||||
|                 writeChunkStore(world, chunkStore); | ||||
|             } | ||||
|             catch (Exception ignore) { } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public synchronized void unloadWorld(World world) { | ||||
|         if (world == null) | ||||
|             return; | ||||
|  | ||||
|     public synchronized void unloadWorld(@NotNull World world) { | ||||
|         UUID wID = world.getUID(); | ||||
|  | ||||
|         // Save and remove all the chunks | ||||
| @@ -177,18 +140,7 @@ public class HashChunkManager implements ChunkManager { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public synchronized void saveAll() { | ||||
|         for (World world : mcMMO.p.getServer().getWorlds()) { | ||||
|             saveWorld(world); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public synchronized boolean isTrue(int x, int y, int z, World world) { | ||||
|         if (world == null) | ||||
|             return false; | ||||
|  | ||||
|     private synchronized boolean isTrue(int x, int y, int z, @NotNull World world) { | ||||
|         CoordinateKey chunkKey = blockCoordinateToChunkKey(world.getUID(), x, y, z); | ||||
|  | ||||
|         // Get chunk, load from file if necessary | ||||
| @@ -214,67 +166,36 @@ public class HashChunkManager implements ChunkManager { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public synchronized boolean isTrue(Block block) { | ||||
|         if (block == null) | ||||
|             return false; | ||||
|  | ||||
|     public synchronized boolean isTrue(@NotNull Block block) { | ||||
|         return isTrue(block.getX(), block.getY(), block.getZ(), block.getWorld()); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public synchronized boolean isTrue(BlockState blockState) { | ||||
|         if (blockState == null) | ||||
|             return false; | ||||
|  | ||||
|     public synchronized boolean isTrue(@NotNull BlockState blockState) { | ||||
|         return isTrue(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld()); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public synchronized void setTrue(int x, int y, int z, World world) { | ||||
|         set(x, y, z, world, true); | ||||
|     public synchronized void setTrue(@NotNull Block block) { | ||||
|         set(block.getX(), block.getY(), block.getZ(), block.getWorld(), true); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public synchronized void setTrue(Block block) { | ||||
|         if (block == null) | ||||
|             return; | ||||
|  | ||||
|         setTrue(block.getX(), block.getY(), block.getZ(), block.getWorld()); | ||||
|     public synchronized void setTrue(@NotNull BlockState blockState) { | ||||
|         set(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld(), true); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public synchronized void setTrue(BlockState blockState) { | ||||
|         if (blockState == null) | ||||
|             return; | ||||
|  | ||||
|         setTrue(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld()); | ||||
|     public synchronized void setFalse(@NotNull Block block) { | ||||
|         set(block.getX(), block.getY(), block.getZ(), block.getWorld(), false); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public synchronized void setFalse(int x, int y, int z, World world) { | ||||
|         set(x, y, z, world, false); | ||||
|     public synchronized void setFalse(@NotNull BlockState blockState) { | ||||
|         set(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld(), false); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public synchronized void setFalse(Block block) { | ||||
|         if (block == null) | ||||
|             return; | ||||
|  | ||||
|         setFalse(block.getX(), block.getY(), block.getZ(), block.getWorld()); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public synchronized void setFalse(BlockState blockState) { | ||||
|         if (blockState == null) | ||||
|             return; | ||||
|  | ||||
|         setFalse(blockState.getX(), blockState.getY(), blockState.getZ(), blockState.getWorld()); | ||||
|     } | ||||
|  | ||||
|     public synchronized  void set(int x, int y, int z, World world, boolean value){ | ||||
|         if (world == null) | ||||
|             return; | ||||
|  | ||||
|     private synchronized void set(int x, int y, int z, @NotNull World world, boolean value){ | ||||
|         CoordinateKey chunkKey = blockCoordinateToChunkKey(world.getUID(), x, y, z); | ||||
|  | ||||
|         // Get/Load/Create chunkstore | ||||
| @@ -307,15 +228,15 @@ public class HashChunkManager implements ChunkManager { | ||||
|         cStore.set(ix, y, iz, value); | ||||
|     } | ||||
|  | ||||
|     private CoordinateKey blockCoordinateToChunkKey(UUID worldUid, int x, int y, int z) { | ||||
|     private CoordinateKey blockCoordinateToChunkKey(@NotNull UUID worldUid, int x, int y, int z) { | ||||
|         return toChunkKey(worldUid, x >> 4, z >> 4); | ||||
|     } | ||||
|  | ||||
|     private CoordinateKey toChunkKey(UUID worldUid, int cx, int cz){ | ||||
|     private CoordinateKey toChunkKey(@NotNull UUID worldUid, int cx, int cz){ | ||||
|         return new CoordinateKey(worldUid, cx, cz); | ||||
|     } | ||||
|  | ||||
|     private CoordinateKey toRegionKey(UUID worldUid, int cx, int cz) { | ||||
|     private CoordinateKey toRegionKey(@NotNull UUID worldUid, int cx, int cz) { | ||||
|         // Compute region index (32x32 chunk regions) | ||||
|         int rx = cx >> 5; | ||||
|         int rz = cz >> 5; | ||||
| @@ -323,11 +244,11 @@ public class HashChunkManager implements ChunkManager { | ||||
|     } | ||||
|  | ||||
|     private static final class CoordinateKey { | ||||
|         public final UUID worldID; | ||||
|         public final @NotNull UUID worldID; | ||||
|         public final int x; | ||||
|         public final int z; | ||||
|  | ||||
|         private CoordinateKey(UUID worldID, int x, int z) { | ||||
|         private CoordinateKey(@NotNull UUID worldID, int x, int z) { | ||||
|             this.worldID = worldID; | ||||
|             this.x = x; | ||||
|             this.z = z; | ||||
| @@ -348,7 +269,4 @@ public class HashChunkManager implements ChunkManager { | ||||
|             return Objects.hash(worldID, x, z); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public synchronized void cleanUp() {} | ||||
| } | ||||
|   | ||||
| @@ -19,6 +19,9 @@ | ||||
|  */ | ||||
| package com.gmail.nossr50.util.blockmeta; | ||||
|  | ||||
| import org.jetbrains.annotations.NotNull; | ||||
| import org.jetbrains.annotations.Nullable; | ||||
|  | ||||
| import java.io.*; | ||||
| import java.util.BitSet; | ||||
| import java.util.zip.DeflaterOutputStream; | ||||
| @@ -54,7 +57,7 @@ public class McMMOSimpleRegionFile { | ||||
|     private final int segmentMask; | ||||
|  | ||||
|     // File location | ||||
|     private final File parent; | ||||
|     private final @NotNull File parent; | ||||
|     // File access | ||||
|     private final RandomAccessFile file; | ||||
|  | ||||
| @@ -62,7 +65,7 @@ public class McMMOSimpleRegionFile { | ||||
|     private final int rx; | ||||
|     private final int rz; | ||||
|  | ||||
|     public McMMOSimpleRegionFile(File f, int rx, int rz) { | ||||
|     public McMMOSimpleRegionFile(@NotNull File f, int rx, int rz) { | ||||
|         this.rx = rx; | ||||
|         this.rz = rz; | ||||
|         this.parent = f; | ||||
| @@ -104,7 +107,7 @@ public class McMMOSimpleRegionFile { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public synchronized DataOutputStream getOutputStream(int x, int z) { | ||||
|     public synchronized @NotNull DataOutputStream getOutputStream(int x, int z) { | ||||
|         int index = getChunkIndex(x, z); // Get chunk index | ||||
|         return new DataOutputStream(new DeflaterOutputStream(new McMMOSimpleChunkBuffer(this, index))); | ||||
|     } | ||||
| @@ -144,7 +147,7 @@ public class McMMOSimpleRegionFile { | ||||
|         file.writeInt(chunkNumBytes[index]); | ||||
|     } | ||||
|  | ||||
|     public synchronized DataInputStream getInputStream(int x, int z) throws IOException { | ||||
|     public synchronized @Nullable DataInputStream getInputStream(int x, int z) throws IOException { | ||||
|         int index = getChunkIndex(x, z); // Get chunk index | ||||
|         int byteLength = chunkNumBytes[index]; // Get byte length of data | ||||
|  | ||||
|   | ||||
| @@ -3,6 +3,7 @@ package com.gmail.nossr50.util.blockmeta; | ||||
| import org.bukkit.World; | ||||
| import org.bukkit.block.Block; | ||||
| import org.bukkit.block.BlockState; | ||||
| import org.jetbrains.annotations.NotNull; | ||||
|  | ||||
| public class NullChunkManager implements ChunkManager { | ||||
|  | ||||
| @@ -10,53 +11,30 @@ public class NullChunkManager implements ChunkManager { | ||||
|     public void closeAll() {} | ||||
|  | ||||
|     @Override | ||||
|     public void saveChunk(int cx, int cz, World world) {} | ||||
|     public void chunkUnloaded(int cx, int cz, @NotNull World world) {} | ||||
|  | ||||
|     @Override | ||||
|     public void chunkUnloaded(int cx, int cz, World world) {} | ||||
|     public void unloadWorld(@NotNull World world) {} | ||||
|  | ||||
|     @Override | ||||
|     public void saveWorld(World world) {} | ||||
|  | ||||
|     @Override | ||||
|     public void unloadWorld(World world) {} | ||||
|  | ||||
|     @Override | ||||
|     public void saveAll() {} | ||||
|  | ||||
|     @Override | ||||
|     public boolean isTrue(int x, int y, int z, World world) { | ||||
|     public boolean isTrue(@NotNull Block block) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean isTrue(Block block) { | ||||
|     public boolean isTrue(@NotNull BlockState blockState) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean isTrue(BlockState blockState) { | ||||
|         return false; | ||||
|     } | ||||
|     public void setTrue(@NotNull Block block) {} | ||||
|  | ||||
|     @Override | ||||
|     public void setTrue(int x, int y, int z, World world) {} | ||||
|     public void setTrue(@NotNull BlockState blockState) {} | ||||
|  | ||||
|     @Override | ||||
|     public void setTrue(Block block) {} | ||||
|     public void setFalse(@NotNull Block block) {} | ||||
|  | ||||
|     @Override | ||||
|     public void setTrue(BlockState blockState) {} | ||||
|  | ||||
|     @Override | ||||
|     public void setFalse(int x, int y, int z, World world) {} | ||||
|  | ||||
|     @Override | ||||
|     public void setFalse(Block block) {} | ||||
|  | ||||
|     @Override | ||||
|     public void setFalse(BlockState blockState) {} | ||||
|  | ||||
|     @Override | ||||
|     public void cleanUp() {} | ||||
|     public void setFalse(@NotNull BlockState blockState) {} | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,56 @@ | ||||
| package com.gmail.nossr50.util.blockmeta; | ||||
|  | ||||
| import com.gmail.nossr50.mcMMO; | ||||
| import org.bukkit.block.Block; | ||||
| import org.bukkit.block.BlockState; | ||||
| import org.jetbrains.annotations.NotNull; | ||||
|  | ||||
| /** | ||||
|  * Contains blockstore methods that are safe for external plugins to access. | ||||
|  * An instance can be retrieved via {@link mcMMO#getPlaceStore() mcMMO.getPlaceStore()} | ||||
|  */ | ||||
| public interface UserBlockTracker { | ||||
|     /** | ||||
|      * Check to see if a given block location is set to true | ||||
|      * | ||||
|      * @param block Block location to check | ||||
|      * @return true if the given block location is set to true, false if otherwise | ||||
|      */ | ||||
|     boolean isTrue(@NotNull Block block); | ||||
|  | ||||
|     /** | ||||
|      * Check to see if a given BlockState location is set to true | ||||
|      * | ||||
|      * @param blockState BlockState to check | ||||
|      * @return true if the given BlockState location is set to true, false if otherwise | ||||
|      */ | ||||
|     boolean isTrue(@NotNull BlockState blockState); | ||||
|  | ||||
|     /** | ||||
|      * Set a given block location to true | ||||
|      * | ||||
|      * @param block Block location to set | ||||
|      */ | ||||
|     void setTrue(@NotNull Block block); | ||||
|  | ||||
|     /** | ||||
|      * Set a given BlockState location to true | ||||
|      * | ||||
|      * @param blockState BlockState location to set | ||||
|      */ | ||||
|     void setTrue(@NotNull BlockState blockState); | ||||
|  | ||||
|     /** | ||||
|      * Set a given block location to false | ||||
|      * | ||||
|      * @param block Block location to set | ||||
|      */ | ||||
|     void setFalse(@NotNull Block block); | ||||
|  | ||||
|     /** | ||||
|      * Set a given BlockState location to false | ||||
|      * | ||||
|      * @param blockState BlockState location to set | ||||
|      */ | ||||
|     void setFalse(@NotNull BlockState blockState); | ||||
| } | ||||
| @@ -2,6 +2,8 @@ import com.gmail.nossr50.util.blockmeta.*; | ||||
| import com.google.common.io.Files; | ||||
| import org.bukkit.Bukkit; | ||||
| import org.bukkit.World; | ||||
| import org.bukkit.block.Block; | ||||
| import org.jetbrains.annotations.NotNull; | ||||
| import org.junit.*; | ||||
| import org.junit.runner.RunWith; | ||||
| import org.mockito.Mockito; | ||||
| @@ -140,9 +142,20 @@ public class ChunkStoreTest { | ||||
|     @Test | ||||
|     public void testRegressionChunkMirrorBug() { | ||||
|         ChunkManager chunkManager = new HashChunkManager(); | ||||
|         chunkManager.setTrue(15,0,15, mockWorld); | ||||
|         chunkManager.setFalse(-15, 0, -15, mockWorld); | ||||
|         Assert.assertTrue(chunkManager.isTrue(15, 0, 15, mockWorld)); | ||||
|         Block mockBlockA = mock(Block.class); | ||||
|         Mockito.when(mockBlockA.getX()).thenReturn(15); | ||||
|         Mockito.when(mockBlockA.getZ()).thenReturn(15); | ||||
|         Mockito.when(mockBlockA.getY()).thenReturn(0); | ||||
|         Mockito.when(mockBlockA.getWorld()).thenReturn(mockWorld); | ||||
|         Block mockBlockB = mock(Block.class); | ||||
|         Mockito.when(mockBlockB.getX()).thenReturn(-15); | ||||
|         Mockito.when(mockBlockB.getZ()).thenReturn(-15); | ||||
|         Mockito.when(mockBlockB.getY()).thenReturn(0); | ||||
|         Mockito.when(mockBlockB.getWorld()).thenReturn(mockWorld); | ||||
|  | ||||
|         chunkManager.setTrue(mockBlockA); | ||||
|         chunkManager.setFalse(mockBlockB); | ||||
|         Assert.assertTrue(chunkManager.isTrue(mockBlockA)); | ||||
|     } | ||||
|  | ||||
|     private interface Delegate { | ||||
| @@ -227,7 +240,7 @@ public class ChunkStoreTest { | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public UUID getWorldId() { | ||||
|         public @NotNull UUID getWorldId() { | ||||
|             return worldUid; | ||||
|         } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 nossr50
					nossr50