001    package cpw.mods.fml.common.network;
002    
003    import java.lang.reflect.Method;
004    import java.util.Set;
005    import java.util.logging.Level;
006    
007    import com.google.common.base.Strings;
008    
009    import cpw.mods.fml.common.FMLCommonHandler;
010    import cpw.mods.fml.common.FMLLog;
011    import cpw.mods.fml.common.ModContainer;
012    import cpw.mods.fml.common.Side;
013    import cpw.mods.fml.common.discovery.ASMDataTable;
014    import cpw.mods.fml.common.discovery.ASMDataTable.ASMData;
015    import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
016    import cpw.mods.fml.common.versioning.InvalidVersionSpecificationException;
017    import cpw.mods.fml.common.versioning.VersionRange;
018    
019    public class NetworkModHandler
020    {
021        private static Object connectionHandlerDefaultValue;
022        private static Object packetHandlerDefaultValue;
023        private static Object clientHandlerDefaultValue;
024        private static Object serverHandlerDefaultValue;
025    
026        private static int assignedIds = 1;
027    
028        private int localId;
029        private int networkId;
030    
031        private ModContainer container;
032        private NetworkMod mod;
033        private Method checkHandler;
034    
035        private VersionRange acceptableRange;
036    
037        public NetworkModHandler(ModContainer container, NetworkMod modAnnotation)
038        {
039            this.container = container;
040            this.mod = modAnnotation;
041            this.localId = assignedIds++;
042            this.networkId = this.localId;
043        }
044        public NetworkModHandler(ModContainer container, Class<?> networkModClass, ASMDataTable table)
045        {
046            this(container, networkModClass.getAnnotation(NetworkMod.class));
047            if (this.mod == null)
048            {
049                return;
050            }
051    
052            Set<ASMData> versionCheckHandlers = table.getAnnotationsFor(container).get(NetworkMod.VersionCheckHandler.class.getName());
053            String versionCheckHandlerMethod = null;
054            for (ASMData vch : versionCheckHandlers)
055            {
056                if (vch.getClassName().equals(networkModClass.getName()))
057                {
058                    versionCheckHandlerMethod = vch.getObjectName();
059                    break;
060                }
061            }
062            if (versionCheckHandlerMethod != null)
063            {
064                try
065                {
066                    Method checkHandlerMethod = networkModClass.getDeclaredMethod(versionCheckHandlerMethod, String.class);
067                    if (checkHandlerMethod.isAnnotationPresent(NetworkMod.VersionCheckHandler.class))
068                    {
069                        this.checkHandler = checkHandlerMethod;
070                    }
071                }
072                catch (Exception e)
073                {
074                    FMLLog.log(Level.WARNING, e, "The declared version check handler method %s on network mod id %s is not accessible", versionCheckHandlerMethod, container.getModId());
075                }
076            }
077    
078            if (this.checkHandler == null)
079            {
080                String versionBounds = mod.versionBounds();
081                if (!Strings.isNullOrEmpty(versionBounds))
082                {
083                    try
084                    {
085                        this.acceptableRange = VersionRange.createFromVersionSpec(versionBounds);
086                    }
087                    catch (InvalidVersionSpecificationException e)
088                    {
089                        FMLLog.log(Level.WARNING, e, "Invalid bounded range %s specified for network mod id %s", versionBounds, container.getModId());
090                    }
091                }
092            }
093    
094            FMLLog.finest("Testing mod %s to very it can accept it's own version in a remote connection", container.getModId());
095            boolean acceptsSelf = acceptVersion(container.getVersion());
096            if (!acceptsSelf)
097            {
098                FMLLog.severe("The mod %s appears to reject it's own version number (%s) in it's version handling. This is likely a severe bug in the mod!", container.getModId(), container.getVersion());
099            }
100            else
101            {
102                FMLLog.finest("The mod %s accepts it's own version (%s)", container.getModId(), container.getVersion());
103            }
104    
105            tryCreatingPacketHandler(container, mod.packetHandler(), mod.channels(), null);
106            if (FMLCommonHandler.instance().getSide().isClient())
107            {
108                if (mod.clientPacketHandlerSpec() != getClientHandlerSpecDefaultValue())
109                {
110                    tryCreatingPacketHandler(container, mod.clientPacketHandlerSpec().packetHandler(), mod.clientPacketHandlerSpec().channels(), Side.CLIENT);
111                }
112            }
113            if (mod.serverPacketHandlerSpec() != getServerHandlerSpecDefaultValue())
114            {
115                tryCreatingPacketHandler(container, mod.serverPacketHandlerSpec().packetHandler(), mod.serverPacketHandlerSpec().channels(), Side.SERVER);
116            }
117    
118            if (mod.connectionHandler() != getConnectionHandlerDefaultValue())
119            {
120                IConnectionHandler instance;
121                try
122                {
123                    instance = mod.connectionHandler().newInstance();
124                }
125                catch (Exception e)
126                {
127                    FMLLog.log(Level.SEVERE, e, "Unable to create connection handler instance %s", mod.connectionHandler().getName());
128                    throw new FMLNetworkException(e);
129                }
130    
131                NetworkRegistry.instance().registerConnectionHandler(instance);
132            }
133        }
134        /**
135         * @param container
136         */
137        private void tryCreatingPacketHandler(ModContainer container, Class<? extends IPacketHandler> clazz, String[] channels, Side side)
138        {
139            if (side!=null && side.isClient() && ! FMLCommonHandler.instance().getSide().isClient())
140            {
141                return;
142            }
143            if (clazz!=getPacketHandlerDefaultValue())
144            {
145                if (channels.length==0)
146                {
147                    FMLLog.log(Level.WARNING, "The mod id %s attempted to register a packet handler without specifying channels for it", container.getModId());
148                }
149                else
150                {
151                    IPacketHandler instance;
152                    try
153                    {
154                        instance = clazz.newInstance();
155                    }
156                    catch (Exception e)
157                    {
158                        FMLLog.log(Level.SEVERE, e, "Unable to create a packet handler instance %s for mod %s", clazz.getName(), container.getModId());
159                        throw new FMLNetworkException(e);
160                    }
161    
162                    for (String channel : channels)
163                    {
164                        NetworkRegistry.instance().registerChannel(instance, channel, side);
165                    }
166                }
167            }
168            else if (channels.length > 0)
169            {
170                FMLLog.warning("The mod id %s attempted to register channels without specifying a packet handler", container.getModId());
171            }
172        }
173        /**
174         * @return
175         */
176        private Object getConnectionHandlerDefaultValue()
177        {
178            try {
179                if (connectionHandlerDefaultValue == null)
180                {
181                    connectionHandlerDefaultValue = NetworkMod.class.getMethod("connectionHandler").getDefaultValue();
182                }
183                return connectionHandlerDefaultValue;
184            }
185            catch (NoSuchMethodException e)
186            {
187                throw new RuntimeException("Derp?", e);
188            }
189        }
190    
191        /**
192         * @return
193         */
194        private Object getPacketHandlerDefaultValue()
195        {
196            try {
197                if (packetHandlerDefaultValue == null)
198                {
199                    packetHandlerDefaultValue = NetworkMod.class.getMethod("packetHandler").getDefaultValue();
200                }
201                return packetHandlerDefaultValue;
202            }
203            catch (NoSuchMethodException e)
204            {
205                throw new RuntimeException("Derp?", e);
206            }
207        }
208    
209        /**
210         * @return
211         */
212        private Object getClientHandlerSpecDefaultValue()
213        {
214            try {
215                if (clientHandlerDefaultValue == null)
216                {
217                    clientHandlerDefaultValue = NetworkMod.class.getMethod("clientPacketHandlerSpec").getDefaultValue();
218                }
219                return clientHandlerDefaultValue;
220            }
221            catch (NoSuchMethodException e)
222            {
223                throw new RuntimeException("Derp?", e);
224            }
225        }
226        /**
227         * @return
228         */
229        private Object getServerHandlerSpecDefaultValue()
230        {
231            try {
232                if (serverHandlerDefaultValue == null)
233                {
234                    serverHandlerDefaultValue = NetworkMod.class.getMethod("serverPacketHandlerSpec").getDefaultValue();
235                }
236                return serverHandlerDefaultValue;
237            }
238            catch (NoSuchMethodException e)
239            {
240                throw new RuntimeException("Derp?", e);
241            }
242        }
243        public boolean requiresClientSide()
244        {
245            return mod.clientSideRequired();
246        }
247    
248        public boolean requiresServerSide()
249        {
250            return mod.serverSideRequired();
251        }
252    
253        public boolean acceptVersion(String version)
254        {
255            if (checkHandler != null)
256            {
257                try
258                {
259                    return (Boolean)checkHandler.invoke(container.getMod(), version);
260                }
261                catch (Exception e)
262                {
263                    FMLLog.log(Level.WARNING, e, "There was a problem invoking the checkhandler method %s for network mod id %s", checkHandler.getName(), container.getModId());
264                    return false;
265                }
266            }
267    
268            if (acceptableRange!=null)
269            {
270                return acceptableRange.containsVersion(new DefaultArtifactVersion(version));
271            }
272    
273            return container.getVersion().equals(version);
274        }
275    
276        public int getLocalId()
277        {
278            return localId;
279        }
280    
281        public int getNetworkId()
282        {
283            return networkId;
284        }
285    
286        public ModContainer getContainer()
287        {
288            return container;
289        }
290    
291        public NetworkMod getMod()
292        {
293            return mod;
294        }
295    
296        public boolean isNetworkMod()
297        {
298            return mod != null;
299        }
300    
301        public void setNetworkId(int value)
302        {
303            this.networkId = value;
304        }
305    }