diff --git a/src/main/java/net/minecraft/client/Minecraft.java b/src/main/java/net/minecraft/client/Minecraft.java index 71caf67..68bd53f 100644 --- a/src/main/java/net/minecraft/client/Minecraft.java +++ b/src/main/java/net/minecraft/client/Minecraft.java @@ -9,12 +9,14 @@ import com.google.common.util.concurrent.ListenableFutureTask; import com.mojang.authlib.minecraft.MinecraftSessionService; import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; + import cpw.mods.fml.client.FMLClientHandler; import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.StartupQuery; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import io.netty.util.concurrent.GenericFutureListener; + import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; @@ -35,7 +37,9 @@ import java.util.concurrent.Callable; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; + import javax.imageio.ImageIO; + import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.client.audio.MusicTicker; @@ -58,6 +62,7 @@ import net.minecraft.client.gui.achievement.GuiAchievement; import net.minecraft.client.gui.inventory.GuiInventory; import net.minecraft.client.gui.stream.GuiStreamUnavailable; +import net.minecraft.client.multiplayer.ChunkProviderClient; import net.minecraft.client.multiplayer.GuiConnecting; import net.minecraft.client.multiplayer.PlayerControllerMP; import net.minecraft.client.multiplayer.ServerData; @@ -155,6 +160,7 @@ import net.minecraft.world.storage.ISaveFormat; import net.minecraft.world.storage.ISaveHandler; import net.minecraft.world.storage.WorldInfo; + import org.apache.commons.lang3.Validate; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -2203,6 +2209,7 @@ this.setServerData((ServerData)null); this.integratedServerIsRunning = false; FMLClientHandler.instance().handleClientWorldClosing(this.theWorld); + ((ChunkProviderClient)theWorld.getChunkProvider()).free(); } this.mcSoundHandler.stopSounds(); diff --git a/src/main/java/net/minecraft/client/multiplayer/ChunkProviderClient.java b/src/main/java/net/minecraft/client/multiplayer/ChunkProviderClient.java index 80a02df..c77b97f 100644 --- a/src/main/java/net/minecraft/client/multiplayer/ChunkProviderClient.java +++ b/src/main/java/net/minecraft/client/multiplayer/ChunkProviderClient.java @@ -45,6 +45,7 @@ if (!chunk.isEmpty()) { chunk.onChunkUnload(); + chunk.free(); } this.chunkMapping.remove(ChunkCoordIntPair.chunkXZ2Int(p_73234_1_, p_73234_2_)); @@ -53,8 +54,17 @@ public Chunk loadChunk(int p_73158_1_, int p_73158_2_) { + long key = ChunkCoordIntPair.chunkXZ2Int(p_73158_1_, p_73158_2_); + Chunk old = (Chunk)chunkMapping.getValueByKey(key); + if(old != null) + { + old.onChunkUnload(); + old.free(); + chunkMapping.remove(key); + chunkListing.remove(old); + } Chunk chunk = new Chunk(this.worldObj, p_73158_1_, p_73158_2_); - this.chunkMapping.add(ChunkCoordIntPair.chunkXZ2Int(p_73158_1_, p_73158_2_), chunk); + this.chunkMapping.add(key, chunk); this.chunkListing.add(chunk); net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.world.ChunkEvent.Load(chunk)); chunk.isChunkLoaded = true; @@ -121,4 +131,11 @@ } public void recreateStructures(int p_82695_1_, int p_82695_2_) {} + + public void free() + { + for(Object o : chunkListing) + ((Chunk)o).free(); + chunkListing.clear(); + } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/network/play/server/S21PacketChunkData.java b/src/main/java/net/minecraft/network/play/server/S21PacketChunkData.java index c9c00e4..ca2c547 100644 --- a/src/main/java/net/minecraft/network/play/server/S21PacketChunkData.java +++ b/src/main/java/net/minecraft/network/play/server/S21PacketChunkData.java @@ -179,7 +179,8 @@ p_149269_0_.sendUpdates = true; } - if(aextendedblockstorage[0] == null) aextendedblockstorage[0] = new ExtendedBlockStorage(0, true); + if(aextendedblockstorage[0] == null) + aextendedblockstorage[0] = new ExtendedBlockStorage(0, true); int l; @@ -189,7 +190,7 @@ { extracted.field_150280_b |= 1 << l; - if (aextendedblockstorage[l].getBlockMSBArray() != null) + if (true/*aextendedblockstorage[l].getBlockMSBArray() != null*/) { extracted.field_150281_c |= 1 << l; ++k; @@ -201,9 +202,8 @@ { if (aextendedblockstorage[l] != null && (l == 0 || !p_149269_1_ || !aextendedblockstorage[l].isEmpty()) && (p_149269_2_ & 1 << l) != 0) { - byte[] abyte1 = aextendedblockstorage[l].getBlockLSBArray(); - System.arraycopy(abyte1, 0, abyte, j, abyte1.length); - j += abyte1.length; + aextendedblockstorage[l].getSlot().copyLSB(abyte, j); + j += 4096; } } @@ -213,9 +213,8 @@ { if (aextendedblockstorage[l] != null && (l == 0 || !p_149269_1_ || !aextendedblockstorage[l].isEmpty()) && (p_149269_2_ & 1 << l) != 0) { - nibblearray = aextendedblockstorage[l].getMetadataArray(); - System.arraycopy(nibblearray.data, 0, abyte, j, nibblearray.data.length); - j += nibblearray.data.length; + aextendedblockstorage[l].getSlot().copyBlockMetadata(abyte, j); + j += 2048; } } @@ -223,9 +222,8 @@ { if (aextendedblockstorage[l] != null && (l == 0 || !p_149269_1_ || !aextendedblockstorage[l].isEmpty()) && (p_149269_2_ & 1 << l) != 0) { - nibblearray = aextendedblockstorage[l].getBlocklightArray(); - System.arraycopy(nibblearray.data, 0, abyte, j, nibblearray.data.length); - j += nibblearray.data.length; + aextendedblockstorage[l].getSlot().copyBlocklight(abyte, j); + j += 2048; } } @@ -235,9 +233,8 @@ { if (aextendedblockstorage[l] != null && (l == 0 || !p_149269_1_ || !aextendedblockstorage[l].isEmpty()) && (p_149269_2_ & 1 << l) != 0) { - nibblearray = aextendedblockstorage[l].getSkylightArray(); - System.arraycopy(nibblearray.data, 0, abyte, j, nibblearray.data.length); - j += nibblearray.data.length; + aextendedblockstorage[l].getSlot().copySkylight(abyte, j); + j += 2048; } } } @@ -246,11 +243,10 @@ { for (l = 0; l < aextendedblockstorage.length; ++l) { - if (aextendedblockstorage[l] != null && (l == 0 || !p_149269_1_ || !aextendedblockstorage[l].isEmpty()) && aextendedblockstorage[l].getBlockMSBArray() != null && (p_149269_2_ & 1 << l) != 0) + if (aextendedblockstorage[l] != null && (l == 0 || !p_149269_1_ || !aextendedblockstorage[l].isEmpty())/* && aextendedblockstorage[l].getBlockMSBArray() != null*/ && (p_149269_2_ & 1 << l) != 0) { - nibblearray = aextendedblockstorage[l].getBlockMSBArray(); - System.arraycopy(nibblearray.data, 0, abyte, j, nibblearray.data.length); - j += nibblearray.data.length; + aextendedblockstorage[l].getSlot().copyMSB(abyte, j); + j += 2048; } } } diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index 0652554..1f5c5f9 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -370,6 +370,7 @@ WorldServer[] tmp = worldServers; for (WorldServer world : tmp) { + world.theChunkProviderServer.free(); DimensionManager.setWorld(world.provider.dimensionId, null); } } diff --git a/src/main/java/net/minecraft/world/chunk/Chunk.java b/src/main/java/net/minecraft/world/chunk/Chunk.java index 52a738d..de039fd 100644 --- a/src/main/java/net/minecraft/world/chunk/Chunk.java +++ b/src/main/java/net/minecraft/world/chunk/Chunk.java @@ -1185,16 +1185,18 @@ { if (this.storageArrays[l] == null) { - this.storageArrays[l] = new ExtendedBlockStorage(l << 4, flag1); + this.storageArrays[l] = new ExtendedBlockStorage(l << 4, flag1, false); + if(!flag1) + this.storageArrays[l].getSlot().clearSkylight(); } - byte[] abyte1 = this.storageArrays[l].getBlockLSBArray(); - System.arraycopy(p_76607_1_, k, abyte1, 0, abyte1.length); - k += abyte1.length; + this.storageArrays[l].getSlot().setLSB(p_76607_1_, k); + k += 4096; } else if (p_76607_4_ && this.storageArrays[l] != null) { this.storageArrays[l] = null; + this.storageArrays[l].free(); } } @@ -1204,9 +1206,8 @@ { if ((p_76607_2_ & 1 << l) != 0 && this.storageArrays[l] != null) { - nibblearray = this.storageArrays[l].getMetadataArray(); - System.arraycopy(p_76607_1_, k, nibblearray.data, 0, nibblearray.data.length); - k += nibblearray.data.length; + this.storageArrays[l].getSlot().setBlockMetadata(p_76607_1_, k); + k += 2048; } } @@ -1214,9 +1215,8 @@ { if ((p_76607_2_ & 1 << l) != 0 && this.storageArrays[l] != null) { - nibblearray = this.storageArrays[l].getBlocklightArray(); - System.arraycopy(p_76607_1_, k, nibblearray.data, 0, nibblearray.data.length); - k += nibblearray.data.length; + this.storageArrays[l].getSlot().setBlocklight(p_76607_1_, k); + k += 2048; } } @@ -1226,9 +1226,8 @@ { if ((p_76607_2_ & 1 << l) != 0 && this.storageArrays[l] != null) { - nibblearray = this.storageArrays[l].getSkylightArray(); - System.arraycopy(p_76607_1_, k, nibblearray.data, 0, nibblearray.data.length); - k += nibblearray.data.length; + this.storageArrays[l].getSlot().setSkylight(p_76607_1_, k); + k += 2048; } } } @@ -1243,18 +1242,11 @@ } else { - nibblearray = this.storageArrays[l].getBlockMSBArray(); - - if (nibblearray == null) - { - nibblearray = this.storageArrays[l].createBlockMSBArray(); - } - - System.arraycopy(p_76607_1_, k, nibblearray.data, 0, nibblearray.data.length); - k += nibblearray.data.length; + this.storageArrays[l].getSlot().setMSB(p_76607_1_, k); + k += 2048; } } - else if (p_76607_4_ && this.storageArrays[l] != null && this.storageArrays[l].getBlockMSBArray() != null) + else if (p_76607_4_ && this.storageArrays[l] != null/* && this.storageArrays[l].getBlockMSBArray() != null*/) { this.storageArrays[l].clearMSBArray(); } @@ -1788,4 +1780,13 @@ return 0; } + + public void free() + { + for(ExtendedBlockStorage exbs : storageArrays) + { + if(exbs != null) + exbs.free(); + } + } } diff --git a/src/main/java/net/minecraft/world/chunk/storage/AnvilChunkLoader.java b/src/main/java/net/minecraft/world/chunk/storage/AnvilChunkLoader.java index b05508e..ffa77ff 100644 --- a/src/main/java/net/minecraft/world/chunk/storage/AnvilChunkLoader.java +++ b/src/main/java/net/minecraft/world/chunk/storage/AnvilChunkLoader.java @@ -286,23 +286,23 @@ { nbttagcompound1 = new NBTTagCompound(); nbttagcompound1.setByte("Y", (byte)(extendedblockstorage.getYLocation() >> 4 & 255)); - nbttagcompound1.setByteArray("Blocks", extendedblockstorage.getBlockLSBArray()); + nbttagcompound1.setByteArray("Blocks", extendedblockstorage.getSlot().copyLSB()); - if (extendedblockstorage.getBlockMSBArray() != null) + if (true/*extendedblockstorage.getBlockMSBArray() != null*/) { - nbttagcompound1.setByteArray("Add", extendedblockstorage.getBlockMSBArray().data); + nbttagcompound1.setByteArray("Add", extendedblockstorage.getSlot().copyMSB()); } - nbttagcompound1.setByteArray("Data", extendedblockstorage.getMetadataArray().data); - nbttagcompound1.setByteArray("BlockLight", extendedblockstorage.getBlocklightArray().data); + nbttagcompound1.setByteArray("Data", extendedblockstorage.getSlot().copyBlockMetadata()); + nbttagcompound1.setByteArray("BlockLight", extendedblockstorage.getSlot().copyBlocklight()); if (flag) { - nbttagcompound1.setByteArray("SkyLight", extendedblockstorage.getSkylightArray().data); + nbttagcompound1.setByteArray("SkyLight", extendedblockstorage.getSlot().copySkylight()); } else { - nbttagcompound1.setByteArray("SkyLight", new byte[extendedblockstorage.getBlocklightArray().data.length]); + nbttagcompound1.setByteArray("SkyLight", new byte[2048]); } nbttaglist.appendTag(nbttagcompound1); @@ -406,20 +406,28 @@ { NBTTagCompound nbttagcompound1 = nbttaglist.getCompoundTagAt(k); byte b1 = nbttagcompound1.getByte("Y"); - ExtendedBlockStorage extendedblockstorage = new ExtendedBlockStorage(b1 << 4, flag); - extendedblockstorage.setBlockLSBArray(nbttagcompound1.getByteArray("Blocks")); + ExtendedBlockStorage extendedblockstorage = new ExtendedBlockStorage(b1 << 4, flag, false); + extendedblockstorage.getSlot().setLSB(nbttagcompound1.getByteArray("Blocks")); if (nbttagcompound1.hasKey("Add", 7)) { - extendedblockstorage.setBlockMSBArray(new NibbleArray(nbttagcompound1.getByteArray("Add"), 4)); + extendedblockstorage.getSlot().setMSB(nbttagcompound1.getByteArray("Add")); + } + else + { + extendedblockstorage.getSlot().clearMSB(); } - extendedblockstorage.setBlockMetadataArray(new NibbleArray(nbttagcompound1.getByteArray("Data"), 4)); - extendedblockstorage.setBlocklightArray(new NibbleArray(nbttagcompound1.getByteArray("BlockLight"), 4)); + extendedblockstorage.getSlot().setBlockMetadata(nbttagcompound1.getByteArray("Data")); + extendedblockstorage.getSlot().setBlocklight(nbttagcompound1.getByteArray("BlockLight")); if (flag) { - extendedblockstorage.setSkylightArray(new NibbleArray(nbttagcompound1.getByteArray("SkyLight"), 4)); + extendedblockstorage.getSlot().setSkylight(nbttagcompound1.getByteArray("SkyLight")); + } + else + { + extendedblockstorage.getSlot().clearSkylight(); } extendedblockstorage.removeInvalidBlocks(); diff --git a/src/main/java/net/minecraft/world/chunk/storage/ExtendedBlockStorage.java b/src/main/java/net/minecraft/world/chunk/storage/ExtendedBlockStorage.java index d816816..7f5bec0 100644 --- a/src/main/java/net/minecraft/world/chunk/storage/ExtendedBlockStorage.java +++ b/src/main/java/net/minecraft/world/chunk/storage/ExtendedBlockStorage.java @@ -1,5 +1,9 @@ package net.minecraft.world.chunk.storage; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.ultramine.server.chunk.OffHeapChunkStorage; + import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.block.Block; @@ -11,48 +15,30 @@ private int yBase; private int blockRefCount; private int tickRefCount; - private byte[] blockLSBArray; - private NibbleArray blockMSBArray; - private NibbleArray blockMetadataArray; - private NibbleArray blocklightArray; - private NibbleArray skylightArray; + private OffHeapChunkStorage.MemSlot slot; private static final String __OBFID = "CL_00000375"; - public ExtendedBlockStorage(int p_i1997_1_, boolean p_i1997_2_) + public ExtendedBlockStorage(int p_i1997_1_, boolean p_i1997_2_, boolean clear) { this.yBase = p_i1997_1_; - this.blockLSBArray = new byte[4096]; - this.blockMetadataArray = new NibbleArray(this.blockLSBArray.length, 4); - this.blocklightArray = new NibbleArray(this.blockLSBArray.length, 4); - - if (p_i1997_2_) - { - this.skylightArray = new NibbleArray(this.blockLSBArray.length, 4); - } + this.slot = OffHeapChunkStorage.instance().allocateSlot(); + if(clear) + slot.clearAll(); + } + + public ExtendedBlockStorage(int p_i1997_1_, boolean p_i1997_2_) + { + this(p_i1997_1_, p_i1997_2_, true); } public Block getBlockByExtId(int p_150819_1_, int p_150819_2_, int p_150819_3_) { - int l = this.blockLSBArray[p_150819_2_ << 8 | p_150819_3_ << 4 | p_150819_1_] & 255; - - if (this.blockMSBArray != null) - { - l |= this.blockMSBArray.get(p_150819_1_, p_150819_2_, p_150819_3_) << 8; - } - - return Block.getBlockById(l); + return Block.getBlockById(slot.getBlockID(p_150819_1_, p_150819_2_, p_150819_3_)); } public void func_150818_a(int p_150818_1_, int p_150818_2_, int p_150818_3_, Block p_150818_4_) { - int l = this.blockLSBArray[p_150818_2_ << 8 | p_150818_3_ << 4 | p_150818_1_] & 255; - - if (this.blockMSBArray != null) - { - l |= this.blockMSBArray.get(p_150818_1_, p_150818_2_, p_150818_3_) << 8; - } - - Block block1 = Block.getBlockById(l); + Block block1 = Block.getBlockById(slot.getBlockID(p_150818_1_, p_150818_2_, p_150818_3_)); if (block1 != Blocks.air) { @@ -75,31 +61,17 @@ } int i1 = Block.getIdFromBlock(p_150818_4_); - this.blockLSBArray[p_150818_2_ << 8 | p_150818_3_ << 4 | p_150818_1_] = (byte)(i1 & 255); - - if (i1 > 255) - { - if (this.blockMSBArray == null) - { - this.blockMSBArray = new NibbleArray(this.blockLSBArray.length, 4); - } - - this.blockMSBArray.set(p_150818_1_, p_150818_2_, p_150818_3_, (i1 & 3840) >> 8); - } - else if (this.blockMSBArray != null) - { - this.blockMSBArray.set(p_150818_1_, p_150818_2_, p_150818_3_, 0); - } + slot.setBlockID(p_150818_1_, p_150818_2_, p_150818_3_, i1); } public int getExtBlockMetadata(int p_76665_1_, int p_76665_2_, int p_76665_3_) { - return this.blockMetadataArray.get(p_76665_1_, p_76665_2_, p_76665_3_); + return slot.getMeta(p_76665_1_, p_76665_2_, p_76665_3_); } public void setExtBlockMetadata(int p_76654_1_, int p_76654_2_, int p_76654_3_, int p_76654_4_) { - this.blockMetadataArray.set(p_76654_1_, p_76654_2_, p_76654_3_, p_76654_4_); + slot.setMeta(p_76654_1_, p_76654_2_, p_76654_3_, p_76654_4_); } public boolean isEmpty() @@ -119,22 +91,22 @@ public void setExtSkylightValue(int p_76657_1_, int p_76657_2_, int p_76657_3_, int p_76657_4_) { - this.skylightArray.set(p_76657_1_, p_76657_2_, p_76657_3_, p_76657_4_); + slot.setSkylight(p_76657_1_, p_76657_2_, p_76657_3_, p_76657_4_); } public int getExtSkylightValue(int p_76670_1_, int p_76670_2_, int p_76670_3_) { - return this.skylightArray.get(p_76670_1_, p_76670_2_, p_76670_3_); + return slot.getSkylight(p_76670_1_, p_76670_2_, p_76670_3_); } public void setExtBlocklightValue(int p_76677_1_, int p_76677_2_, int p_76677_3_, int p_76677_4_) { - this.blocklightArray.set(p_76677_1_, p_76677_2_, p_76677_3_, p_76677_4_); + slot.setBlocklight(p_76677_1_, p_76677_2_, p_76677_3_, p_76677_4_); } public int getExtBlocklightValue(int p_76674_1_, int p_76674_2_, int p_76674_3_) { - return this.blocklightArray.get(p_76674_1_, p_76674_2_, p_76674_3_); + return slot.getBlocklight(p_76674_1_, p_76674_2_, p_76674_3_); } public void removeInvalidBlocks() @@ -164,66 +136,123 @@ } } + @Deprecated public byte[] getBlockLSBArray() { - return this.blockLSBArray; + logDeprecation(); + return slot.copyLSB(); } @SideOnly(Side.CLIENT) public void clearMSBArray() { - this.blockMSBArray = null; + slot.clearMSB(); } + @Deprecated public NibbleArray getBlockMSBArray() { - return this.blockMSBArray; + logDeprecation(); + return new NibbleArray(slot.copyMSB(), 4); } + @Deprecated public NibbleArray getMetadataArray() { - return this.blockMetadataArray; + logDeprecation(); + return new NibbleArray(slot.copyBlockMetadata(), 4); } + @Deprecated public NibbleArray getBlocklightArray() { - return this.blocklightArray; + logDeprecation(); + return new NibbleArray(slot.copyBlocklight(), 4); } + @Deprecated public NibbleArray getSkylightArray() { - return this.skylightArray; + logDeprecation(); + return new NibbleArray(slot.copySkylight(), 4); } + @Deprecated public void setBlockLSBArray(byte[] p_76664_1_) { - this.blockLSBArray = p_76664_1_; + logDeprecation(); + slot.setLSB(p_76664_1_); } + @Deprecated public void setBlockMSBArray(NibbleArray p_76673_1_) { - this.blockMSBArray = p_76673_1_; + logDeprecation(); + slot.setMSB(p_76673_1_.data); } + @Deprecated public void setBlockMetadataArray(NibbleArray p_76668_1_) { - this.blockMetadataArray = p_76668_1_; + logDeprecation(); + slot.setBlockMetadata(p_76668_1_.data); } + @Deprecated public void setBlocklightArray(NibbleArray p_76659_1_) { - this.blocklightArray = p_76659_1_; + logDeprecation(); + slot.setBlocklight(p_76659_1_.data); } + @Deprecated public void setSkylightArray(NibbleArray p_76666_1_) { - this.skylightArray = p_76666_1_; + logDeprecation(); + slot.setSkylight(p_76666_1_.data); } + @Deprecated @SideOnly(Side.CLIENT) public NibbleArray createBlockMSBArray() { - this.blockMSBArray = new NibbleArray(this.blockLSBArray.length, 4); - return this.blockMSBArray; + logDeprecation(); + slot.clearMSB(); + return getBlockMSBArray(); + } + + private static final Logger log = LogManager.getLogger(); + + private static void logDeprecation() + { + log.warn("Called deprecated method in ExtendedBlockStorage. It may have no effect intended by the modder or lead to performance issues", new Throwable()); + } + + public OffHeapChunkStorage.MemSlot getSlot() + { + return slot; + } + + public void free() + { + slot.free(); + slot = null; + } + + @Override + protected void finalize() + { + try + { + if(slot != null) + { + slot.free(); + slot = null; + } + } + catch(Throwable t) + { + t.printStackTrace(); + } } } \ No newline at end of file diff --git a/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java b/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java index 824dfc9..c3000f9 100644 --- a/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/world/gen/ChunkProviderServer.java @@ -410,6 +410,7 @@ } this.safeSaveExtraChunkData(chunk); this.loadedChunkHashMap.remove(hash); + chunk.free(); } } @@ -636,6 +637,7 @@ safeSaveChunk(chunk); else MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Save(chunk, new NBTTagCompound())); //CodeChickenLib memory leak fix + chunk.free(); } loadedChunkHashMap.clear(); @@ -645,6 +647,14 @@ ((AnvilChunkLoader)currentChunkLoader).unsafeRemoveAll(); } + public void free() + { + for(Chunk chunk : loadedChunkHashMap.valueCollection()) + chunk.free(); + loadedChunkHashMap.clear(); + setWorldUnloaded(); + } + public boolean isGenerating() { return isGenerating; diff --git a/src/main/java/org/ultramine/commands/basic/TechCommands.java b/src/main/java/org/ultramine/commands/basic/TechCommands.java index 8d65309..d1a53fc 100644 --- a/src/main/java/org/ultramine/commands/basic/TechCommands.java +++ b/src/main/java/org/ultramine/commands/basic/TechCommands.java @@ -36,6 +36,7 @@ import org.ultramine.server.WorldsConfig.WorldConfig.Border; import org.ultramine.server.chunk.ChunkProfiler; import org.ultramine.server.chunk.IChunkLoadCallback; +import org.ultramine.server.chunk.OffHeapChunkStorage; import org.ultramine.server.util.BasicTypeParser; import org.ultramine.server.world.MultiWorld; import org.ultramine.server.world.WorldDescriptor; @@ -147,6 +148,8 @@ ctx.sendMessage("Heap max: %sm", Runtime.getRuntime().maxMemory() >> 20); ctx.sendMessage("Heap total: %sm", Runtime.getRuntime().totalMemory() >> 20); ctx.sendMessage("Heap free: %sm", Runtime.getRuntime().freeMemory() >> 20); + ctx.sendMessage("Off-Heap chunk total: %sm", OffHeapChunkStorage.instance().getTotalMemory() >> 20); + ctx.sendMessage("Off-Heap chunk used: %sm", OffHeapChunkStorage.instance().getUsedMemory() >> 20); } @Command( diff --git a/src/main/java/org/ultramine/server/chunk/OffHeapChunkStorage.java b/src/main/java/org/ultramine/server/chunk/OffHeapChunkStorage.java new file mode 100644 index 0000000..968cb54 --- /dev/null +++ b/src/main/java/org/ultramine/server/chunk/OffHeapChunkStorage.java @@ -0,0 +1,367 @@ +package org.ultramine.server.chunk; + +import java.util.ArrayList; +import java.util.List; + +import org.ultramine.server.util.UnsafeUtil; + +import cpw.mods.fml.common.FMLCommonHandler; +import sun.misc.Unsafe; + +public class OffHeapChunkStorage +{ + private static final boolean IS_CLIENT = FMLCommonHandler.instance().getSide().isClient(); + private static final Unsafe U = UnsafeUtil.getUnsafe(); + private static final long BYTE_ARRAY_OFFSET = U.arrayBaseOffset(byte[].class); + private static final int SLOT_SIZE = 4096*3; + private static final int SLOTS_PER_CHUNK = IS_CLIENT ? 8*512 : 8*2048; + private static final int SLOT_LIMIT = ((int)Math.round(Integer.parseInt(System.getProperty("org.ultramine.server.offheapchunk.memlimit", "6"))*5.3333333333d))*(8*2048); + + private int counter = -1; + private final List freeSlots = new ArrayList(); + + private MemChunk chunk; + private int slots; + + private static final OffHeapChunkStorage INSTANCE = new OffHeapChunkStorage(); + public static OffHeapChunkStorage instance() + { + return INSTANCE; + } + + private OffHeapChunkStorage() + { + slots = SLOTS_PER_CHUNK; + chunk = new MemChunk(); + } + + public synchronized MemSlot allocateSlot() + { + if(freeSlots.size() != 0) + { + return freeSlots.remove(freeSlots.size()-1); + } + else + { + if(++counter == slots) + { + if(counter >= SLOT_LIMIT) + throw new OutOfMemoryError("Off-heap chunk storage"); + slots += SLOTS_PER_CHUNK; + chunk = new MemChunk(); + } + + return new MemSlot(chunk.nextSlot()); + } + } + + public long getTotalMemory() + { + return slots*SLOT_SIZE; + } + + public long getUsedMemory() + { + return (counter-freeSlots.size())*SLOT_SIZE; + } + + private static class MemChunk + { + private int counter; + private long pointer; + + public MemChunk() + { + pointer = U.allocateMemory(SLOT_SIZE * SLOTS_PER_CHUNK); + } + + public long nextSlot() + { + return pointer + (counter++)*SLOT_SIZE; + } + } + + //LSB + //MSB#META + //BLOCK#SKY + public static class MemSlot + { + private static final int OFFSET_LSB = 0; + private static final int OFFSET_MSB = 4096; + private static final int OFFSET_META = 4096+2048; + private static final int OFFSET_BLOCK = 4096+2048+2048; + private static final int OFFSET_SKY = 4096+2048+2048+2048; + + private final long pointer; + + private MemSlot(long pointer) + { + this.pointer = pointer; + } + + private void setByte(int ind, byte data) + { + U.putByte(pointer + ind, data); + } + + private byte getByte(int ind) + { + return U.getByte(pointer + ind); + } + + //raw set + + public void setLSB(byte[] arr) + { + if(arr == null || arr.length != 4096) throw new IllegalArgumentException(); + U.copyMemory(arr, BYTE_ARRAY_OFFSET, null, pointer, 4096); + } + + public void setLSB(byte[] arr, int start) + { + if(arr == null || arr.length - start < 4096) throw new IllegalArgumentException(); + U.copyMemory(arr, BYTE_ARRAY_OFFSET + start, null, pointer, 4096); + } + + public void setMSB(byte[] arr) + { + if(arr == null || arr.length != 2048) throw new IllegalArgumentException(); + U.copyMemory(arr, BYTE_ARRAY_OFFSET, null, pointer + OFFSET_MSB, 2048); + } + + public void setMSB(byte[] arr, int start) + { + if(arr == null || arr.length - start < 2048) throw new IllegalArgumentException(); + U.copyMemory(arr, BYTE_ARRAY_OFFSET + start, null, pointer + OFFSET_MSB, 2048); + } + + public void setBlockMetadata(byte[] arr) + { + if(arr == null || arr.length != 2048) throw new IllegalArgumentException(); + U.copyMemory(arr, BYTE_ARRAY_OFFSET, null, pointer + OFFSET_META, 2048); + } + + public void setBlockMetadata(byte[] arr, int start) + { + if(arr == null || arr.length - start < 2048) throw new IllegalArgumentException(); + U.copyMemory(arr, BYTE_ARRAY_OFFSET + start, null, pointer + OFFSET_META, 2048); + } + + public void setBlocklight(byte[] arr) + { + if(arr == null || arr.length != 2048) throw new IllegalArgumentException(); + U.copyMemory(arr, BYTE_ARRAY_OFFSET, null, pointer + OFFSET_BLOCK, 2048); + } + + public void setBlocklight(byte[] arr, int start) + { + if(arr == null || arr.length - start < 2048) throw new IllegalArgumentException(); + U.copyMemory(arr, BYTE_ARRAY_OFFSET + start, null, pointer + OFFSET_BLOCK, 2048); + } + + public void setSkylight(byte[] arr) + { + if(arr == null || arr.length != 2048) throw new IllegalArgumentException(); + U.copyMemory(arr, BYTE_ARRAY_OFFSET, null, pointer + OFFSET_SKY, 2048); + } + + public void setSkylight(byte[] arr, int start) + { + if(arr == null || arr.length - start < 2048) throw new IllegalArgumentException(); + U.copyMemory(arr, BYTE_ARRAY_OFFSET + start, null, pointer + OFFSET_SKY, 2048); + } + + //raw copy + + public void copyLSB(byte[] arr) + { + if(arr == null || arr.length != 4096) throw new IllegalArgumentException(); + U.copyMemory(null, pointer, arr, BYTE_ARRAY_OFFSET, 4096); + } + + public void copyLSB(byte[] arr, int start) + { + if(arr == null || arr.length - start < 4096) throw new IllegalArgumentException(); + U.copyMemory(null, pointer, arr, BYTE_ARRAY_OFFSET + start, 4096); + } + + public void copyMSB(byte[] arr) + { + if(arr == null || arr.length != 2048) throw new IllegalArgumentException(); + U.copyMemory(null, pointer + OFFSET_MSB, arr, BYTE_ARRAY_OFFSET, 2048); + } + + public void copyMSB(byte[] arr, int start) + { + if(arr == null || arr.length - start < 2048) throw new IllegalArgumentException(); + U.copyMemory(null, pointer + OFFSET_MSB, arr, BYTE_ARRAY_OFFSET + start, 2048); + } + + public void copyBlockMetadata(byte[] arr) + { + if(arr == null || arr.length != 2048) throw new IllegalArgumentException(); + U.copyMemory(null, pointer + OFFSET_META, arr, BYTE_ARRAY_OFFSET, 2048); + } + + public void copyBlockMetadata(byte[] arr, int start) + { + if(arr == null || arr.length - start < 2048) throw new IllegalArgumentException(); + U.copyMemory(null, pointer + OFFSET_META, arr, BYTE_ARRAY_OFFSET + start, 2048); + } + + public void copyBlocklight(byte[] arr) + { + if(arr == null || arr.length != 2048) throw new IllegalArgumentException(); + U.copyMemory(null, pointer + OFFSET_BLOCK, arr, BYTE_ARRAY_OFFSET, 2048); + } + + public void copyBlocklight(byte[] arr, int start) + { + if(arr == null || arr.length - start < 2048) throw new IllegalArgumentException(); + U.copyMemory(null, pointer + OFFSET_BLOCK, arr, BYTE_ARRAY_OFFSET + start, 2048); + } + + public void copySkylight(byte[] arr) + { + if(arr == null || arr.length != 2048) throw new IllegalArgumentException(); + U.copyMemory(null, pointer + OFFSET_SKY, arr, BYTE_ARRAY_OFFSET, 2048); + } + + public void copySkylight(byte[] arr, int start) + { + if(arr == null || arr.length - start < 2048) throw new IllegalArgumentException(); + U.copyMemory(null, pointer + OFFSET_SKY, arr, BYTE_ARRAY_OFFSET + start, 2048); + } + + //array copy + + public byte[] copyLSB() + { + byte[] arr = new byte[4096]; + copyLSB(arr); + return arr; + } + + public byte[] copyMSB() + { + byte[] arr = new byte[2048]; + copyMSB(arr); + return arr; + } + + public byte[] copyBlockMetadata() + { + byte[] arr = new byte[2048]; + copyBlockMetadata(arr); + return arr; + } + + public byte[] copyBlocklight() + { + byte[] arr = new byte[2048]; + copyBlocklight(arr); + return arr; + } + + public byte[] copySkylight() + { + byte[] arr = new byte[2048]; + copySkylight(arr); + return arr; + } + + //clear + + public void clearMSB() + { + U.setMemory(pointer + OFFSET_MSB, 2048, (byte)0); + } + + public void clearSkylight() + { + U.setMemory(pointer + OFFSET_SKY, 2048, (byte)0); + } + + public void clearAll() + { + U.setMemory(pointer, SLOT_SIZE, (byte)0); + } + + // + + private int get4bits(int start, int x, int y, int z) + { + int l = y << 8 | z << 4 | x; + int i1 = l >> 1; + int j1 = l & 1; + return j1 == 0 ? getByte(start+i1) & 15 : getByte(start+i1) >> 4 & 15; + } + + private void set4bits(int start, int x, int y, int z, int data) + { + int i1 = y << 8 | z << 4 | x; + int j1 = i1 >> 1; + int k1 = i1 & 1; + + int off = start+j1; + if (k1 == 0) + { + setByte(off, (byte)(getByte(off) & 240 | data & 15)); + } + else + { + setByte(off, (byte)(getByte(off) & 15 | (data & 15) << 4)); + } + } + + public int getBlockID(int x, int y, int z) + { + return (getByte(y << 8 | z << 4 | x) & 255) | (get4bits(OFFSET_MSB, x, y, z) << 8); + } + + public void setBlockID(int x, int y, int z, int id) + { + setByte(y << 8 | z << 4 | x, (byte)(id & 0xFF)); + set4bits(OFFSET_MSB, x, y, z, (id & 3840) >> 8); + } + + public int getMeta(int x, int y, int z) + { + return get4bits(OFFSET_META, x, y, z); + } + + public void setMeta(int x, int y, int z, int meta) + { + set4bits(OFFSET_META, x, y, z, meta); + } + + public int getBlocklight(int x, int y, int z) + { + return get4bits(OFFSET_BLOCK, x, y, z); + } + + public void setBlocklight(int x, int y, int z, int val) + { + set4bits(OFFSET_BLOCK, x, y, z, val); + } + + public int getSkylight(int x, int y, int z) + { + return get4bits(OFFSET_SKY, x, y, z); + } + + public void setSkylight(int x, int y, int z, int val) + { + set4bits(OFFSET_SKY, x, y, z, val); + } + + public void free() + { + OffHeapChunkStorage inst = OffHeapChunkStorage.instance(); + synchronized(inst) + { + inst.freeSlots.add(this); + } + } + } +} diff --git a/src/main/java/org/ultramine/server/util/UnsafeUtil.java b/src/main/java/org/ultramine/server/util/UnsafeUtil.java new file mode 100644 index 0000000..2a3b612 --- /dev/null +++ b/src/main/java/org/ultramine/server/util/UnsafeUtil.java @@ -0,0 +1,29 @@ +package org.ultramine.server.util; + +import java.lang.reflect.Field; + +import sun.misc.Unsafe; + +public class UnsafeUtil +{ + private static final Unsafe UNSAFE = createUnsafe(); + + public static Unsafe getUnsafe() + { + return UNSAFE; + } + + private static Unsafe createUnsafe() + { + try + { + Field uf = Unsafe.class.getDeclaredField("theUnsafe"); + uf.setAccessible(true); + return (Unsafe) uf.get(null); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/org/ultramine/server/world/WorldDescriptor.java b/src/main/java/org/ultramine/server/world/WorldDescriptor.java index c599231..f1a66ba 100644 --- a/src/main/java/org/ultramine/server/world/WorldDescriptor.java +++ b/src/main/java/org/ultramine/server/world/WorldDescriptor.java @@ -320,7 +320,7 @@ MinecraftForge.EVENT_BUS.post(new WorldEvent.Unload(world)); DimensionManager.setWorld(world.provider.dimensionId, null); - world.theChunkProviderServer.loadedChunkHashMap.clear(); + world.theChunkProviderServer.free(); for(Object o : world.loadedTileEntityList) ((TileEntity)o).setWorldObj(null); world.loadedTileEntityList.clear();