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, 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, "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}