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