diff --git a/fml/client/cpw/mods/fml/client/FMLClientHandler.java b/fml/client/cpw/mods/fml/client/FMLClientHandler.java index fe1144466..db0986239 100644 --- a/fml/client/cpw/mods/fml/client/FMLClientHandler.java +++ b/fml/client/cpw/mods/fml/client/FMLClientHandler.java @@ -51,7 +51,9 @@ import net.minecraft.src.BaseMod; import net.minecraft.src.BiomeGenBase; import net.minecraft.src.Block; import net.minecraft.src.CrashReport; +import net.minecraft.src.Entity; import net.minecraft.src.EntityItem; +import net.minecraft.src.EntityLiving; import net.minecraft.src.EntityPlayer; import net.minecraft.src.GameSettings; import net.minecraft.src.GuiScreen; @@ -79,6 +81,7 @@ import net.minecraft.src.StringTranslate; import net.minecraft.src.TextureFX; import net.minecraft.src.TexturePackBase; import net.minecraft.src.World; +import net.minecraft.src.WorldClient; import net.minecraft.src.WorldType; import argo.jdom.JdomParser; import argo.jdom.JsonNode; @@ -103,6 +106,9 @@ import cpw.mods.fml.common.TickType; import cpw.mods.fml.common.modloader.ModLoaderHelper; import cpw.mods.fml.common.modloader.ModLoaderModContainer; import cpw.mods.fml.common.modloader.ModProperty; +import cpw.mods.fml.common.network.EntitySpawnPacket; +import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData; +import cpw.mods.fml.common.registry.IThrowableEntity; /** @@ -332,4 +338,65 @@ public class FMLClientHandler implements IFMLSidedHandler GuiScreen gui = (GuiScreen) clientGuiElement; client.func_71373_a(gui); } + + @Override + public Entity spawnEntityIntoClientWorld(Class cls, EntitySpawnPacket packet) + { + WorldClient wc = client.field_71441_e; + + try + { + Entity entity = (Entity)(cls.getConstructor(World.class).newInstance(wc)); + if (entity instanceof IThrowableEntity) + { + Entity thrower = client.field_71439_g.field_70157_k == packet.throwerId ? client.field_71439_g : wc.func_73024_a(packet.throwerId); + ((IThrowableEntity)entity).setThrower(thrower); + } + + entity.field_70118_ct = packet.rawX; + entity.field_70117_cu = packet.rawY; + entity.field_70116_cv = packet.rawZ; + + Entity parts[] = entity.func_70021_al(); + if (parts != null) + { + int i = packet.entityId - entity.field_70157_k; + for (int j = 0; j < parts.length; j++) + { + parts[j].field_70157_k += i; + } + } + + entity.field_70157_k = packet.entityId; + entity.func_70056_a(packet.scaledX, packet.scaledY, packet.scaledZ, packet.scaledYaw, packet.scaledPitch, 1); + + if (entity instanceof EntityLiving) + { + ((EntityLiving)entity).field_70759_as = packet.scaledHeadYaw; + } + + if (packet.metadata != null) + { + entity.func_70096_w().func_75687_a((List)packet.metadata); + } + + if (packet.throwerId > 0) + { + entity.func_70016_h(packet.speedScaledX, packet.speedScaledY, packet.speedScaledZ); + } + + if (entity instanceof IEntityAdditionalSpawnData) + { + ((IEntityAdditionalSpawnData)entity).readSpawnData(packet.dataStream); + } + + wc.func_73027_a(packet.entityId, entity); + return entity; + } + catch (Exception e) + { + FMLLog.log(Level.SEVERE, e, "A severe problem occurred during the spawning of an entity"); + throw Throwables.propagate(e); + } + } } diff --git a/fml/common/cpw/mods/fml/common/FMLCommonHandler.java b/fml/common/cpw/mods/fml/common/FMLCommonHandler.java index eed28e736..40470d7e9 100644 --- a/fml/common/cpw/mods/fml/common/FMLCommonHandler.java +++ b/fml/common/cpw/mods/fml/common/FMLCommonHandler.java @@ -35,6 +35,8 @@ import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; import net.minecraft.server.MinecraftServer; +import net.minecraft.src.Entity; +import net.minecraft.src.World; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; @@ -42,6 +44,7 @@ import com.google.common.collect.ImmutableList.Builder; import com.google.common.collect.Lists; import cpw.mods.fml.common.discovery.ContainerType; +import cpw.mods.fml.common.network.EntitySpawnPacket; import cpw.mods.fml.common.registry.TickRegistry; import cpw.mods.fml.common.registry.GameRegistry; @@ -342,4 +345,9 @@ public class FMLCommonHandler { sidedDelegate.showGuiScreen(clientGuiElement); } + + public Entity spawnEntityIntoClientWorld(Class cls, EntitySpawnPacket entitySpawnPacket) + { + return sidedDelegate.spawnEntityIntoClientWorld(cls, entitySpawnPacket); + } } diff --git a/fml/common/cpw/mods/fml/common/IFMLSidedHandler.java b/fml/common/cpw/mods/fml/common/IFMLSidedHandler.java index 2f8c4bad7..d31911802 100644 --- a/fml/common/cpw/mods/fml/common/IFMLSidedHandler.java +++ b/fml/common/cpw/mods/fml/common/IFMLSidedHandler.java @@ -7,7 +7,11 @@ import java.util.List; import java.util.Properties; import java.util.logging.Logger; +import net.minecraft.src.Entity; +import net.minecraft.src.World; + import cpw.mods.fml.common.modloader.ModProperty; +import cpw.mods.fml.common.network.EntitySpawnPacket; public interface IFMLSidedHandler { @@ -22,4 +26,6 @@ public interface IFMLSidedHandler void haltGame(String message, Throwable exception); void showGuiScreen(Object clientGuiElement); + + Entity spawnEntityIntoClientWorld(Class entityClass, EntitySpawnPacket packet); } diff --git a/fml/common/cpw/mods/fml/common/asm/transformers/SideTransformer.java b/fml/common/cpw/mods/fml/common/asm/transformers/SideTransformer.java index af91e9803..0be4e0499 100644 --- a/fml/common/cpw/mods/fml/common/asm/transformers/SideTransformer.java +++ b/fml/common/cpw/mods/fml/common/asm/transformers/SideTransformer.java @@ -13,11 +13,12 @@ import org.objectweb.asm.tree.MethodNode; import cpw.mods.fml.common.Side; import cpw.mods.fml.common.asm.SideOnly; +import cpw.mods.fml.relauncher.FMLRelauncher; import cpw.mods.fml.relauncher.IClassTransformer; public class SideTransformer implements IClassTransformer { - private static String SIDE = "CLIENT"; //TODO: Pick correct side + private static String SIDE = FMLRelauncher.side(); @SuppressWarnings("unchecked") @Override public byte[] transform(String name, byte[] bytes) diff --git a/fml/common/cpw/mods/fml/common/network/EntitySpawnPacket.java b/fml/common/cpw/mods/fml/common/network/EntitySpawnPacket.java new file mode 100644 index 000000000..1da5b0f19 --- /dev/null +++ b/fml/common/cpw/mods/fml/common/network/EntitySpawnPacket.java @@ -0,0 +1,186 @@ +package cpw.mods.fml.common.network; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.logging.Level; + +import com.google.common.io.ByteArrayDataInput; +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; +import com.google.common.primitives.Bytes; + +import cpw.mods.fml.common.FMLCommonHandler; +import cpw.mods.fml.common.FMLLog; +import cpw.mods.fml.common.ModContainer; +import cpw.mods.fml.common.registry.EntityRegistry; +import cpw.mods.fml.common.registry.EntityRegistry.EntityRegistration; +import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData; +import cpw.mods.fml.common.registry.IThrowableEntity; +import net.minecraft.src.DataWatcher; +import net.minecraft.src.Entity; +import net.minecraft.src.EntityLiving; +import net.minecraft.src.MathHelper; +import net.minecraft.src.NetHandler; +import net.minecraft.src.NetworkManager; +import net.minecraft.src.World; + +public class EntitySpawnPacket extends FMLPacket +{ + + public int networkId; + public int modEntityId; + public int entityId; + public double scaledX; + public double scaledY; + public double scaledZ; + public float scaledYaw; + public float scaledPitch; + public float scaledHeadYaw; + public List metadata; + public int throwerId; + public double speedScaledX; + public double speedScaledY; + public double speedScaledZ; + public ByteArrayDataInput dataStream; + public int rawX; + public int rawY; + public int rawZ; + + public EntitySpawnPacket() + { + super(Type.ENTITYSPAWN); + } + + @Override + public byte[] generatePacket(Object... data) + { + EntityRegistration er = (EntityRegistration) data[0]; + Entity ent = (Entity) data[1]; + NetworkModHandler handler = (NetworkModHandler) data[2]; + ByteArrayDataOutput dat = ByteStreams.newDataOutput(); + + dat.writeInt(handler.getNetworkId()); + dat.writeInt(er.getModEntityId()); + // entity id + dat.writeInt(ent.field_70157_k); + + // entity pos x,y,z + dat.writeInt(MathHelper.func_76128_c(ent.field_70165_t * 32D)); + dat.writeInt(MathHelper.func_76128_c(ent.field_70163_u * 32D)); + dat.writeInt(MathHelper.func_76128_c(ent.field_70161_v * 32D)); + + // yaw, pitch + dat.writeByte((byte) (ent.field_70177_z * 256.0F / 360.0F)); + dat.writeByte((byte) (ent.field_70125_A * 256.0F / 360.0F)); + + // head yaw + if (ent instanceof EntityLiving) + { + dat.writeByte((byte) (((EntityLiving)ent).field_70759_as * 256.0F / 360.0F)); + } + else + { + dat.writeByte(0); + } + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(bos); + try + { + ent.func_70096_w().func_75689_a(dos); + } + catch (IOException e) + { + // unpossible + } + + dat.write(bos.toByteArray()); + + if (ent instanceof IThrowableEntity) + { + Entity owner = ((IThrowableEntity)ent).getThrower(); + dat.writeInt(owner == null ? ent.field_70157_k : owner.field_70157_k); + double maxVel = 3.9D; + double mX = ent.field_70159_w; + double mY = ent.field_70181_x; + double mZ = ent.field_70179_y; + if (mX < -maxVel) mX = -maxVel; + if (mY < -maxVel) mY = -maxVel; + if (mZ < -maxVel) mZ = -maxVel; + if (mX > maxVel) mX = maxVel; + if (mY > maxVel) mY = maxVel; + if (mZ > maxVel) mZ = maxVel; + dat.writeInt((int)(mX * 8000D)); + dat.writeInt((int)(mY * 8000D)); + dat.writeInt((int)(mZ * 8000D)); + } + if (ent instanceof IEntityAdditionalSpawnData) + { + ((IEntityAdditionalSpawnData)ent).writeSpawnData(dat); + } + + + return dat.toByteArray(); + } + + @Override + public FMLPacket consumePacket(byte[] data) + { + ByteArrayDataInput dat = ByteStreams.newDataInput(data); + networkId = dat.readInt(); + modEntityId = dat.readInt(); + entityId = dat.readInt(); + rawX = dat.readInt(); + rawY = dat.readInt(); + rawZ = dat.readInt(); + scaledX = rawX / 32D; + scaledY = rawY / 32D; + scaledZ = rawZ / 32D; + scaledYaw = dat.readByte() * 360F / 256F; + scaledPitch = dat.readByte() * 360F / 256F; + scaledHeadYaw = dat.readByte() * 360F / 256F; + ByteArrayInputStream bis = new ByteArrayInputStream(data, 27, data.length - 27); + DataInputStream dis = new DataInputStream(bis); + try + { + metadata = DataWatcher.func_75686_a(dis); + } + catch (IOException e) + { + // Nope + } + dat.skipBytes(data.length - bis.available()); + throwerId = dat.readInt(); + if (throwerId != 0) + { + speedScaledX = dat.readInt() / 8000D; + speedScaledY = dat.readInt() / 8000D; + speedScaledZ = dat.readInt() / 8000D; + } + + this.dataStream = dat; + return this; + } + + @Override + public void execute(NetworkManager network, FMLNetworkHandler handler, NetHandler netHandler, String userName) + { + NetworkModHandler nmh = handler.findNetworkModHandler(networkId); + ModContainer mc = nmh.getContainer(); + + EntityRegistration registration = EntityRegistry.instance().lookupModSpawn(mc, modEntityId); + Class cls = registration.getEntityClass(); + if (cls == null) + { + FMLLog.log(Level.WARNING, "Missing mod entity information for %s : %d", mc.getModId(), modEntityId); + return; + } + + + Entity entity = FMLCommonHandler.instance().spawnEntityIntoClientWorld(cls, this); + } + +} diff --git a/fml/common/cpw/mods/fml/common/network/FMLNetworkHandler.java b/fml/common/cpw/mods/fml/common/network/FMLNetworkHandler.java index 160325dd0..d27879a9a 100644 --- a/fml/common/cpw/mods/fml/common/network/FMLNetworkHandler.java +++ b/fml/common/cpw/mods/fml/common/network/FMLNetworkHandler.java @@ -7,6 +7,7 @@ import java.util.Map; import java.util.Set; import net.minecraft.server.MinecraftServer; +import net.minecraft.src.Entity; import net.minecraft.src.EntityPlayer; import net.minecraft.src.EntityPlayerMP; import net.minecraft.src.EnumGameType; @@ -28,6 +29,9 @@ import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.ModContainer; import cpw.mods.fml.common.discovery.ASMDataTable; +import cpw.mods.fml.common.network.FMLPacket.Type; +import cpw.mods.fml.common.registry.EntityRegistry; +import cpw.mods.fml.common.registry.EntityRegistry.EntityRegistration; import cpw.mods.fml.relauncher.FMLRelaunchLog; public class FMLNetworkHandler @@ -284,4 +288,18 @@ public class FMLNetworkHandler NetworkRegistry.instance().openLocalGui(instance().findNetworkModHandler(mod), (EntityPlayerMP) player, modGuiId, world, x, y, z); } } + + public static Packet getEntitySpawningPacket(Entity entity) + { + EntityRegistration er = EntityRegistry.instance().lookupModSpawn(entity.getClass(), false); + if (er == null) + { + return null; + } + Packet250CustomPayload pkt = new Packet250CustomPayload(); + pkt.field_73630_a = "FML"; + pkt.field_73629_c = FMLPacket.makePacket(Type.ENTITYSPAWN, er, entity); + pkt.field_73628_b = pkt.field_73629_c.length; + return pkt; + } } \ No newline at end of file diff --git a/fml/common/cpw/mods/fml/common/network/FMLPacket.java b/fml/common/cpw/mods/fml/common/network/FMLPacket.java index be83efb34..33c01a91c 100644 --- a/fml/common/cpw/mods/fml/common/network/FMLPacket.java +++ b/fml/common/cpw/mods/fml/common/network/FMLPacket.java @@ -39,7 +39,12 @@ public abstract class FMLPacket /** * Open a GUI on the client from the server */ - GUIOPEN(OpenGuiPacket.class); + GUIOPEN(OpenGuiPacket.class), + /** + * Spawn an entity on the client from the server + */ + ENTITYSPAWN(EntitySpawnPacket.class); + private Class packetType; diff --git a/fml/common/cpw/mods/fml/common/registry/EntityRegistry.java b/fml/common/cpw/mods/fml/common/registry/EntityRegistry.java index d889ece70..79ce1109a 100644 --- a/fml/common/cpw/mods/fml/common/registry/EntityRegistry.java +++ b/fml/common/cpw/mods/fml/common/registry/EntityRegistry.java @@ -3,8 +3,18 @@ package cpw.mods.fml.common.registry; import java.util.BitSet; import java.util.Iterator; import java.util.List; +import java.util.Map; +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.ListMultimap; +import com.google.common.collect.Maps; + +import cpw.mods.fml.common.FMLCommonHandler; +import cpw.mods.fml.common.FMLLog; +import cpw.mods.fml.common.ModContainer; import net.minecraft.src.BiomeGenBase; import net.minecraft.src.Entity; @@ -16,10 +26,43 @@ import net.minecraft.src.SpawnListEntry; public class EntityRegistry { + public class EntityRegistration + { + private Class entityClass; + private ModContainer container; + private String entityName; + private int modId; + public EntityRegistration(ModContainer mc, Class entityClass, String entityName, int id) + { + this.container = mc; + this.entityClass = entityClass; + this.entityName = entityName; + this.modId = id; + } + public Class getEntityClass() + { + return entityClass; + } + public ModContainer getContainer() + { + return container; + } + public String getEntityName() + { + return entityName; + } + public int getModEntityId() + { + return modId; + } + } + private static final EntityRegistry INSTANCE = new EntityRegistry(); private BitSet availableIndicies; - + private ListMultimap entityRegistrations = ArrayListMultimap.create(); + private BiMap entityNames = HashBiMap.create(); + private BiMap, EntityRegistration> entityClassRegistrations = HashBiMap.create(); public static EntityRegistry instance() { return INSTANCE; @@ -35,7 +78,28 @@ public class EntityRegistry } } - public static void registerEntityID(Class entityClass, String entityName, int id) + public static void registerModEntity(Class entityClass, String entityName, int id, Object mod) + { + instance().doModEntityRegistration(entityClass, entityName, id, mod); + } + private void doModEntityRegistration(Class entityClass, String entityName, int id, Object mod) + { + ModContainer mc = FMLCommonHandler.instance().findContainerFor(mod); + EntityRegistration er = new EntityRegistration(mc, entityClass, entityName, id); + try + { + entityClassRegistrations.put(entityClass, er); + entityNames.put(entityName, mc); + } + catch (IllegalArgumentException e) + { + FMLLog.log(Level.WARNING, e, "The mod %s tried to register the entity (name,class) (%s,%s) one or bothe of which are already registered", mc.getModId(), entityName, entityClass.getName()); + return; + } + entityRegistrations.put(mc, er); + } + + public static void registerGlobalEntityID(Class entityClass, String entityName, int id) { instance().validateAndClaimId(id); EntityList.func_75618_a(entityClass, entityName, id); @@ -50,7 +114,7 @@ public class EntityRegistry availableIndicies.clear(id); } - public static void registerEntityID(Class entityClass, String entityName, int id, int backgroundEggColour, int foregroundEggColour) + public static void registerGlobalEntityID(Class entityClass, String entityName, int id, int backgroundEggColour, int foregroundEggColour) { instance().validateAndClaimId(id); EntityList.func_75614_a(entityClass, entityName, id, backgroundEggColour, foregroundEggColour); @@ -127,4 +191,35 @@ public class EntityRegistry return res; } + public EntityRegistration lookupModSpawn(Class clazz, boolean keepLooking) + { + Class localClazz = clazz; + + do + { + EntityRegistration er = entityClassRegistrations.get(localClazz); + if (er != null) + { + return er; + } + localClazz = localClazz.getSuperclass(); + keepLooking = (!Object.class.equals(localClazz)); + } + while (keepLooking); + + return null; + } + + public EntityRegistration lookupModSpawn(ModContainer mc, int modEntityId) + { + for (EntityRegistration er : entityRegistrations.get(mc)) + { + if (er.getModEntityId() == modEntityId) + { + return er; + } + } + return null; + } + } diff --git a/fml/common/cpw/mods/fml/common/registry/IEntityAdditionalSpawnData.java b/fml/common/cpw/mods/fml/common/registry/IEntityAdditionalSpawnData.java new file mode 100644 index 000000000..db25bd59a --- /dev/null +++ b/fml/common/cpw/mods/fml/common/registry/IEntityAdditionalSpawnData.java @@ -0,0 +1,27 @@ +package cpw.mods.fml.common.registry; + +import com.google.common.io.ByteArrayDataInput; +import com.google.common.io.ByteArrayDataOutput; + +/** + * A interface for Entities that need extra information to be communicated + * between the server and client when they are spawned. + */ +public interface IEntityAdditionalSpawnData +{ + /** + * Called by the server when constructing the spawn packet. + * Data should be added to the provided stream. + * + * @param data The packet data stream + */ + public void writeSpawnData(ByteArrayDataOutput data); + + /** + * Called by the client when it receives a Entity spawn packet. + * Data should be read out of the stream in the same way as it was written. + * + * @param data The packet data stream + */ + public void readSpawnData(ByteArrayDataInput data); +} diff --git a/fml/common/cpw/mods/fml/common/registry/IThrowableEntity.java b/fml/common/cpw/mods/fml/common/registry/IThrowableEntity.java new file mode 100644 index 000000000..091c71014 --- /dev/null +++ b/fml/common/cpw/mods/fml/common/registry/IThrowableEntity.java @@ -0,0 +1,22 @@ +package cpw.mods.fml.common.registry; + +import net.minecraft.src.Entity; + +/** + * This interface should be implemented by an Entity that can be 'thrown', like snowballs. + * This was created to mimic ModLoaderMP's 'owner' functionality. + */ +public interface IThrowableEntity +{ + /** + * Gets the entity that threw/created this entity. + * @return The owner instance, Null if none. + */ + public Entity getThrower(); + + /** + * Sets the entity that threw/created this entity. + * @param entity The new thrower/creator. + */ + public void setThrower(Entity entity); +} diff --git a/fml/common/cpw/mods/fml/relauncher/FMLRelauncher.java b/fml/common/cpw/mods/fml/relauncher/FMLRelauncher.java index 89c6ccc79..836d6372a 100644 --- a/fml/common/cpw/mods/fml/relauncher/FMLRelauncher.java +++ b/fml/common/cpw/mods/fml/relauncher/FMLRelauncher.java @@ -25,6 +25,7 @@ public class FMLRelauncher { private static FMLRelauncher INSTANCE; public static String logFileNamePattern; + private static String side; private RelaunchClassLoader classLoader; private Object newApplet; private Class appletClass; @@ -34,12 +35,14 @@ public class FMLRelauncher public static void handleClientRelaunch(ArgsWrapper wrap) { logFileNamePattern = "ForgeModLoader-client-%g.log"; + side = "CLIENT"; instance().relaunchClient(wrap); } public static void handleServerRelaunch(ArgsWrapper wrap) { logFileNamePattern = "ForgeModLoader-server-%g.log"; + side = "SERVER"; instance().relaunchServer(wrap); } @@ -182,6 +185,8 @@ public class FMLRelauncher public static void appletEntry(Applet minecraftApplet) { + side = "CLIENT"; + logFileNamePattern = "ForgeModLoader-client-%g.log"; instance().relaunchApplet(minecraftApplet); } @@ -269,4 +274,9 @@ public class FMLRelauncher return inputStream; } } + + public static String side() + { + return side; + } } diff --git a/fml/patches/common/net/minecraft/src/EntityTrackerEntry.java.patch b/fml/patches/common/net/minecraft/src/EntityTrackerEntry.java.patch new file mode 100644 index 000000000..2b8b2c5ad --- /dev/null +++ b/fml/patches/common/net/minecraft/src/EntityTrackerEntry.java.patch @@ -0,0 +1,25 @@ +--- ../src-base/common/net/minecraft/src/EntityTrackerEntry.java ++++ ../src-work/common/net/minecraft/src/EntityTrackerEntry.java +@@ -4,6 +4,8 @@ + import java.util.Iterator; + import java.util.List; + import java.util.Set; ++ ++import cpw.mods.fml.common.network.FMLNetworkHandler; + + public class EntityTrackerEntry + { +@@ -306,6 +308,13 @@ + System.out.println("Fetching addPacket for removed entity"); + } + ++ Packet pkt = FMLNetworkHandler.getEntitySpawningPacket(this.field_73132_a); ++ ++ if (pkt != null) ++ { ++ return pkt; ++ } ++ + if (this.field_73132_a instanceof EntityItem) + { + EntityItem var8 = (EntityItem)this.field_73132_a;