package cpw.mods.fml.common.network; import java.lang.reflect.Method; import java.util.Set; import java.util.logging.Level; import net.minecraft.item.Item; import com.google.common.base.Strings; import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.FMLLog; import cpw.mods.fml.common.ModContainer; import cpw.mods.fml.common.discovery.ASMDataTable; import cpw.mods.fml.common.discovery.ASMDataTable.ASMData; import cpw.mods.fml.common.versioning.DefaultArtifactVersion; import cpw.mods.fml.common.versioning.InvalidVersionSpecificationException; import cpw.mods.fml.common.versioning.VersionRange; import cpw.mods.fml.relauncher.Side; public class NetworkModHandler { private static Object connectionHandlerDefaultValue; private static Object packetHandlerDefaultValue; private static Object clientHandlerDefaultValue; private static Object serverHandlerDefaultValue; private static Object tinyPacketHandlerDefaultValue; private static int assignedIds = 1; private int localId; private int networkId; private ModContainer container; private NetworkMod mod; private Method checkHandler; private VersionRange acceptableRange; private ITinyPacketHandler tinyPacketHandler; public NetworkModHandler(ModContainer container, NetworkMod modAnnotation) { this.container = container; this.mod = modAnnotation; this.localId = assignedIds++; this.networkId = this.localId; // Skip over the map object because it has special network id meaning if (Item.map.itemID == assignedIds) { assignedIds++; } } public NetworkModHandler(ModContainer container, Class<?> networkModClass, ASMDataTable table) { this(container, networkModClass.getAnnotation(NetworkMod.class)); if (this.mod == null) { return; } Set<ASMData> versionCheckHandlers = table.getAnnotationsFor(container).get(NetworkMod.VersionCheckHandler.class.getName()); String versionCheckHandlerMethod = null; for (ASMData vch : versionCheckHandlers) { if (vch.getClassName().equals(networkModClass.getName())) { versionCheckHandlerMethod = vch.getObjectName(); break; } } if (versionCheckHandlerMethod != null) { try { Method checkHandlerMethod = networkModClass.getDeclaredMethod(versionCheckHandlerMethod, String.class); if (checkHandlerMethod.isAnnotationPresent(NetworkMod.VersionCheckHandler.class)) { this.checkHandler = checkHandlerMethod; } } catch (Exception e) { FMLLog.log(Level.WARNING, e, "The declared version check handler method %s on network mod id %s is not accessible", versionCheckHandlerMethod, container.getModId()); } } if (this.checkHandler == null) { String versionBounds = mod.versionBounds(); if (!Strings.isNullOrEmpty(versionBounds)) { try { this.acceptableRange = VersionRange.createFromVersionSpec(versionBounds); } catch (InvalidVersionSpecificationException e) { FMLLog.log(Level.WARNING, e, "Invalid bounded range %s specified for network mod id %s", versionBounds, container.getModId()); } } } FMLLog.finest("Testing mod %s to verify it accepts its own version in a remote connection", container.getModId()); boolean acceptsSelf = acceptVersion(container.getVersion()); if (!acceptsSelf) { FMLLog.severe("The mod %s appears to reject its own version number (%s) in its version handling. This is likely a severe bug in the mod!", container.getModId(), container.getVersion()); } else { FMLLog.finest("The mod %s accepts its own version (%s)", container.getModId(), container.getVersion()); } tryCreatingPacketHandler(container, mod.packetHandler(), mod.channels(), null); if (FMLCommonHandler.instance().getSide().isClient()) { if (mod.clientPacketHandlerSpec() != getClientHandlerSpecDefaultValue()) { tryCreatingPacketHandler(container, mod.clientPacketHandlerSpec().packetHandler(), mod.clientPacketHandlerSpec().channels(), Side.CLIENT); } } if (mod.serverPacketHandlerSpec() != getServerHandlerSpecDefaultValue()) { tryCreatingPacketHandler(container, mod.serverPacketHandlerSpec().packetHandler(), mod.serverPacketHandlerSpec().channels(), Side.SERVER); } if (mod.connectionHandler() != getConnectionHandlerDefaultValue()) { IConnectionHandler instance; try { instance = mod.connectionHandler().newInstance(); } catch (Exception e) { FMLLog.log(Level.SEVERE, e, "Unable to create connection handler instance %s", mod.connectionHandler().getName()); throw new FMLNetworkException(e); } NetworkRegistry.instance().registerConnectionHandler(instance); } if (mod.tinyPacketHandler()!=getTinyPacketHandlerDefaultValue()) { try { tinyPacketHandler = mod.tinyPacketHandler().newInstance(); } catch (Exception e) { FMLLog.log(Level.SEVERE, e, "Unable to create tiny packet handler instance %s", mod.tinyPacketHandler().getName()); throw new FMLNetworkException(e); } } } /** * @param container */ private void tryCreatingPacketHandler(ModContainer container, Class<? extends IPacketHandler> clazz, String[] channels, Side side) { if (side!=null && side.isClient() && ! FMLCommonHandler.instance().getSide().isClient()) { return; } if (clazz!=getPacketHandlerDefaultValue()) { if (channels.length==0) { FMLLog.log(Level.WARNING, "The mod id %s attempted to register a packet handler without specifying channels for it", container.getModId()); } else { IPacketHandler instance; try { instance = clazz.newInstance(); } catch (Exception e) { FMLLog.log(Level.SEVERE, e, "Unable to create a packet handler instance %s for mod %s", clazz.getName(), container.getModId()); throw new FMLNetworkException(e); } for (String channel : channels) { NetworkRegistry.instance().registerChannel(instance, channel, side); } } } else if (channels.length > 0) { FMLLog.warning("The mod id %s attempted to register channels without specifying a packet handler", container.getModId()); } } /** * @return */ private Object getConnectionHandlerDefaultValue() { try { if (connectionHandlerDefaultValue == null) { connectionHandlerDefaultValue = NetworkMod.class.getMethod("connectionHandler").getDefaultValue(); } return connectionHandlerDefaultValue; } catch (NoSuchMethodException e) { throw new RuntimeException("Derp?", e); } } /** * @return */ private Object getPacketHandlerDefaultValue() { try { if (packetHandlerDefaultValue == null) { packetHandlerDefaultValue = NetworkMod.class.getMethod("packetHandler").getDefaultValue(); } return packetHandlerDefaultValue; } catch (NoSuchMethodException e) { throw new RuntimeException("Derp?", e); } } private Object getTinyPacketHandlerDefaultValue() { try { if (tinyPacketHandlerDefaultValue == null) { tinyPacketHandlerDefaultValue = NetworkMod.class.getMethod("tinyPacketHandler").getDefaultValue(); } return tinyPacketHandlerDefaultValue; } catch (NoSuchMethodException e) { throw new RuntimeException("Derp?", e); } } /** * @return */ private Object getClientHandlerSpecDefaultValue() { try { if (clientHandlerDefaultValue == null) { clientHandlerDefaultValue = NetworkMod.class.getMethod("clientPacketHandlerSpec").getDefaultValue(); } return clientHandlerDefaultValue; } catch (NoSuchMethodException e) { throw new RuntimeException("Derp?", e); } } /** * @return */ private Object getServerHandlerSpecDefaultValue() { try { if (serverHandlerDefaultValue == null) { serverHandlerDefaultValue = NetworkMod.class.getMethod("serverPacketHandlerSpec").getDefaultValue(); } return serverHandlerDefaultValue; } catch (NoSuchMethodException e) { throw new RuntimeException("Derp?", e); } } public boolean requiresClientSide() { return mod.clientSideRequired(); } public boolean requiresServerSide() { return mod.serverSideRequired(); } public boolean acceptVersion(String version) { if (checkHandler != null) { try { return (Boolean)checkHandler.invoke(container.getMod(), version); } catch (Exception e) { FMLLog.log(Level.WARNING, e, "There was a problem invoking the checkhandler method %s for network mod id %s", checkHandler.getName(), container.getModId()); return false; } } if (acceptableRange!=null) { return acceptableRange.containsVersion(new DefaultArtifactVersion(version)); } return container.getVersion().equals(version); } public int getLocalId() { return localId; } public int getNetworkId() { return networkId; } public ModContainer getContainer() { return container; } public NetworkMod getMod() { return mod; } public boolean isNetworkMod() { return mod != null; } public void setNetworkId(int value) { this.networkId = value; } public boolean hasTinyPacketHandler() { return tinyPacketHandler != null; } public ITinyPacketHandler getTinyPacketHandler() { return tinyPacketHandler; } }