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