001 package cpw.mods.fml.common.network;
002
003 import java.util.Arrays;
004 import java.util.concurrent.ConcurrentMap;
005 import java.util.logging.Level;
006
007 import net.minecraft.network.INetworkManager;
008 import net.minecraft.network.packet.NetHandler;
009
010 import com.google.common.base.Throwables;
011 import com.google.common.collect.MapMaker;
012 import com.google.common.primitives.Bytes;
013 import com.google.common.primitives.Ints;
014 import com.google.common.primitives.UnsignedBytes;
015
016 import cpw.mods.fml.common.FMLLog;
017
018 public abstract class FMLPacket
019 {
020 enum Type
021 {
022 /**
023 * Opening salutation from the server to the client -> request all mods from the client
024 */
025 MOD_LIST_REQUEST(ModListRequestPacket.class, false),
026 /**
027 * The client responds with the list of mods and versions it has. This is verified by the server.
028 */
029 MOD_LIST_RESPONSE(ModListResponsePacket.class, false),
030 /**
031 * At which point the server tells the client the mod identifiers for this session.
032 */
033 MOD_IDENTIFIERS(ModIdentifiersPacket.class, false),
034 /**
035 * Or, if there is missing stuff, the server tells the client what's missing and drops the connection.
036 */
037 MOD_MISSING(ModMissingPacket.class, false),
038 /**
039 * Open a GUI on the client from the server
040 */
041 GUIOPEN(OpenGuiPacket.class, false),
042 /**
043 * Spawn an entity on the client from the server
044 */
045 ENTITYSPAWN(EntitySpawnPacket.class, false),
046 /**
047 * Fixes entity location data after spawning
048 */
049 ENTITYSPAWNADJUSTMENT(EntitySpawnAdjustmentPacket.class, false),
050 /**
051 * The ID map to send to the client
052 */
053 MOD_IDMAP(ModIdMapPacket.class, true);
054
055
056 private Class<? extends FMLPacket> packetType;
057 private boolean isMultipart;
058 private ConcurrentMap<INetworkManager, FMLPacket> partTracker;
059
060 private Type(Class<? extends FMLPacket> clazz, boolean isMultipart)
061 {
062 this.packetType = clazz;
063 this.isMultipart = isMultipart;
064 }
065
066 FMLPacket make()
067 {
068 try
069 {
070 return this.packetType.newInstance();
071 }
072 catch (Exception e)
073 {
074 Throwables.propagateIfPossible(e);
075 FMLLog.log(Level.SEVERE, e, "A bizarre critical error occured during packet encoding");
076 throw new FMLNetworkException(e);
077 }
078 }
079
080 public boolean isMultipart()
081 {
082 return isMultipart;
083 }
084
085 private FMLPacket findCurrentPart(INetworkManager network)
086 {
087 if (partTracker == null)
088 {
089 partTracker = new MapMaker().weakKeys().weakValues().makeMap();
090 }
091 if (!partTracker.containsKey(network))
092 {
093 partTracker.put(network, make());
094 }
095 return partTracker.get(network);
096 }
097 }
098
099 private Type type;
100
101 public static byte[][] makePacketSet(Type type, Object... data)
102 {
103 if (!type.isMultipart())
104 {
105 return new byte[0][];
106 }
107 byte[] packetData = type.make().generatePacket(data);
108
109 byte[][] chunks = new byte[packetData.length / 32000 + 1][];
110 for (int i = 0; i < packetData.length / 32000 + 1; i++)
111 {
112 int len = Math.min(32000, packetData.length - i* 32000);
113 chunks[i] = Bytes.concat(new byte[] { UnsignedBytes.checkedCast(type.ordinal()), UnsignedBytes.checkedCast(i), UnsignedBytes.checkedCast(chunks.length)}, Ints.toByteArray(len), Arrays.copyOfRange(packetData, i * 32000, len));
114 }
115 return chunks;
116 }
117 public static byte[] makePacket(Type type, Object... data)
118 {
119 byte[] packetData = type.make().generatePacket(data);
120 return Bytes.concat(new byte[] { UnsignedBytes.checkedCast(type.ordinal()) }, packetData );
121 }
122
123 public static FMLPacket readPacket(INetworkManager network, byte[] payload)
124 {
125 int type = UnsignedBytes.toInt(payload[0]);
126 Type eType = Type.values()[type];
127 FMLPacket pkt;
128 if (eType.isMultipart())
129 {
130 pkt = eType.findCurrentPart(network);
131 }
132 else
133 {
134 pkt = eType.make();
135 }
136 return pkt.consumePacket(Arrays.copyOfRange(payload, 1, payload.length));
137 }
138
139 public FMLPacket(Type type)
140 {
141 this.type = type;
142 }
143
144 public abstract byte[] generatePacket(Object... data);
145
146 public abstract FMLPacket consumePacket(byte[] data);
147
148 public abstract void execute(INetworkManager network, FMLNetworkHandler handler, NetHandler netHandler, String userName);
149 }