405 lines
16 KiB
Java
405 lines
16 KiB
Java
/*
|
|
* Minecraft Forge
|
|
* Copyright (c) 2016.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation version 2.1
|
|
* of the License.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
package net.minecraftforge.fml.common.registry;
|
|
|
|
import java.lang.annotation.ElementType;
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.lang.annotation.Target;
|
|
import java.lang.reflect.Constructor;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.Comparator;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Random;
|
|
import java.util.Set;
|
|
|
|
import net.minecraft.block.Block;
|
|
import net.minecraft.item.Item;
|
|
import net.minecraft.item.ItemBlock;
|
|
import net.minecraft.item.ItemStack;
|
|
import net.minecraft.item.crafting.CraftingManager;
|
|
import net.minecraft.item.crafting.FurnaceRecipes;
|
|
import net.minecraft.item.crafting.IRecipe;
|
|
import net.minecraft.nbt.JsonToNBT;
|
|
import net.minecraft.nbt.NBTBase;
|
|
import net.minecraft.nbt.NBTException;
|
|
import net.minecraft.nbt.NBTTagCompound;
|
|
import net.minecraft.tileentity.TileEntity;
|
|
import net.minecraft.util.ResourceLocation;
|
|
import net.minecraft.world.World;
|
|
import net.minecraft.world.chunk.IChunkGenerator;
|
|
import net.minecraft.world.chunk.IChunkProvider;
|
|
import net.minecraftforge.fml.common.FMLLog;
|
|
import net.minecraftforge.fml.common.IFuelHandler;
|
|
import net.minecraftforge.fml.common.IWorldGenerator;
|
|
import net.minecraftforge.fml.common.Loader;
|
|
import net.minecraftforge.fml.common.LoaderException;
|
|
import net.minecraftforge.fml.common.LoaderState;
|
|
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
|
|
|
|
import org.apache.logging.log4j.Level;
|
|
|
|
import com.google.common.base.Strings;
|
|
import com.google.common.base.Throwables;
|
|
import com.google.common.collect.ImmutableList;
|
|
import com.google.common.collect.Lists;
|
|
import com.google.common.collect.Maps;
|
|
import com.google.common.collect.ObjectArrays;
|
|
import com.google.common.collect.Sets;
|
|
import com.google.common.primitives.Ints;
|
|
|
|
import javax.annotation.Nonnull;
|
|
|
|
@SuppressWarnings({"WeakerAccess", "unused"})
|
|
public class GameRegistry
|
|
{
|
|
private static Set<IWorldGenerator> worldGenerators = Sets.newHashSet();
|
|
private static Map<IWorldGenerator, Integer> worldGeneratorIndex = Maps.newHashMap();
|
|
private static List<IFuelHandler> fuelHandlers = Lists.newArrayList();
|
|
private static List<IWorldGenerator> sortedGeneratorList;
|
|
|
|
/**
|
|
* Register a world generator - something that inserts new block types into the world
|
|
*
|
|
* @param generator the generator
|
|
* @param modGenerationWeight a weight to assign to this generator. Heavy weights tend to sink to the bottom of
|
|
* list of world generators (i.e. they run later)
|
|
*/
|
|
public static void registerWorldGenerator(IWorldGenerator generator, int modGenerationWeight)
|
|
{
|
|
worldGenerators.add(generator);
|
|
worldGeneratorIndex.put(generator, modGenerationWeight);
|
|
if (sortedGeneratorList != null)
|
|
{
|
|
sortedGeneratorList = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Callback hook for world gen - if your mod wishes to add extra mod related generation to the world
|
|
* call this
|
|
*
|
|
* @param chunkX Chunk X coordinate
|
|
* @param chunkZ Chunk Z coordinate
|
|
* @param world World we're generating into
|
|
* @param chunkGenerator The chunk generator
|
|
* @param chunkProvider The chunk provider
|
|
*/
|
|
public static void generateWorld(int chunkX, int chunkZ, World world, IChunkGenerator chunkGenerator, IChunkProvider chunkProvider)
|
|
{
|
|
if (sortedGeneratorList == null)
|
|
{
|
|
computeSortedGeneratorList();
|
|
}
|
|
long worldSeed = world.getSeed();
|
|
Random fmlRandom = new Random(worldSeed);
|
|
long xSeed = fmlRandom.nextLong() >> 2 + 1L;
|
|
long zSeed = fmlRandom.nextLong() >> 2 + 1L;
|
|
long chunkSeed = (xSeed * chunkX + zSeed * chunkZ) ^ worldSeed;
|
|
|
|
for (IWorldGenerator generator : sortedGeneratorList)
|
|
{
|
|
fmlRandom.setSeed(chunkSeed);
|
|
generator.generate(fmlRandom, chunkX, chunkZ, world, chunkGenerator, chunkProvider);
|
|
}
|
|
}
|
|
|
|
private static void computeSortedGeneratorList()
|
|
{
|
|
ArrayList<IWorldGenerator> list = Lists.newArrayList(worldGenerators);
|
|
Collections.sort(list, new Comparator<IWorldGenerator>()
|
|
{
|
|
@Override
|
|
public int compare(IWorldGenerator o1, IWorldGenerator o2)
|
|
{
|
|
return Ints.compare(worldGeneratorIndex.get(o1), worldGeneratorIndex.get(o2));
|
|
}
|
|
});
|
|
sortedGeneratorList = ImmutableList.copyOf(list);
|
|
}
|
|
|
|
/**
|
|
* Register the previously named {@link IForgeRegistry} object with the registry system.
|
|
* Always ensure the object is already named using {@link IForgeRegistryEntry#setRegistryName(ResourceLocation)}
|
|
* or another mechanism.
|
|
*
|
|
* Note: That DOES NOT create the ItemBlock for you if this is a Block, you should register that item separately.
|
|
*
|
|
* @param object The object to register with a registry
|
|
* @param <K> The registry supertype
|
|
* @throws IllegalArgumentException if the object is not yet named (use {@link #register(IForgeRegistryEntry, ResourceLocation)} instead)
|
|
* @return The object
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
public static <K extends IForgeRegistryEntry<?>> K register(K object)
|
|
{
|
|
return (K)GameData.register_impl(object);
|
|
}
|
|
|
|
/**
|
|
* Register the unnamed {@link IForgeRegistry} object with the registry system.
|
|
* Always make sure you have not previously named the object.
|
|
*
|
|
* It is advised that you set the object's registry name and use {@link #register(IForgeRegistryEntry)} instead.
|
|
*
|
|
* Note: That DOES NOT create the ItemBlock for you if this is a Block, you should register that item separately.
|
|
*
|
|
* @param object The object to register
|
|
* @param name The name to register it with
|
|
* @param <K> The registry supertype
|
|
* @throws IllegalStateException if the object already has an existing name (use {@link #register(IForgeRegistryEntry)} instead)
|
|
* @return The object
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
public static <K extends IForgeRegistryEntry<?>> K register(K object, ResourceLocation name)
|
|
{
|
|
return (K)GameData.register_impl(object, name);
|
|
}
|
|
|
|
|
|
/**
|
|
* Registers a named block with the Block registry. This WILL create a new ItemBlock for you with the
|
|
* same name and register it with the ItemRegistry. This method is created as a convince method for
|
|
* modder and SHOULD NOT be used. Modders should create and register their ItemBlocks like normal items.
|
|
*
|
|
* @param block The block to register with a registry
|
|
* @throws IllegalArgumentException if the object is not yet named
|
|
* @return The block
|
|
*/
|
|
@Deprecated //Modders SHOULD NOT use this, so it'll stay deprecated. Purely added to make lazy modders happy -.-
|
|
public static Block registerWithItem(Block block)
|
|
{
|
|
register(block);
|
|
register(new ItemBlock(block).setRegistryName(block.getRegistryName()));
|
|
return block;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the registry associated with this super class type.
|
|
* If the return is non-null it is HIGHLY recommended that modders cache this
|
|
* value as the return will never change for a given type in a single run of Minecraft once set.
|
|
*
|
|
* @param registryType The base class of items in this registry.
|
|
* @return The registry, Null if none is registered.
|
|
*/
|
|
public static <K extends IForgeRegistryEntry<K>> IForgeRegistry<K> findRegistry(Class<K> registryType)
|
|
{
|
|
return PersistentRegistryManager.findRegistryByType(registryType);
|
|
}
|
|
|
|
/**
|
|
* Add a forced persistent substitution alias for the block or item to another block or item. This will have
|
|
* the effect of using the substituted block or item instead of the original, where ever it is
|
|
* referenced.
|
|
*
|
|
* @param nameToSubstitute The name to link to (this is the NEW block or item)
|
|
* @param type The type (Block or Item)
|
|
* @param object a NEW instance that is type compatible with the existing instance
|
|
* @throws ExistingSubstitutionException if someone else has already registered an alias either from or to one of the names
|
|
* @throws IncompatibleSubstitutionException if the substitution is incompatible
|
|
*/
|
|
public static void addSubstitutionAlias(String nameToSubstitute, GameRegistry.Type type, Object object) throws ExistingSubstitutionException
|
|
{
|
|
GameData.getMain().registerSubstitutionAlias(nameToSubstitute, type, object);
|
|
}
|
|
|
|
public static void addRecipe(@Nonnull ItemStack output, Object... params)
|
|
{
|
|
addShapedRecipe(output, params);
|
|
}
|
|
|
|
public static IRecipe addShapedRecipe(@Nonnull ItemStack output, Object... params)
|
|
{
|
|
return CraftingManager.getInstance().addRecipe(output, params);
|
|
}
|
|
|
|
public static void addShapelessRecipe(@Nonnull ItemStack output, Object... params)
|
|
{
|
|
CraftingManager.getInstance().addShapelessRecipe(output, params);
|
|
}
|
|
|
|
public static void addRecipe(IRecipe recipe)
|
|
{
|
|
CraftingManager.getInstance().getRecipeList().add(recipe);
|
|
}
|
|
|
|
public static void addSmelting(Block input, @Nonnull ItemStack output, float xp)
|
|
{
|
|
FurnaceRecipes.instance().addSmeltingRecipeForBlock(input, output, xp);
|
|
}
|
|
|
|
public static void addSmelting(Item input, @Nonnull ItemStack output, float xp)
|
|
{
|
|
FurnaceRecipes.instance().addSmelting(input, output, xp);
|
|
}
|
|
|
|
public static void addSmelting(@Nonnull ItemStack input, @Nonnull ItemStack output, float xp)
|
|
{
|
|
FurnaceRecipes.instance().addSmeltingRecipe(input, output, xp);
|
|
}
|
|
|
|
public static void registerTileEntity(Class<? extends TileEntity> tileEntityClass, String id)
|
|
{
|
|
GameData.getTileEntityRegistry().putObject(new ResourceLocation(id), tileEntityClass);
|
|
}
|
|
|
|
/**
|
|
* Register a tile entity, with alternative TileEntity identifiers. Use with caution!
|
|
* This method allows for you to "rename" the 'id' of the tile entity.
|
|
*
|
|
* @param tileEntityClass The tileEntity class to register
|
|
* @param id The primary ID, this will be the ID that the tileentity saves as
|
|
* @param alternatives A list of alternative IDs that will also map to this class. These will never save, but they will load
|
|
*/
|
|
public static void registerTileEntityWithAlternatives(Class<? extends TileEntity> tileEntityClass, String id, String... alternatives)
|
|
{
|
|
GameRegistry.registerTileEntity(tileEntityClass, id);
|
|
for (String s : alternatives)
|
|
{
|
|
GameData.getTileEntityRegistry().addLegacyName(new ResourceLocation(s), new ResourceLocation(id));
|
|
}
|
|
}
|
|
|
|
public static void registerFuelHandler(IFuelHandler handler)
|
|
{
|
|
fuelHandlers.add(handler);
|
|
}
|
|
|
|
public static int getFuelValue(@Nonnull ItemStack itemStack)
|
|
{
|
|
int fuelValue = 0;
|
|
for (IFuelHandler handler : fuelHandlers)
|
|
{
|
|
fuelValue = Math.max(fuelValue, handler.getBurnTime(itemStack));
|
|
}
|
|
return fuelValue;
|
|
}
|
|
|
|
public enum Type
|
|
{
|
|
BLOCK,
|
|
ITEM;
|
|
}
|
|
|
|
/**
|
|
* ObjectHolder can be used to automatically populate public static final fields with entries
|
|
* from the registry. These values can then be referred within mod code directly.
|
|
*/
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
@Target({ElementType.TYPE, ElementType.FIELD})
|
|
public @interface ObjectHolder
|
|
{
|
|
/**
|
|
* If used on a class, this represents a modid only.
|
|
* If used on a field, it represents a name, which can be abbreviated or complete.
|
|
* Abbreviated names derive their modid from an enclosing ObjectHolder at the class level.
|
|
*
|
|
* @return either a modid or a name based on the rules above
|
|
*/
|
|
String value();
|
|
}
|
|
|
|
/**
|
|
* ItemStackHolder can be used to automatically populate public static final fields with
|
|
* {@link ItemStack} instances, referring a specific item, potentially configured with NBT.
|
|
* These values can then be used in things like recipes and other places where ItemStacks
|
|
* might be required.
|
|
* <p/>
|
|
* If the item is not found, the field will be populated with null.
|
|
*/
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
@Target(ElementType.FIELD)
|
|
public @interface ItemStackHolder
|
|
{
|
|
/**
|
|
* The registry name of the item being looked up.
|
|
*
|
|
* @return The registry name
|
|
*/
|
|
public String value();
|
|
|
|
/**
|
|
* The metadata or damage value for the itemstack, defaults to 0.
|
|
*
|
|
* @return the metadata value
|
|
*/
|
|
public int meta() default 0;
|
|
|
|
/**
|
|
* The string serialized nbt value for the itemstack. Defaults to empty for no nbt.
|
|
*
|
|
* @return a nbt string
|
|
*/
|
|
public String nbt() default "";
|
|
}
|
|
|
|
/**
|
|
* Makes an {@link ItemStack} based on the itemName reference, with supplied meta, stackSize and nbt, if possible
|
|
* <p/>
|
|
* Will return null if the item doesn't exist (because it's not from a loaded mod for example)
|
|
* Will throw a {@link RuntimeException} if the nbtString is invalid for use in an {@link ItemStack}
|
|
*
|
|
* @param itemName a registry name reference
|
|
* @param meta the meta
|
|
* @param stackSize the stack size
|
|
* @param nbtString an nbt stack as a string, will be processed by {@link JsonToNBT}
|
|
* @return a new itemstack
|
|
*/
|
|
@Nonnull
|
|
public static ItemStack makeItemStack(String itemName, int meta, int stackSize, String nbtString)
|
|
{
|
|
if (itemName == null)
|
|
{
|
|
throw new IllegalArgumentException("The itemName cannot be null");
|
|
}
|
|
Item item = GameData.getItemRegistry().getObject(new ResourceLocation(itemName));
|
|
if (item == null)
|
|
{
|
|
FMLLog.getLogger().log(Level.TRACE, "Unable to find item with name {}", itemName);
|
|
return ItemStack.EMPTY;
|
|
}
|
|
ItemStack is = new ItemStack(item, stackSize, meta);
|
|
if (!Strings.isNullOrEmpty(nbtString))
|
|
{
|
|
NBTBase nbttag = null;
|
|
try
|
|
{
|
|
nbttag = JsonToNBT.getTagFromJson(nbtString);
|
|
} catch (NBTException e)
|
|
{
|
|
FMLLog.getLogger().log(Level.WARN, "Encountered an exception parsing ItemStack NBT string {}", nbtString, e);
|
|
throw Throwables.propagate(e);
|
|
}
|
|
if (!(nbttag instanceof NBTTagCompound))
|
|
{
|
|
FMLLog.getLogger().log(Level.WARN, "Unexpected NBT string - multiple values {}", nbtString);
|
|
throw new RuntimeException("Invalid NBT JSON");
|
|
}
|
|
else
|
|
{
|
|
is.setTagCompound((NBTTagCompound)nbttag);
|
|
}
|
|
}
|
|
return is;
|
|
}
|
|
}
|