diff --git a/patches/minecraft/net/minecraft/server/MinecraftServer.java.patch b/patches/minecraft/net/minecraft/server/MinecraftServer.java.patch index 2d72a40a28..599ac8f8ed 100644 --- a/patches/minecraft/net/minecraft/server/MinecraftServer.java.patch +++ b/patches/minecraft/net/minecraft/server/MinecraftServer.java.patch @@ -126,8 +126,11 @@ if (this.field_71307_n.func_76468_d()) { this.field_71307_n.func_76470_e(); -@@ -592,13 +579,15 @@ +@@ -590,15 +577,18 @@ + public void func_71190_q() + { this.field_71304_b.func_76320_a("levels"); ++ net.minecraftforge.common.chunkio.ChunkIOExecutor.tick(); int i; - for (i = 0; i < this.field_71305_c.length; ++i) @@ -145,7 +148,7 @@ this.field_71304_b.func_76320_a(worldserver.func_72912_H().func_76065_j()); this.field_71304_b.func_76320_a("pools"); worldserver.func_82732_R().func_72343_a(); -@@ -645,9 +634,11 @@ +@@ -645,9 +635,11 @@ this.field_71304_b.func_76319_b(); } @@ -158,7 +161,7 @@ this.field_71304_b.func_76318_c("connection"); this.func_147137_ag().func_151269_c(); this.field_71304_b.func_76318_c("players"); -@@ -692,7 +683,13 @@ +@@ -692,7 +684,13 @@ public WorldServer func_71218_a(int p_71218_1_) { @@ -173,7 +176,7 @@ } public String func_71249_w() -@@ -951,6 +948,7 @@ +@@ -951,6 +949,7 @@ if (worldserver != null) { @@ -181,7 +184,7 @@ worldserver.func_73041_k(); } } -@@ -1421,7 +1419,6 @@ +@@ -1421,7 +1420,6 @@ this.field_147141_M = p_155759_1_; } diff --git a/patches/minecraft/net/minecraft/server/management/PlayerManager.java.patch b/patches/minecraft/net/minecraft/server/management/PlayerManager.java.patch index adb6ee9e7c..81d3dd1d5f 100644 --- a/patches/minecraft/net/minecraft/server/management/PlayerManager.java.patch +++ b/patches/minecraft/net/minecraft/server/management/PlayerManager.java.patch @@ -1,24 +1,176 @@ --- ../src-base/minecraft/net/minecraft/server/management/PlayerManager.java +++ ../src-work/minecraft/net/minecraft/server/management/PlayerManager.java -@@ -1,6 +1,7 @@ +@@ -1,7 +1,11 @@ package net.minecraft.server.management; import java.util.ArrayList; +import java.util.Arrays; ++import java.util.Collections; ++import java.util.HashMap; import java.util.List; ++ import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.network.Packet; -@@ -13,6 +14,9 @@ + import net.minecraft.network.play.server.S21PacketChunkData; +@@ -13,6 +17,11 @@ import net.minecraft.world.WorldProvider; import net.minecraft.world.WorldServer; import net.minecraft.world.chunk.Chunk; +import net.minecraftforge.common.ForgeModContainer; +import net.minecraftforge.common.MinecraftForge; ++import net.minecraftforge.common.chunkio.ChunkIOExecutor; ++import net.minecraftforge.common.util.ChunkCoordComparator; +import net.minecraftforge.event.world.ChunkWatchEvent; public class PlayerManager { -@@ -320,6 +324,8 @@ +@@ -120,15 +129,24 @@ + int j = (int)p_72683_1_.field_70161_v >> 4; + p_72683_1_.field_71131_d = p_72683_1_.field_70165_t; + p_72683_1_.field_71132_e = p_72683_1_.field_70161_v; ++ // Load nearby chunks first ++ List chunkList = new ArrayList(); + + for (int k = i - this.field_72698_e; k <= i + this.field_72698_e; ++k) + { + for (int l = j - this.field_72698_e; l <= j + this.field_72698_e; ++l) + { +- this.func_72690_a(k, l, true).func_73255_a(p_72683_1_); ++ chunkList.add(new ChunkCoordIntPair(k, l)); + } + } + ++ Collections.sort(chunkList, new ChunkCoordComparator(p_72683_1_)); ++ ++ for (ChunkCoordIntPair pair : chunkList) ++ { ++ this.func_72690_a(pair.field_77276_a, pair.field_77275_b, true).func_73255_a(p_72683_1_); ++ } ++ + this.field_72699_b.add(p_72683_1_); + this.func_72691_b(p_72683_1_); + } +@@ -230,6 +248,7 @@ + int i1 = this.field_72698_e; + int j1 = i - k; + int k1 = j - l; ++ List chunksToLoad = new ArrayList(); + + if (j1 != 0 || k1 != 0) + { +@@ -239,7 +258,7 @@ + { + if (!this.func_72684_a(l1, i2, k, l, i1)) + { +- this.func_72690_a(l1, i2, true).func_73255_a(p_72685_1_); ++ chunksToLoad.add(new ChunkCoordIntPair(l1, i2)); + } + + if (!this.func_72684_a(l1 - j1, i2 - k1, i, j, i1)) +@@ -257,6 +276,18 @@ + this.func_72691_b(p_72685_1_); + p_72685_1_.field_71131_d = p_72685_1_.field_70165_t; + p_72685_1_.field_71132_e = p_72685_1_.field_70161_v; ++ // send nearest chunks first ++ Collections.sort(chunksToLoad, new ChunkCoordComparator(p_72685_1_)); ++ ++ for (ChunkCoordIntPair pair : chunksToLoad) ++ { ++ this.func_72690_a(pair.field_77276_a, pair.field_77275_b, true).func_73255_a(p_72685_1_); ++ } ++ ++ if (i1 > 1 || i1 < -1 || j1 > 1 || j1 < -1) ++ { ++ Collections.sort(p_72685_1_.field_71129_f, new ChunkCoordComparator(p_72685_1_)); ++ } + } + } + } +@@ -280,15 +311,24 @@ + private int field_73262_e; + private int field_73260_f; + private long field_111198_g; ++ private final HashMap players = new HashMap(); ++ private boolean loaded = false; ++ private Runnable loadedRunnable = new Runnable() ++ { ++ public void run() ++ { ++ PlayerInstance.this.loaded = true; ++ } ++ }; + private static final String __OBFID = "CL_00001435"; + + public PlayerInstance(int p_i1518_2_, int p_i1518_3_) + { + this.field_73264_c = new ChunkCoordIntPair(p_i1518_2_, p_i1518_3_); +- PlayerManager.this.func_72688_a().field_73059_b.func_73158_c(p_i1518_2_, p_i1518_3_); ++ PlayerManager.this.field_72701_a.field_73059_b.loadChunk(p_i1518_2_, p_i1518_3_, this.loadedRunnable); + } + +- public void func_73255_a(EntityPlayerMP p_73255_1_) ++ public void func_73255_a(final EntityPlayerMP p_73255_1_) + { + if (this.field_73263_b.contains(p_73255_1_)) + { +@@ -302,7 +342,26 @@ + } + + this.field_73263_b.add(p_73255_1_); +- p_73255_1_.field_71129_f.add(this.field_73264_c); ++ Runnable playerRunnable; ++ ++ if (this.loaded) ++ { ++ playerRunnable = null; ++ p_73255_1_.field_71129_f.add(this.field_73264_c); ++ } ++ else ++ { ++ playerRunnable = new Runnable() ++ { ++ public void run() ++ { ++ p_73255_1_.field_71129_f.add(PlayerInstance.this.field_73264_c); ++ } ++ }; ++ PlayerManager.this.func_72688_a().field_73059_b.loadChunk(this.field_73264_c.field_77276_a, this.field_73264_c.field_77275_b, playerRunnable); ++ } ++ ++ this.players.put(p_73255_1_, playerRunnable); + } + } + +@@ -310,6 +369,24 @@ + { + if (this.field_73263_b.contains(p_73252_1_)) + { ++ // If we haven't loaded yet don't load the chunk just so we can clean it up ++ if (!this.loaded) ++ { ++ ChunkIOExecutor.dropQueuedChunkLoad(PlayerManager.this.func_72688_a(), this.field_73264_c.field_77276_a, this.field_73264_c.field_77275_b, this.players.get(p_73252_1_)); ++ this.field_73263_b.remove(p_73252_1_); ++ this.players.remove(p_73252_1_); ++ ++ if (this.field_73263_b.isEmpty()) ++ { ++ ChunkIOExecutor.dropQueuedChunkLoad(PlayerManager.this.func_72688_a(), this.field_73264_c.field_77276_a, this.field_73264_c.field_77275_b, this.loadedRunnable); ++ long i = (long) this.field_73264_c.field_77276_a + 2147483647L | (long) this.field_73264_c.field_77275_b + 2147483647L << 32; ++ PlayerManager.this.field_72700_c.func_76159_d(i); ++ PlayerManager.this.field_111193_e.remove(this); ++ } ++ ++ return; ++ } ++ + Chunk chunk = PlayerManager.this.field_72701_a.func_72964_e(this.field_73264_c.field_77276_a, this.field_73264_c.field_77275_b); + + if (chunk.func_150802_k()) +@@ -317,9 +394,12 @@ + p_73252_1_.field_71135_a.func_147359_a(new S21PacketChunkData(chunk, true, 0)); + } + ++ this.players.remove(p_73252_1_); this.field_73263_b.remove(p_73252_1_); p_73252_1_.field_71129_f.remove(this.field_73264_c); @@ -27,7 +179,7 @@ if (this.field_73263_b.isEmpty()) { long i = (long)this.field_73264_c.field_77276_a + 2147483647L | (long)this.field_73264_c.field_77275_b + 2147483647L << 32; -@@ -357,7 +363,7 @@ +@@ -357,7 +437,7 @@ this.field_73260_f |= 1 << (p_151253_2_ >> 4); @@ -36,7 +188,7 @@ { short short1 = (short)(p_151253_1_ << 12 | p_151253_3_ << 8 | p_151253_2_); -@@ -369,6 +375,10 @@ +@@ -369,6 +449,10 @@ } } @@ -47,7 +199,7 @@ this.field_151254_d[this.field_73262_e++] = short1; } } -@@ -401,7 +411,7 @@ +@@ -401,7 +485,7 @@ k = this.field_73264_c.field_77275_b * 16 + (this.field_151254_d[0] >> 8 & 15); this.func_151251_a(new S23PacketBlockChange(i, j, k, PlayerManager.this.field_72701_a)); @@ -56,7 +208,7 @@ { this.func_151252_a(PlayerManager.this.field_72701_a.func_147438_o(i, j, k)); } -@@ -410,13 +420,14 @@ +@@ -410,13 +494,14 @@ { int l; @@ -73,7 +225,7 @@ { if ((this.field_73260_f & 1 << k) != 0) { -@@ -433,14 +444,17 @@ +@@ -433,14 +518,17 @@ else { this.func_151251_a(new S22PacketMultiBlockChange(this.field_73262_e, this.field_151254_d, PlayerManager.this.field_72701_a.func_72964_e(this.field_73264_c.field_77276_a, this.field_73264_c.field_77275_b))); diff --git a/patches/minecraft/net/minecraft/server/management/ServerConfigurationManager.java.patch b/patches/minecraft/net/minecraft/server/management/ServerConfigurationManager.java.patch index 24a08810ff..2d171ebdb8 100644 --- a/patches/minecraft/net/minecraft/server/management/ServerConfigurationManager.java.patch +++ b/patches/minecraft/net/minecraft/server/management/ServerConfigurationManager.java.patch @@ -1,6 +1,22 @@ --- ../src-base/minecraft/net/minecraft/server/management/ServerConfigurationManager.java +++ ../src-work/minecraft/net/minecraft/server/management/ServerConfigurationManager.java -@@ -54,7 +54,9 @@ +@@ -7,6 +7,7 @@ + import cpw.mods.fml.common.FMLCommonHandler; + import cpw.mods.fml.relauncher.Side; + import cpw.mods.fml.relauncher.SideOnly; ++ + import java.io.File; + import java.net.SocketAddress; + import java.text.SimpleDateFormat; +@@ -18,6 +19,7 @@ + import java.util.Map; + import java.util.Set; + import java.util.Map.Entry; ++ + import net.minecraft.entity.Entity; + import net.minecraft.entity.EntityList; + import net.minecraft.entity.player.EntityPlayer; +@@ -54,11 +56,15 @@ import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.IChatComponent; import net.minecraft.util.MathHelper; @@ -10,7 +26,29 @@ import net.minecraft.world.WorldServer; import net.minecraft.world.WorldSettings; import net.minecraft.world.demo.DemoWorldManager; -@@ -361,13 +363,23 @@ + import net.minecraft.world.storage.IPlayerFileData; ++import net.minecraftforge.common.chunkio.ChunkIOExecutor; ++ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + +@@ -244,6 +250,7 @@ + this.func_148540_a(new S38PacketPlayerListItem(p_72377_1_.func_70005_c_(), true, 1000)); + this.field_72404_b.add(p_72377_1_); + WorldServer worldserver = this.field_72400_f.func_71218_a(p_72377_1_.field_71093_bK); ++ ChunkIOExecutor.adjustPoolSize(this.func_72394_k()); + worldserver.func_72838_d(p_72377_1_); + this.func_72375_a(p_72377_1_, (WorldServer)null); + +@@ -276,6 +283,7 @@ + worldserver.func_73040_p().func_72695_c(p_72367_1_); + this.field_72404_b.remove(p_72367_1_); + this.field_148547_k.remove(p_72367_1_.func_70005_c_()); ++ ChunkIOExecutor.adjustPoolSize(this.func_72394_k()); + this.func_148540_a(new S38PacketPlayerListItem(p_72367_1_.func_70005_c_(), false, 9999)); + } + +@@ -361,13 +369,23 @@ public EntityPlayerMP func_72368_a(EntityPlayerMP p_72368_1_, int p_72368_2_, boolean p_72368_3_) { @@ -36,7 +74,7 @@ p_72368_1_.field_71093_bK = p_72368_2_; Object object; -@@ -383,6 +395,7 @@ +@@ -383,6 +401,7 @@ EntityPlayerMP entityplayermp1 = new EntityPlayerMP(this.field_72400_f, this.field_72400_f.func_71218_a(p_72368_1_.field_71093_bK), p_72368_1_.func_146103_bH(), (ItemInWorldManager)object); entityplayermp1.field_71135_a = p_72368_1_.field_71135_a; entityplayermp1.func_71049_a(p_72368_1_, p_72368_3_); @@ -44,7 +82,7 @@ entityplayermp1.func_145769_d(p_72368_1_.func_145782_y()); WorldServer worldserver = this.field_72400_f.func_71218_a(p_72368_1_.field_71093_bK); this.func_72381_a(entityplayermp1, p_72368_1_, worldserver); -@@ -427,6 +440,11 @@ +@@ -427,6 +446,11 @@ public void func_72356_a(EntityPlayerMP p_72356_1_, int p_72356_2_) { @@ -56,7 +94,7 @@ int j = p_72356_1_.field_71093_bK; WorldServer worldserver = this.field_72400_f.func_71218_a(p_72356_1_.field_71093_bK); p_72356_1_.field_71093_bK = p_72356_2_; -@@ -434,7 +452,7 @@ +@@ -434,7 +458,7 @@ p_72356_1_.field_71135_a.func_147359_a(new S07PacketRespawn(p_72356_1_.field_71093_bK, p_72356_1_.field_70170_p.field_73013_u, p_72356_1_.field_70170_p.func_72912_H().func_76067_t(), p_72356_1_.field_71134_c.func_73081_b())); worldserver.func_72973_f(p_72356_1_); p_72356_1_.field_70128_L = false; @@ -65,7 +103,7 @@ this.func_72375_a(p_72356_1_, worldserver); p_72356_1_.field_71135_a.func_147364_a(p_72356_1_.field_70165_t, p_72356_1_.field_70163_u, p_72356_1_.field_70161_v, p_72356_1_.field_70177_z, p_72356_1_.field_70125_A); p_72356_1_.field_71134_c.func_73080_a(worldserver1); -@@ -452,38 +470,47 @@ +@@ -452,38 +476,47 @@ public void func_82448_a(Entity p_82448_1_, int p_82448_2_, WorldServer p_82448_3_, WorldServer p_82448_4_) { @@ -125,7 +163,7 @@ { ChunkCoordinates chunkcoordinates; -@@ -518,7 +545,7 @@ +@@ -518,7 +551,7 @@ if (p_82448_1_.func_70089_S()) { p_82448_1_.func_70012_b(d0, p_82448_1_.field_70163_u, d1, p_82448_1_.field_70177_z, p_82448_1_.field_70125_A); diff --git a/patches/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch b/patches/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch index 8ac12773ec..6b887661f0 100644 --- a/patches/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch +++ b/patches/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch @@ -9,15 +9,128 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; -@@ -106,6 +108,7 @@ +@@ -46,8 +48,46 @@ + this.field_75825_d = p_i2003_1_; + } + ++ public boolean chunkExists(World world, int i, int j) ++ { ++ ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i, j); ++ ++ synchronized (this.field_75827_c) ++ { ++ if (this.field_75826_b.contains(chunkcoordintpair)) ++ { ++ Iterator iter = this.field_75828_a.iterator(); ++ while (iter.hasNext()) ++ { ++ PendingChunk pendingChunk = (PendingChunk)iter.next(); ++ if (pendingChunk.field_76548_a.equals(chunkcoordintpair)) ++ { ++ return true; ++ } ++ } ++ } ++ } ++ ++ return RegionFileCache.func_76550_a(this.field_75825_d, i, j).chunkExists(i & 31, j & 31); ++ } ++ + public Chunk func_75815_a(World p_75815_1_, int p_75815_2_, int p_75815_3_) throws IOException + { ++ Object[] data = this.loadChunk__Async(p_75815_1_, p_75815_2_, p_75815_3_); ++ ++ if (data != null) ++ { ++ Chunk chunk = (Chunk) data[0]; ++ NBTTagCompound nbttagcompound = (NBTTagCompound) data[1]; ++ this.loadEntities(p_75815_1_, nbttagcompound.func_74775_l("Level"), chunk); ++ return chunk; ++ } ++ ++ return null; ++ } ++ ++ public Object[] loadChunk__Async(World p_75815_1_, int p_75815_2_, int p_75815_3_) throws IOException ++ { + NBTTagCompound nbttagcompound = null; + ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(p_75815_2_, p_75815_3_); + Object object = this.field_75827_c; +@@ -56,11 +96,13 @@ + { + if (this.field_75826_b.contains(chunkcoordintpair)) + { +- for (int k = 0; k < this.field_75828_a.size(); ++k) ++ Iterator iter = this.field_75828_a.iterator(); ++ while (iter.hasNext()) + { +- if (((AnvilChunkLoader.PendingChunk)this.field_75828_a.get(k)).field_76548_a.equals(chunkcoordintpair)) ++ PendingChunk pendingChunk = (PendingChunk)iter.next(); ++ if (pendingChunk.field_76548_a.equals(chunkcoordintpair)) + { +- nbttagcompound = ((AnvilChunkLoader.PendingChunk)this.field_75828_a.get(k)).field_76547_b; ++ nbttagcompound = pendingChunk.field_76547_b; + break; + } + } +@@ -79,11 +121,24 @@ + nbttagcompound = CompressedStreamTools.func_74794_a(datainputstream); + } + +- return this.func_75822_a(p_75815_1_, p_75815_2_, p_75815_3_, nbttagcompound); ++ return this.checkedReadChunkFromNBT__Async(p_75815_1_, p_75815_2_, p_75815_3_, nbttagcompound); + } + + protected Chunk func_75822_a(World p_75822_1_, int p_75822_2_, int p_75822_3_, NBTTagCompound p_75822_4_) + { ++ Object[] data = this.checkedReadChunkFromNBT__Async(p_75822_1_, p_75822_2_, p_75822_3_, p_75822_4_); ++ ++ if (data != null) ++ { ++ Chunk chunk = (Chunk) data[0]; ++ return chunk; ++ } ++ ++ return null; ++ } ++ ++ protected Object[] checkedReadChunkFromNBT__Async(World p_75822_1_, int p_75822_2_, int p_75822_3_, NBTTagCompound p_75822_4_) ++ { + if (!p_75822_4_.func_150297_b("Level", 10)) + { + field_151505_a.error("Chunk file at " + p_75822_2_ + "," + p_75822_3_ + " is missing level data, skipping"); +@@ -103,10 +158,29 @@ + field_151505_a.error("Chunk file at " + p_75822_2_ + "," + p_75822_3_ + " is in the wrong location; relocating. (Expected " + p_75822_2_ + ", " + p_75822_3_ + ", got " + chunk.field_76635_g + ", " + chunk.field_76647_h + ")"); + p_75822_4_.func_74768_a("xPos", p_75822_2_); + p_75822_4_.func_74768_a("zPos", p_75822_3_); ++ // Have to move tile entities since we don't load them at this stage ++ NBTTagList tileEntities = p_75822_4_.func_74775_l("Level").func_150295_c("TileEntities", 10); ++ ++ if (tileEntities != null) ++ { ++ for (int te = 0; te < tileEntities.func_74745_c(); te++) ++ { ++ NBTTagCompound tileEntity = (NBTTagCompound) tileEntities.func_150305_b(te); ++ int x = tileEntity.func_74762_e("x") - chunk.field_76635_g * 16; ++ int z = tileEntity.func_74762_e("z") - chunk.field_76647_h * 16; ++ tileEntity.func_74768_a("x", p_75822_2_ * 16 + x); ++ tileEntity.func_74768_a("z", p_75822_3_ * 16 + z); ++ } ++ } ++ chunk = this.func_75823_a(p_75822_1_, p_75822_4_.func_74775_l("Level")); } +- return chunk; ++ Object[] data = new Object[2]; ++ data[0] = chunk; ++ data[1] = p_75822_4_; + MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Load(chunk, p_75822_4_)); - return chunk; ++ return data; } } -@@ -120,6 +123,7 @@ + +@@ -120,6 +194,7 @@ NBTTagCompound nbttagcompound1 = new NBTTagCompound(); nbttagcompound.func_74782_a("Level", nbttagcompound1); this.func_75820_a(p_75816_2_, p_75816_1_, nbttagcompound1); @@ -25,3 +138,25 @@ this.func_75824_a(p_75816_2_.func_76632_l(), nbttagcompound); } catch (Exception exception) +@@ -373,6 +448,12 @@ + chunk.func_76616_a(p_75823_2_.func_74770_j("Biomes")); + } + ++ // End this method here and split off entity loading to another method ++ return chunk; ++ } ++ ++ public void loadEntities(World p_75823_1_, NBTTagCompound p_75823_2_, Chunk chunk) ++ { + NBTTagList nbttaglist1 = p_75823_2_.func_150295_c("Entities", 10); + + if (nbttaglist1 != null) +@@ -434,7 +515,7 @@ + } + } + +- return chunk; ++ // return chunk; + } + + static class PendingChunk diff --git a/patches/minecraft/net/minecraft/world/chunk/storage/RegionFile.java.patch b/patches/minecraft/net/minecraft/world/chunk/storage/RegionFile.java.patch new file mode 100644 index 0000000000..623f8ca54f --- /dev/null +++ b/patches/minecraft/net/minecraft/world/chunk/storage/RegionFile.java.patch @@ -0,0 +1,42 @@ +--- ../src-base/minecraft/net/minecraft/world/chunk/storage/RegionFile.java ++++ ../src-work/minecraft/net/minecraft/world/chunk/storage/RegionFile.java +@@ -104,6 +104,39 @@ + } + } + ++ // This is a copy (sort of) of the method below it, make sure they stay in sync ++ public synchronized boolean chunkExists(int x, int z) ++ { ++ if (this.func_76705_d(x, z)) return false; ++ ++ try ++ { ++ int offset = this.func_76707_e(x, z); ++ ++ if (offset == 0) return false; ++ ++ int sectorNumber = offset >> 8; ++ int numSectors = offset & 255; ++ ++ if (sectorNumber + numSectors > this.field_76714_f.size()) return false; ++ ++ this.field_76719_c.seek((long)(sectorNumber * 4096)); ++ int length = this.field_76719_c.readInt(); ++ ++ if (length > 4096 * numSectors || length <= 0) return false; ++ ++ byte version = this.field_76719_c.readByte(); ++ ++ if (version == 1 || version == 2) return true; ++ } ++ catch (IOException ioexception) ++ { ++ return false; ++ } ++ ++ return false; ++ } ++ + public synchronized DataInputStream func_76704_a(int p_76704_1_, int p_76704_2_) + { + if (this.func_76705_d(p_76704_1_, p_76704_2_)) diff --git a/patches/minecraft/net/minecraft/world/gen/ChunkProviderServer.java.patch b/patches/minecraft/net/minecraft/world/gen/ChunkProviderServer.java.patch index 73d867f4d5..3ad0874d85 100644 --- a/patches/minecraft/net/minecraft/world/gen/ChunkProviderServer.java.patch +++ b/patches/minecraft/net/minecraft/world/gen/ChunkProviderServer.java.patch @@ -1,23 +1,27 @@ --- ../src-base/minecraft/net/minecraft/world/gen/ChunkProviderServer.java +++ ../src-work/minecraft/net/minecraft/world/gen/ChunkProviderServer.java -@@ -24,6 +24,8 @@ +@@ -23,7 +23,12 @@ + import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.EmptyChunk; import net.minecraft.world.chunk.IChunkProvider; ++import net.minecraft.world.chunk.storage.AnvilChunkLoader; import net.minecraft.world.chunk.storage.IChunkLoader; +import net.minecraftforge.common.DimensionManager; +import net.minecraftforge.common.ForgeChunkManager; ++import net.minecraftforge.common.chunkio.ChunkIOExecutor; ++ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -@@ -38,6 +40,7 @@ - private LongHashMap field_73244_f = new LongHashMap(); - private List field_73245_g = new ArrayList(); - private WorldServer field_73251_h; +@@ -38,6 +43,7 @@ + public LongHashMap field_73244_f = new LongHashMap(); + public List field_73245_g = new ArrayList(); + public WorldServer field_73251_h; + private Set loadingChunks = com.google.common.collect.Sets.newHashSet(); private static final String __OBFID = "CL_00001436"; public ChunkProviderServer(WorldServer p_i1520_1_, IChunkLoader p_i1520_2_, IChunkProvider p_i1520_3_) -@@ -55,7 +58,7 @@ +@@ -55,7 +61,7 @@ public void func_73241_b(int p_73241_1_, int p_73241_2_) { @@ -26,7 +30,57 @@ { ChunkCoordinates chunkcoordinates = this.field_73251_h.func_72861_E(); int k = p_73241_1_ * 16 + 8 - chunkcoordinates.field_71574_a; -@@ -92,7 +95,16 @@ +@@ -86,13 +92,66 @@ + + public Chunk func_73158_c(int p_73158_1_, int p_73158_2_) + { ++ return loadChunk(p_73158_1_, p_73158_2_, null); ++ } ++ ++ public Chunk loadChunk(int par1, int par2, Runnable runnable) ++ { ++ long k = ChunkCoordIntPair.func_77272_a(par1, par2); ++ this.field_73248_b.remove(Long.valueOf(k)); ++ Chunk chunk = (Chunk)this.field_73244_f.func_76164_a(k); ++ AnvilChunkLoader loader = null; ++ ++ if (this.field_73247_e instanceof AnvilChunkLoader) ++ { ++ loader = (AnvilChunkLoader) this.field_73247_e; ++ } ++ ++ // We can only use the queue for already generated chunks ++ if (chunk == null && loader != null && loader.chunkExists(this.field_73251_h, par1, par2)) ++ { ++ if (runnable != null) ++ { ++ ChunkIOExecutor.queueChunkLoad(this.field_73251_h, loader, this, par1, par2, runnable); ++ return null; ++ } ++ else ++ { ++ chunk = ChunkIOExecutor.syncChunkLoad(this.field_73251_h, loader, this, par1, par2); ++ } ++ } ++ else if (chunk == null) ++ { ++ chunk = this.originalLoadChunk(par1, par2); ++ } ++ ++ // If we didn't load the chunk async and have a callback run it now ++ if (runnable != null) ++ { ++ runnable.run(); ++ } ++ ++ return chunk; ++ } ++ ++ public Chunk originalLoadChunk(int p_73158_1_, int p_73158_2_) ++ { + long k = ChunkCoordIntPair.func_77272_a(p_73158_1_, p_73158_2_); + this.field_73248_b.remove(Long.valueOf(k)); + Chunk chunk = (Chunk)this.field_73244_f.func_76164_a(k); if (chunk == null) { @@ -44,7 +98,7 @@ if (chunk == null) { -@@ -120,6 +132,7 @@ +@@ -120,6 +179,7 @@ this.field_73244_f.func_76163_a(k, chunk); this.field_73245_g.add(chunk); @@ -52,7 +106,7 @@ chunk.func_76631_c(); chunk.func_76624_a(this, this, p_73158_1_, p_73158_2_); } -@@ -258,6 +271,11 @@ +@@ -258,6 +318,11 @@ { if (!this.field_73251_h.field_73058_d) { @@ -64,7 +118,7 @@ for (int i = 0; i < 100; ++i) { if (!this.field_73248_b.isEmpty()) -@@ -270,6 +288,11 @@ +@@ -270,6 +335,11 @@ this.field_73248_b.remove(olong); this.field_73244_f.func_76159_d(olong.longValue()); this.field_73245_g.remove(chunk); diff --git a/src/main/java/net/minecraftforge/common/chunkio/ChunkIOExecutor.java b/src/main/java/net/minecraftforge/common/chunkio/ChunkIOExecutor.java new file mode 100644 index 0000000000..e4cda1d766 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/chunkio/ChunkIOExecutor.java @@ -0,0 +1,32 @@ +package net.minecraftforge.common.chunkio; + +import net.minecraftforge.common.util.AsynchronousExecutor; + +public class ChunkIOExecutor { + static final int BASE_THREADS = 1; + static final int PLAYERS_PER_THREAD = 50; + + private static final AsynchronousExecutor instance = new AsynchronousExecutor(new ChunkIOProvider(), BASE_THREADS); + + public static net.minecraft.world.chunk.Chunk syncChunkLoad(net.minecraft.world.World world, net.minecraft.world.chunk.storage.AnvilChunkLoader loader, net.minecraft.world.gen.ChunkProviderServer provider, int x, int z) { + return instance.getSkipQueue(new QueuedChunk(x, z, loader, world, provider)); + } + + public static void queueChunkLoad(net.minecraft.world.World world, net.minecraft.world.chunk.storage.AnvilChunkLoader loader, net.minecraft.world.gen.ChunkProviderServer provider, int x, int z, Runnable runnable) { + instance.add(new QueuedChunk(x, z, loader, world, provider), runnable); + } + + // Abuses the fact that hashCode and equals for QueuedChunk only use world and coords + public static void dropQueuedChunkLoad(net.minecraft.world.World world, int x, int z, Runnable runnable) { + instance.drop(new QueuedChunk(x, z, null, world, null), runnable); + } + + public static void adjustPoolSize(int players) { + int size = Math.max(BASE_THREADS, (int) Math.ceil(players / PLAYERS_PER_THREAD)); + instance.setActiveThreads(size); + } + + public static void tick() { + instance.finishActive(); + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/common/chunkio/ChunkIOProvider.java b/src/main/java/net/minecraftforge/common/chunkio/ChunkIOProvider.java new file mode 100644 index 0000000000..10bedffac3 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/chunkio/ChunkIOProvider.java @@ -0,0 +1,61 @@ +package net.minecraftforge.common.chunkio; + + +import net.minecraft.world.ChunkCoordIntPair; +import net.minecraftforge.common.util.AsynchronousExecutor; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; + +class ChunkIOProvider implements AsynchronousExecutor.CallBackProvider { + private final AtomicInteger threadNumber = new AtomicInteger(1); + + // async stuff + public net.minecraft.world.chunk.Chunk callStage1(QueuedChunk queuedChunk) throws RuntimeException { + net.minecraft.world.chunk.storage.AnvilChunkLoader loader = queuedChunk.loader; + Object[] data = null; + try { + data = loader.loadChunk__Async(queuedChunk.world, queuedChunk.x, queuedChunk.z); + } catch (IOException e) { + e.printStackTrace(); + } + + if (data != null) { + queuedChunk.compound = (net.minecraft.nbt.NBTTagCompound) data[1]; + return (net.minecraft.world.chunk.Chunk) data[0]; + } + + return null; + } + + // sync stuff + public void callStage2(QueuedChunk queuedChunk, net.minecraft.world.chunk.Chunk chunk) throws RuntimeException { + if(chunk == null) { + // If the chunk loading failed just do it synchronously (may generate) + queuedChunk.provider.loadChunk(queuedChunk.x, queuedChunk.z); + return; + } + + queuedChunk.loader.loadEntities(queuedChunk.world, queuedChunk.compound.getCompoundTag("Level"), chunk); + chunk.lastSaveTime = queuedChunk.provider.worldObj.getTotalWorldTime(); + queuedChunk.provider.loadedChunkHashMap.add(ChunkCoordIntPair.chunkXZ2Int(queuedChunk.x, queuedChunk.z), chunk); + queuedChunk.provider.loadedChunks.add(chunk); + chunk.onChunkLoad(); + + if (queuedChunk.provider.currentChunkProvider != null) { + queuedChunk.provider.currentChunkProvider.recreateStructures(queuedChunk.x, queuedChunk.z); + } + + chunk.populateChunk(queuedChunk.provider, queuedChunk.provider, queuedChunk.x, queuedChunk.z); + } + + public void callStage3(QueuedChunk queuedChunk, net.minecraft.world.chunk.Chunk chunk, Runnable runnable) throws RuntimeException { + runnable.run(); + } + + public Thread newThread(Runnable runnable) { + Thread thread = new Thread(runnable, "Chunk I/O Executor Thread-" + threadNumber.getAndIncrement()); + thread.setDaemon(true); + return thread; + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/common/chunkio/QueuedChunk.java b/src/main/java/net/minecraftforge/common/chunkio/QueuedChunk.java new file mode 100644 index 0000000000..e847a6a86a --- /dev/null +++ b/src/main/java/net/minecraftforge/common/chunkio/QueuedChunk.java @@ -0,0 +1,52 @@ +package net.minecraftforge.common.chunkio; + + +class QueuedChunk { + final int x; + final int z; + final net.minecraft.world.chunk.storage.AnvilChunkLoader loader; + final net.minecraft.world.World world; + final net.minecraft.world.gen.ChunkProviderServer provider; + net.minecraft.nbt.NBTTagCompound compound; + + public QueuedChunk(int x, int z, net.minecraft.world.chunk.storage.AnvilChunkLoader loader, net.minecraft.world.World world, net.minecraft.world.gen.ChunkProviderServer provider) { + this.x = x; + this.z = z; + this.loader = loader; + this.world = world; + this.provider = provider; + } + + @Override + public int hashCode() { + return (x * 31 + z * 29) ^ world.hashCode(); + } + + @Override + public boolean equals(Object object) { + if (object instanceof QueuedChunk) { + QueuedChunk other = (QueuedChunk) object; + return x == other.x && z == other.z && world == other.world; + } + + return false; + } + + @Override + public String toString() + { + StringBuilder result = new StringBuilder(); + String NEW_LINE = System.getProperty("line.separator"); + + result.append(this.getClass().getName() + " {" + NEW_LINE); + result.append(" x: " + x + NEW_LINE); + result.append(" z: " + z + NEW_LINE); + result.append(" loader: " + loader + NEW_LINE ); + result.append(" world: " + world.getWorldInfo().getWorldName() + NEW_LINE); + result.append(" dimension: " + world.provider.dimensionId + NEW_LINE); + result.append(" provider: " + world.provider.getClass().getName() + NEW_LINE); + result.append("}"); + + return result.toString(); + } +} diff --git a/src/main/java/net/minecraftforge/common/util/AsynchronousExecutor.java b/src/main/java/net/minecraftforge/common/util/AsynchronousExecutor.java new file mode 100644 index 0000000000..0029488a3f --- /dev/null +++ b/src/main/java/net/minecraftforge/common/util/AsynchronousExecutor.java @@ -0,0 +1,361 @@ +package net.minecraftforge.common.util; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + +import cpw.mods.fml.common.FMLLog; + + +/** + * Executes tasks using a multi-stage process executor. Synchronous executions are via {@link AsynchronousExecutor#finishActive()} or the {@link AsynchronousExecutor#get(Object)} methods. + *
  • Stage 1 creates the object from a parameter, and is usually called asynchronously. + *
  • Stage 2 takes the parameter and object from stage 1 and does any synchronous processing to prepare it. + *
  • Stage 3 takes the parameter and object from stage 1, as well as a callback that was registered, and performs any synchronous calculations. + * + * @param

    The type of parameter you provide to make the object that will be created. It should implement {@link Object#hashCode()} and {@link Object#equals(Object)} if you want to get the value early. + * @param The type of object you provide. This is created in stage 1, and passed to stage 2, 3, and returned if get() is called. + * @param The type of callback you provide. You may register many of these to be passed to the provider in stage 3, one at a time. + * @param A type of exception you may throw and expect to be handled by the main thread + * @author Wesley Wolfe (c) 2012, 2014 + */ +public final class AsynchronousExecutor { + + public static interface CallBackProvider extends ThreadFactory { + + /** + * Normally an asynchronous call, but can be synchronous + * + * @param parameter parameter object provided + * @return the created object + */ + T callStage1(P parameter) throws E; + + /** + * Synchronous call + * + * @param parameter parameter object provided + * @param object the previously created object + */ + void callStage2(P parameter, T object) throws E; + + /** + * Synchronous call, called multiple times, once per registered callback + * + * @param parameter parameter object provided + * @param object the previously created object + * @param callback the current callback to execute + */ + void callStage3(P parameter, T object, C callback) throws E; + } + + @SuppressWarnings("rawtypes") + static final AtomicIntegerFieldUpdater STATE_FIELD = AtomicIntegerFieldUpdater.newUpdater(AsynchronousExecutor.Task.class, "state"); + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static boolean set(AsynchronousExecutor.Task $this, int expected, int value) { + return STATE_FIELD.compareAndSet($this, expected, value); + } + + class Task implements Runnable { + static final int PENDING = 0x0; + static final int STAGE_1_ASYNC = PENDING + 1; + static final int STAGE_1_SYNC = STAGE_1_ASYNC + 1; + static final int STAGE_1_COMPLETE = STAGE_1_SYNC + 1; + static final int FINISHED = STAGE_1_COMPLETE + 1; + + volatile int state = PENDING; + final P parameter; + T object; + final List callbacks = new LinkedList(); + E t = null; + + Task(final P parameter) { + this.parameter = parameter; + } + + public void run() { + if (initAsync()) { + finished.add(this); + } + } + + boolean initAsync() { + if (set(this, PENDING, STAGE_1_ASYNC)) { + boolean ret = true; + + try { + init(); + } finally { + if (set(this, STAGE_1_ASYNC, STAGE_1_COMPLETE)) { + // No one is/will be waiting + } else { + // We know that the sync thread will be waiting + synchronized (this) { + if (state != STAGE_1_SYNC) { + // They beat us to the synchronized block + this.notifyAll(); + } else { + // We beat them to the synchronized block + } + state = STAGE_1_COMPLETE; // They're already synchronized, atomic locks are not needed + } + // We want to return false, because we know a synchronous task already handled the finish() + ret = false; // Don't return inside finally; VERY bad practice. + } + } + + return ret; + } else { + return false; + } + } + + void initSync() { + if (set(this, PENDING, STAGE_1_COMPLETE)) { + // If we succeed that variable switch, good as done + init(); + } else if (set(this, STAGE_1_ASYNC, STAGE_1_SYNC)) { + // Async thread is running, but this shouldn't be likely; we need to sync to wait on them because of it. + synchronized (this) { + if (set(this, STAGE_1_SYNC, PENDING)) { // They might NOT synchronized yet, atomic lock IS needed + // We are the first into the lock + while (state != STAGE_1_COMPLETE) { + try { + this.wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Unable to handle interruption on " + parameter, e); + } + } + } else { + // They beat us to the synchronized block + } + } + } else { + // Async thread is not pending, the more likely situation for a task not pending + } + } + + @SuppressWarnings("unchecked") + void init() { + try { + object = provider.callStage1(parameter); + } catch (final Throwable t) { + this.t = (E) t; + } + } + + @SuppressWarnings("unchecked") + T get() throws E { + initSync(); + if (callbacks.isEmpty()) { + // 'this' is a placeholder to prevent callbacks from being empty during finish call + // See get method below + callbacks.add((C) this); + } + finish(); + return object; + } + + void finish() throws E { + switch (state) { + default: + case PENDING: + case STAGE_1_ASYNC: + case STAGE_1_SYNC: + throw new IllegalStateException("Attempting to finish unprepared(" + state + ") task(" + parameter + ")"); + case STAGE_1_COMPLETE: + try { + if (t != null) { + throw t; + } + if (callbacks.isEmpty()) { + return; + } + + final CallBackProvider provider = AsynchronousExecutor.this.provider; + final P parameter = this.parameter; + final T object = this.object; + + provider.callStage2(parameter, object); + for (C callback : callbacks) { + if (callback == this) { + // 'this' is a placeholder to prevent callbacks from being empty on a get() call + // See get method above + continue; + } + provider.callStage3(parameter, object, callback); + } + } finally { + tasks.remove(parameter); + state = FINISHED; + } + case FINISHED: + } + } + + boolean drop() { + if (set(this, PENDING, FINISHED)) { + // If we succeed that variable switch, good as forgotten + tasks.remove(parameter); + return true; + } else { + // We need the async thread to finish normally to properly dispose of the task + return false; + } + } + } + + final CallBackProvider provider; + final Queue finished = new ConcurrentLinkedQueue(); + final Map tasks = new HashMap(); + final ThreadPoolExecutor pool; + + /** + * Uses a thread pool to pass executions to the provider. + * @see AsynchronousExecutor + */ + public AsynchronousExecutor(final CallBackProvider provider, final int coreSize) { + if (provider == null) { + throw new IllegalArgumentException("Provider cannot be null"); + } + this.provider = provider; + + // We have an unbound queue size so do not need a max thread size + pool = new ThreadPoolExecutor(coreSize, Integer.MAX_VALUE, 60l, TimeUnit.SECONDS, new LinkedBlockingQueue(), provider); + } + + /** + * Adds a callback to the parameter provided, adding parameter to the queue if needed. + *

    + * This should always be synchronous. + */ + public void add(P parameter, C callback) { + Task task = tasks.get(parameter); + if (task == null) { + tasks.put(parameter, task = new Task(parameter)); + pool.execute(task); + } + task.callbacks.add(callback); + } + + /** + * This removes a particular callback from the specified parameter. + *

    + * If no callbacks remain for a given parameter, then the {@link CallBackProvider CallBackProvider's} stages may be omitted from execution. + * Stage 3 will have no callbacks, stage 2 will be skipped unless a {@link #get(Object)} is used, and stage 1 will be avoided on a best-effort basis. + *

    + * Subsequent calls to {@link #getSkipQueue(Object)} will always work. + *

    + * Subsequent calls to {@link #get(Object)} might work. + *

    + * This should always be synchronous + * @return true if no further execution for the parameter is possible, such that, no exceptions will be thrown in {@link #finishActive()} for the parameter, and {@link #get(Object)} will throw an {@link IllegalStateException}, false otherwise + * @throws IllegalStateException if parameter is not in the queue anymore + * @throws IllegalStateException if the callback was not specified for given parameter + */ + public boolean drop(P parameter, C callback) throws IllegalStateException { + final Task task = tasks.get(parameter); + if (task == null) { + // Print debug info for QueuedChunk and avoid crash + //throw new IllegalStateException("Unknown " + parameter); + FMLLog.info("Unknown %s", parameter); + FMLLog.info("This should not happen. Please report this error to Forge."); + return false; + } + if (!task.callbacks.remove(callback)) { + throw new IllegalStateException("Unknown " + callback + " for " + parameter); + } + if (task.callbacks.isEmpty()) { + return task.drop(); + } + return false; + } + + /** + * This method attempts to skip the waiting period for said parameter. + *

    + * This should always be synchronous. + * @throws IllegalStateException if the parameter is not in the queue anymore, or sometimes if called from asynchronous thread + */ + public T get(P parameter) throws E, IllegalStateException { + final Task task = tasks.get(parameter); + if (task == null) { + throw new IllegalStateException("Unknown " + parameter); + } + return task.get(); + } + + /** + * Processes a parameter as if it was in the queue, without ever passing to another thread. + */ + public T getSkipQueue(P parameter) throws E { + return skipQueue(parameter); + } + + /** + * Processes a parameter as if it was in the queue, without ever passing to another thread. + */ + public T getSkipQueue(P parameter, C callback) throws E { + final T object = skipQueue(parameter); + provider.callStage3(parameter, object, callback); + return object; + } + + /** + * Processes a parameter as if it was in the queue, without ever passing to another thread. + */ + public T getSkipQueue(P parameter, C...callbacks) throws E { + final CallBackProvider provider = this.provider; + final T object = skipQueue(parameter); + for (C callback : callbacks) { + provider.callStage3(parameter, object, callback); + } + return object; + } + + /** + * Processes a parameter as if it was in the queue, without ever passing to another thread. + */ + public T getSkipQueue(P parameter, Iterable callbacks) throws E { + final CallBackProvider provider = this.provider; + final T object = skipQueue(parameter); + for (C callback : callbacks) { + provider.callStage3(parameter, object, callback); + } + return object; + } + + private T skipQueue(P parameter) throws E { + Task task = tasks.get(parameter); + if (task != null) { + return task.get(); + } + T object = provider.callStage1(parameter); + provider.callStage2(parameter, object); + return object; + } + + /** + * This is the 'heartbeat' that should be called synchronously to finish any pending tasks + */ + public void finishActive() throws E { + final Queue finished = this.finished; + while (!finished.isEmpty()) { + finished.poll().finish(); + } + } + + public void setActiveThreads(final int coreSize) { + pool.setCorePoolSize(coreSize); + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/common/util/ChunkCoordComparator.java b/src/main/java/net/minecraftforge/common/util/ChunkCoordComparator.java new file mode 100644 index 0000000000..bb87e901b8 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/util/ChunkCoordComparator.java @@ -0,0 +1,60 @@ +package net.minecraftforge.common.util; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.world.ChunkCoordIntPair; + +// Sorter to load nearby chunks first +public class ChunkCoordComparator implements java.util.Comparator +{ + private int x; + private int z; + + public ChunkCoordComparator(EntityPlayerMP entityplayer) + { + x = (int) entityplayer.posX >> 4; + z = (int) entityplayer.posZ >> 4; + } + + public int compare(ChunkCoordIntPair a, ChunkCoordIntPair b) + { + if (a.equals(b)) + { + return 0; + } + + // Subtract current position to set center point + int ax = a.chunkXPos - this.x; + int az = a.chunkZPos - this.z; + int bx = b.chunkXPos - this.x; + int bz = b.chunkZPos - this.z; + int result = ((ax - bx) * (ax + bx)) + ((az - bz) * (az + bz)); + + if (result != 0) + { + return result; + } + + if (ax < 0) + { + if (bx < 0) + { + return bz - az; + } + else + { + return -1; + } + } + else + { + if (bx < 0) + { + return 1; + } + else + { + return az - bz; + } + } + } +} \ No newline at end of file diff --git a/src/main/resources/forge.exc b/src/main/resources/forge.exc index 8a3c3af133..5db7d2a772 100644 --- a/src/main/resources/forge.exc +++ b/src/main/resources/forge.exc @@ -10,3 +10,8 @@ net/minecraft/world/World.getSkyColorBody(Lnet/minecraft/entity/Entity;F)Lnet/mi net/minecraft/world/World.drawCloudsBody(F)Lnet/minecraft/util/Vec3;=|p_72824_1_ net/minecraft/world/World.canBlockFreezeBody(IIIZ)Z=|p_72834_1_,p_72834_2_,p_72834_3_,p_72834_4_ net/minecraft/world/biome/BiomeGenBase.(IZ)V=|p_i1971_1_,register +net/minecraft/world/chunk/storage/AnvilChunkLoader.loadChunk__Async(Lnet/minecraft/world/World;II)[Ljava/lang/Object;=|p_75815_1_,p_75815_2_,p_75815_3_ +net/minecraft/world/chunk/storage/AnvilChunkLoader.checkedReadChunkFromNBT__Async(Lnet/minecraft/world/World;IILnet/minecraft/nbt/NBTTagCompound;)[Ljava/lang/Object;=|p_75822_1_,p_75822_2_,p_75822_3_,p_75822_4_ +net/minecraft/world/chunk/storage/AnvilChunkLoader.loadEntities(Lnet/minecraft/world/World;Lnet/minecraft/nbt/NBTTagCompound;Lnet/minecraft/world/chunk/Chunk;)V=|p_75823_1_,p_75823_2_,chunk +net/minecraft/world/gen/ChunkProviderServer.loadChunk(II;Ljava/lang/Runnable;)Lnet/minecraft/world/chunk/Chunk;=|p_73158_1,p_73158_2,runnable +net/minecraft/world/gen/ChunkProviderServer.originalLoadChunk(II)Lnet/minecraft/world/chunk/Chunk;=|p_73158_1_,p_73158_2_ diff --git a/src/main/resources/forge_at.cfg b/src/main/resources/forge_at.cfg index 984c702e44..794c113066 100644 --- a/src/main/resources/forge_at.cfg +++ b/src/main/resources/forge_at.cfg @@ -178,4 +178,8 @@ public net.minecraft.client.renderer.entity.RenderBiped field_77071_a #modelBipe public net.minecraft.client.renderer.entity.RenderPlayer field_77109_a #modelBipedMain public net.minecraft.client.renderer.entity.RenderPlayer field_77108_b #modelArmorChestplate public net.minecraft.client.renderer.entity.RenderPlayer field_77111_i #modelArmor - +# ChunkProviderServer +public net.minecraft.world.gen.ChunkProviderServer field_73246_d # currentChunkProvider +public net.minecraft.world.gen.ChunkProviderServer field_73244_f # loadedChunkHashMap +public net.minecraft.world.gen.ChunkProviderServer field_73245_g # loadedChunks +public net.minecraft.world.gen.ChunkProviderServer field_73251_h # worldObj diff --git a/src/main/resources/lgpl-3.0.txt b/src/main/resources/lgpl-3.0.txt new file mode 100644 index 0000000000..02bbb60bc4 --- /dev/null +++ b/src/main/resources/lgpl-3.0.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file