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