Add support for named sub-caps

This commit is contained in:
LexManos committed 2024-06-19 15:30:46 -07:00
1 parent c04e533a61
commit 8e0845d032
6 files changed
+102 -93

No files matched your search

@@ -16,19 +16,17 @@ import net.minecraftforge.registries.GameData;
import net.minecraftforge.registries.ObjectHolderRegistry;
import net.minecraftforge.registries.RegistryManager;
public class ForgeStatesProvider implements IModStateProvider
{
public class ForgeStatesProvider implements IModStateProvider {
final ModLoadingState CREATE_REGISTRIES = ModLoadingState.withInline("CREATE_REGISTRIES", "CONSTRUCT", ModLoadingPhase.GATHER, ml -> RegistryManager.postNewRegistryEvent());
final ModLoadingState OBJECT_HOLDERS = ModLoadingState.withInline("OBJECT_HOLDERS", "CREATE_REGISTRIES", ModLoadingPhase.GATHER, ml -> ObjectHolderRegistry.findObjectHolders());
final ModLoadingState INJECT_CAPABILITIES = ModLoadingState.withInline("INJECT_CAPABILITIES", "OBJECT_HOLDERS", ModLoadingPhase.GATHER, ml -> CapabilityManager.INSTANCE.injectCapabilities(ml.getAllScanData()));
final ModLoadingState INJECT_CAPABILITIES = ModLoadingState.withInline("INJECT_CAPABILITIES", "OBJECT_HOLDERS", ModLoadingPhase.GATHER, CapabilityManager::injectCapabilities);
final ModLoadingState UNFREEZE = ModLoadingState.withInline("UNFREEZE_DATA", "INJECT_CAPABILITIES", ModLoadingPhase.GATHER, ml -> GameData.unfreezeData());
final ModLoadingState LOAD_REGISTRIES = ModLoadingState.withInline("LOAD_REGISTRIES", "UNFREEZE_DATA", ModLoadingPhase.GATHER, ml -> GameData.postRegisterEvents());
final ModLoadingState FREEZE = ModLoadingState.withInline("FREEZE_DATA", "COMPLETE", ModLoadingPhase.COMPLETE, ml -> GameData.freezeData());
final ModLoadingState NETLOCK = ModLoadingState.withInline("NETWORK_LOCK", "FREEZE_DATA", ModLoadingPhase.COMPLETE, ml -> NetworkRegistry.lock());
@Override
public List<IModLoadingState> getAllStates()
{
public List<IModLoadingState> getAllStates() {
return List.of(CREATE_REGISTRIES, OBJECT_HOLDERS, INJECT_CAPABILITIES, UNFREEZE, LOAD_REGISTRIES, FREEZE, NETLOCK);
}
}
@@ -19,16 +19,16 @@ import java.util.function.Consumer;
*
* The CapabilityManager is in charge of creating this class.
*/
public class Capability<T>
{
public final class Capability<T> {
/**
* @return The unique name of this capability, typically this is
* the fully qualified class name for the target interface.
*/
public String getName() { return name; }
public String getName() {
return name;
}
public @NotNull <R> LazyOptional<R> orEmpty(Capability<R> toCheck, LazyOptional<T> inst)
{
public @NotNull <R> LazyOptional<R> orEmpty(Capability<R> toCheck, LazyOptional<T> inst) {
return this == toCheck ? inst.cast() : LazyOptional.empty();
}
@@ -36,8 +36,7 @@ public class Capability<T>
* @return true if something has registered this capability to the Manager.
* This is a marker that the class for this capability exists, and can be used.
*/
public boolean isRegistered()
{
public boolean isRegistered() {
return this.listeners == null;
}
@@ -48,8 +47,7 @@ public class Capability<T>
* @param listener Function to fire when capability is registered.
* @return self, in case people want to use builder pattern.
*/
public synchronized Capability<T> addListener(Consumer<Capability<T>> listener)
{
public synchronized Capability<T> addListener(Consumer<Capability<T>> listener) {
if (this.isRegistered())
listener.accept(this);
else
@@ -61,13 +59,11 @@ public class Capability<T>
private final String name;
List<Consumer<Capability<T>>> listeners = new ArrayList<>();
Capability(String name)
{
Capability(String name) {
this.name = name;
}
void onRegister()
{
void onRegister() {
var listeners = this.listeners;
this.listeners = null;
listeners.forEach(l -> l.accept(this));
@@ -5,65 +5,90 @@
package net.minecraftforge.common.capabilities;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.fml.Logging;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.ModLoader;
import net.minecraftforge.forgespi.language.ModFileScanData;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.ApiStatus;
import org.objectweb.asm.Type;
import java.util.Comparator;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
public enum CapabilityManager
{
INSTANCE;
static final Logger LOGGER = LogManager.getLogger();
public final class CapabilityManager {
@Deprecated(forRemoval = true, since = "1.21")
public static final CapabilityManager INSTANCE = new CapabilityManager();
private static final Logger LOGGER = LogManager.getLogger();
public static <T> Capability<T> get(CapabilityToken<T> type)
{
return INSTANCE.get(type.getType(), false);
/**
* Retrieves the 'root' capability for the specified type. This is the generic one that is used when the API
* registers the specified type. It is also safe to assume that if your child token is a valid return when
* this Capability is requested.
*/
public static <T> Capability<T> get(CapabilityToken<T> type) {
return get(type.getType(), null, false);
}
/**
* Retrieves a named capability of the specified type. This is useful when providers supply multiple instances of the same interface and want to
* distinguish between them. It is generally not safe to return your generic implementation for the root type when this is requested.
*/
public static <T> Capability<T> get(CapabilityToken<T> type, String modid, String name) {
return get(type, ResourceLocation.fromNamespaceAndPath(modid, name));
}
/**
* Retrieves a named capability of the specified type. This is useful when providers supply multiple instances of the same interface and want to
* distinguish between them. It is generally not safe to return your generic implementation for the root type when this is requested.
*/
public static <T> Capability<T> get(CapabilityToken<T> type, ResourceLocation name) {
return get(type.getType(), name, false);
}
@SuppressWarnings("unchecked")
<T> Capability<T> get(String realName, boolean registering)
{
static <T> Capability<T> get(String type, ResourceLocation name, boolean registering) {
Capability<T> cap;
synchronized (providers)
{
realName = realName.intern();
cap = (Capability<T>)providers.computeIfAbsent(realName, Capability::new);
synchronized (providers) {
final var parent = (Capability<T>)providers.computeIfAbsent(new Key(type, null), k -> new Capability<>(type.intern()));
if (name == null) {
cap = parent;
} else { // A Named child
cap = (Capability<T>)providers.computeIfAbsent(new Key(type, name), k -> {
var ret = new Capability<>((parent.getName() + '#' + name.toString()).intern());
parent.addListener(p -> ret.onRegister());
return ret;
});
}
}
if (registering) {
if (name != null)
error("Cannot register capability type " + type + " + with a name: " + name);
if (registering)
{
synchronized (cap)
{
synchronized (cap) {
if (cap.isRegistered())
{
LOGGER.error(Logging.CAPABILITIES, "Cannot register capability implementation multiple times : {}", realName);
throw new IllegalArgumentException("Cannot register a capability implementation multiple times : "+ realName);
}
else
{
cap.onRegister();
}
error("Cannot register capability type " + type + " multiple times");
cap.onRegister();
}
}
return cap;
}
// INTERNAL
private static final Type AUTO_REGISTER = Type.getType(AutoRegisterCapability.class);
private final IdentityHashMap<String, Capability<?>> providers = new IdentityHashMap<>();
public void injectCapabilities(List<ModFileScanData> data)
{
var autos = data.stream()
private record Key(String type, ResourceLocation name) {};
private static final Map<Key, Capability<?>> providers = new HashMap<>();
@ApiStatus.Internal
public static void injectCapabilities(ModList modlist) {
var autos = modlist.getAllScanData().stream()
.flatMap(e -> e.getAnnotations().stream())
.filter(a -> AUTO_REGISTER.equals(a.annotationType()))
.map(a -> a.clazz())
@@ -71,13 +96,18 @@ public enum CapabilityManager
.sorted(Comparator.comparing(Type::toString))
.toList();
for (var auto : autos)
{
for (var auto : autos) {
LOGGER.debug(Logging.CAPABILITIES, "Attempting to automatically register: " + auto);
get(auto.getInternalName(), true);
get(auto.getInternalName(), null, true);
}
@SuppressWarnings("removal")
var event = new RegisterCapabilitiesEvent();
ModLoader.get().postEvent(event);
}
private static void error(String message) {
LOGGER.error(Logging.CAPABILITIES, message);
throw new IllegalStateException(message);
}
}
@@ -21,16 +21,13 @@ import net.minecraftforge.fml.common.asm.CapabilityTokenSubclass;
* }</pre>
*
*/
public abstract class CapabilityToken<T>
{
protected final String getType()
{
public abstract class CapabilityToken<T> {
protected final String getType() {
throw new RuntimeException("This will be implemented by a transformer");
}
@Override
public String toString()
{
public String toString() {
return "CapabilityToken[" + getType() + "]";
}
}
@@ -15,19 +15,21 @@ import net.minecraftforge.fml.event.IModBusEvent;
/**
* This event fires when it is time to register your capabilities.
* @see Capability
*
* @deprecated Use {@link AutoRegisterCapability} annotation on your class.
*/
public final class RegisterCapabilitiesEvent extends Event implements IModBusEvent
{
@Deprecated(forRemoval = true, since = "1.21")
public final class RegisterCapabilitiesEvent extends Event implements IModBusEvent {
/**
* Registers a capability to be consumed by others.
* APIs who define the capability should call this.
* To retrieve the Capability instance, use the @CapabilityInject annotation.
* This is meant to allow Capability consumers to have soft dependencies on the Capability type.
* But be automatically notified when the Class actually exists. Meaning it's safe to create their implementations.
*
* @param type The type to be registered
* To retrieve the Capability instance, use the {@link CapabilityManager} gets functions.
*/
public <T> void register(Class<T> type)
{
Objects.requireNonNull(type,"Attempted to register a capability with invalid type");
CapabilityManager.INSTANCE.get(Type.getInternalName(type), true);
public <T> void register(Class<T> type) {
Objects.requireNonNull(type, "Attempted to register a capability with invalid type");
CapabilityManager.get(Type.getInternalName(type), null, true);
}
}