From f5c38e2359c2e6eca13cd6606465ee36086a7113 Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 6 Dec 2013 14:52:33 -0500 Subject: [PATCH] OK, network channels appear to be working well for both dedi and integrated servers. The beginnings of the new mod structure too. --- .../net/minecraft/client/Minecraft.java.patch | 16 --- .../cpw/mods/fml/client/FMLClientHandler.java | 9 +- .../cpw/mods/fml/common/FMLModContainer.java | 3 +- src/main/java/cpw/mods/fml/common/Mod.java | 22 +++-- .../network/ClientSidePacketHandler.java | 23 ----- .../FMLIndexedMessageToMessageCodec.java | 2 +- .../fml/common/network/FMLNetworkHandler.java | 40 +++++++- .../fml/common/network/IPacketHandler.java | 11 --- .../common/network/IPacketHandlerFactory.java | 52 ---------- .../common/network/NetworkCheckHandler.java | 6 ++ .../mods/fml/common/network/NetworkMod.java | 58 ----------- .../fml/common/network/NetworkModHolder.java | 99 ++++++++++--------- .../fml/common/network/NetworkRegistry.java | 14 +++ .../network/ServerSidePacketHandler.java | 20 ---- .../handshake/FMLHandshakeClientState.java | 4 - .../handshake/FMLHandshakeMessage.java | 30 +++--- .../handshake/FMLHandshakeServerState.java | 18 +++- .../network/handshake/NetworkDispatcher.java | 16 ++- 18 files changed, 168 insertions(+), 275 deletions(-) delete mode 100644 src/main/java/cpw/mods/fml/common/network/ClientSidePacketHandler.java delete mode 100644 src/main/java/cpw/mods/fml/common/network/IPacketHandler.java delete mode 100644 src/main/java/cpw/mods/fml/common/network/IPacketHandlerFactory.java create mode 100644 src/main/java/cpw/mods/fml/common/network/NetworkCheckHandler.java delete mode 100644 src/main/java/cpw/mods/fml/common/network/NetworkMod.java delete mode 100644 src/main/java/cpw/mods/fml/common/network/ServerSidePacketHandler.java diff --git a/patches/minecraft/net/minecraft/client/Minecraft.java.patch b/patches/minecraft/net/minecraft/client/Minecraft.java.patch index b8f5b18a98..2d13c36f19 100644 --- a/patches/minecraft/net/minecraft/client/Minecraft.java.patch +++ b/patches/minecraft/net/minecraft/client/Minecraft.java.patch @@ -81,19 +81,3 @@ this.field_71424_I.func_76319_b(); this.field_71423_H = func_71386_F(); } -@@ -2046,6 +2063,7 @@ - networkmanager.func_150725_a(new C00Handshake(4, socketaddress.toString(), 0, EnumConnectionState.LOGIN), new GenericFutureListener[0]); - networkmanager.func_150725_a(new C00PacketLoginStart(this.func_110432_I().func_148256_e()), new GenericFutureListener[0]); - this.field_71453_ak = networkmanager; -+ FMLClientHandler.instance().currentConnection(networkmanager, true); - } - - public void func_71403_a(WorldClient p_71403_1_) -@@ -2076,6 +2094,7 @@ - - this.field_71451_h = null; - this.field_71453_ak = null; -+ FMLClientHandler.instance().currentConnection(null, p_71353_1_ == null); - - if (this.field_71461_s != null) - { diff --git a/src/main/java/cpw/mods/fml/client/FMLClientHandler.java b/src/main/java/cpw/mods/fml/client/FMLClientHandler.java index b151bce675..66fdfddd13 100644 --- a/src/main/java/cpw/mods/fml/client/FMLClientHandler.java +++ b/src/main/java/cpw/mods/fml/client/FMLClientHandler.java @@ -123,8 +123,6 @@ public class FMLClientHandler implements IFMLSidedHandler private Map resourcePackMap; - private NetworkManager networkConnection; - /** * Called to start the whole game off * @@ -571,11 +569,6 @@ public class FMLClientHandler implements IFMLSidedHandler @Override public NetworkManager getClientToServerNetworkManager() { - return this.networkConnection; - } - - public void currentConnection(NetworkManager networkManager, boolean shouldChange) - { - if (shouldChange) this.networkConnection = networkManager; + return this.client.func_147114_u()!=null ? this.client.func_147114_u().func_147298_b() : null; } } diff --git a/src/main/java/cpw/mods/fml/common/FMLModContainer.java b/src/main/java/cpw/mods/fml/common/FMLModContainer.java index cebec194b0..1182e8c9ed 100644 --- a/src/main/java/cpw/mods/fml/common/FMLModContainer.java +++ b/src/main/java/cpw/mods/fml/common/FMLModContainer.java @@ -61,7 +61,7 @@ import cpw.mods.fml.common.event.FMLServerStartedEvent; import cpw.mods.fml.common.event.FMLServerStartingEvent; import cpw.mods.fml.common.event.FMLServerStoppedEvent; import cpw.mods.fml.common.event.FMLServerStoppingEvent; -import cpw.mods.fml.common.network.FMLNetworkHandler; +import cpw.mods.fml.common.network.NetworkRegistry; import cpw.mods.fml.common.versioning.ArtifactVersion; import cpw.mods.fml.common.versioning.DefaultArtifactVersion; import cpw.mods.fml.common.versioning.VersionParser; @@ -494,6 +494,7 @@ public class FMLModContainer implements ModContainer Method factoryMethod = gatherAnnotations(clazz); modInstance = getLanguageAdapter().getNewInstance(this,clazz, modClassLoader, factoryMethod); + NetworkRegistry.INSTANCE.register(this, clazz, (String)(descriptor.containsKey("acceptableRemoteVersions") ? descriptor.get("acceptableRemoteVersions") : null), event.getASMHarvestedData()); if (fingerprintNotPresent) { eventBus.post(new FMLFingerprintViolationEvent(source.isDirectory(), source, ImmutableSet.copyOf(this.sourceFingerprints), expectedFingerprint)); diff --git a/src/main/java/cpw/mods/fml/common/Mod.java b/src/main/java/cpw/mods/fml/common/Mod.java index 96f04fa635..ddbd60bc93 100644 --- a/src/main/java/cpw/mods/fml/common/Mod.java +++ b/src/main/java/cpw/mods/fml/common/Mod.java @@ -29,8 +29,7 @@ import cpw.mods.fml.common.event.FMLServerStartedEvent; import cpw.mods.fml.common.event.FMLServerStartingEvent; import cpw.mods.fml.common.event.FMLServerStoppedEvent; import cpw.mods.fml.common.event.FMLServerStoppingEvent; -import cpw.mods.fml.common.network.IPacketHandler; -import cpw.mods.fml.common.network.NetworkMod; +import cpw.mods.fml.common.network.NetworkCheckHandler; import cpw.mods.fml.common.registry.GameRegistry; /** @@ -40,19 +39,12 @@ import cpw.mods.fml.common.registry.GameRegistry; * at pre-defined times during the loading of the game, based on where you have applied the {@link EventHandler} * annotation. * - * This annotation is usually paired with a second annotation {@link NetworkMod}, which declares the - * network related properties of this mod. - * *

This is a simple example of a Mod. It has the modId of "MyModId", the name of "My example mod", it is - * version 1.0, and depends on FML being loaded. It has the {@link NetworkMod} annotation as well, declaring it uses channel - * "MyModChannel" and {@link IPacketHandler} class PacketHandler. + * version 1.0, and depends on FML being loaded. *

{@code
  * package mymod;
  * // Declare that this is a mod with modId "MyModId", name "My example mod", version "1.0" and dependency on FML.
  * {@literal @}Mod(modId="MyModId",name="My example mod",version="1.0",dependencies="required-after:FML")
- * // Declare that this is a network mod using the {@link Packet250CustomPayload} channel "MyModChannel", required on the client if it's present
- * // on the server, with {@link IPacketHandler} class PacketHandler.
- * {@literal @}NetworkMod(channels = { "MyModChannel" }, clientSideRequired = true, serverSideRequired = false, packetHandler = PacketHandler.class)
  * public class MyMod {
  *      // Populate this field with the instance of the mod created by FML
  *      {@literal @}Instance("MyModId")
@@ -103,6 +95,16 @@ public @interface Mod
      * @return A version range as specified by the maven version range specification or the empty string
      */
     String acceptedMinecraftVersions() default "";
+    /**
+     * A replacement for the no-longer-existing "versionRange" of NetworkMod. Specify a remote version range
+     * that this mod will accept as valid. Defaults to nothing, which is interpreted as "only this version".
+     * Another special value is '*' which means accept all versions.
+     *
+     * This is ignored if there is a {@link NetworkCheckHandler} annotation on a method in this class.
+     *
+     * @return
+     */
+    String acceptableRemoteVersions() default "";
     /**
      * An optional bukkit plugin that will be injected into the bukkit plugin framework if
      * this mod is loaded into the FML framework and the bukkit coremod is present.
diff --git a/src/main/java/cpw/mods/fml/common/network/ClientSidePacketHandler.java b/src/main/java/cpw/mods/fml/common/network/ClientSidePacketHandler.java
deleted file mode 100644
index 262d5732e1..0000000000
--- a/src/main/java/cpw/mods/fml/common/network/ClientSidePacketHandler.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package cpw.mods.fml.common.network;
-
-import cpw.mods.fml.common.network.NetworkSide.ClientSide;
-import net.minecraft.network.play.server.S3FPacketCustomPayload;
-
-/**
- * Handle packets from the server at the client.
- *
- * @author cpw
- *
- */
-public abstract class ClientSidePacketHandler implements IPacketHandler {
-    @Override
-    public ClientSide side()
-    {
-        return NetworkSide.CLIENT;
-    }
-    /**
-     * Handle the custompayload packet
-     * @param packet
-     */
-    public abstract void handleCustomPayload(S3FPacketCustomPayload packet);
-}
diff --git a/src/main/java/cpw/mods/fml/common/network/FMLIndexedMessageToMessageCodec.java b/src/main/java/cpw/mods/fml/common/network/FMLIndexedMessageToMessageCodec.java
index 947664e03c..0caef3611b 100644
--- a/src/main/java/cpw/mods/fml/common/network/FMLIndexedMessageToMessageCodec.java
+++ b/src/main/java/cpw/mods/fml/common/network/FMLIndexedMessageToMessageCodec.java
@@ -32,7 +32,7 @@ public abstract class FMLIndexedMessageToMessageCodec extends MessageToMessag
         byte discriminator = types.get(clazz);
         buffer.writeByte(discriminator);
         encodeInto(ctx, msg, buffer);
-        FMLProxyPacket proxy = new FMLProxyPacket(buffer, ctx.channel().attr(NetworkRegistry.FML_CHANNEL).get());
+        FMLProxyPacket proxy = new FMLProxyPacket(buffer.copy(), ctx.channel().attr(NetworkRegistry.FML_CHANNEL).get());
         out.add(proxy);
     }
 
