001 package cpw.mods.fml.common.network; 002 003 import static cpw.mods.fml.common.network.FMLPacket.Type.MOD_LIST_REQUEST; 004 005 import java.net.SocketAddress; 006 import java.util.Map; 007 import java.util.Set; 008 009 import net.minecraft.server.MinecraftServer; 010 import net.minecraft.src.Entity; 011 import net.minecraft.src.EntityPlayer; 012 import net.minecraft.src.EntityPlayerMP; 013 import net.minecraft.src.EnumGameType; 014 import net.minecraft.src.NetHandler; 015 import net.minecraft.src.NetLoginHandler; 016 import net.minecraft.src.NetServerHandler; 017 import net.minecraft.src.NetworkManager; 018 import net.minecraft.src.Packet; 019 import net.minecraft.src.Packet1Login; 020 import net.minecraft.src.Packet250CustomPayload; 021 import net.minecraft.src.ServerConfigurationManager; 022 import net.minecraft.src.World; 023 import net.minecraft.src.WorldType; 024 025 import com.google.common.collect.Maps; 026 import com.google.common.hash.Hashing; 027 028 import cpw.mods.fml.common.FMLCommonHandler; 029 import cpw.mods.fml.common.FMLLog; 030 import cpw.mods.fml.common.Loader; 031 import cpw.mods.fml.common.ModContainer; 032 import cpw.mods.fml.common.discovery.ASMDataTable; 033 import cpw.mods.fml.common.network.FMLPacket.Type; 034 import cpw.mods.fml.common.registry.EntityRegistry; 035 import cpw.mods.fml.common.registry.GameRegistry; 036 import cpw.mods.fml.common.registry.EntityRegistry.EntityRegistration; 037 038 public class FMLNetworkHandler 039 { 040 private static final int FML_HASH = Hashing.murmur3_32().hashString("FML").asInt(); 041 private static final int PROTOCOL_VERSION = 0x1; 042 private static final FMLNetworkHandler INSTANCE = new FMLNetworkHandler(); 043 044 private Map<NetLoginHandler, Integer> loginStates = Maps.newHashMap(); 045 private Map<ModContainer, NetworkModHandler> networkModHandlers = Maps.newHashMap(); 046 047 private Map<Integer, NetworkModHandler> networkIdLookup = Maps.newHashMap(); 048 049 public static void handlePacket250Packet(Packet250CustomPayload packet, NetworkManager network, NetHandler handler) 050 { 051 String target = packet.channel; 052 053 if (target.startsWith("MC|")) 054 { 055 handler.handleVanilla250Packet(packet); 056 } 057 if (target.equals("FML")) 058 { 059 instance().handleFMLPacket(packet, network, handler); 060 } 061 else 062 { 063 NetworkRegistry.instance().handleCustomPacket(packet, network, handler); 064 } 065 } 066 067 public static void onConnectionEstablishedToServer(NetHandler clientHandler, NetworkManager manager, Packet1Login login) 068 { 069 NetworkRegistry.instance().clientLoggedIn(clientHandler, manager, login); 070 } 071 072 private void handleFMLPacket(Packet250CustomPayload packet, NetworkManager network, NetHandler netHandler) 073 { 074 FMLPacket pkt = FMLPacket.readPacket(packet.data); 075 String userName = ""; 076 if (netHandler instanceof NetLoginHandler) 077 { 078 userName = ((NetLoginHandler) netHandler).clientUsername; 079 } 080 else 081 { 082 EntityPlayer pl = netHandler.getPlayer(); 083 if (pl != null) 084 { 085 userName = pl.getCommandSenderName(); 086 } 087 } 088 089 pkt.execute(network, this, netHandler, userName); 090 } 091 092 public static void onConnectionReceivedFromClient(NetLoginHandler netLoginHandler, MinecraftServer server, SocketAddress address, String userName) 093 { 094 instance().handleClientConnection(netLoginHandler, server, address, userName); 095 } 096 097 private void handleClientConnection(NetLoginHandler netLoginHandler, MinecraftServer server, SocketAddress address, String userName) 098 { 099 if (!loginStates.containsKey(netLoginHandler)) 100 { 101 if (handleVanillaLoginKick(netLoginHandler, server, address, userName)) 102 { 103 // No FML on the client 104 FMLLog.fine("Connection from %s rejected - no FML packet received from client", userName); 105 netLoginHandler.completeConnection("You don't have FML installed, or your installation is too old"); 106 return; 107 } 108 109 } 110 // Are we ready to negotiate with the client? 111 if (loginStates.get(netLoginHandler) == 1) 112 { 113 // mods can try and kick undesireables here 114 String modKick = NetworkRegistry.instance().connectionReceived(netLoginHandler, netLoginHandler.myTCPConnection); 115 if (modKick != null) 116 { 117 netLoginHandler.completeConnection(modKick); 118 loginStates.remove(netLoginHandler); 119 return; 120 } 121 // The vanilla side wanted to kick 122 if (!handleVanillaLoginKick(netLoginHandler, server, address, userName)) 123 { 124 loginStates.remove(netLoginHandler); 125 return; 126 } 127 128 // Reset the "connection completed" flag so processing can continue 129 NetLoginHandler.func_72531_a(netLoginHandler, false); 130 // Send the mod list request packet to the client from the server 131 netLoginHandler.myTCPConnection.addToSendQueue(getModListRequestPacket()); 132 loginStates.put(netLoginHandler, 2); 133 } 134 // We must be good to go - the ModIdentifiers packet was sent and the 135 // continuation signal was indicated 136 else if (loginStates.get(netLoginHandler) == 2) 137 { 138 netLoginHandler.completeConnection(null); 139 loginStates.remove(netLoginHandler); 140 } 141 else if (loginStates.get(netLoginHandler) == 3) 142 { 143 netLoginHandler.completeConnection("The server requires mods that are missing on your client"); 144 loginStates.remove(netLoginHandler); 145 } 146 // We have to abort this connection - there was a negotiation problem 147 // (most likely missing mods) 148 else 149 { 150 netLoginHandler.completeConnection("There was a problem during FML negotiation"); 151 loginStates.remove(netLoginHandler); 152 } 153 } 154 155 /** 156 * @param netLoginHandler 157 * @param server 158 * @param address 159 * @param userName 160 * @return if the user can carry on 161 */ 162 private boolean handleVanillaLoginKick(NetLoginHandler netLoginHandler, MinecraftServer server, SocketAddress address, String userName) 163 { 164 // Vanilla reasons first 165 ServerConfigurationManager playerList = server.getConfigurationManager(); 166 String kickReason = playerList.allowUserToConnect(address, userName); 167 168 if (kickReason != null) 169 { 170 netLoginHandler.completeConnection(kickReason); 171 } 172 return kickReason == null; 173 } 174 175 public static void handleLoginPacketOnServer(NetLoginHandler handler, Packet1Login login) 176 { 177 if (login.clientEntityId == FML_HASH && login.dimension == PROTOCOL_VERSION) 178 { 179 FMLLog.finest("Received valid FML login packet from %s", handler.myTCPConnection.getSocketAddress()); 180 instance().loginStates.put(handler, 1); 181 } 182 else 183 { 184 FMLLog.fine("Received invalid FML login packet %d, %d from %s", login.clientEntityId, login.dimension, 185 handler.myTCPConnection.getSocketAddress()); 186 } 187 } 188 189 static void setHandlerState(NetLoginHandler handler, int state) 190 { 191 instance().loginStates.put(handler, state); 192 } 193 194 public static FMLNetworkHandler instance() 195 { 196 return INSTANCE; 197 } 198 199 public static Packet1Login getFMLFakeLoginPacket() 200 { 201 Packet1Login fake = new Packet1Login(); 202 // Hash FML using a simple function 203 fake.clientEntityId = FML_HASH; 204 // The FML protocol version 205 fake.dimension = PROTOCOL_VERSION; 206 fake.gameType = EnumGameType.NOT_SET; 207 fake.terrainType = WorldType.worldTypes[0]; 208 return fake; 209 } 210 211 public Packet250CustomPayload getModListRequestPacket() 212 { 213 Packet250CustomPayload pkt = new Packet250CustomPayload(); 214 pkt.channel = "FML"; 215 pkt.data = FMLPacket.makePacket(MOD_LIST_REQUEST); 216 pkt.length = pkt.data.length; 217 return pkt; 218 } 219 220 public void registerNetworkMod(NetworkModHandler handler) 221 { 222 networkModHandlers.put(handler.getContainer(), handler); 223 networkIdLookup.put(handler.getNetworkId(), handler); 224 } 225 public boolean registerNetworkMod(ModContainer container, Class<?> networkModClass, ASMDataTable asmData) 226 { 227 NetworkModHandler handler = new NetworkModHandler(container, networkModClass, asmData); 228 if (handler.isNetworkMod()) 229 { 230 registerNetworkMod(handler); 231 } 232 233 return handler.isNetworkMod(); 234 } 235 236 public NetworkModHandler findNetworkModHandler(Object mc) 237 { 238 if (mc instanceof ModContainer) 239 { 240 return networkModHandlers.get(mc); 241 } 242 else if (mc instanceof Integer) 243 { 244 return networkIdLookup.get(mc); 245 } 246 else 247 { 248 return networkModHandlers.get(FMLCommonHandler.instance().findContainerFor(mc)); 249 } 250 } 251 252 public Set<ModContainer> getNetworkModList() 253 { 254 return networkModHandlers.keySet(); 255 } 256 257 public static void handlePlayerLogin(EntityPlayerMP player, NetServerHandler netHandler, NetworkManager manager) 258 { 259 NetworkRegistry.instance().playerLoggedIn(player, netHandler, manager); 260 GameRegistry.onPlayerLogin(player); 261 } 262 263 public Map<Integer, NetworkModHandler> getNetworkIdMap() 264 { 265 return networkIdLookup; 266 } 267 268 public void bindNetworkId(String key, Integer value) 269 { 270 Map<String, ModContainer> mods = Loader.instance().getIndexedModList(); 271 NetworkModHandler handler = findNetworkModHandler(mods.get(key)); 272 if (handler != null) 273 { 274 handler.setNetworkId(value); 275 networkIdLookup.put(value, handler); 276 } 277 } 278 279 public static void onClientConnectionToRemoteServer(NetHandler netClientHandler, String server, int port, NetworkManager networkManager) 280 { 281 NetworkRegistry.instance().connectionOpened(netClientHandler, server, port, networkManager); 282 } 283 284 public static void onClientConnectionToIntegratedServer(NetHandler netClientHandler, MinecraftServer server, NetworkManager networkManager) 285 { 286 NetworkRegistry.instance().connectionOpened(netClientHandler, server, networkManager); 287 } 288 289 public static void onConnectionClosed(NetworkManager manager) 290 { 291 NetworkRegistry.instance().connectionClosed(manager); 292 } 293 294 295 public static void sendPacket(Player player, Packet packet) 296 { 297 } 298 299 public static void openGui(EntityPlayer player, Object mod, int modGuiId, World world, int x, int y, int z) 300 { 301 ModContainer mc = FMLCommonHandler.instance().findContainerFor(mod); 302 if (mc == null) 303 { 304 NetworkModHandler nmh = instance().findNetworkModHandler(mod); 305 if (nmh != null) 306 { 307 mc = nmh.getContainer(); 308 } 309 else 310 { 311 FMLLog.warning("A mod tried to open a gui on the server without being a NetworkMod"); 312 return; 313 } 314 } 315 if (player instanceof EntityPlayerMP) 316 { 317 NetworkRegistry.instance().openRemoteGui(mc, (EntityPlayerMP) player, modGuiId, world, x, y, z); 318 } 319 else 320 { 321 NetworkRegistry.instance().openLocalGui(mc, player, modGuiId, world, x, y, z); 322 } 323 } 324 325 public static Packet getEntitySpawningPacket(Entity entity) 326 { 327 EntityRegistration er = EntityRegistry.instance().lookupModSpawn(entity.getClass(), false); 328 if (er == null) 329 { 330 return null; 331 } 332 if (er.usesVanillaSpawning()) 333 { 334 return null; 335 } 336 Packet250CustomPayload pkt = new Packet250CustomPayload(); 337 pkt.channel = "FML"; 338 pkt.data = FMLPacket.makePacket(Type.ENTITYSPAWN, er, entity, instance().findNetworkModHandler(er.getContainer())); 339 pkt.length = pkt.data.length; 340 return pkt; 341 } 342 343 public static void makeEntitySpawnAdjustment(int entityId, EntityPlayerMP player, int serverX, int serverY, int serverZ) 344 { 345 Packet250CustomPayload pkt = new Packet250CustomPayload(); 346 pkt.channel = "FML"; 347 pkt.data = FMLPacket.makePacket(Type.ENTITYSPAWNADJUSTMENT, entityId, serverX, serverY, serverZ); 348 pkt.length = pkt.data.length; 349 350 player.serverForThisPlayer.sendPacketToPlayer(pkt); 351 } 352 }