base mod created
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,821 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.registries;
|
||||
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimap;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockAir;
|
||||
import net.minecraft.block.BlockObserver;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.enchantment.Enchantment;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityList;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.inventory.InventoryCrafting;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemBlock;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.crafting.IRecipe;
|
||||
import net.minecraft.potion.Potion;
|
||||
import net.minecraft.potion.PotionType;
|
||||
import net.minecraft.util.ObjectIntIdentityMap;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.registry.RegistryNamespaced;
|
||||
import net.minecraft.util.registry.RegistryNamespacedDefaultedByKey;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.RegistryEvent;
|
||||
import net.minecraftforge.event.RegistryEvent.MissingMappings;
|
||||
import net.minecraftforge.fml.common.EnhancedRuntimeException;
|
||||
import net.minecraftforge.fml.common.FMLCommonHandler;
|
||||
import net.minecraftforge.fml.common.FMLContainer;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import net.minecraftforge.fml.common.InjectedModContainer;
|
||||
import net.minecraftforge.fml.common.Loader;
|
||||
import net.minecraftforge.fml.common.ModContainer;
|
||||
import net.minecraftforge.fml.common.StartupQuery;
|
||||
import net.minecraftforge.fml.common.ZipperUtil;
|
||||
import net.minecraftforge.fml.common.registry.EntityEntry;
|
||||
import net.minecraftforge.fml.common.registry.EntityEntryBuilder;
|
||||
import net.minecraftforge.fml.common.registry.GameRegistry;
|
||||
import net.minecraftforge.fml.common.registry.VillagerRegistry.VillagerProfession;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.BiMap;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
import net.minecraftforge.fml.common.EnhancedRuntimeException.WrappedPrintStream;
|
||||
|
||||
/**
|
||||
* INTERNAL ONLY
|
||||
* MODDERS SHOULD HAVE NO REASON TO USE THIS CLASS
|
||||
*/
|
||||
public class GameData
|
||||
{
|
||||
public static final ResourceLocation BLOCKS = new ResourceLocation("minecraft:blocks");
|
||||
public static final ResourceLocation ITEMS = new ResourceLocation("minecraft:items");
|
||||
public static final ResourceLocation POTIONS = new ResourceLocation("minecraft:potions");
|
||||
public static final ResourceLocation BIOMES = new ResourceLocation("minecraft:biomes");
|
||||
public static final ResourceLocation SOUNDEVENTS = new ResourceLocation("minecraft:soundevents");
|
||||
public static final ResourceLocation POTIONTYPES = new ResourceLocation("minecraft:potiontypes");
|
||||
public static final ResourceLocation ENCHANTMENTS = new ResourceLocation("minecraft:enchantments");
|
||||
public static final ResourceLocation ENTITIES = new ResourceLocation("minecraft:entities");
|
||||
public static final ResourceLocation RECIPES = new ResourceLocation("minecraft:recipes");
|
||||
public static final ResourceLocation PROFESSIONS = new ResourceLocation("minecraft:villagerprofessions");
|
||||
private static final int MAX_BLOCK_ID = 4095;
|
||||
private static final int MIN_ITEM_ID = MAX_BLOCK_ID + 1;
|
||||
private static final int MAX_ITEM_ID = 31999;
|
||||
private static final int MAX_POTION_ID = 255; // SPacketEntityEffect sends bytes, we can only use 255
|
||||
private static final int MAX_BIOME_ID = 255; // Maximum number in a byte in the chunk
|
||||
private static final int MAX_SOUND_ID = Integer.MAX_VALUE >> 5; // Varint (SPacketSoundEffect)
|
||||
private static final int MAX_POTIONTYPE_ID = Integer.MAX_VALUE >> 5; // Int (SPacketEffect)
|
||||
private static final int MAX_ENCHANTMENT_ID = Short.MAX_VALUE - 1; // Short - serialized as a short in ItemStack NBTs.
|
||||
private static final int MAX_ENTITY_ID = Integer.MAX_VALUE >> 5; // Varint (SPacketSpawnMob)
|
||||
private static final int MAX_RECIPE_ID = Integer.MAX_VALUE >> 5; // Varint CPacketRecipeInfo/SPacketRecipeBook
|
||||
private static final int MAX_PROFESSION_ID = 1024; //TODO: Is this serialized anywhere anymore?
|
||||
|
||||
private static final ResourceLocation BLOCK_TO_ITEM = new ResourceLocation("minecraft:blocktoitemmap");
|
||||
private static final ResourceLocation BLOCKSTATE_TO_ID = new ResourceLocation("minecraft:blockstatetoid");
|
||||
private static boolean hasInit = false;
|
||||
private static final boolean DISABLE_VANILLA_REGISTRIES = Boolean.parseBoolean(System.getProperty("forge.disableVanillaGameData", "false")); // Use for unit tests/debugging
|
||||
private static final BiConsumer<ResourceLocation, ForgeRegistry<?>> LOCK_VANILLA = (name, reg) -> reg.slaves.values().stream().filter(o -> o instanceof ILockableRegistry).forEach(o -> ((ILockableRegistry)o).lock());
|
||||
|
||||
static {
|
||||
init();
|
||||
}
|
||||
|
||||
public static void init()
|
||||
{
|
||||
if ( DISABLE_VANILLA_REGISTRIES)
|
||||
{
|
||||
FMLLog.bigWarning("DISABLING VANILLA REGISTRY CREATION AS PER SYSTEM VARIABLE SETTING! forge.disableVanillaGameData");
|
||||
return;
|
||||
}
|
||||
if (hasInit)
|
||||
return;
|
||||
hasInit = true;
|
||||
makeRegistry(BLOCKS, Block.class, MAX_BLOCK_ID, new ResourceLocation("air")).addCallback(BlockCallbacks.INSTANCE).create();
|
||||
makeRegistry(ITEMS, Item.class, MIN_ITEM_ID, MAX_ITEM_ID).addCallback(ItemCallbacks.INSTANCE).create();
|
||||
makeRegistry(POTIONS, Potion.class, MAX_POTION_ID).create();
|
||||
makeRegistry(BIOMES, Biome.class, MAX_BIOME_ID).create();
|
||||
makeRegistry(SOUNDEVENTS, SoundEvent.class, MAX_SOUND_ID).create();
|
||||
makeRegistry(POTIONTYPES, PotionType.class, MAX_POTIONTYPE_ID, new ResourceLocation("empty")).create();
|
||||
makeRegistry(ENCHANTMENTS, Enchantment.class, MAX_ENCHANTMENT_ID).create();
|
||||
makeRegistry(RECIPES, IRecipe.class, MAX_RECIPE_ID).disableSaving().allowModification().addCallback(RecipeCallbacks.INSTANCE).create();
|
||||
makeRegistry(PROFESSIONS, VillagerProfession.class, MAX_PROFESSION_ID).create();
|
||||
entityRegistry = (ForgeRegistry<EntityEntry>)makeRegistry(ENTITIES, EntityEntry.class, MAX_ENTITY_ID).addCallback(EntityCallbacks.INSTANCE).create();
|
||||
}
|
||||
|
||||
private static <T extends IForgeRegistryEntry<T>> RegistryBuilder<T> makeRegistry(ResourceLocation name, Class<T> type, int min, int max)
|
||||
{
|
||||
return new RegistryBuilder<T>().setName(name).setType(type).setIDRange(min, max).addCallback(new NamespacedWrapper.Factory<T>());
|
||||
}
|
||||
private static <T extends IForgeRegistryEntry<T>> RegistryBuilder<T> makeRegistry(ResourceLocation name, Class<T> type, int max)
|
||||
{
|
||||
return new RegistryBuilder<T>().setName(name).setType(type).setMaxID(max).addCallback(new NamespacedWrapper.Factory<T>());
|
||||
}
|
||||
private static <T extends IForgeRegistryEntry<T>> RegistryBuilder<T> makeRegistry(ResourceLocation name, Class<T> type, int max, ResourceLocation _default)
|
||||
{
|
||||
return new RegistryBuilder<T>().setName(name).setType(type).setMaxID(max).addCallback(new NamespacedDefaultedWrapper.Factory<T>()).setDefaultKey(_default);
|
||||
}
|
||||
|
||||
public static <V extends IForgeRegistryEntry<V>> RegistryNamespacedDefaultedByKey<ResourceLocation, V> getWrapperDefaulted(Class<V> cls)
|
||||
{
|
||||
IForgeRegistry<V> reg = GameRegistry.findRegistry(cls);
|
||||
Validate.notNull(reg, "Attempted to get vanilla wrapper for unknown registry: " + cls.toString());
|
||||
@SuppressWarnings("unchecked")
|
||||
RegistryNamespacedDefaultedByKey<ResourceLocation, V> ret = reg.getSlaveMap(NamespacedDefaultedWrapper.Factory.ID, NamespacedDefaultedWrapper.class);
|
||||
Validate.notNull(ret, "Attempted to get vanilla wrapper for registry created incorrectly: " + cls.toString());
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static <V extends IForgeRegistryEntry<V>> RegistryNamespaced<ResourceLocation, V> getWrapper(Class<V> cls)
|
||||
{
|
||||
IForgeRegistry<V> reg = GameRegistry.findRegistry(cls);
|
||||
Validate.notNull(reg, "Attempted to get vanilla wrapper for unknown registry: " + cls.toString());
|
||||
@SuppressWarnings("unchecked")
|
||||
RegistryNamespaced<ResourceLocation, V> ret = reg.getSlaveMap(NamespacedWrapper.Factory.ID, NamespacedWrapper.class);
|
||||
Validate.notNull(ret, "Attempted to get vanilla wrapper for registry created incorrectly: " + cls.toString());
|
||||
return ret;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static BiMap<Block,Item> getBlockItemMap()
|
||||
{
|
||||
return GameRegistry.findRegistry(Item.class).getSlaveMap(BLOCK_TO_ITEM, BiMap.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static ObjectIntIdentityMap<IBlockState> getBlockStateIDMap()
|
||||
{
|
||||
return GameRegistry.findRegistry(Block.class).getSlaveMap(BLOCKSTATE_TO_ID, ObjectIntIdentityMap.class);
|
||||
}
|
||||
|
||||
public static <K extends IForgeRegistryEntry<K>> K register_impl(K value)
|
||||
{
|
||||
Validate.notNull(value, "Attempted to register a null object");
|
||||
Validate.notNull(value.getRegistryName(), String.format("Attempt to register object without having set a registry name %s (type %s)", value, value.getClass().getName()));
|
||||
final IForgeRegistry<K> registry = GameRegistry.findRegistry(value.getRegistryType());
|
||||
Validate.notNull(registry, "Attempted to registry object without creating registry first: " + value.getRegistryType().getName());
|
||||
registry.register(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public static void vanillaSnapshot()
|
||||
{
|
||||
FMLLog.log.debug("Creating vanilla freeze snapshot");
|
||||
for (Map.Entry<ResourceLocation, ForgeRegistry<? extends IForgeRegistryEntry<?>>> r : RegistryManager.ACTIVE.registries.entrySet())
|
||||
{
|
||||
final Class<? extends IForgeRegistryEntry> clazz = RegistryManager.ACTIVE.getSuperType(r.getKey());
|
||||
loadRegistry(r.getKey(), RegistryManager.ACTIVE, RegistryManager.VANILLA, clazz, true);
|
||||
}
|
||||
RegistryManager.VANILLA.registries.forEach((name, reg) ->
|
||||
{
|
||||
reg.validateContent(name);
|
||||
reg.freeze();
|
||||
});
|
||||
RegistryManager.VANILLA.registries.forEach(LOCK_VANILLA);
|
||||
RegistryManager.ACTIVE.registries.forEach(LOCK_VANILLA);
|
||||
FMLLog.log.debug("Vanilla freeze snapshot created");
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public static void freezeData()
|
||||
{
|
||||
FMLLog.log.debug("Freezing registries");
|
||||
for (Map.Entry<ResourceLocation, ForgeRegistry<? extends IForgeRegistryEntry<?>>> r : RegistryManager.ACTIVE.registries.entrySet())
|
||||
{
|
||||
final Class<? extends IForgeRegistryEntry> clazz = RegistryManager.ACTIVE.getSuperType(r.getKey());
|
||||
loadRegistry(r.getKey(), RegistryManager.ACTIVE, RegistryManager.FROZEN, clazz, true);
|
||||
}
|
||||
RegistryManager.FROZEN.registries.forEach((name, reg) ->
|
||||
{
|
||||
reg.validateContent(name);
|
||||
reg.freeze();
|
||||
});
|
||||
RegistryManager.ACTIVE.registries.forEach((name, reg) -> reg.freeze());
|
||||
|
||||
// the id mapping is fanilized, no ids actually changed but this is a good place to tell everyone to 'bake' their stuff.
|
||||
Loader.instance().fireRemapEvent(ImmutableMap.of(), true);
|
||||
|
||||
FMLLog.log.debug("All registries frozen");
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public static void revertToFrozen()
|
||||
{
|
||||
if (RegistryManager.FROZEN.registries.isEmpty())
|
||||
{
|
||||
FMLLog.log.warn("Can't revert to frozen GameData state without freezing first.");
|
||||
return;
|
||||
}
|
||||
RegistryManager.ACTIVE.registries.forEach((name, reg) -> reg.resetDelegates());
|
||||
|
||||
FMLLog.log.debug("Reverting to frozen data state.");
|
||||
for (Map.Entry<ResourceLocation, ForgeRegistry<? extends IForgeRegistryEntry<?>>> r : RegistryManager.ACTIVE.registries.entrySet())
|
||||
{
|
||||
final Class<? extends IForgeRegistryEntry> clazz = RegistryManager.ACTIVE.getSuperType(r.getKey());
|
||||
loadRegistry(r.getKey(), RegistryManager.FROZEN, RegistryManager.ACTIVE, clazz, true);
|
||||
}
|
||||
// the id mapping has reverted, fire remap events for those that care about id changes
|
||||
Loader.instance().fireRemapEvent(ImmutableMap.of(), true);
|
||||
|
||||
// the id mapping has reverted, ensure we sync up the object holders
|
||||
ObjectHolderRegistry.INSTANCE.applyObjectHolders();
|
||||
FMLLog.log.debug("Frozen state restored.");
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public static void revert(RegistryManager state, ResourceLocation registry, boolean lock)
|
||||
{
|
||||
FMLLog.log.debug("Reverting {} to {}", registry, state.getName());
|
||||
final Class<? extends IForgeRegistryEntry> clazz = RegistryManager.ACTIVE.getSuperType(registry);
|
||||
loadRegistry(registry, state, RegistryManager.ACTIVE, clazz, lock);
|
||||
FMLLog.log.debug("Reverting complete");
|
||||
}
|
||||
|
||||
//Lets us clear the map so we can rebuild it.
|
||||
static class ClearableObjectIntIdentityMap<I> extends ObjectIntIdentityMap<I>
|
||||
{
|
||||
void clear()
|
||||
{
|
||||
this.identityMap.clear();
|
||||
this.objectList.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class BlockCallbacks implements IForgeRegistry.AddCallback<Block>, IForgeRegistry.ClearCallback<Block>, IForgeRegistry.CreateCallback<Block>, IForgeRegistry.DummyFactory<Block>
|
||||
{
|
||||
static final BlockCallbacks INSTANCE = new BlockCallbacks();
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void onAdd(IForgeRegistryInternal<Block> owner, RegistryManager stage, int id, Block block, @Nullable Block oldBlock)
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
ClearableObjectIntIdentityMap<IBlockState> blockstateMap = owner.getSlaveMap(BLOCKSTATE_TO_ID, ClearableObjectIntIdentityMap.class);
|
||||
|
||||
if ("minecraft:tripwire".equals(block.getRegistryName().toString())) //Tripwire is crap so we have to special case whee!
|
||||
{
|
||||
for (int meta = 0; meta < 15; meta++)
|
||||
blockstateMap.put(block.getStateFromMeta(meta), id << 4 | meta);
|
||||
}
|
||||
|
||||
//So, due to blocks having more in-world states then metadata allows, we have to turn the map into a semi-milti-bimap.
|
||||
//We can do this however because the implementation of the map is last set wins. So we can add all states, then fix the meta bimap.
|
||||
//Multiple states -> meta. But meta to CORRECT state.
|
||||
|
||||
final boolean[] usedMeta = new boolean[16]; //Hold a list of known meta from all states.
|
||||
for (IBlockState state : block.getBlockState().getValidStates())
|
||||
{
|
||||
final int meta = block.getMetaFromState(state);
|
||||
blockstateMap.put(state, id << 4 | meta); //Add ALL the things!
|
||||
usedMeta[meta] = true;
|
||||
}
|
||||
|
||||
for (int meta = 0; meta < 16; meta++)
|
||||
{
|
||||
if (block.getClass() == BlockObserver.class)
|
||||
continue; //Observers are bad and have non-cyclical states. So we HAVE to use the vanilla logic above.
|
||||
if (usedMeta[meta])
|
||||
blockstateMap.put(block.getStateFromMeta(meta), id << 4 | meta); // Put the CORRECT thing!
|
||||
}
|
||||
|
||||
if (oldBlock != null)
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
BiMap<Block, Item> blockToItem = owner.getSlaveMap(BLOCK_TO_ITEM, BiMap.class);
|
||||
Item item = blockToItem.get(oldBlock);
|
||||
if (item != null)
|
||||
blockToItem.forcePut(block, item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClear(IForgeRegistryInternal<Block> owner, RegistryManager stage)
|
||||
{
|
||||
owner.getSlaveMap(BLOCKSTATE_TO_ID, ClearableObjectIntIdentityMap.class).clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(IForgeRegistryInternal<Block> owner, RegistryManager stage)
|
||||
{
|
||||
final ClearableObjectIntIdentityMap<IBlockState> idMap = new ClearableObjectIntIdentityMap<IBlockState>()
|
||||
{
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public int get(IBlockState key)
|
||||
{
|
||||
Integer integer = (Integer)this.identityMap.get(key);
|
||||
// There are some cases where this map is queried to serialize a state that is valid,
|
||||
//but somehow not in this list, so attempt to get real metadata. Doing this hear saves us 7 patches
|
||||
if (integer == null && key != null)
|
||||
integer = this.identityMap.get(key.getBlock().getStateFromMeta(key.getBlock().getMetaFromState(key)));
|
||||
return integer == null ? -1 : integer.intValue();
|
||||
}
|
||||
};
|
||||
owner.setSlaveMap(BLOCKSTATE_TO_ID, idMap);
|
||||
owner.setSlaveMap(BLOCK_TO_ITEM, HashBiMap.create());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block createDummy(ResourceLocation key)
|
||||
{
|
||||
Block ret = new BlockDummyAir().setUnlocalizedName("air");
|
||||
GameData.forceRegistryName(ret, key);
|
||||
return ret;
|
||||
}
|
||||
private static class BlockDummyAir extends BlockAir //A named class so DummyBlockReplacementTest can detect if its a dummy
|
||||
{
|
||||
private BlockDummyAir()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ItemCallbacks implements IForgeRegistry.AddCallback<Item>, IForgeRegistry.ClearCallback<Item>, IForgeRegistry.CreateCallback<Item>
|
||||
{
|
||||
static final ItemCallbacks INSTANCE = new ItemCallbacks();
|
||||
|
||||
@Override
|
||||
public void onAdd(IForgeRegistryInternal<Item> owner, RegistryManager stage, int id, Item item, @Nullable Item oldItem)
|
||||
{
|
||||
if (item instanceof ItemBlock)
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
BiMap<Block, Item> blockToItem = owner.getSlaveMap(BLOCK_TO_ITEM, BiMap.class);
|
||||
blockToItem.forcePut(((ItemBlock)item).getBlock(), item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClear(IForgeRegistryInternal<Item> owner, RegistryManager stage)
|
||||
{
|
||||
owner.getSlaveMap(BLOCK_TO_ITEM, BiMap.class).clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(IForgeRegistryInternal<Item> owner, RegistryManager stage)
|
||||
{
|
||||
// We share the blockItem map between items and blocks registries
|
||||
BiMap<?, ?> map = stage.getRegistry(BLOCKS).getSlaveMap(BLOCK_TO_ITEM, BiMap.class);
|
||||
owner.setSlaveMap(BLOCK_TO_ITEM, map);
|
||||
}
|
||||
}
|
||||
|
||||
private static class RecipeCallbacks implements IForgeRegistry.ValidateCallback<IRecipe>, IForgeRegistry.MissingFactory<IRecipe>
|
||||
{
|
||||
static final RecipeCallbacks INSTANCE = new RecipeCallbacks();
|
||||
|
||||
@Override
|
||||
public void onValidate(IForgeRegistryInternal<IRecipe> owner, RegistryManager stage, int id, ResourceLocation key, IRecipe obj)
|
||||
{
|
||||
if (stage != RegistryManager.ACTIVE) return;
|
||||
// verify the recipe output yields a registered item
|
||||
Item item = obj.getRecipeOutput().getItem();
|
||||
if (!stage.getRegistry(Item.class).containsValue(item))
|
||||
{
|
||||
throw new IllegalStateException(String.format("Recipe %s (%s) produces unregistered item %s (%s)", key, obj, item.getRegistryName(), item));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IRecipe createMissing(ResourceLocation key, boolean isNetwork)
|
||||
{
|
||||
return isNetwork ? new DummyRecipe().setRegistryName(key) : null;
|
||||
}
|
||||
private static class DummyRecipe implements IRecipe
|
||||
{
|
||||
private static ItemStack result = new ItemStack(Items.DIAMOND, 64);
|
||||
private ResourceLocation name;
|
||||
|
||||
@Override
|
||||
public IRecipe setRegistryName(ResourceLocation name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
@Override public ResourceLocation getRegistryName() { return name; }
|
||||
@Override public Class<IRecipe> getRegistryType() { return IRecipe.class; }
|
||||
@Override public boolean matches(InventoryCrafting inv, World worldIn) { return false; } //dirt?
|
||||
@Override public ItemStack getCraftingResult(InventoryCrafting inv) { return result; }
|
||||
@Override public boolean canFit(int width, int height) { return false; }
|
||||
@Override public ItemStack getRecipeOutput() { return result; }
|
||||
@Override public boolean isDynamic() { return true; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static ForgeRegistry<EntityEntry> entityRegistry;
|
||||
public static ForgeRegistry<EntityEntry> getEntityRegistry() { return entityRegistry; }
|
||||
public static void registerEntity(int id, ResourceLocation key, Class<? extends Entity> clazz, String oldName)
|
||||
{
|
||||
RegistryNamespaced<ResourceLocation, EntityEntry> reg = getWrapper(EntityEntry.class);
|
||||
reg.register(id, key, new EntityEntry(clazz, oldName));
|
||||
}
|
||||
|
||||
private static class EntityCallbacks implements IForgeRegistry.AddCallback<EntityEntry>
|
||||
{
|
||||
static final EntityCallbacks INSTANCE = new EntityCallbacks();
|
||||
|
||||
@Override
|
||||
public void onAdd(IForgeRegistryInternal<EntityEntry> owner, RegistryManager stage, int id, EntityEntry entry, @Nullable EntityEntry oldEntry)
|
||||
{
|
||||
if (entry instanceof EntityEntryBuilder.BuiltEntityEntry)
|
||||
{
|
||||
((EntityEntryBuilder.BuiltEntityEntry) entry).addedToRegistry();
|
||||
}
|
||||
if (entry.getEgg() != null)
|
||||
EntityList.ENTITY_EGGS.put(entry.getRegistryName(), entry.getEgg());
|
||||
}
|
||||
}
|
||||
|
||||
private static <T extends IForgeRegistryEntry<T>> void loadRegistry(final ResourceLocation registryName, final RegistryManager from, final RegistryManager to, final Class<T> regType, boolean freeze)
|
||||
{
|
||||
ForgeRegistry<T> fromRegistry = from.getRegistry(registryName);
|
||||
if (fromRegistry == null)
|
||||
{
|
||||
ForgeRegistry<T> toRegistry = to.getRegistry(registryName);
|
||||
if (toRegistry == null)
|
||||
{
|
||||
throw new EnhancedRuntimeException("Could not find registry to load: " + registryName){
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Override
|
||||
protected void printStackTrace(WrappedPrintStream stream)
|
||||
{
|
||||
stream.println("Looking For: " + registryName);
|
||||
stream.println("Found From:");
|
||||
for (ResourceLocation name : from.registries.keySet())
|
||||
stream.println(" " + name);
|
||||
stream.println("Found To:");
|
||||
for (ResourceLocation name : to.registries.keySet())
|
||||
stream.println(" " + name);
|
||||
}
|
||||
};
|
||||
}
|
||||
// We found it in to, so lets trust to's state...
|
||||
// This happens when connecting to a server that doesn't have this registry.
|
||||
// Such as a 1.8.0 Forge server with 1.8.8+ Forge.
|
||||
// We must however, re-fire the callbacks as some internal data may be corrupted {potions}
|
||||
//TODO: With my rework of how registries add callbacks are done.. I don't think this is necessary.
|
||||
//fire addCallback for each entry
|
||||
}
|
||||
else
|
||||
{
|
||||
ForgeRegistry<T> toRegistry = to.getRegistry(registryName, from);
|
||||
toRegistry.sync(registryName, fromRegistry);
|
||||
if (freeze)
|
||||
toRegistry.isFrozen = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public static Multimap<ResourceLocation, ResourceLocation> injectSnapshot(Map<ResourceLocation, ForgeRegistry.Snapshot> snapshot, boolean injectFrozenData, boolean isLocalWorld)
|
||||
{
|
||||
FMLLog.log.info("Injecting existing registry data into this {} instance", FMLCommonHandler.instance().getEffectiveSide().isServer() ? "server" : "client");
|
||||
RegistryManager.ACTIVE.registries.forEach((name, reg) -> reg.validateContent(name));
|
||||
RegistryManager.ACTIVE.registries.forEach((name, reg) -> reg.dump(name));
|
||||
RegistryManager.ACTIVE.registries.forEach((name, reg) -> reg.resetDelegates());
|
||||
|
||||
List<ResourceLocation> missingRegs = snapshot.keySet().stream().filter(name -> !RegistryManager.ACTIVE.registries.containsKey(name)).collect(Collectors.toList());
|
||||
if (missingRegs.size() > 0)
|
||||
{
|
||||
String text = "Forge Mod Loader detected missing/unknown registrie(s).\n\n" +
|
||||
"There are " + missingRegs.size() + " missing registries in this save.\n" +
|
||||
"If you continue the missing registries will get removed.\n" +
|
||||
"This may cause issues, it is advised that you create a world backup before continuing.\n\n" +
|
||||
"Missing Registries:\n";
|
||||
|
||||
for (ResourceLocation s : missingRegs)
|
||||
text += s.toString() + "\n";
|
||||
|
||||
if (!StartupQuery.confirm(text))
|
||||
StartupQuery.abort();
|
||||
}
|
||||
|
||||
RegistryManager STAGING = new RegistryManager("STAGING");
|
||||
|
||||
final Map<ResourceLocation, Map<ResourceLocation, Integer[]>> remaps = Maps.newHashMap();
|
||||
final LinkedHashMap<ResourceLocation, Map<ResourceLocation, Integer>> missing = Maps.newLinkedHashMap();
|
||||
// Load the snapshot into the "STAGING" registry
|
||||
snapshot.forEach((key, value) ->
|
||||
{
|
||||
final Class<? extends IForgeRegistryEntry> clazz = RegistryManager.ACTIVE.getSuperType(key);
|
||||
remaps.put(key, Maps.newLinkedHashMap());
|
||||
missing.put(key, Maps.newHashMap());
|
||||
loadPersistentDataToStagingRegistry(RegistryManager.ACTIVE, STAGING, remaps.get(key), missing.get(key), key, value, clazz);
|
||||
});
|
||||
|
||||
snapshot.forEach((key, value) ->
|
||||
{
|
||||
value.dummied.forEach(dummy ->
|
||||
{
|
||||
Map<ResourceLocation, Integer> m = missing.get(key);
|
||||
ForgeRegistry<?> reg = STAGING.getRegistry(key);
|
||||
|
||||
// Currently missing locally, we just inject and carry on
|
||||
if (m.containsKey(dummy))
|
||||
{
|
||||
if (reg.markDummy(dummy, m.get(dummy)))
|
||||
m.remove(dummy);
|
||||
}
|
||||
else if (isLocalWorld)
|
||||
{
|
||||
if (ForgeRegistry.DEBUG)
|
||||
FMLLog.log.debug("Registry {}: Resuscitating dummy entry {}", key, dummy);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The server believes this is a dummy block identity, but we seem to have one locally. This is likely a conflict
|
||||
// in mod setup - Mark this entry as a dummy
|
||||
int id = reg.getID(dummy);
|
||||
FMLLog.log.warn("Registry {}: The ID {} @ {} is currently locally mapped - it will be replaced with a dummy for this session", dummy, key, id);
|
||||
reg.markDummy(dummy, id);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
int count = missing.values().stream().mapToInt(Map::size).sum();
|
||||
if (count > 0)
|
||||
{
|
||||
FMLLog.log.debug("There are {} mappings missing - attempting a mod remap", count);
|
||||
Multimap<ResourceLocation, ResourceLocation> defaulted = ArrayListMultimap.create();
|
||||
Multimap<ResourceLocation, ResourceLocation> failed = ArrayListMultimap.create();
|
||||
|
||||
missing.entrySet().stream().filter(e -> e.getValue().size() > 0).forEach(m ->
|
||||
{
|
||||
ResourceLocation name = m.getKey();
|
||||
ForgeRegistry<?> reg = STAGING.getRegistry(name);
|
||||
RegistryEvent.MissingMappings<?> event = reg.getMissingEvent(name, m.getValue());
|
||||
MinecraftForge.EVENT_BUS.post(event);
|
||||
|
||||
List<MissingMappings.Mapping<?>> lst = event.getAllMappings().stream().filter(e -> e.getAction() == MissingMappings.Action.DEFAULT).sorted((a, b) -> a.toString().compareTo(b.toString())).collect(Collectors.toList());
|
||||
if (!lst.isEmpty())
|
||||
{
|
||||
FMLLog.log.error("Unidentified mapping from registry {}", name);
|
||||
lst.forEach(map -> FMLLog.log.error(" {}: {}", map.key, map.id));
|
||||
}
|
||||
event.getAllMappings().stream().filter(e -> e.getAction() == MissingMappings.Action.FAIL).forEach(fail -> failed.put(name, fail.key));
|
||||
|
||||
final Class<? extends IForgeRegistryEntry> clazz = RegistryManager.ACTIVE.getSuperType(name);
|
||||
processMissing(clazz, name, STAGING, event, m.getValue(), remaps.get(name), defaulted.get(name), failed.get(name), !isLocalWorld);
|
||||
});
|
||||
|
||||
if (!defaulted.isEmpty() && !isLocalWorld)
|
||||
return defaulted;
|
||||
|
||||
if (!defaulted.isEmpty())
|
||||
{
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("Forge Mod Loader detected missing registry entries.\n\n")
|
||||
.append("There are ").append(defaulted.size()).append(" missing entries in this save.\n")
|
||||
.append("If you continue the missing entries will get removed.\n")
|
||||
.append("A world backup will be automatically created in your saves directory.\n\n");
|
||||
|
||||
defaulted.asMap().forEach((name, entries) ->
|
||||
{
|
||||
buf.append("Missing ").append(name).append(":\n");
|
||||
entries.forEach(rl -> buf.append(" ").append(rl).append("\n"));
|
||||
});
|
||||
|
||||
boolean confirmed = StartupQuery.confirm(buf.toString());
|
||||
if (!confirmed)
|
||||
StartupQuery.abort();
|
||||
|
||||
try
|
||||
{
|
||||
String skip = System.getProperty("fml.doNotBackup");
|
||||
if (skip == null || !"true".equals(skip))
|
||||
{
|
||||
ZipperUtil.backupWorld();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int x = 0; x < 10; x++)
|
||||
FMLLog.log.error("!!!!!!!!!! UPDATING WORLD WITHOUT DOING BACKUP !!!!!!!!!!!!!!!!");
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
StartupQuery.notify("The world backup couldn't be created.\n\n" + e);
|
||||
StartupQuery.abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (!defaulted.isEmpty())
|
||||
{
|
||||
if (isLocalWorld)
|
||||
FMLLog.log.error("There are unidentified mappings in this world - we are going to attempt to process anyway");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (injectFrozenData)
|
||||
{
|
||||
// If we're loading from disk, we can actually substitute air in the block map for anything that is otherwise "missing". This keeps the reference in the map, in case
|
||||
// the block comes back later
|
||||
missing.forEach((name, m) ->
|
||||
{
|
||||
ForgeRegistry<?> reg = STAGING.getRegistry(name);
|
||||
m.forEach((rl, id) -> reg.markDummy(rl, id));
|
||||
});
|
||||
|
||||
|
||||
// If we're loading up the world from disk, we want to add in the new data that might have been provisioned by mods
|
||||
// So we load it from the frozen persistent registry
|
||||
RegistryManager.ACTIVE.registries.forEach((name, reg) ->
|
||||
{
|
||||
final Class<? extends IForgeRegistryEntry> clazz = RegistryManager.ACTIVE.getSuperType(name);
|
||||
loadFrozenDataToStagingRegistry(STAGING, name, remaps.get(name), clazz);
|
||||
});
|
||||
}
|
||||
|
||||
// Validate that all the STAGING data is good
|
||||
STAGING.registries.forEach((name, reg) -> reg.validateContent(name));
|
||||
|
||||
// Load the STAGING registry into the ACTIVE registry
|
||||
for (Map.Entry<ResourceLocation, ForgeRegistry<? extends IForgeRegistryEntry<?>>> r : RegistryManager.ACTIVE.registries.entrySet())
|
||||
{
|
||||
final Class<? extends IForgeRegistryEntry> registrySuperType = RegistryManager.ACTIVE.getSuperType(r.getKey());
|
||||
loadRegistry(r.getKey(), STAGING, RegistryManager.ACTIVE, registrySuperType, true);
|
||||
}
|
||||
|
||||
// Dump the active registry
|
||||
RegistryManager.ACTIVE.registries.forEach((name, reg) -> reg.dump(name));
|
||||
|
||||
// Tell mods that the ids have changed
|
||||
Loader.instance().fireRemapEvent(remaps, false);
|
||||
|
||||
// The id map changed, ensure we apply object holders
|
||||
ObjectHolderRegistry.INSTANCE.applyObjectHolders();
|
||||
|
||||
// Return an empty list, because we're good
|
||||
return ArrayListMultimap.create();
|
||||
}
|
||||
|
||||
//Has to be split because of generics, Yay!
|
||||
private static <T extends IForgeRegistryEntry<T>> void loadPersistentDataToStagingRegistry(RegistryManager pool, RegistryManager to, Map<ResourceLocation, Integer[]> remaps, Map<ResourceLocation, Integer> missing, ResourceLocation name, ForgeRegistry.Snapshot snap, Class<T> regType)
|
||||
{
|
||||
ForgeRegistry<T> active = pool.getRegistry(name);
|
||||
if (active == null)
|
||||
return; // We've already asked the user if they wish to continue. So if the reg isnt found just assume the user knows and accepted it.
|
||||
ForgeRegistry<T> _new = to.getRegistry(name, RegistryManager.ACTIVE);
|
||||
snap.aliases.forEach(_new::addAlias);
|
||||
snap.blocked.forEach(_new::block);
|
||||
// Load current dummies BEFORE the snapshot is loaded so that add() will remove from the list.
|
||||
snap.dummied.forEach(_new::addDummy);
|
||||
_new.loadIds(snap.ids, snap.overrides, missing, remaps, active, name);
|
||||
}
|
||||
|
||||
//Another bouncer for generic reasons
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends IForgeRegistryEntry<T>> void processMissing(Class<T> clazz, ResourceLocation name, RegistryManager STAGING, MissingMappings<?> e, Map<ResourceLocation, Integer> missing, Map<ResourceLocation, Integer[]> remaps, Collection<ResourceLocation> defaulted, Collection<ResourceLocation> failed, boolean injectNetworkDummies)
|
||||
{
|
||||
List<MissingMappings.Mapping<T>> mappings = ((MissingMappings<T>)e).getAllMappings();
|
||||
ForgeRegistry<T> active = RegistryManager.ACTIVE.getRegistry(name);
|
||||
ForgeRegistry<T> staging = STAGING.getRegistry(name);
|
||||
staging.processMissingEvent(name, active, mappings, missing, remaps, defaulted, failed, injectNetworkDummies);
|
||||
}
|
||||
|
||||
private static <T extends IForgeRegistryEntry<T>> void loadFrozenDataToStagingRegistry(RegistryManager STAGING, ResourceLocation name, Map<ResourceLocation, Integer[]> remaps, Class<T> clazz)
|
||||
{
|
||||
ForgeRegistry<T> frozen = RegistryManager.FROZEN.getRegistry(name);
|
||||
ForgeRegistry<T> newRegistry = STAGING.getRegistry(name, RegistryManager.FROZEN);
|
||||
Map<ResourceLocation, Integer> _new = Maps.newHashMap();
|
||||
frozen.getKeys().stream().filter(key -> !newRegistry.containsKey(key)).forEach(key -> _new.put(key, frozen.getID(key)));
|
||||
newRegistry.loadIds(_new, frozen.getOverrideOwners(), Maps.newLinkedHashMap(), remaps, frozen, name);
|
||||
}
|
||||
|
||||
public static void fireCreateRegistryEvents()
|
||||
{
|
||||
MinecraftForge.EVENT_BUS.post(new RegistryEvent.NewRegistry());
|
||||
}
|
||||
|
||||
public static void fireRegistryEvents()
|
||||
{
|
||||
fireRegistryEvents(rl -> true);
|
||||
}
|
||||
public static void fireRegistryEvents(Predicate<ResourceLocation> filter)
|
||||
{
|
||||
List<ResourceLocation> keys = Lists.newArrayList(RegistryManager.ACTIVE.registries.keySet());
|
||||
Collections.sort(keys, (o1, o2) -> o1.toString().compareToIgnoreCase(o2.toString()));
|
||||
/*
|
||||
RegistryManager.ACTIVE.registries.forEach((name, reg) -> {
|
||||
if (filter.test(name))
|
||||
((ForgeRegistry<?>)reg).unfreeze();
|
||||
});
|
||||
*/
|
||||
|
||||
if (filter.test(BLOCKS))
|
||||
{
|
||||
MinecraftForge.EVENT_BUS.post(RegistryManager.ACTIVE.getRegistry(BLOCKS).getRegisterEvent(BLOCKS));
|
||||
ObjectHolderRegistry.INSTANCE.applyObjectHolders(); // inject any blocks
|
||||
}
|
||||
if (filter.test(ITEMS))
|
||||
{
|
||||
MinecraftForge.EVENT_BUS.post(RegistryManager.ACTIVE.getRegistry(ITEMS).getRegisterEvent(ITEMS));
|
||||
ObjectHolderRegistry.INSTANCE.applyObjectHolders(); // inject any items
|
||||
}
|
||||
for (ResourceLocation rl : keys)
|
||||
{
|
||||
if (!filter.test(rl)) continue;
|
||||
if (rl == BLOCKS || rl == ITEMS) continue;
|
||||
MinecraftForge.EVENT_BUS.post(RegistryManager.ACTIVE.getRegistry(rl).getRegisterEvent(rl));
|
||||
}
|
||||
ObjectHolderRegistry.INSTANCE.applyObjectHolders(); // inject everything else
|
||||
|
||||
|
||||
/*
|
||||
RegistryManager.ACTIVE.registries.forEach((name, reg) -> {
|
||||
if (filter.test(name))
|
||||
((ForgeRegistry<?>)reg).freeze();
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
public static ResourceLocation checkPrefix(String name)
|
||||
{
|
||||
int index = name.lastIndexOf(':');
|
||||
String oldPrefix = index == -1 ? "" : name.substring(0, index).toLowerCase(Locale.ROOT);
|
||||
name = index == -1 ? name : name.substring(index + 1);
|
||||
ModContainer mc = Loader.instance().activeModContainer();
|
||||
String prefix = mc == null || (mc instanceof InjectedModContainer && ((InjectedModContainer)mc).wrappedContainer instanceof FMLContainer) ? "minecraft" : mc.getModId().toLowerCase(Locale.ROOT);
|
||||
if (!oldPrefix.equals(prefix) && oldPrefix.length() > 0)
|
||||
{
|
||||
FMLLog.log.warn("Potentially Dangerous alternative prefix `{}` for name `{}`, expected `{}`. This could be a intended override, but in most cases indicates a broken mod.", oldPrefix, name, prefix);
|
||||
prefix = oldPrefix;
|
||||
}
|
||||
return new ResourceLocation(prefix, name);
|
||||
}
|
||||
|
||||
private static Field regName;
|
||||
private static void forceRegistryName(IForgeRegistryEntry<?> entry, ResourceLocation name)
|
||||
{
|
||||
if (regName == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
regName = IForgeRegistryEntry.Impl.class.getDeclaredField("registryName");
|
||||
regName.setAccessible(true);
|
||||
}
|
||||
catch (NoSuchFieldException | SecurityException e)
|
||||
{
|
||||
FMLLog.log.error("Could not get `registryName` field from IForgeRegistryEntry.Impl");
|
||||
FMLLog.log.throwing(Level.ERROR, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
regName.set(entry, name);
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalAccessException e)
|
||||
{
|
||||
FMLLog.log.error("Could not set `registryName` field in IForgeRegistryEntry.Impl to `{}`", name.toString());
|
||||
FMLLog.log.throwing(Level.ERROR, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.registries;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Main interface for the registry system. Use this to query the registry system.
|
||||
*
|
||||
* @param <V> The top level type for the registry
|
||||
*/
|
||||
public interface IForgeRegistry<V extends IForgeRegistryEntry<V>> extends Iterable<V>
|
||||
{
|
||||
Class<V> getRegistrySuperType();
|
||||
|
||||
void register(V value);
|
||||
|
||||
void registerAll(@SuppressWarnings("unchecked") V... values);
|
||||
|
||||
boolean containsKey(ResourceLocation key);
|
||||
boolean containsValue(V value);
|
||||
|
||||
@Nullable V getValue(ResourceLocation key);
|
||||
@Nullable ResourceLocation getKey(V value);
|
||||
|
||||
@Nonnull Set<ResourceLocation> getKeys();
|
||||
/** @deprecated use {@link #getValuesCollection} */
|
||||
@Deprecated // TODO: remove in 1.13
|
||||
@Nonnull List<V> getValues();
|
||||
@Nonnull
|
||||
default Collection<V> getValuesCollection() { // TODO rename this to getValues in 1.13
|
||||
return getValues();
|
||||
}
|
||||
@Nonnull Set<Entry<ResourceLocation, V>> getEntries();
|
||||
|
||||
/**
|
||||
* Retrieve the slave map of type T from the registry.
|
||||
* Slave maps are maps which are dependent on registry content in some way.
|
||||
* @param slaveMapName The name of the slavemap
|
||||
* @param type The type
|
||||
* @param <T> Type to return
|
||||
* @return The slavemap if present
|
||||
*/
|
||||
<T> T getSlaveMap(ResourceLocation slaveMapName, Class<T> type);
|
||||
|
||||
/**
|
||||
* Callback fired when objects are added to the registry. This will fire when the registry is rebuilt
|
||||
* on the client side from a server side synchronization, or when a world is loaded.
|
||||
*/
|
||||
interface AddCallback<V extends IForgeRegistryEntry<V>>
|
||||
{
|
||||
void onAdd(IForgeRegistryInternal<V> owner, RegistryManager stage, int id, V obj, @Nullable V oldObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback fired when the registry is cleared. This is done before a registry is reloaded from client
|
||||
* or server.
|
||||
*/
|
||||
interface ClearCallback<V extends IForgeRegistryEntry<V>>
|
||||
{
|
||||
void onClear(IForgeRegistryInternal<V> owner, RegistryManager stage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback fired when a registry instance is created. Populate slave maps here.
|
||||
*/
|
||||
interface CreateCallback<V extends IForgeRegistryEntry<V>>
|
||||
{
|
||||
void onCreate(IForgeRegistryInternal<V> owner, RegistryManager stage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback fired when the registry contents are validated.
|
||||
*/
|
||||
interface ValidateCallback<V extends IForgeRegistryEntry<V>>
|
||||
{
|
||||
void onValidate(IForgeRegistryInternal<V> owner, RegistryManager stage, int id, ResourceLocation key, V obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for creating dummy entries, allowing worlds to be loaded and keep the missing block references.
|
||||
*/
|
||||
interface DummyFactory<V extends IForgeRegistryEntry<V>>
|
||||
{
|
||||
V createDummy(ResourceLocation key);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
interface MissingFactory<V extends IForgeRegistryEntry<V>>
|
||||
{
|
||||
V createMissing(ResourceLocation key, boolean isNetwork);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.registries;
|
||||
|
||||
import com.google.common.reflect.TypeToken;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface IForgeRegistryEntry<V>
|
||||
{
|
||||
/**
|
||||
* Sets a unique name for this Item. This should be used for uniquely identify the instance of the Item.
|
||||
* This is the valid replacement for the atrocious 'getUnlocalizedName().substring(6)' stuff that everyone does.
|
||||
* Unlocalized names have NOTHING to do with unique identifiers. As demonstrated by vanilla blocks and items.
|
||||
*
|
||||
* The supplied name will be prefixed with the currently active mod's modId.
|
||||
* If the supplied name already has a prefix that is different, it will be used and a warning will be logged.
|
||||
*
|
||||
* If a name already exists, or this Item is already registered in a registry, then an IllegalStateException is thrown.
|
||||
*
|
||||
* Returns 'this' to allow for chaining.
|
||||
*
|
||||
* @param name Unique registry name
|
||||
* @return This instance
|
||||
*/
|
||||
V setRegistryName(ResourceLocation name);
|
||||
|
||||
/**
|
||||
* A unique identifier for this entry, if this entry is registered already it will return it's official registry name.
|
||||
* Otherwise it will return the name set in setRegistryName().
|
||||
* If neither are valid null is returned.
|
||||
*
|
||||
* @return Unique identifier or null.
|
||||
*/
|
||||
@Nullable
|
||||
ResourceLocation getRegistryName();
|
||||
|
||||
Class<V> getRegistryType();
|
||||
|
||||
// Default implementation, modders who make extra items SHOULD extend this instead of Object.
|
||||
// So, all fields in interfaces are forced static, so even with Java8 people must still extend this.
|
||||
@SuppressWarnings({ "serial", "unchecked" })
|
||||
public static class Impl<T extends IForgeRegistryEntry<T>> implements IForgeRegistryEntry<T>
|
||||
{
|
||||
private TypeToken<T> token = new TypeToken<T>(getClass()){};
|
||||
public final IRegistryDelegate<T> delegate = new RegistryDelegate<T>((T)this, (Class<T>)token.getRawType());
|
||||
private ResourceLocation registryName = null;
|
||||
|
||||
public final T setRegistryName(String name)
|
||||
{
|
||||
if (getRegistryName() != null)
|
||||
throw new IllegalStateException("Attempted to set registry name with existing registry name! New: " + name + " Old: " + getRegistryName());
|
||||
|
||||
this.registryName = GameData.checkPrefix(name);
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
//Helper functions
|
||||
@Override
|
||||
public final T setRegistryName(ResourceLocation name){ return setRegistryName(name.toString()); }
|
||||
public final T setRegistryName(String modID, String name){ return setRegistryName(modID + ":" + name); }
|
||||
@Override
|
||||
@Nullable
|
||||
public final ResourceLocation getRegistryName()
|
||||
{
|
||||
if (delegate.name() != null) return delegate.name();
|
||||
return registryName != null ? registryName : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Class<T> getRegistryType() { return (Class<T>) token.getRawType(); };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.registries;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public interface IForgeRegistryInternal<V extends IForgeRegistryEntry<V>> extends IForgeRegistry<V>
|
||||
{
|
||||
void setSlaveMap(ResourceLocation name, Object obj);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.registries;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public interface IForgeRegistryModifiable<V extends IForgeRegistryEntry<V>> extends IForgeRegistry<V>
|
||||
{
|
||||
void clear();
|
||||
V remove(ResourceLocation key);
|
||||
boolean isLocked();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.registries;
|
||||
|
||||
public interface ILockableRegistry {
|
||||
|
||||
//Lock a registry to disable any direct Register calls.
|
||||
// All future calls must be done via Forge registry methods not vanilla
|
||||
void lock();
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.registries;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
|
||||
/**
|
||||
* A registry delegate for holding references to items or blocks
|
||||
* These should be safe to use in things like lists though aliased items and blocks will not
|
||||
* have object identity with respect to their delegate.
|
||||
*
|
||||
* @author cpw
|
||||
*
|
||||
* @param <T> the type of thing we're holding onto
|
||||
*/
|
||||
public interface IRegistryDelegate<T> {
|
||||
/**
|
||||
* Get the referent pointed at by this delegate. This will be the currently active item or block, and will change
|
||||
* as world saves come and go. Note that item.delegate.get() may NOT be the same object as item, due to item and
|
||||
* block substitution.
|
||||
*
|
||||
* @return The referred object
|
||||
*/
|
||||
T get();
|
||||
|
||||
/**
|
||||
* Get the unique resource location for this delegate. Completely static after registration has completed, and
|
||||
* will never change.
|
||||
* @return The name
|
||||
*/
|
||||
ResourceLocation name();
|
||||
|
||||
/**
|
||||
* Get the delegate type. It will be dependent on the registry this delegate is sourced from.
|
||||
* @return The type of delegate
|
||||
*/
|
||||
Class<T> type();
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.registries;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.registry.RegistryNamespacedDefaultedByKey;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
|
||||
class NamespacedDefaultedWrapper<V extends IForgeRegistryEntry<V>> extends RegistryNamespacedDefaultedByKey<ResourceLocation, V> implements ILockableRegistry
|
||||
{
|
||||
private boolean locked = false;
|
||||
private ForgeRegistry<V> delegate;
|
||||
|
||||
private NamespacedDefaultedWrapper(ForgeRegistry<V> owner)
|
||||
{
|
||||
super(null);
|
||||
this.delegate = owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(int id, ResourceLocation key, V value)
|
||||
{
|
||||
if (locked)
|
||||
throw new IllegalStateException("Can not register to a locked registry. Modder should use Forge Register methods.");
|
||||
Validate.notNull(value);
|
||||
|
||||
if (value.getRegistryName() == null)
|
||||
value.setRegistryName(key);
|
||||
|
||||
int realId = this.delegate.add(id, value);
|
||||
if (realId != id && id != -1)
|
||||
FMLLog.log.warn("Registered object did not get ID it asked for. Name: {} Type: {} Expected: {} Got: {}", key, value.getRegistryType().getName(), id, realId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an object on this registry.
|
||||
*/
|
||||
@Override
|
||||
public void putObject(ResourceLocation key, V value)
|
||||
{
|
||||
register(-1, key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* validates that this registry's key is non-null
|
||||
*/
|
||||
@Override
|
||||
public void validateKey()
|
||||
{
|
||||
this.delegate.validateKey();
|
||||
}
|
||||
|
||||
// Reading Functions
|
||||
@Override
|
||||
@Nullable
|
||||
public V getObject(@Nullable ResourceLocation name)
|
||||
{
|
||||
return this.delegate.getValue(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name we use to identify the given object.
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public ResourceLocation getNameForObject(V value)
|
||||
{
|
||||
return this.delegate.getKey(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this registry contain an entry for the given key?
|
||||
*/
|
||||
@Override
|
||||
public boolean containsKey(ResourceLocation key)
|
||||
{
|
||||
return this.delegate.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the integer ID we use to identify the given object.
|
||||
*/
|
||||
@Override
|
||||
public int getIDForObject(@Nullable V value)
|
||||
{
|
||||
return this.delegate.getID(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object identified by the given ID.
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public V getObjectById(int id)
|
||||
{
|
||||
return this.delegate.getValue(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<V> iterator()
|
||||
{
|
||||
return this.delegate.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the keys recognized by this registry.
|
||||
*/
|
||||
@Override
|
||||
public Set<ResourceLocation> getKeys()
|
||||
{
|
||||
return this.delegate.getKeys();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public V getRandomObject(Random random)
|
||||
{
|
||||
Collection<V> values = this.delegate.getValuesCollection();
|
||||
return values.stream().skip(random.nextInt(values.size())).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
//internal
|
||||
@Override
|
||||
public void lock(){ this.locked = true; }
|
||||
|
||||
public static class Factory<V extends IForgeRegistryEntry<V>> implements IForgeRegistry.CreateCallback<V>
|
||||
{
|
||||
public static final ResourceLocation ID = new ResourceLocation("forge", "registry_defaulted_wrapper");
|
||||
@Override
|
||||
public void onCreate(IForgeRegistryInternal<V> owner, RegistryManager stage)
|
||||
{
|
||||
owner.setSlaveMap(ID, new NamespacedDefaultedWrapper<V>((ForgeRegistry<V>)owner));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.registries;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.registry.RegistryNamespaced;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
|
||||
class NamespacedWrapper<V extends IForgeRegistryEntry<V>> extends RegistryNamespaced<ResourceLocation, V> implements ILockableRegistry
|
||||
{
|
||||
private boolean locked = false;
|
||||
private ForgeRegistry<V> delegate;
|
||||
|
||||
public NamespacedWrapper(ForgeRegistry<V> owner)
|
||||
{
|
||||
this.delegate = owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(int id, ResourceLocation key, V value)
|
||||
{
|
||||
if (locked)
|
||||
throw new IllegalStateException("Can not register to a locked registry. Modder should use Forge Register methods.");
|
||||
|
||||
Validate.notNull(value);
|
||||
|
||||
if (value.getRegistryName() == null)
|
||||
value.setRegistryName(key);
|
||||
|
||||
int realId = this.delegate.add(id, value);
|
||||
if (realId != id && id != -1)
|
||||
FMLLog.log.warn("Registered object did not get ID it asked for. Name: {} Type: {} Expected: {} Got: {}", key, value.getRegistryType().getName(), id, realId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an object on this registry.
|
||||
*/
|
||||
@Override
|
||||
public void putObject(ResourceLocation key, V value)
|
||||
{
|
||||
register(-1, key, value);
|
||||
}
|
||||
|
||||
|
||||
// Reading Functions
|
||||
@Override
|
||||
@Nullable
|
||||
public V getObject(@Nullable ResourceLocation name)
|
||||
{
|
||||
return this.delegate.getValue(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name we use to identify the given object.
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public ResourceLocation getNameForObject(V value)
|
||||
{
|
||||
return this.delegate.getKey(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this registry contain an entry for the given key?
|
||||
*/
|
||||
@Override
|
||||
public boolean containsKey(ResourceLocation key)
|
||||
{
|
||||
return this.delegate.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the integer ID we use to identify the given object.
|
||||
*/
|
||||
@Override
|
||||
public int getIDForObject(@Nullable V value)
|
||||
{
|
||||
return this.delegate.getID(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object identified by the given ID.
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public V getObjectById(int id)
|
||||
{
|
||||
return this.delegate.getValue(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<V> iterator()
|
||||
{
|
||||
return this.delegate.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the keys recognized by this registry.
|
||||
*/
|
||||
@Override
|
||||
public Set<ResourceLocation> getKeys()
|
||||
{
|
||||
return this.delegate.getKeys();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public V getRandomObject(Random random)
|
||||
{
|
||||
Collection<V> values = this.delegate.getValuesCollection();
|
||||
return values.stream().skip(random.nextInt(values.size())).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
//internal
|
||||
@Override
|
||||
public void lock(){ this.locked = true; }
|
||||
|
||||
public static class Factory<V extends IForgeRegistryEntry<V>> implements IForgeRegistry.CreateCallback<V>
|
||||
{
|
||||
public static final ResourceLocation ID = new ResourceLocation("forge", "registry_defaulted_wrapper");
|
||||
@Override
|
||||
public void onCreate(IForgeRegistryInternal<V> owner, RegistryManager stage)
|
||||
{
|
||||
owner.setSlaveMap(ID, new NamespacedWrapper<V>((ForgeRegistry<V>)owner));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.registries;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import net.minecraftforge.fml.common.registry.GameRegistry;
|
||||
import net.minecraftforge.fml.common.registry.GameRegistry.ObjectHolder;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Internal class used in tracking {@link ObjectHolder} references
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
class ObjectHolderRef
|
||||
{
|
||||
private Field field;
|
||||
private ResourceLocation injectedObject;
|
||||
private boolean isValid;
|
||||
private ForgeRegistry<?> registry;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectHolderRef(Field field, ResourceLocation injectedObject, boolean extractFromExistingValues)
|
||||
{
|
||||
registry = getRegistryForType(field);
|
||||
|
||||
this.field = field;
|
||||
this.isValid = registry != null;
|
||||
if (extractFromExistingValues)
|
||||
{
|
||||
try
|
||||
{
|
||||
Object existing = field.get(null);
|
||||
// nothing is ever allowed to replace AIR
|
||||
if (existing == null || existing == registry.getDefault())
|
||||
{
|
||||
this.injectedObject = null;
|
||||
this.field = null;
|
||||
this.isValid = false;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.injectedObject = ((IForgeRegistryEntry)existing).getRegistryName();
|
||||
}
|
||||
}
|
||||
catch (IllegalAccessException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.injectedObject = injectedObject;
|
||||
}
|
||||
|
||||
if (this.injectedObject == null || !isValid())
|
||||
{
|
||||
throw new IllegalStateException(String.format("The ObjectHolder annotation cannot apply to a field that does not map to a registry. Ensure the registry was created during the RegistryEvent.NewRegistry event. (found : %s at %s.%s)", field.getType().getName(), field.getClass().getName(), field.getName()));
|
||||
}
|
||||
try
|
||||
{
|
||||
FinalFieldHelper.makeWritable(field);
|
||||
}
|
||||
catch (ReflectiveOperationException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
private ForgeRegistry<?> getRegistryForType(Field field)
|
||||
{
|
||||
Queue<Class<?>> typesToExamine = new LinkedList<Class<?>>();
|
||||
typesToExamine.add(field.getType());
|
||||
|
||||
ForgeRegistry<?> registry = null;
|
||||
while (!typesToExamine.isEmpty() && registry == null)
|
||||
{
|
||||
Class<?> type = typesToExamine.remove();
|
||||
Collections.addAll(typesToExamine, type.getInterfaces());
|
||||
if (IForgeRegistryEntry.class.isAssignableFrom(type))
|
||||
{
|
||||
registry = (ForgeRegistry<?>)GameRegistry.findRegistry((Class<IForgeRegistryEntry>)type);
|
||||
final Class<?> parentType = type.getSuperclass();
|
||||
if (parentType != null)
|
||||
{
|
||||
typesToExamine.add(parentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
return registry;
|
||||
}
|
||||
|
||||
public boolean isValid()
|
||||
{
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public void apply()
|
||||
{
|
||||
Object thing;
|
||||
if (isValid && registry.containsKey(injectedObject) && !registry.isDummied(injectedObject))
|
||||
{
|
||||
thing = registry.getValue(injectedObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
thing = null;
|
||||
}
|
||||
|
||||
if (thing == null)
|
||||
{
|
||||
FMLLog.log.debug("Unable to lookup {} for {}. This means the object wasn't registered. It's likely just mod options.", injectedObject, field);
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
FinalFieldHelper.setField(field, null, thing);
|
||||
}
|
||||
catch (IllegalArgumentException | ReflectiveOperationException e)
|
||||
{
|
||||
FMLLog.log.warn("Unable to set {} with value {} ({})", this.field, thing, this.injectedObject, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class FinalFieldHelper
|
||||
{
|
||||
private static Field modifiersField;
|
||||
private static Object reflectionFactory;
|
||||
private static Method newFieldAccessor;
|
||||
private static Method fieldAccessorSet;
|
||||
|
||||
static Field makeWritable(Field f) throws ReflectiveOperationException
|
||||
{
|
||||
f.setAccessible(true);
|
||||
if (modifiersField == null)
|
||||
{
|
||||
Method getReflectionFactory = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("getReflectionFactory");
|
||||
reflectionFactory = getReflectionFactory.invoke(null);
|
||||
newFieldAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newFieldAccessor", Field.class, boolean.class);
|
||||
fieldAccessorSet = Class.forName("sun.reflect.FieldAccessor").getDeclaredMethod("set", Object.class, Object.class);
|
||||
modifiersField = Field.class.getDeclaredField("modifiers");
|
||||
modifiersField.setAccessible(true);
|
||||
}
|
||||
modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);
|
||||
return f;
|
||||
}
|
||||
|
||||
static void setField(Field field, @Nullable Object instance, Object thing) throws ReflectiveOperationException
|
||||
{
|
||||
Object fieldAccessor = newFieldAccessor.invoke(reflectionFactory, field, false);
|
||||
fieldAccessorSet.invoke(fieldAccessor, instance, thing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.registries;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.discovery.ASMDataTable;
|
||||
import net.minecraftforge.fml.common.discovery.ASMDataTable.ASMData;
|
||||
import net.minecraftforge.fml.common.registry.GameRegistry;
|
||||
import net.minecraftforge.fml.common.registry.GameRegistry.ObjectHolder;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Internal registry for tracking {@link ObjectHolder} references
|
||||
*/
|
||||
public enum ObjectHolderRegistry
|
||||
{
|
||||
INSTANCE;
|
||||
private List<ObjectHolderRef> objectHolders = Lists.newArrayList();
|
||||
|
||||
public void findObjectHolders(ASMDataTable table)
|
||||
{
|
||||
FMLLog.log.info("Processing ObjectHolder annotations");
|
||||
Set<ASMData> allObjectHolders = table.getAll(GameRegistry.ObjectHolder.class.getName());
|
||||
Map<String, String> classModIds = Maps.newHashMap();
|
||||
Map<String, Class<?>> classCache = Maps.newHashMap();
|
||||
for (ASMData data : table.getAll(Mod.class.getName()))
|
||||
{
|
||||
String modid = (String)data.getAnnotationInfo().get("modid");
|
||||
classModIds.put(data.getClassName(), modid);
|
||||
}
|
||||
for (ASMData data : allObjectHolders)
|
||||
{
|
||||
String className = data.getClassName();
|
||||
String annotationTarget = data.getObjectName();
|
||||
String value = (String) data.getAnnotationInfo().get("value");
|
||||
boolean isClass = className.equals(annotationTarget);
|
||||
if (isClass)
|
||||
{
|
||||
scanTarget(classModIds, classCache, className, annotationTarget, value, isClass, false);
|
||||
}
|
||||
}
|
||||
// double pass - get all the class level annotations first, then the field level annotations
|
||||
for (ASMData data : allObjectHolders)
|
||||
{
|
||||
String className = data.getClassName();
|
||||
String annotationTarget = data.getObjectName();
|
||||
String value = (String) data.getAnnotationInfo().get("value");
|
||||
boolean isClass = className.equals(annotationTarget);
|
||||
if (!isClass)
|
||||
{
|
||||
scanTarget(classModIds, classCache, className, annotationTarget, value, isClass, false);
|
||||
}
|
||||
}
|
||||
scanTarget(classModIds, classCache, "net.minecraft.init.Blocks", null, "minecraft", true, true);
|
||||
scanTarget(classModIds, classCache, "net.minecraft.init.Items", null, "minecraft", true, true);
|
||||
scanTarget(classModIds, classCache, "net.minecraft.init.MobEffects", null, "minecraft", true, true);
|
||||
scanTarget(classModIds, classCache, "net.minecraft.init.Biomes", null, "minecraft", true, true);
|
||||
scanTarget(classModIds, classCache, "net.minecraft.init.Enchantments", null, "minecraft", true, true);
|
||||
scanTarget(classModIds, classCache, "net.minecraft.init.SoundEvents", null, "minecraft", true, true);
|
||||
scanTarget(classModIds, classCache, "net.minecraft.init.PotionTypes", null, "minecraft", true, true);
|
||||
FMLLog.log.info("Found {} ObjectHolder annotations", objectHolders.size());
|
||||
}
|
||||
|
||||
private void scanTarget(Map<String, String> classModIds, Map<String, Class<?>> classCache, String className, @Nullable String annotationTarget, String value, boolean isClass, boolean extractFromValue)
|
||||
{
|
||||
Class<?> clazz;
|
||||
if (classCache.containsKey(className))
|
||||
{
|
||||
clazz = classCache.get(className);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
clazz = Class.forName(className, extractFromValue, getClass().getClassLoader());
|
||||
classCache.put(className, clazz);
|
||||
}
|
||||
catch (ClassNotFoundException ex)
|
||||
{
|
||||
// unpossible?
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
if (isClass)
|
||||
{
|
||||
scanClassForFields(classModIds, className, value, clazz, extractFromValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value.indexOf(':') == -1)
|
||||
{
|
||||
String prefix = classModIds.get(className);
|
||||
if (prefix == null)
|
||||
{
|
||||
FMLLog.log.warn("Found an unqualified ObjectHolder annotation ({}) without a modid context at {}.{}, ignoring", value, className, annotationTarget);
|
||||
throw new IllegalStateException("Unqualified reference to ObjectHolder");
|
||||
}
|
||||
value = prefix + ":" + value;
|
||||
}
|
||||
try
|
||||
{
|
||||
Field f = clazz.getDeclaredField(annotationTarget);
|
||||
addHolderReference(new ObjectHolderRef(f, new ResourceLocation(value), extractFromValue));
|
||||
}
|
||||
catch (NoSuchFieldException ex)
|
||||
{
|
||||
// unpossible?
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void scanClassForFields(Map<String, String> classModIds, String className, String value, Class<?> clazz, boolean extractFromExistingValues)
|
||||
{
|
||||
classModIds.put(className, value);
|
||||
for (Field f : clazz.getFields())
|
||||
{
|
||||
int mods = f.getModifiers();
|
||||
boolean isMatch = Modifier.isPublic(mods) && Modifier.isStatic(mods) && Modifier.isFinal(mods);
|
||||
if (!isMatch || f.isAnnotationPresent(ObjectHolder.class))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
addHolderReference(new ObjectHolderRef(f, new ResourceLocation(value, f.getName()), extractFromExistingValues));
|
||||
}
|
||||
}
|
||||
|
||||
private void addHolderReference(ObjectHolderRef ref)
|
||||
{
|
||||
if (ref.isValid())
|
||||
{
|
||||
objectHolders.add(ref);
|
||||
}
|
||||
}
|
||||
|
||||
public void applyObjectHolders()
|
||||
{
|
||||
FMLLog.log.info("Applying holder lookups");
|
||||
for (ObjectHolderRef ohr : objectHolders)
|
||||
{
|
||||
ohr.apply();
|
||||
}
|
||||
FMLLog.log.info("Holder lookups applied");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.registries;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.registries.IForgeRegistry.*;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class RegistryBuilder<T extends IForgeRegistryEntry<T>>
|
||||
{
|
||||
private ResourceLocation registryName;
|
||||
private Class<T> registryType;
|
||||
private ResourceLocation optionalDefaultKey;
|
||||
private int minId = 0;
|
||||
private int maxId = Integer.MAX_VALUE - 1;
|
||||
private List<AddCallback<T>> addCallback = Lists.newArrayList();
|
||||
private List<ClearCallback<T>> clearCallback = Lists.newArrayList();
|
||||
private List<CreateCallback<T>> createCallback = Lists.newArrayList();
|
||||
private List<ValidateCallback<T>> validateCallback = Lists.newArrayList();
|
||||
private boolean saveToDisc = true;
|
||||
private boolean allowOverrides = true;
|
||||
private boolean allowModifications = false;
|
||||
private DummyFactory<T> dummyFactory;
|
||||
private MissingFactory<T> missingFactory;
|
||||
|
||||
public RegistryBuilder<T> setName(ResourceLocation name)
|
||||
{
|
||||
this.registryName = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RegistryBuilder<T> setType(Class<T> type)
|
||||
{
|
||||
this.registryType = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RegistryBuilder<T> setIDRange(int min, int max)
|
||||
{
|
||||
this.minId = min;
|
||||
this.maxId = max;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RegistryBuilder<T> setMaxID(int max)
|
||||
{
|
||||
return this.setIDRange(0, max);
|
||||
}
|
||||
|
||||
public RegistryBuilder<T> setDefaultKey(ResourceLocation key)
|
||||
{
|
||||
this.optionalDefaultKey = key;
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public RegistryBuilder<T> addCallback(Object inst)
|
||||
{
|
||||
if (inst instanceof AddCallback)
|
||||
this.add((AddCallback<T>)inst);
|
||||
if (inst instanceof ClearCallback)
|
||||
this.add((ClearCallback<T>)inst);
|
||||
if (inst instanceof CreateCallback)
|
||||
this.add((CreateCallback<T>)inst);
|
||||
if (inst instanceof ValidateCallback)
|
||||
this.add((ValidateCallback<T>)inst);
|
||||
if (inst instanceof DummyFactory)
|
||||
this.set((DummyFactory<T>)inst);
|
||||
if (inst instanceof MissingFactory)
|
||||
this.set((MissingFactory<T>)inst);
|
||||
return this;
|
||||
}
|
||||
|
||||
public RegistryBuilder<T> add(AddCallback<T> add)
|
||||
{
|
||||
this.addCallback.add(add);
|
||||
return this;
|
||||
}
|
||||
|
||||
public RegistryBuilder<T> add(ClearCallback<T> clear)
|
||||
{
|
||||
this.clearCallback.add(clear);
|
||||
return this;
|
||||
}
|
||||
|
||||
public RegistryBuilder<T> add(CreateCallback<T> create)
|
||||
{
|
||||
this.createCallback.add(create);
|
||||
return this;
|
||||
}
|
||||
|
||||
public RegistryBuilder<T> add(ValidateCallback<T> validate)
|
||||
{
|
||||
this.validateCallback.add(validate);
|
||||
return this;
|
||||
}
|
||||
|
||||
public RegistryBuilder<T> set(DummyFactory<T> factory)
|
||||
{
|
||||
this.dummyFactory = factory;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RegistryBuilder<T> set(MissingFactory<T> missing)
|
||||
{
|
||||
this.missingFactory = missing;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RegistryBuilder<T> disableSaving()
|
||||
{
|
||||
this.saveToDisc = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RegistryBuilder<T> disableOverrides()
|
||||
{
|
||||
this.allowOverrides = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RegistryBuilder<T> allowModification()
|
||||
{
|
||||
this.allowModifications = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IForgeRegistry<T> create()
|
||||
{
|
||||
return RegistryManager.ACTIVE.createRegistry(registryName, registryType, optionalDefaultKey, minId, maxId,
|
||||
getAdd(), getClear(), getCreate(), getValidate(), saveToDisc, allowOverrides, allowModifications, dummyFactory, missingFactory);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private AddCallback<T> getAdd()
|
||||
{
|
||||
if (addCallback.isEmpty())
|
||||
return null;
|
||||
if (addCallback.size() == 1)
|
||||
return addCallback.get(0);
|
||||
|
||||
return (owner, stage, id, obj, old) ->
|
||||
{
|
||||
for (AddCallback<T> cb : this.addCallback)
|
||||
cb.onAdd(owner, stage, id, obj, old);
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ClearCallback<T> getClear()
|
||||
{
|
||||
if (clearCallback.isEmpty())
|
||||
return null;
|
||||
if (clearCallback.size() == 1)
|
||||
return clearCallback.get(0);
|
||||
|
||||
return (owner, stage) ->
|
||||
{
|
||||
for (ClearCallback<T> cb : this.clearCallback)
|
||||
cb.onClear(owner, stage);
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private CreateCallback<T> getCreate()
|
||||
{
|
||||
if (createCallback.isEmpty())
|
||||
return null;
|
||||
if (createCallback.size() == 1)
|
||||
return createCallback.get(0);
|
||||
|
||||
return (owner, stage) ->
|
||||
{
|
||||
for (CreateCallback<T> cb : this.createCallback)
|
||||
cb.onCreate(owner, stage);
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ValidateCallback<T> getValidate()
|
||||
{
|
||||
if (validateCallback.isEmpty())
|
||||
return null;
|
||||
if (validateCallback.size() == 1)
|
||||
return validateCallback.get(0);
|
||||
|
||||
return (owner, stage, id, key, obj) ->
|
||||
{
|
||||
for (ValidateCallback<T> cb : this.validateCallback)
|
||||
cb.onValidate(owner, stage, id, key, obj);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.registries;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
/*
|
||||
* This is the internal implementation class of the delegate.
|
||||
*/
|
||||
final class RegistryDelegate<T> implements IRegistryDelegate<T>
|
||||
{
|
||||
private T referent;
|
||||
private ResourceLocation name;
|
||||
private final Class<T> type;
|
||||
|
||||
public RegistryDelegate(T referent, Class<T> type)
|
||||
{
|
||||
this.referent = referent;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() { return referent; }
|
||||
@Override
|
||||
public ResourceLocation name() { return name; }
|
||||
@Override
|
||||
public Class<T> type() { return this.type; }
|
||||
|
||||
void changeReference(T newTarget){ this.referent = newTarget; }
|
||||
public void setName(ResourceLocation name) { this.name = name; }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj instanceof RegistryDelegate)
|
||||
{
|
||||
RegistryDelegate<?> other = (RegistryDelegate<?>) obj;
|
||||
return Objects.equal(other.name, name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2018.
|
||||
*
|
||||
* 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.registries;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Sets.SetView;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.common.FMLLog;
|
||||
import net.minecraftforge.registries.ForgeRegistry.Snapshot;
|
||||
import net.minecraftforge.registries.IForgeRegistry.*;
|
||||
|
||||
public class RegistryManager
|
||||
{
|
||||
public static final RegistryManager ACTIVE = new RegistryManager("ACTIVE");
|
||||
public static final RegistryManager VANILLA = new RegistryManager("VANILLA");
|
||||
public static final RegistryManager FROZEN = new RegistryManager("FROZEN");
|
||||
|
||||
BiMap<ResourceLocation, ForgeRegistry<? extends IForgeRegistryEntry<?>>> registries = HashBiMap.create();
|
||||
private BiMap<Class<? extends IForgeRegistryEntry<?>>, ResourceLocation> superTypes = HashBiMap.create();
|
||||
private Set<ResourceLocation> persisted = Sets.newHashSet();
|
||||
private final String name;
|
||||
|
||||
public RegistryManager(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V extends IForgeRegistryEntry<V>> Class<V> getSuperType(ResourceLocation key)
|
||||
{
|
||||
return (Class<V>)superTypes.inverse().get(key);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V extends IForgeRegistryEntry<V>> ForgeRegistry<V> getRegistry(ResourceLocation key)
|
||||
{
|
||||
return (ForgeRegistry<V>)this.registries.get(key);
|
||||
}
|
||||
|
||||
public <V extends IForgeRegistryEntry<V>> IForgeRegistry<V> getRegistry(Class<V> cls)
|
||||
{
|
||||
return getRegistry(superTypes.get(cls));
|
||||
}
|
||||
|
||||
public <V extends IForgeRegistryEntry<V>> ResourceLocation getName(IForgeRegistry<V> reg)
|
||||
{
|
||||
return this.registries.inverse().get(reg);
|
||||
}
|
||||
|
||||
public <V extends IForgeRegistryEntry<V>> ForgeRegistry<V> getRegistry(ResourceLocation key, RegistryManager other)
|
||||
{
|
||||
if (!this.registries.containsKey(key))
|
||||
{
|
||||
ForgeRegistry<V> ot = other.getRegistry(key);
|
||||
if (ot == null)
|
||||
return null;
|
||||
this.registries.put(key, ot.copy(this));
|
||||
this.superTypes.put(ot.getRegistrySuperType(), key);
|
||||
if (other.persisted.contains(key))
|
||||
this.persisted.add(key);
|
||||
}
|
||||
return getRegistry(key);
|
||||
}
|
||||
|
||||
<V extends IForgeRegistryEntry<V>> ForgeRegistry<V> createRegistry(ResourceLocation name, Class<V> type, ResourceLocation defaultKey, int min, int max,
|
||||
@Nullable AddCallback<V> add, @Nullable ClearCallback<V> clear, @Nullable CreateCallback<V> create, @Nullable ValidateCallback<V> validate,
|
||||
boolean persisted, boolean allowOverrides, boolean isModifiable, @Nullable DummyFactory<V> dummyFactory, @Nullable MissingFactory<V> missing)
|
||||
{
|
||||
Set<Class<?>> parents = Sets.newHashSet();
|
||||
findSuperTypes(type, parents);
|
||||
SetView<Class<?>> overlappedTypes = Sets.intersection(parents, superTypes.keySet());
|
||||
if (!overlappedTypes.isEmpty())
|
||||
{
|
||||
Class<?> foundType = overlappedTypes.iterator().next();
|
||||
FMLLog.log.error("Found existing registry of type {} named {}, you cannot create a new registry ({}) with type {}, as {} has a parent of that type", foundType, superTypes.get(foundType), name, type, type);
|
||||
throw new IllegalArgumentException("Duplicate registry parent type found - you can only have one registry for a particular super type");
|
||||
}
|
||||
ForgeRegistry<V> reg = new ForgeRegistry<V>(type, defaultKey, min, max, create, add, clear, validate, this, allowOverrides, isModifiable, dummyFactory, missing);
|
||||
registries.put(name, reg);
|
||||
superTypes.put(type, name);
|
||||
if (persisted)
|
||||
this.persisted.add(name);
|
||||
return getRegistry(name);
|
||||
}
|
||||
|
||||
private void findSuperTypes(Class<?> type, Set<Class<?>> types)
|
||||
{
|
||||
if (type == null || type == Object.class)
|
||||
{
|
||||
return;
|
||||
}
|
||||
types.add(type);
|
||||
for (Class<?> interfac : type.getInterfaces())
|
||||
{
|
||||
findSuperTypes(interfac, types);
|
||||
}
|
||||
findSuperTypes(type.getSuperclass(), types);
|
||||
}
|
||||
|
||||
public Map<ResourceLocation, Snapshot> takeSnapshot(boolean savingToDisc)
|
||||
{
|
||||
Map<ResourceLocation, Snapshot> ret = Maps.newHashMap();
|
||||
Set<ResourceLocation> keys = savingToDisc ? this.persisted : this.registries.keySet();
|
||||
keys.forEach(name -> ret.put(name, getRegistry(name).makeSnapshot()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
//Public for testing only
|
||||
public void clean()
|
||||
{
|
||||
this.persisted.clear();
|
||||
this.registries.clear();
|
||||
this.superTypes.clear();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user