initial set of experiments with RPC implementation

This commit is contained in:
SpaceToad 2014-01-06 19:57:54 +01:00
parent 8ee5270145
commit b1f5a4b96a
6 changed files with 348 additions and 0 deletions

View file

@ -0,0 +1,36 @@
package buildcraft.core;
import buildcraft.factory.TileRefinery;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import java.util.List;
import java.util.Random;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.texture.IconRegister;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
public class BlockPingPong extends BlockBuildCraft {
public static final Random rand = new Random();
public BlockPingPong(int id) {
super(id, Material.rock);
setBlockUnbreakable();
setResistance(6000000.0F);
setStepSound(soundStoneFootstep);
disableStats();
setTickRandomly(true);
setCreativeTab(CreativeTabBuildCraft.MACHINES.get());
}
@Override
public TileEntity createNewTileEntity(World var1) {
return new TilePingPong();
}
}

View file

@ -0,0 +1,42 @@
package buildcraft.core;
import buildcraft.api.core.SafeTimeTracker;
import buildcraft.core.network.RPC;
import buildcraft.core.network.RPCHandler;
import buildcraft.core.network.RPCMessageInfo;
import net.minecraft.tileentity.TileEntity;
public class TilePingPong extends TileBuildCraft {
@RPC
public void ping (int time, RPCMessageInfo info) {
System.out.println ("ping " + time);
RPCHandler.rpcPlayer(this, "pong", info.sender, time);
}
@RPC
public void pong (int time) {
System.out.println ("pong " + time);
}
SafeTimeTracker tracker;
@Override
public void updateEntity() {
super.updateEntity();
if (worldObj.isRemote) {
if (tracker == null) {
tracker = new SafeTimeTracker();
tracker.markTimeIfDelay(worldObj, 50);
}
if (tracker.markTimeIfDelay(worldObj, 50)) {
RPCHandler.rpcServer(this, "ping", (int) worldObj.getWorldTime());
}
}
}
}

View file

@ -0,0 +1,52 @@
package buildcraft.core.network;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import org.bouncycastle.crypto.util.Pack;
import cpw.mods.fml.common.network.Player;
public class PacketRPC extends BuildCraftPacket {
public TileEntity tile;
byte [] contents;
public EntityPlayer sender;
public PacketRPC () {
}
public PacketRPC (byte [] bytes) {
contents = bytes;
}
public void setTile (TileEntity aTile) {
tile = aTile;
}
@Override
public int getID() {
return PacketIds.RPC;
}
@Override
public void readData(DataInputStream data) throws IOException {
RPCMessageInfo info = new RPCMessageInfo();
info.sender = sender;
RPCHandler.receiveRPC(tile, info, data);
}
@Override
public void writeData(DataOutputStream data) throws IOException {
data.write(contents);
}
}

View file

@ -0,0 +1,11 @@
package buildcraft.core.network;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface RPC {
}

View file