diff --git a/src/main/java/cpw/mods/fml/common/network/FMLNetworkHandler.java b/src/main/java/cpw/mods/fml/common/network/FMLNetworkHandler.java
index 6c2e8f7472..79a238230f 100644
--- a/src/main/java/cpw/mods/fml/common/network/FMLNetworkHandler.java
+++ b/src/main/java/cpw/mods/fml/common/network/FMLNetworkHandler.java
@@ -12,19 +12,28 @@
 
 package cpw.mods.fml.common.network;
 
-import org.apache.logging.log4j.core.helpers.Integers;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 
-import cpw.mods.fml.common.network.handshake.NetworkDispatcher;
 import net.minecraft.entity.Entity;
 import net.minecraft.entity.player.EntityPlayer;
 import net.minecraft.entity.player.EntityPlayerMP;
-import net.minecraft.network.EnumConnectionState;
-import net.minecraft.network.NetHandlerPlayServer;
 import net.minecraft.network.NetworkManager;
 import net.minecraft.network.Packet;
 import net.minecraft.server.management.ServerConfigurationManager;
 import net.minecraft.world.World;
 
+import org.apache.logging.log4j.core.helpers.Integers;
+
+import com.google.common.collect.Lists;
+
+import cpw.mods.fml.common.FMLLog;
+import cpw.mods.fml.common.ModContainer;
+import cpw.mods.fml.common.network.handshake.FMLHandshakeMessage.ClientModList;
+import cpw.mods.fml.common.network.handshake.NetworkDispatcher;
+import cpw.mods.fml.relauncher.Side;
+
 public class FMLNetworkHandler
 {
     public static final int READ_TIMEOUT = Integers.parseInt(System.getProperty("fml.readTimeout","30"),30);
@@ -442,4 +451,27 @@ public class FMLNetworkHandler
         return null;
     }
 
