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