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}