    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 the default {@link NetworkMod#connectionHandler()} annotation value
    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 the default {@link NetworkMod#packetHandler()} annotation value
    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    /**
    232     * @return the default {@link NetworkMod#tinyPacketHandler()} annotation value
    233     */
    234    private Object getTinyPacketHandlerDefaultValue()
    235    {
    236        try {
    237            if (tinyPacketHandlerDefaultValue == null)
    238            {
    239                tinyPacketHandlerDefaultValue = NetworkMod.class.getMethod("tinyPacketHandler").getDefaultValue();
    240            }
    241            return tinyPacketHandlerDefaultValue;
    242        }
    243        catch (NoSuchMethodException e)
    244        {
    245            throw new RuntimeException("Derp?", e);
    246        }
    247    }
    248    /**
    249     * @return the {@link NetworkMod#clientPacketHandlerSpec()} default annotation value
    250     */
    251    private Object getClientHandlerSpecDefaultValue()
    252    {
    253        try {
    254            if (clientHandlerDefaultValue == null)
    255            {
    256                clientHandlerDefaultValue = NetworkMod.class.getMethod("clientPacketHandlerSpec").getDefaultValue();
    257            }
    258            return clientHandlerDefaultValue;
    259        }
    260        catch (NoSuchMethodException e)
    261        {
    262            throw new RuntimeException("Derp?", e);
    263        }
    264    }
    265    /**
    266     * @return the default {@link NetworkMod#serverPacketHandlerSpec()} annotation value
    267     */
    268    private Object getServerHandlerSpecDefaultValue()
    269    {
    270        try {
    271            if (serverHandlerDefaultValue == null)
    272            {
    273                serverHandlerDefaultValue = NetworkMod.class.getMethod("serverPacketHandlerSpec").getDefaultValue();
    274            }
    275            return serverHandlerDefaultValue;
    276        }
    277        catch (NoSuchMethodException e)
    278        {
    279            throw new RuntimeException("Derp?", e);
    280        }
    281    }
    282    public boolean requiresClientSide()
    283    {
    284        return mod.clientSideRequired();
    285    }
    286
    287    public boolean requiresServerSide()
    288    {
    289        return mod.serverSideRequired();
    290    }
    291
    292    public boolean acceptVersion(String version)
    293    {
    294        if (checkHandler != null)
    295        {
    296            try
    297            {
    298                return (Boolean)checkHandler.invoke(container.getMod(), version);
    299            }
    300            catch (Exception e)
    301            {
    302                FMLLog.log(Level.WARNING, e, "There was a problem invoking the checkhandler method %s for network mod id %s", checkHandler.getName(), container.getModId());
    303                return false;
    304            }
    305        }
    306
    307        if (acceptableRange!=null)
    308        {
    309            return acceptableRange.containsVersion(new DefaultArtifactVersion(version));
    310        }
    311
    312        return container.getVersion().equals(version);
    313    }
    314
    315    public int getLocalId()
    316    {
    317        return localId;
    318    }
    319
    320    public int getNetworkId()
    321    {
    322        return networkId;
    323    }
    324
    325    public ModContainer getContainer()
    326    {
    327        return container;
    328    }
    329
    330    public NetworkMod getMod()
    331    {
    332        return mod;
    333    }
    334
    335    public boolean isNetworkMod()
    336    {
    337        return mod != null;
    338    }
    339
    340    public void setNetworkId(int value)
    341    {
    342        this.networkId = value;
    343    }
    344
    345    public boolean hasTinyPacketHandler()
    346    {
    347        return tinyPacketHandler != null;
    348    }
    349    public ITinyPacketHandler getTinyPacketHandler()
    350    {
    351        return tinyPacketHandler;
    352    }
    353}