001package cpw.mods.fml.common.network;
002
003import java.lang.reflect.Method;
004import java.util.Set;
005import java.util.logging.Level;
006
007import net.minecraft.item.Item;
008
009import com.google.common.base.Strings;
010
011import cpw.mods.fml.common.FMLCommonHandler;
012import cpw.mods.fml.common.FMLLog;
013import cpw.mods.fml.common.ModContainer;
014import cpw.mods.fml.common.discovery.ASMDataTable;
015import cpw.mods.fml.common.discovery.ASMDataTable.ASMData;
016import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
017import cpw.mods.fml.common.versioning.InvalidVersionSpecificationException;
018import cpw.mods.fml.common.versioning.VersionRange;
019import cpw.mods.fml.relauncher.Side;
020
021public 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}