Merge pull request #938 from bloodmc/chunkasync
Load chunks asynchronously for players.
This commit is contained in:
commit
96286b77f9
14 changed files with 1198 additions and 34 deletions
|
@ -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_;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<ChunkCoordIntPair> chunkList = new ArrayList<ChunkCoordIntPair>();
|
||||
|
||||
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<ChunkCoordIntPair> chunksToLoad = new ArrayList<ChunkCoordIntPair>();
|
||||
|
||||
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<EntityPlayerMP, Runnable> players = new HashMap<EntityPlayerMP, Runnable>();
|
||||
+ 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)));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_))
|
|
@ -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<Long> 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);
|
||||
|
|
|
@ -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<QueuedChunk, net.minecraft.world.chunk.Chunk, Runnable, RuntimeException> instance = new AsynchronousExecutor<QueuedChunk, net.minecraft.world.chunk.Chunk, Runnable, RuntimeException>(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();
|
||||
}
|
||||
}
|
|
@ -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<QueuedChunk, net.minecraft.world.chunk.Chunk, Runnable, RuntimeException> {
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
* <li \> Stage 1 creates the object from a parameter, and is usually called asynchronously.
|
||||
* <li \> Stage 2 takes the parameter and object from stage 1 and does any synchronous processing to prepare it.
|
||||
* <li \> Stage 3 takes the parameter and object from stage 1, as well as a callback that was registered, and performs any synchronous calculations.
|
||||
*
|
||||
* @param <P> 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 <T> 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 <C> 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 <E> 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<P, T, C, E extends Throwable> {
|
||||
|
||||
public static interface CallBackProvider<P, T, C, E extends Throwable> 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<C> callbacks = new LinkedList<C>();
|
||||
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<P, T, C, E> 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<P, T, C, E> provider;
|
||||
final Queue<Task> finished = new ConcurrentLinkedQueue<Task>();
|
||||
final Map<P, Task> tasks = new HashMap<P, Task>();
|
||||
final ThreadPoolExecutor pool;
|
||||
|
||||
/**
|
||||
* Uses a thread pool to pass executions to the provider.
|
||||
* @see AsynchronousExecutor
|
||||
*/
|
||||
public AsynchronousExecutor(final CallBackProvider<P, T, C, E> 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<Runnable>(), provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a callback to the parameter provided, adding parameter to the queue if needed.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* Subsequent calls to {@link #getSkipQueue(Object)} will always work.
|
||||
* <p>
|
||||
* Subsequent calls to {@link #get(Object)} might work.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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<P, T, C, E> 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<C> callbacks) throws E {
|
||||
final CallBackProvider<P, T, C, E> 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<Task> finished = this.finished;
|
||||
while (!finished.isEmpty()) {
|
||||
finished.poll().finish();
|
||||
}
|
||||
}
|
||||
|
||||
public void setActiveThreads(final int coreSize) {
|
||||
pool.setCorePoolSize(coreSize);
|
||||
}
|
||||
}
|
|
@ -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<ChunkCoordIntPair>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.<init>(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_
|
||||
|
|
|
@ -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
|
||||
|
|
165
src/main/resources/lgpl-3.0.txt
Normal file
165
src/main/resources/lgpl-3.0.txt
Normal file
|
@ -0,0 +1,165 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
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.
|
Loading…
Reference in a new issue