Entity spawning - done

This commit is contained in:
Christian 2012-08-09 01:58:14 -04:00
parent d970f7a530
commit 185babbd85
12 changed files with 475 additions and 5 deletions

View file

@ -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<? extends Entity> 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);
}
}
}

View file

@ -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<? extends Entity> cls, EntitySpawnPacket entitySpawnPacket)
{
return sidedDelegate.spawnEntityIntoClientWorld(cls, entitySpawnPacket);
}
}

View file

@ -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<? extends Entity> entityClass, EntitySpawnPacket packet);
}

View file

@ -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)

View file

@ -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<? extends Entity> 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);
}
}

View file

@ -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;
}
}

View file

@ -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<? extends FMLPacket> packetType;

View file

@ -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<? extends Entity> entityClass;
private ModContainer container;
private String entityName;
private int modId;
public EntityRegistration(ModContainer mc, Class<? extends Entity> entityClass, String entityName, int id)
{
this.container = mc;
this.entityClass = entityClass;
this.entityName = entityName;
this.modId = id;
}
public Class<? extends Entity> 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<ModContainer, EntityRegistration> entityRegistrations = ArrayListMultimap.create();
private BiMap<String,ModContainer> entityNames = HashBiMap.create();
private BiMap<Class<? extends Entity>, EntityRegistration> entityClassRegistrations = HashBiMap.create();
public static EntityRegistry instance()
{
return INSTANCE;
@ -35,7 +78,28 @@ public class EntityRegistry
}
}
public static void registerEntityID(Class <? extends Entity > entityClass, String entityName, int id)
public static void registerModEntity(Class<? extends Entity> entityClass, String entityName, int id, Object mod)
{
instance().doModEntityRegistration(entityClass, entityName, id, mod);
}
private void doModEntityRegistration(Class<? extends Entity> 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 <? extends Entity > 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 <? extends Entity > entityClass, String entityName, int id, int backgroundEggColour, int foregroundEggColour)
public static void registerGlobalEntityID(Class <? extends Entity > 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<? extends Entity> 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;
}
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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<? super Object> 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;
}
}

View file

@ -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;