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