    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.registry;
    014
    015import java.lang.reflect.Constructor;
    016import java.util.List;
    017import java.util.Map;
    018import java.util.Random;
    019import java.util.Set;
    020import java.util.concurrent.CountDownLatch;
    021import java.util.logging.Level;
    022
    023import net.minecraft.entity.item.EntityItem;
    024import net.minecraft.entity.player.EntityPlayer;
    025import net.minecraft.inventory.IInventory;
    026import net.minecraft.item.Item;
    027import net.minecraft.item.ItemBlock;
    028import net.minecraft.item.ItemStack;
    029import net.minecraft.item.crafting.CraftingManager;
    030import net.minecraft.item.crafting.FurnaceRecipes;
    031import net.minecraft.item.crafting.IRecipe;
    032import net.minecraft.nbt.NBTTagCompound;
    033import net.minecraft.nbt.NBTTagList;
    034import net.minecraft.tileentity.TileEntity;
    035import net.minecraft.world.World;
    036import net.minecraft.world.WorldType;
    037import net.minecraft.world.biome.BiomeGenBase;
    038import net.minecraft.world.chunk.IChunkProvider;
    039
    040import com.google.common.base.Function;
    041import com.google.common.collect.ArrayListMultimap;
    042import com.google.common.collect.Lists;
    043import com.google.common.collect.MapDifference;
    044import com.google.common.collect.Maps;
    045import com.google.common.collect.Multimap;
    046import com.google.common.collect.Multimaps;
    047import com.google.common.collect.Sets;
    048import com.google.common.collect.Sets.SetView;
    049
    050import cpw.mods.fml.common.FMLLog;
    051import cpw.mods.fml.common.ICraftingHandler;
    052import cpw.mods.fml.common.IFuelHandler;
    053import cpw.mods.fml.common.IPickupNotifier;
    054import cpw.mods.fml.common.IPlayerTracker;
    055import cpw.mods.fml.common.IWorldGenerator;
    056import cpw.mods.fml.common.Loader;
    057import cpw.mods.fml.common.LoaderException;
    058import cpw.mods.fml.common.LoaderState;
    059import cpw.mods.fml.common.ObfuscationReflectionHelper;
    060import cpw.mods.fml.common.Mod.Block;
    061import cpw.mods.fml.common.ModContainer;
    062
    063public class GameRegistry
    064{
    065    private static Multimap<ModContainer, BlockProxy> blockRegistry = ArrayListMultimap.create();
    066    private static Set<IWorldGenerator> worldGenerators = Sets.newHashSet();
    067    private static List<IFuelHandler> fuelHandlers = Lists.newArrayList();
    068    private static List<ICraftingHandler> craftingHandlers = Lists.newArrayList();
    069    private static List<IPickupNotifier> pickupHandlers = Lists.newArrayList();
    070    private static List<IPlayerTracker> playerTrackers = Lists.newArrayList();
    071
    072    /**
    073     * Register a world generator - something that inserts new block types into the world
    074     *
    075     * @param generator
    076     */
    077    public static void registerWorldGenerator(IWorldGenerator generator)
    078    {
    079        worldGenerators.add(generator);
    080    }
    081
    082    /**
    083     * Callback hook for world gen - if your mod wishes to add extra mod related generation to the world
    084     * call this
    085     *
    086     * @param chunkX
    087     * @param chunkZ
    088     * @param world
    089     * @param chunkGenerator
    090     * @param chunkProvider
    091     */
    092    public static void generateWorld(int chunkX, int chunkZ, World world, IChunkProvider chunkGenerator, IChunkProvider chunkProvider)
    093    {
    094        long worldSeed = world.getSeed();
    095        Random fmlRandom = new Random(worldSeed);
    096        long xSeed = fmlRandom.nextLong() >> 2 + 1L;
    097        long zSeed = fmlRandom.nextLong() >> 2 + 1L;
    098        fmlRandom.setSeed((xSeed * chunkX + zSeed * chunkZ) ^ worldSeed);
    099
    100        for (IWorldGenerator generator : worldGenerators)
    101        {
    102            generator.generate(fmlRandom, chunkX, chunkZ, world, chunkGenerator, chunkProvider);
    103        }
    104    }
    105
    106    /**
    107     * Internal method for creating an @Block instance
    108     * @param container
    109     * @param type
    110     * @param annotation
    111     * @throws Exception
    112     */
    113    public static Object buildBlock(ModContainer container, Class<?> type, Block annotation) throws Exception
    114    {
    115        Object o = type.getConstructor(int.class).newInstance(findSpareBlockId());
    116        registerBlock((net.minecraft.block.Block) o);
    117        return o;
    118    }
    119
    120    /**
    121     * Private and not yet working properly
    122     *
    123     * @return a block id
    124     */
    125    private static int findSpareBlockId()
    126    {
    127        return BlockTracker.nextBlockId();
    128    }
    129
    130    /**
    131     * Register an item with the item registry with a custom name : this allows for easier server->client resolution
    132     *
    133     * @param item The item to register
    134     * @param name The mod-unique name of the item
    135     */
    136    public static void registerItem(net.minecraft.item.Item item, String name)
    137    {
    138        registerItem(item, name, null);
    139    }
    140
    141    /**
    142     * Register the specified Item with a mod specific name : overrides the standard type based name
    143     * @param item The item to register
    144     * @param name The mod-unique name to register it as - null will remove a custom name
    145     * @param modId An optional modId that will "own" this block - generally used by multi-mod systems
    146     * where one mod should "own" all the blocks of all the mods, null defaults to the active mod
    147     */
    148    public static void registerItem(net.minecraft.item.Item item, String name, String modId)
    149    {
    150        GameData.setName(item, name, modId);
    151    }
    152
    153    /**
    154     * Register a block with the world
    155     *
    156     */
    157    @Deprecated
    158    public static void registerBlock(net.minecraft.block.Block block)
    159    {
    160        registerBlock(block, ItemBlock.class);
    161    }
    162
    163
    164    /**
    165     * Register a block with the specified mod specific name : overrides the standard type based name
    166     * @param block The block to register
    167     * @param name The mod-unique name to register it as
    168     */
    169    public static void registerBlock(net.minecraft.block.Block block, String name)
    170    {
    171        registerBlock(block, ItemBlock.class, name);
    172    }
    173
    174    /**
    175     * Register a block with the world, with the specified item class
    176     *
    177     * Deprecated in favour of named versions
    178     *
    179     * @param block The block to register
    180     * @param itemclass The item type to register with it
    181     */
    182    @Deprecated
    183    public static void registerBlock(net.minecraft.block.Block block, Class<? extends ItemBlock> itemclass)
    184    {
    185        registerBlock(block, itemclass, null);
    186    }
    187    /**
    188     * Register a block with the world, with the specified item class and block name
    189     * @param block The block to register
    190     * @param itemclass The item type to register with it
    191     * @param name The mod-unique name to register it with
    192     */
    193    public static void registerBlock(net.minecraft.block.Block block, Class<? extends ItemBlock> itemclass, String name)
    194    {
    195        registerBlock(block, itemclass, name, null);
    196    }
    197    /**
    198     * Register a block with the world, with the specified item class, block name and owning modId
    199     * @param block The block to register
    200     * @param itemclass The iterm type to register with it
    201     * @param name The mod-unique name to register it with
    202     * @param modId The modId that will own the block name. null defaults to the active modId
    203     */
    204    public static void registerBlock(net.minecraft.block.Block block, Class<? extends ItemBlock> itemclass, String name, String modId)
    205    {
    206        if (Loader.instance().isInState(LoaderState.CONSTRUCTING))
    207        {
    208            FMLLog.warning("The mod %s is attempting to register a block whilst it it being constructed. This is bad modding practice - please use a proper mod lifecycle event.", Loader.instance().activeModContainer());
    209        }
    210        try
    211        {
    212            assert block != null : "registerBlock: block cannot be null";
    213            assert itemclass != null : "registerBlock: itemclass cannot be null";
    214            int blockItemId = block.blockID - 256;
    215            Constructor<? extends ItemBlock> itemCtor;
    216            Item i;
    217            try
    218            {
    219                itemCtor = itemclass.getConstructor(int.class);
    220                i = itemCtor.newInstance(blockItemId);
    221            }
    222            catch (NoSuchMethodException e)
    223            {
    224                itemCtor = itemclass.getConstructor(int.class, net.minecraft.block.Block.class);
    225                i = itemCtor.newInstance(blockItemId, block);
    226            }
    227            GameRegistry.registerItem(i,name, modId);
    228        }
    229        catch (Exception e)
    230        {
    231            FMLLog.log(Level.SEVERE, e, "Caught an exception during block registration");
    232            throw new LoaderException(e);
    233        }
    234        blockRegistry.put(Loader.instance().activeModContainer(), (BlockProxy) block);
    235    }
    236
    237    public static void addRecipe(ItemStack output, Object... params)
    238    {
    239        addShapedRecipe(output, params);
    240    }
    241
    242    public static IRecipe addShapedRecipe(ItemStack output, Object... params)
    243    {
    244        return CraftingManager.getInstance().addRecipe(output, params);
    245    }
    246
    247    public static void addShapelessRecipe(ItemStack output, Object... params)
    248    {
    249        CraftingManager.getInstance().addShapelessRecipe(output, params);
    250    }
    251
    252    public static void addRecipe(IRecipe recipe)
    253    {
    254        CraftingManager.getInstance().getRecipeList().add(recipe);
    255    }
    256
    257    public static void addSmelting(int input, ItemStack output, float xp)
    258    {
    259        FurnaceRecipes.smelting().addSmelting(input, output, xp);
    260    }
    261
    262    public static void registerTileEntity(Class<? extends TileEntity> tileEntityClass, String id)
    263    {
    264        TileEntity.addMapping(tileEntityClass, id);
    265    }
    266
    267    /**
    268     * Register a tile entity, with alternative TileEntity identifiers. Use with caution!
    269     * This method allows for you to "rename" the 'id' of the tile entity.
    270     *
    271     * @param tileEntityClass The tileEntity class to register
    272     * @param id The primary ID, this will be the ID that the tileentity saves as
    273     * @param alternatives A list of alternative IDs that will also map to this class. These will never save, but they will load
    274     */
    275    public static void registerTileEntityWithAlternatives(Class<? extends TileEntity> tileEntityClass, String id, String... alternatives)
    276    {
    277        TileEntity.addMapping(tileEntityClass, id);
    278        Map<String,Class> teMappings = ObfuscationReflectionHelper.getPrivateValue(TileEntity.class, null, "field_" + "70326_a", "nameToClassMap", "a");
    279        for (String s: alternatives)
    280        {
    281            if (!teMappings.containsKey(s))
    282            {
    283                teMappings.put(s, tileEntityClass);
    284            }
    285        }
    286    }
    287
    288    public static void addBiome(BiomeGenBase biome)
    289    {
    290        WorldType.DEFAULT.addNewBiome(biome);
    291    }
    292
    293    public static void removeBiome(BiomeGenBase biome)
    294    {
    295        WorldType.DEFAULT.removeBiome(biome);
    296    }
    297
    298    public static void registerFuelHandler(IFuelHandler handler)
    299    {
    300        fuelHandlers.add(handler);
    301    }
    302    public static int getFuelValue(ItemStack itemStack)
    303    {
    304        int fuelValue = 0;
    305        for (IFuelHandler handler : fuelHandlers)
    306        {
    307            fuelValue = Math.max(fuelValue, handler.getBurnTime(itemStack));
    308        }
    309        return fuelValue;
    310    }
    311
    312    public static void registerCraftingHandler(ICraftingHandler handler)
    313    {
    314        craftingHandlers.add(handler);
    315    }
    316
    317    public static void onItemCrafted(EntityPlayer player, ItemStack item, IInventory craftMatrix)
    318    {
    319        for (ICraftingHandler handler : craftingHandlers)
    320        {
    321            handler.onCrafting(player, item, craftMatrix);
    322        }
    323    }
    324
    325    public static void onItemSmelted(EntityPlayer player, ItemStack item)
    326    {
    327        for (ICraftingHandler handler : craftingHandlers)
    328        {
    329            handler.onSmelting(player, item);
    330        }
    331    }
    332
    333    public static void registerPickupHandler(IPickupNotifier handler)
    334    {
    335        pickupHandlers.add(handler);
    336    }
    337
    338    public static void onPickupNotification(EntityPlayer player, EntityItem item)
    339    {
    340        for (IPickupNotifier notify : pickupHandlers)
    341        {
    342            notify.notifyPickup(item, player);
    343        }
    344    }
    345
    346    public static void registerPlayerTracker(IPlayerTracker tracker)
    347    {
    348        playerTrackers.add(tracker);
    349    }
    350
    351    public static void onPlayerLogin(EntityPlayer player)
    352    {
    353        for(IPlayerTracker tracker : playerTrackers)
    354            tracker.onPlayerLogin(player);
    355    }
    356
    357    public static void onPlayerLogout(EntityPlayer player)
    358    {
    359        for(IPlayerTracker tracker : playerTrackers)
    360            tracker.onPlayerLogout(player);
    361    }
    362
    363    public static void onPlayerChangedDimension(EntityPlayer player)
    364    {
    365        for(IPlayerTracker tracker : playerTrackers)
    366            tracker.onPlayerChangedDimension(player);
    367    }
    368
    369    public static void onPlayerRespawn(EntityPlayer player)
    370    {
    371        for(IPlayerTracker tracker : playerTrackers)
    372            tracker.onPlayerRespawn(player);
    373    }
    374
    375
    376    /**
    377     * Look up a mod block in the global "named item list"
    378     * @param modId The modid owning the block
    379     * @param name The name of the block itself
    380     * @return The block or null if not found
    381     */
    382    public static net.minecraft.block.Block findBlock(String modId, String name)
    383    {
    384        return GameData.findBlock(modId, name);
    385    }
    386
    387    /**
    388     * Look up a mod item in the global "named item list"
    389     * @param modId The modid owning the item
    390     * @param name The name of the item itself
    391     * @return The item or null if not found
    392     */
    393    public static net.minecraft.item.Item findItem(String modId, String name)
    394    {
    395        return GameData.findItem(modId, name);
    396    }
    397
    398    /**
    399     * Manually register a custom item stack with FML for later tracking. It is automatically scoped with the active modid
    400     *
    401     * @param name The name to register it under
    402     * @param itemStack The itemstack to register
    403     */
    404    public static void registerCustomItemStack(String name, ItemStack itemStack)
    405    {
    406        GameData.registerCustomItemStack(name, itemStack);
    407    }
    408    /**
    409     * Lookup an itemstack based on mod and name. It will create "default" itemstacks from blocks and items if no
    410     * explicit itemstack is found.
    411     *
    412     * If it is built from a block, the metadata is by default the "wildcard" value.
    413     *
    414     * Custom itemstacks can be dumped from minecraft by setting the system property fml.dumpRegistry to true
    415     * (-Dfml.dumpRegistry=true on the command line will work)
    416     *
    417     * @param modId The modid of the stack owner
    418     * @param name The name of the stack
    419     * @param stackSize The size of the stack returned
    420     * @return The custom itemstack or null if no such itemstack was found
    421     */
    422    public static ItemStack findItemStack(String modId, String name, int stackSize)
    423    {
    424        ItemStack foundStack = GameData.findItemStack(modId, name);
    425        if (foundStack != null)
    426        {
    427            ItemStack is = foundStack.copy();
    428            is.stackSize = Math.min(stackSize, is.getMaxStackSize());
    429            return is;
    430        }
    431        return null;
    432    }
    433}