Add custom registry type support to DeferredRegister. (#6742)

It's still a hard dependency because it requires a class reference.
But there is not a sane way to test registry types without a class reference.
This commit is contained in:
LexManos 2020-06-12 15:28:43 -07:00 committed by GitHub
parent 5dbea9ea8d
commit fd9bf64d53
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 244 additions and 25 deletions

View file

@ -50,6 +50,10 @@ public final class RegistryObject<T extends IForgeRegistryEntry<? super T>> impl
return new RegistryObject<>(name, registry);
}
public static <T extends IForgeRegistryEntry<T>, U extends T> RegistryObject<U> of(final ResourceLocation name, final Class<T> baseType, String modid) {
return new RegistryObject<>(name, baseType, modid);
}
private static RegistryObject<?> EMPTY = new RegistryObject<>();
private static <T extends IForgeRegistryEntry<? super T>> RegistryObject<T> empty() {
@ -80,6 +84,30 @@ public final class RegistryObject<T extends IForgeRegistryEntry<? super T>> impl
});
}
@SuppressWarnings("unchecked")
private <V extends IForgeRegistryEntry<V>> RegistryObject(final ResourceLocation name, final Class<V> baseType, final String modid)
{
this.name = name;
final Throwable callerStack = new Throwable("Calling Site from mod: " + modid);
ObjectHolderRegistry.addHandler(new Consumer<Predicate<ResourceLocation>>()
{
private IForgeRegistry<V> registry;
@Override
public void accept(Predicate<ResourceLocation> pred)
{
if (registry == null)
{
this.registry = RegistryManager.ACTIVE.getRegistry(baseType);
if (registry == null)
throw new IllegalStateException("Unable to find registry for type " + baseType.getName() + " for mod \"" + modid + "\". Check the 'caused by' to see futher stack.", callerStack);
}
if (pred.test(registry.getRegistryName()))
RegistryObject.this.value = registry.containsKey(RegistryObject.this.name) ? (T)registry.getValue(RegistryObject.this.name) : null;
}
});
}
/**
* Directly retrieves the wrapped Registry Object. This value will automatically be updated when the backing registry is updated.
* Will throw NPE if the value is null, use isPresent to check first. Or use any of the other guarded functions.

View file

@ -21,6 +21,7 @@ package net.minecraftforge.registries;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.RegistryObject;
@ -33,6 +34,8 @@ import java.util.Objects;
import java.util.Set;
import java.util.function.Supplier;
import com.google.common.reflect.TypeToken;
/**
* Utility class to help with managing registry entries.
* Maintains a list of all suppliers for entries and registers them during the proper Register event.
@ -56,15 +59,41 @@ import java.util.function.Supplier;
*/
public class DeferredRegister<T extends IForgeRegistryEntry<T>>
{
private final IForgeRegistry<T> type;
/**
* Use for vanilla/forge registries. See example above.
*/
public static <B extends IForgeRegistryEntry<B>> DeferredRegister<B> create(IForgeRegistry<B> reg, String modid)
{
return new DeferredRegister<B>(reg, modid);
}
/**
* Use for custom registries that are made during the NewRegistry event.
*/
public static <B extends IForgeRegistryEntry<B>> DeferredRegister<B> create(Class<B> base, String modid)
{
return new DeferredRegister<B>(base, modid);
}
private final Class<T> superType;
private final String modid;
private final Map<RegistryObject<T>, Supplier<? extends T>> entries = new LinkedHashMap<>();
private final Set<RegistryObject<T>> entriesView = Collections.unmodifiableSet(entries.keySet());
private IForgeRegistry<T> type;
private Supplier<RegistryBuilder<T>> registryFactory;
private DeferredRegister(Class<T> base, String modid)
{
this.superType = base;
this.modid = modid;
}
@Deprecated // Make private in 1.16, Use create. Constructors are an implementation detail.
public DeferredRegister(IForgeRegistry<T> reg, String modid)
{
this(reg.getRegistrySuperType(), modid);
this.type = reg;
this.modid = modid;
}
/**
@ -80,13 +109,41 @@ public class DeferredRegister<T extends IForgeRegistryEntry<T>>
Objects.requireNonNull(name);
Objects.requireNonNull(sup);
final ResourceLocation key = new ResourceLocation(modid, name);
RegistryObject<I> ret = RegistryObject.of(key, this.type);
RegistryObject<I> ret;
if (this.type != null)
ret = RegistryObject.of(key, this.type);
else if (this.superType != null)
ret = RegistryObject.of(key, this.superType, this.modid);
else
throw new IllegalStateException("Could not create RegistryObject in DeferredRegister");
if (entries.putIfAbsent((RegistryObject<T>) ret, () -> sup.get().setRegistryName(key)) != null) {
throw new IllegalArgumentException("Duplicate registration " + name);
}
return ret;
}
/**
* For custom registries only, fills the {@link #registryFactory} to be called later see {@link #register(IEventBus)}
*
* Calls {@link RegistryBuilder#setName} and {@link RegistryBuilder#setType} automatically.
*
* @param name Path of the registry's {@link ResourceLocation}
* @param sup Supplier of the RegistryBuilder that is called to fill {@link #type} during the NewRegistry event
* @return A supplier of the {@link IForgeRegistry} created by the builder.
*/
public Supplier<IForgeRegistry<T>> makeRegistry(final String name, final Supplier<RegistryBuilder<T>> sup) {
if (this.superType == null)
throw new IllegalStateException("Cannot create a registry without specifying a base type");
if (this.type != null || this.registryFactory != null)
throw new IllegalStateException("Cannot create a registry for a type that already exists");
this.registryFactory = () -> sup.get().setName(new ResourceLocation(modid, name)).setType(this.superType);
return () -> this.type;
}
/**
* Adds our event handler to the specified event bus, this MUST be called in order for this class to function.
* See the example usage.
@ -96,6 +153,12 @@ public class DeferredRegister<T extends IForgeRegistryEntry<T>>
public void register(IEventBus bus)
{
bus.addListener(this::addEntries);
if (this.type == null) {
if (this.registryFactory != null)
bus.addListener(this::createRegistry);
else
bus.addListener(EventPriority.LOWEST, this::captureRegistry);
}
}
/**
@ -108,7 +171,7 @@ public class DeferredRegister<T extends IForgeRegistryEntry<T>>
private void addEntries(RegistryEvent.Register<?> event)
{
if (event.getGenericType() == this.type.getRegistrySuperType())
if (this.type != null && event.getGenericType() == this.type.getRegistrySuperType())
{
@SuppressWarnings("unchecked")
IForgeRegistry<T> reg = (IForgeRegistry<T>)event.getRegistry();
@ -119,4 +182,21 @@ public class DeferredRegister<T extends IForgeRegistryEntry<T>>
}
}
}
private void createRegistry(RegistryEvent.NewRegistry event)
{
this.type = this.registryFactory.get().create();
}
private void captureRegistry(RegistryEvent.NewRegistry event)
{
if (this.superType != null)
{
this.type = RegistryManager.ACTIVE.getRegistry(this.superType);
if (this.type == null)
throw new IllegalStateException("Unable to find registry for type " + this.superType.getName() + " for modid \"" + modid + "\" after NewRegistry event");
}
else
throw new IllegalStateException("Unable to find registry for mod \"" + modid + "\" No lookup criteria specified.");
}
}

View file

@ -26,12 +26,6 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.minecraft.util.ResourceLocation;
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.
*
@ -72,6 +66,7 @@ public interface IForgeRegistry<V extends IForgeRegistryEntry<V>> extends Iterab
* 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.
*/
@FunctionalInterface
interface AddCallback<V extends IForgeRegistryEntry<V>>
{
void onAdd(IForgeRegistryInternal<V> owner, RegistryManager stage, int id, V obj, @Nullable V oldObj);
@ -81,6 +76,7 @@ public interface IForgeRegistry<V extends IForgeRegistryEntry<V>> extends Iterab
* Callback fired when the registry is cleared. This is done before a registry is reloaded from client
* or server.
*/
@FunctionalInterface
interface ClearCallback<V extends IForgeRegistryEntry<V>>
{
void onClear(IForgeRegistryInternal<V> owner, RegistryManager stage);
@ -89,6 +85,7 @@ public interface IForgeRegistry<V extends IForgeRegistryEntry<V>> extends Iterab
/**
* Callback fired when a registry instance is created. Populate slave maps here.
*/
@FunctionalInterface
interface CreateCallback<V extends IForgeRegistryEntry<V>>
{
void onCreate(IForgeRegistryInternal<V> owner, RegistryManager stage);
@ -97,6 +94,7 @@ public interface IForgeRegistry<V extends IForgeRegistryEntry<V>> extends Iterab
/**
* Callback fired when the registry contents are validated.
*/
@FunctionalInterface
interface ValidateCallback<V extends IForgeRegistryEntry<V>>
{
void onValidate(IForgeRegistryInternal<V> owner, RegistryManager stage, int id, ResourceLocation key, V obj);
@ -105,6 +103,7 @@ public interface IForgeRegistry<V extends IForgeRegistryEntry<V>> extends Iterab
/**
* Callback fired when the registry is done processing. Used to calculate state ID maps.
*/
@FunctionalInterface
interface BakeCallback<V extends IForgeRegistryEntry<V>>
{
void onBake(IForgeRegistryInternal<V> owner, RegistryManager stage);
@ -113,6 +112,7 @@ public interface IForgeRegistry<V extends IForgeRegistryEntry<V>> extends Iterab
/**
* Factory for creating dummy entries, allowing worlds to be loaded and keep the missing block references.
*/
@FunctionalInterface
interface DummyFactory<V extends IForgeRegistryEntry<V>>
{
V createDummy(ResourceLocation key);
@ -121,6 +121,7 @@ public interface IForgeRegistry<V extends IForgeRegistryEntry<V>> extends Iterab
/**
*
*/
@FunctionalInterface
interface MissingFactory<V extends IForgeRegistryEntry<V>>
{
V createMissing(ResourceLocation key, boolean isNetwork);

View file

@ -45,7 +45,6 @@ import org.apache.logging.log4j.Logger;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import static net.minecraftforge.fml.Logging.CORE;
import static net.minecraftforge.registries.ForgeRegistry.REGISTRIES;
/**

View file

@ -108,48 +108,83 @@ public class RegistryBuilder<T extends IForgeRegistryEntry<T>>
return this;
}
public RegistryBuilder<T> onAdd(AddCallback<T> add)
{
return this.add(add);
}
public RegistryBuilder<T> add(ClearCallback<T> clear)
{
this.clearCallback.add(clear);
return this;
}
public RegistryBuilder<T> onClear(ClearCallback<T> clear)
{
return this.add(clear);
}
public RegistryBuilder<T> add(CreateCallback<T> create)
{
this.createCallback.add(create);
return this;
}
public RegistryBuilder<T> onCreate(CreateCallback<T> create)
{
return this.add(create);
}
public RegistryBuilder<T> add(ValidateCallback<T> validate)
{
this.validateCallback.add(validate);
return this;
}
public RegistryBuilder<T> onValidate(ValidateCallback<T> validate)
{
return this.add(validate);
}
public RegistryBuilder<T> add(BakeCallback<T> bake)
{
this.bakeCallback.add(bake);
return this;
}
public RegistryBuilder<T> onBake(BakeCallback<T> bake)
{
return this.add(bake);
}
public RegistryBuilder<T> set(DummyFactory<T> factory)
{
this.dummyFactory = factory;
return this;
}
public RegistryBuilder<T> dummy(DummyFactory<T> factory)
{
return this.set(factory);
}
public RegistryBuilder<T> set(MissingFactory<T> missing)
{
this.missingFactory = missing;
return this;
}
public RegistryBuilder<T> missing(MissingFactory<T> missing)
{
return this.set(missing);
}
public RegistryBuilder<T> disableSaving()
{
this.saveToDisc = false;
return this;
}
public RegistryBuilder<T> disableSync()
{
this.sync = false;
@ -167,12 +202,12 @@ public class RegistryBuilder<T extends IForgeRegistryEntry<T>>
this.allowModifications = true;
return this;
}
public RegistryBuilder<T> legacyName(String name)
{
return legacyName(new ResourceLocation(name));
}
public RegistryBuilder<T> legacyName(ResourceLocation name)
{
this.legacyNames.add(name);
@ -306,12 +341,12 @@ public class RegistryBuilder<T extends IForgeRegistryEntry<T>>
{
return saveToDisc;
}
public boolean getSync()
{
return sync;
}
public Set<ResourceLocation> getLegacyNames()
{
return legacyNames;

View file

@ -0,0 +1,75 @@
package net.minecraftforge.debug;
import java.util.function.Supplier;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.data.DataGenerator;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.generators.BlockStateProvider;
import net.minecraftforge.client.model.generators.ModelFile;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.RegistryObject;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.GatherDataEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.ForgeRegistryEntry;
import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.RegistryBuilder;
@SuppressWarnings("unused")
@Mod(DeferredRegistryTest.MODID)
public class DeferredRegistryTest {
static final String MODID = "deferred_registry_test";
private static final Logger LOGGER = LogManager.getLogger();
private static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID);
private static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID);
private static final DeferredRegister<Custom> CUSTOMS = DeferredRegister.create(Custom.class, MODID);
private static final RegistryObject<Block> BLOCK = BLOCKS.register("test", () -> new Block(Block.Properties.create(Material.ROCK)));
private static final RegistryObject<Item> ITEM = ITEMS .register("test", () -> new BlockItem(BLOCK.get(), new Item.Properties().group(ItemGroup.BUILDING_BLOCKS)));
private static final RegistryObject<Custom> CUSTOM = CUSTOMS.register("test", () -> new Custom(){});
private static final Supplier<IForgeRegistry<Custom>> CUSTOM_REG = CUSTOMS.makeRegistry("test_registry", () ->
new RegistryBuilder<Custom>().disableSaving().setMaxID(Integer.MAX_VALUE - 1)
.onAdd((owner, stage, id, obj, old) -> LOGGER.info("Custom Added: " + id + " " + obj.foo()))
);
public DeferredRegistryTest() {
IEventBus modBus = FMLJavaModLoadingContext.get().getModEventBus();
BLOCKS.register(modBus);
ITEMS.register(modBus);
CUSTOMS.register(modBus);
modBus.addListener(this::gatherData);
}
public void gatherData(GatherDataEvent event) {
DataGenerator gen = event.getGenerator();
if (event.includeClient()) {
gen.addProvider(new BlockStateProvider(gen, MODID, event.getExistingFileHelper()) {
@Override
protected void registerStatesAndModels() {
ModelFile model = models().cubeAll(BLOCK.get().getRegistryName().getPath(), mcLoc("block/furnace_top"));
simpleBlock(BLOCK.get(), model);
simpleBlockItem(BLOCK.get(), model);
}
});
}
}
public static class Custom extends ForgeRegistryEntry<Custom> {
public String foo() {
return this.getClass().getName();
}
}
}

View file

@ -40,7 +40,6 @@ import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.model.generators.BlockStateProvider;
import net.minecraftforge.client.model.generators.ExistingFileHelper;
import net.minecraftforge.client.model.generators.ItemModelProvider;
import net.minecraftforge.client.model.generators.ModelFile;
import net.minecraftforge.event.world.PistonEvent;
import net.minecraftforge.event.world.PistonEvent.PistonMoveType;
@ -66,8 +65,8 @@ public class PistonEventTest
{
public static final String MODID = "piston_event_test";
public static String blockName = "shiftonmove";
private static DeferredRegister<Block> BLOCKS = new DeferredRegister<Block>(ForgeRegistries.BLOCKS, MODID);
private static DeferredRegister<Item> ITEMS = new DeferredRegister<Item> (ForgeRegistries.ITEMS, MODID);
private static DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID);
private static DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID);
private static RegistryObject<Block> shiftOnMove = BLOCKS.register(blockName, () -> new Block(Block.Properties.create(Material.ROCK)));
static {

View file

@ -47,6 +47,12 @@ loaderVersion="[28,)"
modId="nameplate_render_test"
[[mods]]
modId="global_loot_test"
[[dependencies.global_loot_test]]
modId="forge"
mandatory=true
versionRange="[1,)"
ordering="AFTER"
side="BOTH"
[[mods]]
modId="custom_plant_type_test"
[[mods]]
@ -57,9 +63,5 @@ loaderVersion="[28,)"
modId="music_disc_test"
[[mods]]
modId="stencil_enable_test"
[[dependencies.global_loot_test]]
modId="forge"
mandatory=true
versionRange="[1,)"
ordering="AFTER"
side="BOTH"
[[mods]]
modId="deferred_registry_test"