diff --git a/common/net/minecraftforge/common/ForgeChunkManager.java b/common/net/minecraftforge/common/ForgeChunkManager.java index 518290661..7d91e49fb 100644 --- a/common/net/minecraftforge/common/ForgeChunkManager.java +++ b/common/net/minecraftforge/common/ForgeChunkManager.java @@ -9,9 +9,14 @@ import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.UUID; import java.util.logging.Level; import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.ListMultimap; @@ -27,6 +32,7 @@ import cpw.mods.fml.common.FMLLog; import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.ModContainer; +import net.minecraft.src.Chunk; import net.minecraft.src.ChunkCoordIntPair; import net.minecraft.src.CompressedStreamTools; import net.minecraft.src.Entity; @@ -68,6 +74,7 @@ public class ForgeChunkManager private static Map callbacks = Maps.newHashMap(); private static Map> forcedChunks = Maps.newHashMap(); + private static BiMap pendingEntities = HashBiMap.create(); /** * All mods requiring chunkloading need to implement this to handle the @@ -108,9 +115,8 @@ public class ForgeChunkManager private World world; private int maxDepth; private String entityClazz; - private int entityX; - private int entityY; - private int entityZ; + private int entityChunkX; + private int entityChunkZ; private Entity entity; Ticket(String modId, Type type, World world) @@ -122,14 +128,6 @@ public class ForgeChunkManager this.requestedChunks = Sets.newLinkedHashSet(); } - void bindEntityData(int x, int y, int z, String clazz) - { - this.entityX = x; - this.entityY = y; - this.entityZ = z; - this.entityClazz = clazz; - } - /** * The chunk list depth can be manipulated up to the maximal grant allowed for the mod. This value is configurable. Once the maximum is reached, * the least recently forced chunk, by original registration time, is removed from the forced chunk list. @@ -189,37 +187,12 @@ public class ForgeChunkManager } /** - * Get the entity class associated with this ticket. Only valid for callbacks. + * Get the entity associated with this {@link Type#ENTITY} type ticket * @return */ - public String getEntityClass() + public Entity getEntity() { - return this.entityClazz; - } - - /** - * Get the last known entity X coordinate for this ticket. Only valid for callbacks. - * @return - */ - public int getEntityX() - { - return entityX; - } - /** - * Get the last known entity Y coordinate for this ticket. Only valid for callbacks. - * @return - */ - public int getEntityY() - { - return entityY; - } - /** - * Get the last known entity Z coordinate for this ticket. Only valid for callbacks. - * @return - */ - public int getEntityZ() - { - return entityZ; + return entity; } } @@ -287,16 +260,35 @@ public class ForgeChunkManager tick.modData = modData; if (type == Type.ENTITY) { - int entX = ticket.getInteger("entityX"); - int entY = ticket.getInteger("entityY"); - int entZ = ticket.getInteger("entityZ"); - String entClass = ticket.getString("entityClass"); - tick.bindEntityData(entX, entY, entZ, entClass); + tick.entityChunkX = ticket.getInteger("chunkX"); + tick.entityChunkZ = ticket.getInteger("chunkZ"); + UUID uuid = new UUID(ticket.getLong("PersistentIDMSB"), ticket.getLong("PersistentIDLSB")); + // add the ticket to the "pending entity" list + pendingEntities.put(uuid, tick); } loadedTickets.put(modId, tick); } } + for (Ticket tick : ImmutableSet.copyOf(pendingEntities.values())) + { + if (tick.ticketType == Type.ENTITY && tick.entity == null) + { + // force the world to load the entity's chunk + // the load will come back through the loadEntity method and attach the entity + // to the ticket + world.getChunkFromChunkCoords(tick.entityChunkX, tick.entityChunkZ); + } + } + for (Ticket tick : ImmutableSet.copyOf(pendingEntities.values())) + { + if (tick.ticketType == Type.ENTITY && tick.entity == null) + { + FMLLog.warning("Failed to load persistent chunkloading entity %s from store.", pendingEntities.inverse().get(tick)); + loadedTickets.remove(tick.modId, tick); + } + } + pendingEntities.clear(); // send callbacks for (String modId : loadedTickets.keySet()) { @@ -537,10 +529,10 @@ public class ForgeChunkManager ticket.setCompoundTag("ModData", tick.modData); if (tick.ticketType == Type.ENTITY) { - ticket.setInteger("entityX", MathHelper.floor_double(tick.entity.posX)); - ticket.setInteger("entityY", MathHelper.floor_double(tick.entity.posY)); - ticket.setInteger("entityZ", MathHelper.floor_double(tick.entity.posZ)); - ticket.setString("entityClass", tick.entity.getClass().getName()); + ticket.setInteger("chunkX", MathHelper.floor_double(tick.entity.chunkCoordX)); + ticket.setInteger("chunkZ", MathHelper.floor_double(tick.entity.chunkCoordZ)); + ticket.setLong("persistentIDMSB", tick.entity.getPersistentID().getMostSignificantBits()); + ticket.setLong("persistentIDLSB", tick.entity.getPersistentID().getLeastSignificantBits()); } } } @@ -554,4 +546,15 @@ public class ForgeChunkManager return; } } + + static void loadEntity(Entity entity) + { + UUID id = entity.getPersistentID(); + Ticket tick = pendingEntities.get(id); + if (tick != null) + { + tick.bindEntity(entity); + pendingEntities.remove(id); + } + } } diff --git a/common/net/minecraftforge/common/ForgeInternalHandler.java b/common/net/minecraftforge/common/ForgeInternalHandler.java index d9264386d..7def0ddbb 100644 --- a/common/net/minecraftforge/common/ForgeInternalHandler.java +++ b/common/net/minecraftforge/common/ForgeInternalHandler.java @@ -1,5 +1,7 @@ package net.minecraftforge.common; +import java.util.UUID; + import net.minecraft.src.*; import net.minecraftforge.event.*; import net.minecraftforge.event.entity.*; @@ -10,6 +12,17 @@ public class ForgeInternalHandler @ForgeSubscribe(priority = EventPriority.HIGHEST) public void onEntityJoinWorld(EntityJoinWorldEvent event) { + if (!event.world.isRemote) + { + if (event.entity.getPersistentID() == null) + { + event.entity.generatePersistentID(); + } + else + { + ForgeChunkManager.loadEntity(event.entity); + } + } Entity entity = event.entity; if (entity instanceof EntityItem) { diff --git a/patches/common/net/minecraft/src/Entity.java.patch b/patches/common/net/minecraft/src/Entity.java.patch index fd46d3152..c7ff3cd1f 100644 --- a/patches/common/net/minecraft/src/Entity.java.patch +++ b/patches/common/net/minecraft/src/Entity.java.patch @@ -1,6 +1,6 @@ --- ../src_base/common/net/minecraft/src/Entity.java +++ ../src_work/common/net/minecraft/src/Entity.java -@@ -2,6 +2,8 @@ +@@ -2,9 +2,12 @@ import cpw.mods.fml.common.Side; import cpw.mods.fml.common.asm.SideOnly; @@ -9,7 +9,11 @@ import java.util.Iterator; import java.util.List; import java.util.Random; -@@ -186,6 +188,10 @@ ++import java.util.UUID; + + public abstract class Entity + { +@@ -186,6 +189,11 @@ public boolean ignoreFrustumCheck; public boolean isAirBorne; public EnumEntitySize myEntitySize; @@ -17,13 +21,19 @@ + private NBTTagCompound customEntityData; + public boolean captureDrops = false; + public ArrayList capturedDrops = new ArrayList(); ++ private UUID persistentID; public Entity(World par1World) { -@@ -1382,6 +1388,10 @@ +@@ -1382,6 +1390,15 @@ par1NBTTagCompound.setShort("Fire", (short)this.fire); par1NBTTagCompound.setShort("Air", (short)this.getAir()); par1NBTTagCompound.setBoolean("OnGround", this.onGround); ++ if (persistentID != null) ++ { ++ par1NBTTagCompound.setLong("PersistentIDMSB", persistentID.getMostSignificantBits()); ++ par1NBTTagCompound.setLong("PersistentIDLSB", persistentID.getLeastSignificantBits()); ++ } + if (customEntityData != null) + { + par1NBTTagCompound.setCompoundTag("ForgeData", customEntityData); @@ -31,18 +41,22 @@ this.writeEntityToNBT(par1NBTTagCompound); } -@@ -1423,6 +1433,10 @@ +@@ -1423,6 +1440,14 @@ this.onGround = par1NBTTagCompound.getBoolean("OnGround"); this.setPosition(this.posX, this.posY, this.posZ); this.setRotation(this.rotationYaw, this.rotationPitch); + if (par1NBTTagCompound.hasKey("ForgeData")) + { + customEntityData = par1NBTTagCompound.getCompoundTag("ForgeData"); ++ } ++ if (par1NBTTagCompound.hasKey("PersistentIDMSB") && par1NBTTagCompound.hasKey("PersistentIDLSB")) ++ { ++ persistentID = new UUID(par1NBTTagCompound.getLong("PersistentIDMSB"), par1NBTTagCompound.getLong("PersistentIDLSB")); + } this.readEntityFromNBT(par1NBTTagCompound); } -@@ -1509,7 +1523,14 @@ +@@ -1509,7 +1534,14 @@ { EntityItem var3 = new EntityItem(this.worldObj, this.posX, this.posY + (double)par2, this.posZ, par1ItemStack); var3.delayBeforeCanPickup = 10; @@ -58,7 +72,7 @@ return var3; } -@@ -1843,7 +1864,7 @@ +@@ -1843,7 +1875,7 @@ */ public boolean isRiding() { @@ -67,7 +81,7 @@ } /** -@@ -2107,4 +2128,59 @@ +@@ -2107,4 +2139,72 @@ { return String.format("%s[\'%s\'/%d, l=\'%s\', x=%.2f, y=%.2f, z=%.2f]", new Object[] {this.getClass().getSimpleName(), this.getEntityName(), Integer.valueOf(this.entityId), this.worldObj == null ? "~NULL~" : this.worldObj.getWorldInfo().getWorldName(), Double.valueOf(this.posX), Double.valueOf(this.posY), Double.valueOf(this.posZ)}); } @@ -98,7 +112,7 @@ + + /** + * Called when a user uses the creative pick block button on this entity. -+ * ++ * + * @param target The full target the player is looking at + * @return A ItemStack to add to the player's inventory, Null if nothing should be added. + */ @@ -125,5 +139,18 @@ + } + } + return null; ++ } ++ ++ public UUID getPersistentID() ++ { ++ return persistentID; ++ } ++ ++ public synchronized void generatePersistentID() ++ { ++ if (persistentID == null) ++ { ++ persistentID = UUID.randomUUID(); ++ } + } }