@ -0,0 +1,200 @@
package buildcraft.core.network;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;
import org.apache.commons.lang3.ArrayUtils;
import buildcraft.core.proxy.CoreProxy;
import buildcraft.core.proxy.CoreProxyClient;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.common.network.Player;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.network.packet.Packet;
import net.minecraft.tileentity.TileEntity;
import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.common.MinecraftForge;
/**
* This is a first implementation of a RPC connector, using the regular tile
* synchronization layers as a communication protocol. As a result, these
* RPCs must be sent and received by a tile entity.
*/
public class RPCHandler {
private static Map <String, RPCHandler> handlers =
new TreeMap <String, RPCHandler> ();
private Map<String, Integer> methodsMap = new TreeMap<String, Integer>();
private Method [] methods;
public RPCHandler (Class c) {
methods = c.getMethods();
Arrays.sort(methods, new Comparator <Method> () {
@Override
public int compare(Method o1, Method o2) {
return o1.getName().compareTo(o2.getName());
}
});
LinkedList <Method> rpcMethods = new LinkedList<Method>();
for (int i = 0; i < methods.length; ++i) {
if (methods [i].getAnnotation (RPC.class) != null) {
methodsMap.put(methods [i].getName(), rpcMethods.size());
rpcMethods.add(methods [i]);
Class formals [] = methods [i].getParameterTypes();
for (int j = 0; i < formals.length; ++i) {
if (formals [j].equals(int.class)) {
// accepted
} else if (formals [j].equals(String.class)) {
// accepted
} else if (formals [j].equals(RPCMessageInfo.class)) {
// accepted
// FIXME: only if last one
} else {
throw new RuntimeException
("parameter type " + formals [j].getName() + " not supported in RPC.");
}
}
}
}
methods = rpcMethods.toArray(new Method [rpcMethods.size()]);
}
public static void rpcServer (TileEntity tile, String method, Object ... actuals) {
if (!handlers.containsKey(tile.getClass().getName())) {
handlers.put (tile.getClass().getName(), new RPCHandler (tile.getClass()));
}
PacketRPC packet = handlers.get (tile.getClass().getName()).createRCPPacket(tile, method, actuals);
if (packet != null) {
CoreProxy.proxy.sendToServer(packet.getPacket());
}
}
public static void rpcPlayer (TileEntity tile, String method, EntityPlayer player, Object ... actuals) {
if (!handlers.containsKey(tile.getClass().getName())) {
handlers.put (tile.getClass().getName(), new RPCHandler (tile.getClass()));
}
PacketRPC packet = handlers.get (tile.getClass().getName()).createRCPPacket(tile, method, actuals);
if (packet != null) {
CoreProxy.proxy.sendToPlayer(player, packet);
}
}
public static void receiveRPC (TileEntity tile, RPCMessageInfo info, DataInputStream data) {
if (!handlers.containsKey(tile.getClass().getName())) {
handlers.put (tile.getClass().getName(), new RPCHandler (tile.getClass()));
}
handlers.get (tile.getClass().getName()).internalRpcReceive(tile, info, data);
}
private PacketRPC createRCPPacket (TileEntity tile, String method, Object ... actuals) {
if (!methodsMap.containsKey(method)) {
throw new RuntimeException(method + " is not a callable method of " + getClass().getName());
}
int methodIndex = methodsMap.get(method);
Method m = methods [methodIndex];
Class formals [] = m.getParameterTypes();
boolean hasSpecialMessageInfo = formals.length > 0 && formals [formals.length - 1].equals(RPCMessageInfo.class);
int expectedParameters = hasSpecialMessageInfo ? formals.length - 1 : formals.length;
if (expectedParameters != actuals.length) {
// We accept formals + 1 as an argument, in order to support the
// special last argument RPCMessageInfo
throw new RuntimeException(getClass().getName() + "." + method
+ " expects " + m.getParameterTypes().length + "parameters, not " + actuals.length);
}
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
DataOutputStream data = new DataOutputStream(bytes);
try {
data.writeInt(tile.xCoord);
data.writeInt(tile.yCoord);
data.writeInt(tile.zCoord);
data.writeShort(methodIndex);
for (int i = 0; i < actuals.length; ++i) {
if (formals [i].equals(int.class)) {
data.writeInt((Integer) actuals [i]);
} else if (formals [i].equals(String.class)) {
data.writeUTF((String) actuals [i]);
}
}
data.flush();
} catch (IOException e) {
e.printStackTrace();
}
return new PacketRPC(bytes.toByteArray());
}
private void internalRpcReceive (TileEntity tile, RPCMessageInfo info, DataInputStream data) {
try {
short methodIndex = data.readShort();
Method m = methods [methodIndex];
Class formals [] = m.getParameterTypes();
Object [] actuals = new Object [formals.length];
boolean hasSpecialMessageInfo = formals.length > 0 && formals [formals.length - 1].equals(RPCMessageInfo.class);
int expectedParameters = hasSpecialMessageInfo ? formals.length - 1 : formals.length;
for (int i = 0; i < expectedParameters; ++i) {
if (formals [i].equals(int.class)) {
actuals [i] = data.readInt();
} else if (formals [i].equals(String.class)) {
actuals [i] = data.readUTF();
}
}
if (hasSpecialMessageInfo) {
actuals [actuals.length - 1] = info;
}
m.invoke(tile, actuals);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

View file

@ -0,0 +1,7 @@
package buildcraft.core.network;
import net.minecraft.entity.player.EntityPlayer;
public class RPCMessageInfo {
public EntityPlayer sender;
}