    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}