+    public static String checkClientModList(ClientModList clientModList)
+    {
+        Map modList = clientModList.modList();
+        List rejects = Lists.newArrayList();
+        for (Entry networkMod : NetworkRegistry.INSTANCE.registry().entrySet())
+        {
+            boolean result = networkMod.getValue().check(modList, Side.CLIENT);
+            if (!result)
+            {
+                rejects.add(networkMod.getKey());
+            }
+        }
+        if (rejects.isEmpty())
+        {
+            return null;
+        }
+        else
+        {
+            FMLLog.info("Rejecting client : %s", rejects);
+            return String.format("Mod rejections %s",rejects);
+        }
+    }
+
 }
diff --git a/src/main/java/cpw/mods/fml/common/network/IPacketHandler.java b/src/main/java/cpw/mods/fml/common/network/IPacketHandler.java
deleted file mode 100644
index 600a9ab31a..0000000000
--- a/src/main/java/cpw/mods/fml/common/network/IPacketHandler.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package cpw.mods.fml.common.network;
-
-
-/**
- * Marker for packet handlers
- * @author cpw
- *
- */
-public interface IPacketHandler {
-    S side();
-}
diff --git a/src/main/java/cpw/mods/fml/common/network/IPacketHandlerFactory.java b/src/main/java/cpw/mods/fml/common/network/IPacketHandlerFactory.java
deleted file mode 100644
index 6025e328d8..0000000000
--- a/src/main/java/cpw/mods/fml/common/network/IPacketHandlerFactory.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Forge Mod Loader
- * Copyright (c) 2012-2013 cpw.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Lesser Public License v2.1
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
- *
- * Contributors:
- *     cpw - implementation
- */
-
-package cpw.mods.fml.common.network;
-
-import cpw.mods.fml.common.network.NetworkSide.ClientSide;
-import cpw.mods.fml.common.network.NetworkSide.ServerSide;
-import net.minecraft.network.INetHandler;
-import net.minecraft.network.NetworkManager;
-
-/**
- * Whenever a network connection is constructed, the appropriate factory method is called, once for each channel.
- * Return null if you don't want a handler for a specific channel on a specific side.
- *
- * @author cpw
- *
- */
-public interface IPacketHandlerFactory
-{
-    /**
-     * Return the list of channels this packet handler factory wants to claim
-     * @return a list of channels
-     */
-    public String[] channels();
-    /**
-     * Return a client side packet handler for the specified channel or null if it is not handled on this side
-     *
-     * @param mod
-     * @param manager
-     * @param clientPlayHandler
-     * @param channel
-     * @return
-     */
-    public IPacketHandler makeClientPacketHandler(NetworkManager manager, INetHandler clientPlayHandler, String channel);
-    /**
-     * Return a server side packet handler for the specified channel or null if it is not handled on this side
-     * @param mod
-     * @param manager
-     * @param serverPlayHandler
-     * @return
-     */
-    public IPacketHandler makeServerPacketHandler(NetworkManager manager, INetHandler serverPlayHandler, String channel);
-}
diff --git a/src/main/java/cpw/mods/fml/common/network/NetworkCheckHandler.java b/src/main/java/cpw/mods/fml/common/network/NetworkCheckHandler.java
new file mode 100644
index 0000000000..9d6a28a437
--- /dev/null
+++ b/src/main/java/cpw/mods/fml/common/network/NetworkCheckHandler.java
@@ -0,0 +1,6 @@
+package cpw.mods.fml.common.network;
+
+public @interface NetworkCheckHandler
+{
+
+}
diff --git a/src/main/java/cpw/mods/fml/common/network/NetworkMod.java b/src/main/java/cpw/mods/fml/common/network/NetworkMod.java
deleted file mode 100644
index f99635049e..0000000000
--- a/src/main/java/cpw/mods/fml/common/network/NetworkMod.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Forge Mod Loader
- * Copyright (c) 2012-2013 cpw.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Lesser Public License v2.1
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
- *
- * Contributors:
- *     cpw - implementation
- */
-
-package cpw.mods.fml.common.network;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-import cpw.mods.fml.common.Mod;
-
-/**
- * {@link Mod} classes also adorned with this annotation are considered network capable mods.
- * They require a {@link IPacketHandlerFactory} that will generate instances of packet handlers.
- * They are version checked for compatibility between client and server. The default mechanism
- * matches the {@link Mod#version()} on client and server. It can be overridden with either a
- * {@link #versionBounds()} or a more complex {@link VersionCheckHandler}.
- *
- * @author cpw
- *
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
-public @interface NetworkMod
-{
-    /**
-     * A packet handler implementation for channels registered through this annotation
-     * - this packet handler will be universal and handle both client and server
-     * requests.
-     */
-    Class packetHandlerFactory();
-    /**
-     * An optional range check for client to server communication version compatibility.
-     */
-    String versionBounds() default "";
-    /**
-     * A marker for a method that will be offered the client's version string
-     * if more sophisticated version rejection handling is required:
-     * The method should accept a "String" (the version) and return a boolean true
-     * if the version can be accepted.
-     * It can only be applied to the {@link NetworkMod} annotated class.
-     * @author cpw
-     *
-     */
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target(ElementType.METHOD)
-    public @interface VersionCheckHandler { }
-}
diff --git a/src/main/java/cpw/mods/fml/common/network/NetworkModHolder.java b/src/main/java/cpw/mods/fml/common/network/NetworkModHolder.java
index b032dca1d2..3ba59c18eb 100644
--- a/src/main/java/cpw/mods/fml/common/network/NetworkModHolder.java
+++ b/src/main/java/cpw/mods/fml/common/network/NetworkModHolder.java
@@ -13,6 +13,7 @@
 package cpw.mods.fml.common.network;
 
 import java.lang.reflect.Method;
