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