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