+import java.util.Map;
 import java.util.Set;
 import java.util.logging.Level;
 
@@ -25,71 +26,87 @@ 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 NetworkModHolder
 {
+    private abstract class NetworkChecker {
+        public abstract boolean check(Map remoteVersions, Side side);
+    }
+
+    private class DefaultNetworkChecker extends NetworkChecker {
+        @Override
+        public boolean check(Map remoteVersions, Side side)
+        {
+            return acceptVersion(remoteVersions.get(container.getModId()));
+        }
+
+    }
+    private class MethodNetworkChecker extends NetworkChecker {
+        @Override
+        public boolean check(Map remoteVersions, Side side)
+        {
+            try
+            {
+                return (Boolean) checkHandler.invoke(container, remoteVersions, side);
+            }
+            catch (Exception e)
+            {
+                FMLLog.log(Level.SEVERE, e, "Error occurred invoking NetworkCheckHandler %s at %s", checkHandler.getName(), container);
+                return false;
+            }
+        }
+    }
     private static int assignedIds = 1;
 
     private int localId;
     private int networkId;
 
     private ModContainer container;
-    private NetworkMod mod;
     private Method checkHandler;
 
     private VersionRange acceptableRange;
 
-    private IPacketHandlerFactory packetHandlerFactory;
+    private NetworkChecker checker;
 
-    public NetworkModHolder(ModContainer container, NetworkMod modAnnotation)
+    public NetworkModHolder(ModContainer container)
     {
         this.container = container;
-        this.mod = modAnnotation;
         this.localId = assignedIds++;
         this.networkId = this.localId;
     }
-    public NetworkModHolder(ModContainer container, Class networkModClass, ASMDataTable table)
+    public NetworkModHolder(ModContainer container, Class modClass, String acceptableVersionRange, ASMDataTable table)
     {
-        this(container, networkModClass.getAnnotation(NetworkMod.class));
-        if (this.mod == null)
-        {
-            return;
-        }
-
-        Set versionCheckHandlers = table.getAnnotationsFor(container).get(NetworkMod.VersionCheckHandler.class.getName());
-        String versionCheckHandlerMethod = null;
+        this(container);
+        Set versionCheckHandlers = table.getAnnotationsFor(container).get(NetworkCheckHandler.class.getName());
+        String networkCheckHandlerMethod = null;
         for (ASMData vch : versionCheckHandlers)
         {
-            if (vch.getClassName().equals(networkModClass.getName()))
+            if (vch.getClassName().equals(modClass.getName()))
             {
-                versionCheckHandlerMethod = vch.getObjectName();
-                versionCheckHandlerMethod = versionCheckHandlerMethod.substring(0,versionCheckHandlerMethod.indexOf('('));
+                networkCheckHandlerMethod = vch.getObjectName();
+                networkCheckHandlerMethod = networkCheckHandlerMethod.substring(0,networkCheckHandlerMethod.indexOf('('));
                 break;
             }
         }
-        if (versionCheckHandlerMethod != null)
+        if (networkCheckHandlerMethod != null)
         {
             try
             {
-                Method checkHandlerMethod = networkModClass.getDeclaredMethod(versionCheckHandlerMethod, String.class);
-                if (checkHandlerMethod.isAnnotationPresent(NetworkMod.VersionCheckHandler.class))
+                Method checkHandlerMethod = modClass.getDeclaredMethod(networkCheckHandlerMethod, Map.class, Side.class);
+                if (checkHandlerMethod.isAnnotationPresent(NetworkCheckHandler.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());
+                FMLLog.log(Level.WARNING, e, "The declared version check handler method %s on network mod id %s is not accessible", networkCheckHandlerMethod, container.getModId());
             }
         }
-
-        configureNetworkMod(container);
-    }
-    protected void configureNetworkMod(ModContainer container)
-    {
         if (this.checkHandler == null)
         {
-            String versionBounds = mod.versionBounds();
+            String versionBounds = acceptableVersionRange;
             if (!Strings.isNullOrEmpty(versionBounds))
             {
                 try
@@ -113,23 +130,12 @@ public class NetworkModHolder
         {
             FMLLog.finest("The mod %s accepts its own version (%s)", container.getModId(), container.getVersion());
         }
+
+        this.checker = checkHandler == null ? new DefaultNetworkChecker() : new MethodNetworkChecker();
     }
 
     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));
@@ -138,6 +144,11 @@ public class NetworkModHolder
         return container.getVersion().equals(version);
     }
 
+    public boolean check(Map data, Side side)
+    {
+        return checker.check(data, side);
+    }
+
     public int getLocalId()
     {
         return localId;
@@ -153,16 +164,6 @@ public class NetworkModHolder
         return container;
     }
 
-    public NetworkMod getMod()
-    {
-        return mod;
-    }
-
-    public boolean isNetworkMod()
-    {
-        return mod != null;
-    }
-
     public void setNetworkId(int value)
     {
         this.networkId = value;
diff --git a/src/main/java/cpw/mods/fml/common/network/NetworkRegistry.java b/src/main/java/cpw/mods/fml/common/network/NetworkRegistry.java
index 447e905776..1feb7d7b60 100644
--- a/src/main/java/cpw/mods/fml/common/network/NetworkRegistry.java
+++ b/src/main/java/cpw/mods/fml/common/network/NetworkRegistry.java
@@ -26,6 +26,7 @@ import java.util.logging.Level;
 
 import net.minecraft.entity.player.EntityPlayer;
 import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.network.EnumConnectionState;
 import net.minecraft.network.NetworkManager;
 import net.minecraft.world.World;
 
@@ -35,6 +36,7 @@ import com.google.common.collect.Maps;
 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.network.handshake.NetworkDispatcher;
 import cpw.mods.fml.relauncher.Side;
 
@@ -46,6 +48,7 @@ public enum NetworkRegistry
 {
     INSTANCE;
     private EnumMap> channels = Maps.newEnumMap(Side.class);
+    private Map registry = Maps.newHashMap();
     private Map serverGuiHandlers = Maps.newHashMap();
     private Map clientGuiHandlers = Maps.newHashMap();
 
@@ -504,4 +507,15 @@ public enum NetworkRegistry
     {
         return channels.get(source).containsKey(channelName);
     }
+
+    public void register(ModContainer fmlModContainer, Class clazz, String remoteVersionRange, ASMDataTable asmHarvestedData)
+    {
+        NetworkModHolder networkModHolder = new NetworkModHolder(fmlModContainer, clazz, remoteVersionRange, asmHarvestedData);
+        registry.put(fmlModContainer, networkModHolder);
+    }
+
+    Map registry()
+    {
+        return registry;
+    }
 }
diff --git a/src/main/java/cpw/mods/fml/common/network/ServerSidePacketHandler.java b/src/main/java/cpw/mods/fml/common/network/ServerSidePacketHandler.java
deleted file mode 100644
index e3d7714f53..0000000000
--- a/src/main/java/cpw/mods/fml/common/network/ServerSidePacketHandler.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package cpw.mods.fml.common.network;
-
-import net.minecraft.network.play.client.C17PacketCustomPayload;
-import cpw.mods.fml.common.network.NetworkSide.ServerSide;
-
-/**
- * Handle packets from the client at the server.
- *
- * @author cpw
- *
- */
-public abstract class ServerSidePacketHandler implements IPacketHandler {
-    @Override
-    public ServerSide side()
-    {
-        return NetworkSide.SERVER;
-    }
-
-    public abstract void handleCustomPayload(C17PacketCustomPayload packet);
-}
diff --git a/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeClientState.java b/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeClientState.java
index a0eeef0752..570f938185 100644
--- a/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeClientState.java
+++ b/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeClientState.java
@@ -1,12 +1,8 @@
 package cpw.mods.fml.common.network.handshake;
 
-import java.util.List;
-
 import io.netty.channel.ChannelHandlerContext;
 import cpw.mods.fml.common.FMLLog;
 import cpw.mods.fml.common.Loader;
-import cpw.mods.fml.common.ModContainer;
-import cpw.mods.fml.common.network.NetworkRegistry;
 
 enum FMLHandshakeClientState implements IHandshakeState
 {
diff --git a/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeMessage.java b/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeMessage.java
index 31b06cc44b..2ba54407e7 100644
--- a/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeMessage.java
+++ b/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeMessage.java
@@ -3,8 +3,11 @@ package cpw.mods.fml.common.network.handshake;
 import io.netty.buffer.ByteBuf;
 
 import java.util.List;
+import java.util.Map;
 
+import com.google.common.base.Joiner;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 
 import cpw.mods.fml.common.ModContainer;
 import cpw.mods.fml.common.network.ByteBufUtils;
@@ -80,20 +83,20 @@ public abstract class FMLHandshakeMessage {
         {
             for (ModContainer mod : modList)
             {
-                modTags.add(new String[] { mod.getModId(), mod.getVersion() });
+                modTags.put(mod.getModId(), mod.getVersion());
             }
         }
-        private List modTags = Lists.newArrayList();
+        private Map modTags = Maps.newHashMap();
 
         @Override
         public void toBytes(ByteBuf buffer)
         {
             super.toBytes(buffer);
             ByteBufUtils.writeVarInt(buffer, modTags.size(), 2);
-            for (String[] modTag: modTags)
+            for (Map.Entry modTag: modTags.entrySet())
             {
-                ByteBufUtils.writeUTF8String(buffer, modTag[0]);
-                ByteBufUtils.writeUTF8String(buffer, modTag[1]);
+                ByteBufUtils.writeUTF8String(buffer, modTag.getKey());
+                ByteBufUtils.writeUTF8String(buffer, modTag.getValue());
             }
         }
 
@@ -104,28 +107,23 @@ public abstract class FMLHandshakeMessage {
             int modCount = ByteBufUtils.readVarInt(buffer, 2);
             for (int i = 0; i < modCount; i++)
             {
-                modTags.add(new String[] { ByteBufUtils.readUTF8String(buffer), ByteBufUtils.readUTF8String(buffer)});
+                modTags.put(ByteBufUtils.readUTF8String(buffer), ByteBufUtils.readUTF8String(buffer));
             }
         }
 
         public String modListAsString()
         {
-            StringBuffer sb = new StringBuffer();
-            sb.append("[ ");
-            for (int i = 0; i < modTags.size(); i++)
-            {
-                String[] mod = modTags.get(i);
-                sb.append(mod[0]).append("@").append(mod[1]);
-                if (i < modTags.size() - 1) sb.append(", ");
-            }
-            sb.append(" ]");
-            return sb.toString();
+            return Joiner.on(',').withKeyValueSeparator("@").join(modTags);
         }
 
         public int modListSize()
         {
             return modTags.size();
         }
+        public Map modList()
+        {
+            return modTags;
+        }
     }
 
     public static class ClientAck extends FMLHandshakeMessage {
diff --git a/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeServerState.java b/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeServerState.java
index 1c8c9e94ba..b79bac8b58 100644
--- a/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeServerState.java
+++ b/src/main/java/cpw/mods/fml/common/network/handshake/FMLHandshakeServerState.java
@@ -2,6 +2,7 @@ package cpw.mods.fml.common.network.handshake;
 
 import io.netty.channel.ChannelHandlerContext;
 import cpw.mods.fml.common.FMLLog;
+import cpw.mods.fml.common.network.FMLNetworkHandler;
 
 enum FMLHandshakeServerState implements IHandshakeState
 {
@@ -29,7 +30,14 @@ enum FMLHandshakeServerState implements IHandshakeState
             }
 
             FMLHandshakeMessage.ClientModList client = (FMLHandshakeMessage.ClientModList)msg;
-            FMLLog.info("Client joining with %d mods : %s", client.modListSize(), client.modListAsString());
+            FMLLog.info("Client attempting to join with %d mods : %s", client.modListSize(), client.modListAsString());
+            String result = FMLNetworkHandler.checkClientModList(client);
+            if (result != null)
+            {
+                NetworkDispatcher dispatcher = ctx.channel().attr(NetworkDispatcher.FML_DISPATCHER).get();
+                dispatcher.rejectHandshake(result);
+                return ERROR;
+            }
             ctx.writeAndFlush(new FMLHandshakeMessage.ServerModList());
             return COMPLETE;
         }
@@ -44,5 +52,13 @@ enum FMLHandshakeServerState implements IHandshakeState
             dispatcher.continueToServerPlayState();
             return this;
         }
+    },
+    ERROR
+    {
+        @Override
+        public FMLHandshakeServerState accept(ChannelHandlerContext ctx, FMLHandshakeMessage msg)
+        {
+            return this;
+        }
     };
 }
