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