mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-12 08:17:35 +00:00
Add support for named sub-caps
This commit is contained in:
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() + "]";
|
||||
}
|
||||
}
|
||||
+10
-8
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user