\ No newline at end of file
diff --git a/src/main/java/cpw/mods/fml/common/network/handshake/NetworkDispatcher.java b/src/main/java/cpw/mods/fml/common/network/handshake/NetworkDispatcher.java
index 56039f8be9..162a8abd1b 100644
--- a/src/main/java/cpw/mods/fml/common/network/handshake/NetworkDispatcher.java
+++ b/src/main/java/cpw/mods/fml/common/network/handshake/NetworkDispatcher.java
@@ -164,6 +164,11 @@ public class NetworkDispatcher extends SimpleChannelInboundHandler {
         {
             handled = handleClientSideCustomPacket((S3FPacketCustomPayload)msg, ctx);
         }
+        else if (msg instanceof S40PacketDisconnect && state != ConnectionState.CONNECTED)
+        {
+            // Switch to play state to handle the disconnect message
+            continueToClientPlayState();
+        }
         else if (state != ConnectionState.CONNECTED)
         {
             FMLLog.info("Unexpected packet during modded negotiation - assuming vanilla");
@@ -187,7 +192,11 @@ public class NetworkDispatcher extends SimpleChannelInboundHandler {
 
     private void kickVanilla()
     {
-        final ChatComponentText chatcomponenttext = new ChatComponentText("This is modded. No modded response received. Bye!");
+        kickWithMessage("This is modded. No modded response received. Bye!");
+    }
+    private void kickWithMessage(String message)
+    {
+        final ChatComponentText chatcomponenttext = new ChatComponentText(message);
         manager.func_150725_a(new S40PacketDisconnect(chatcomponenttext), new GenericFutureListener>()
         {
             public void operationComplete(Future result)
@@ -282,4 +291,9 @@ public class NetworkDispatcher extends SimpleChannelInboundHandler {
         else
             manager.func_150725_a(msg.toS3FPacket());
     }
+
+    public void rejectHandshake(String result)
+    {
+        kickWithMessage(result);
+    }
 }