base mod created

This commit is contained in:
Mohammad-Ali Minaie
2018-10-08 09:07:47 -04:00
parent 0a7700c356
commit b86dedad2f
7848 changed files with 584664 additions and 1 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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(); };
}
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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));
}
}
}

View File

@@ -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));
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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");
}
}

View File

@@ -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);
};
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}