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