mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-12 08:17:35 +00:00
Capability rework to get rid of @CapabilityInject and use CapabilityToken as the identifier, deprecated old system for removal in 1.18. (#8116)
This commit is contained in:
1 parent
166feefc02
commit
96303f6570
12 files changed
+285
-67
No files matched your search
@@ -21,6 +21,10 @@ package net.minecraftforge.common.capabilities;
|
||||
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
@@ -43,11 +47,44 @@ public class Capability<T>
|
||||
return this == toCheck ? inst.cast() : LazyOptional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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()
|
||||
{
|
||||
return this.listeners == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener to be called when someone registers this capability.
|
||||
* May be called instantly if this is already registered.
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
if (this.isRegistered())
|
||||
listener.accept(this);
|
||||
else
|
||||
this.listeners.add(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
// INTERNAL
|
||||
private final String name;
|
||||
List<Consumer<Capability<T>>> listeners = new ArrayList<>();
|
||||
|
||||
Capability(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
void onRegister()
|
||||
{
|
||||
var listeners = this.listeners;
|
||||
this.listeners = null;
|
||||
listeners.forEach(l -> l.accept(this));
|
||||
}
|
||||
}
|
||||
@@ -45,9 +45,12 @@ import java.lang.annotation.*;
|
||||
*
|
||||
* <b>Warning</b>: Capability injections are run in the thread that the capablity is registered.
|
||||
* Due to parallel mod loading, this can potentially be off of the main thread.
|
||||
*
|
||||
* @deprecated Use {@link CapabilityManager#get(CapabilityToken)}
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
@Deprecated(since = "1.18", forRemoval = true)
|
||||
public @interface CapabilityInject
|
||||
{
|
||||
/**
|
||||
|
||||
@@ -22,7 +22,6 @@ package net.minecraftforge.common.capabilities;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
@@ -55,30 +54,53 @@ public enum CapabilityManager
|
||||
* @param type The class type to be registered
|
||||
* @deprecated use {@link RegisterCapabilitiesEvent}
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
@Deprecated(since = "1.18", forRemoval = true)
|
||||
public <T> void register(Class<T> type)
|
||||
{
|
||||
Objects.requireNonNull(type,"Attempted to register a capability with invalid type");
|
||||
String realName = type.getName().intern();
|
||||
get(Type.getInternalName(type), true);
|
||||
}
|
||||
|
||||
|
||||
public static <T> Capability<T> get(CapabilityToken<T> type)
|
||||
{
|
||||
return INSTANCE.get(type.getType(), false);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<T> Capability<T> get(String realName, boolean registering)
|
||||
{
|
||||
Capability<T> cap;
|
||||
|
||||
synchronized (providers)
|
||||
{
|
||||
if (providers.containsKey(realName)) {
|
||||
LOGGER.error(CAPABILITIES, "Cannot register capability implementation multiple times : {}", realName);
|
||||
throw new IllegalArgumentException("Cannot register a capability implementation multiple times : "+ realName);
|
||||
}
|
||||
realName = realName.intern();
|
||||
cap = (Capability<T>)providers.computeIfAbsent(realName, Capability::new);
|
||||
|
||||
cap = new Capability<>(realName);
|
||||
providers.put(realName, cap);
|
||||
}
|
||||
|
||||
fireCallbacks(this.callbacks, List.of(cap));
|
||||
|
||||
if (registering)
|
||||
{
|
||||
synchronized (cap)
|
||||
{
|
||||
if (cap.isRegistered())
|
||||
{
|
||||
LOGGER.error(CAPABILITIES, "Cannot register capability implementation multiple times : {}", realName);
|
||||
throw new IllegalArgumentException("Cannot register a capability implementation multiple times : "+ realName);
|
||||
}
|
||||
else
|
||||
{
|
||||
cap.onRegister();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cap;
|
||||
}
|
||||
|
||||
// INTERNAL
|
||||
private final IdentityHashMap<String, Capability<?>> providers = new IdentityHashMap<>();
|
||||
private volatile IdentityHashMap<String, List<Function<Capability<?>, Object>>> callbacks;
|
||||
public void injectCapabilities(List<ModFileScanData> data)
|
||||
{
|
||||
final List<ModFileScanData.AnnotationData> elementsToInject = data.stream()
|
||||
@@ -91,22 +113,9 @@ public enum CapabilityManager
|
||||
|
||||
var event = new RegisterCapabilitiesEvent();
|
||||
ModLoader.get().postEvent(event);
|
||||
fireCallbacks(callbacks, event.getCapabilities().values());
|
||||
|
||||
// TODO 1.18: remove these fields
|
||||
this.providers.putAll(event.getCapabilities());
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
|
||||
private static void fireCallbacks(Map<String, List<Function<Capability<?>, Object>>> callbacks, Iterable<Capability<?>> caps)
|
||||
{
|
||||
for (var cap : caps)
|
||||
{
|
||||
callbacks.getOrDefault(cap.getName(), List.of()).forEach(f -> f.apply(cap));
|
||||
}
|
||||
}
|
||||
|
||||
private static void gatherCallbacks(Map<String, List<Function<Capability<?>, Object>>> callbacks, ModFileScanData.AnnotationData annotationData)
|
||||
private void gatherCallbacks(Map<String, List<Function<Capability<?>, Object>>> callbacks, ModFileScanData.AnnotationData annotationData)
|
||||
{
|
||||
final String targetClass = annotationData.clazz().getClassName();
|
||||
final String targetName = annotationData.memberName();
|
||||
@@ -116,13 +125,14 @@ public enum CapabilityManager
|
||||
LOGGER.warn(CAPABILITIES,"Unable to inject capability at {}.{} (Invalid Annotation)", targetClass, targetName);
|
||||
return;
|
||||
}
|
||||
final String capabilityName = type.getInternalName().replace('/', '.').intern();
|
||||
final String capabilityName = type.getInternalName();
|
||||
|
||||
List<Function<Capability<?>, Object>> list = callbacks.computeIfAbsent(capabilityName, k -> new ArrayList<>());
|
||||
final Capability<?> cap = get(capabilityName, false);
|
||||
|
||||
if (annotationData.memberName().indexOf('(') > 0)
|
||||
{
|
||||
list.add(input -> {
|
||||
cap.addListener(input ->
|
||||
{
|
||||
try
|
||||
{
|
||||
for (Method mtd : Class.forName(targetClass).getDeclaredMethods())
|
||||
@@ -132,12 +142,12 @@ public enum CapabilityManager
|
||||
if ((mtd.getModifiers() & Modifier.STATIC) != Modifier.STATIC)
|
||||
{
|
||||
LOGGER.warn(CAPABILITIES,"Unable to inject capability {} at {}.{} (Non-Static)", capabilityName, targetClass, targetName);
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
mtd.setAccessible(true);
|
||||
mtd.invoke(null, input);
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOGGER.warn(CAPABILITIES,"Unable to inject capability {} at {}.{} (Method Not Found)", capabilityName, targetClass, targetName);
|
||||
@@ -146,19 +156,19 @@ public enum CapabilityManager
|
||||
{
|
||||
LOGGER.warn(CAPABILITIES,"Unable to inject capability {} at {}.{}", capabilityName, targetClass, targetName, e);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
list.add(input -> {
|
||||
cap.addListener(input ->
|
||||
{
|
||||
try
|
||||
{
|
||||
Field field = Class.forName(targetClass).getDeclaredField(targetName);
|
||||
if ((field.getModifiers() & Modifier.STATIC) != Modifier.STATIC)
|
||||
{
|
||||
LOGGER.warn(CAPABILITIES,"Unable to inject capability {} at {}.{} (Non-Static)", capabilityName, targetClass, targetName);
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
field.setAccessible(true);
|
||||
field.set(null, input);
|
||||
@@ -167,7 +177,6 @@ public enum CapabilityManager
|
||||
{
|
||||
LOGGER.warn(CAPABILITIES,"Unable to inject capability {} at {}.{}", capabilityName, targetClass, targetName, e);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2021.
|
||||
*
|
||||
* 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.common.capabilities;
|
||||
|
||||
/**
|
||||
* Inspired by {@link com.google.common.reflect.TypeToken TypeToken}, use a subclass to capture
|
||||
* generic types. Then uses {@link net.minecraftforge.common.asm.CapabilityTokenSubclass a transformer}
|
||||
* to convert that generic into a string returned by {@link #getType}
|
||||
* This allows us to know the generic type, without having a hard reference to the
|
||||
* class.
|
||||
*
|
||||
* Example usage:
|
||||
* <pre>{@code
|
||||
* public static Capability<IDataHolder> DATA_HOLDER_CAPABILITY
|
||||
* = CapabilityManager.get(new CapabilityToken<>(){});
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
public abstract class CapabilityToken<T>
|
||||
{
|
||||
protected final String getType()
|
||||
{
|
||||
throw new RuntimeException("This will be implemented by a transformer");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "CapabilityToken[" + getType() + "]";
|
||||
}
|
||||
}
|
||||
+5
-18
@@ -19,22 +19,19 @@
|
||||
|
||||
package net.minecraftforge.common.capabilities;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static net.minecraftforge.fml.Logging.CAPABILITIES;
|
||||
|
||||
/**
|
||||
* This event fires when it is time to register your capabilities.
|
||||
* @see Capability
|
||||
*/
|
||||
public final class RegisterCapabilitiesEvent extends Event implements IModBusEvent
|
||||
{
|
||||
|
||||
private final IdentityHashMap<String, Capability<?>> capabilities = new IdentityHashMap<>();
|
||||
|
||||
/**
|
||||
* Registers a capability to be consumed by others.
|
||||
* APIs who define the capability should call this.
|
||||
@@ -45,16 +42,6 @@ public final class RegisterCapabilitiesEvent extends Event implements IModBusEve
|
||||
public <T> void register(Class<T> type)
|
||||
{
|
||||
Objects.requireNonNull(type,"Attempted to register a capability with invalid type");
|
||||
String realName = type.getName().intern();
|
||||
if (capabilities.putIfAbsent(realName, new Capability<>(realName)) != null) {
|
||||
CapabilityManager.LOGGER.error(CAPABILITIES, "Cannot register capability implementation multiple times : {}", realName);
|
||||
throw new IllegalArgumentException("Cannot register a capability implementation multiple times : "+ realName);
|
||||
}
|
||||
CapabilityManager.INSTANCE.get(Type.getInternalName(type), true);
|
||||
}
|
||||
|
||||
IdentityHashMap<String, Capability<?>> getCapabilities()
|
||||
{
|
||||
return this.capabilities;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,7 +21,8 @@ package net.minecraftforge.common.model.animation;
|
||||
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.CapabilityInject;
|
||||
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||
import net.minecraftforge.common.capabilities.CapabilityToken;
|
||||
import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
|
||||
import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
@@ -31,8 +32,7 @@ import javax.annotation.Nullable;
|
||||
|
||||
public class CapabilityAnimation
|
||||
{
|
||||
@CapabilityInject(IAnimationStateMachine.class)
|
||||
public static Capability<IAnimationStateMachine> ANIMATION_CAPABILITY = null;
|
||||
public static Capability<IAnimationStateMachine> ANIMATION_CAPABILITY = CapabilityManager.get(new CapabilityToken<>(){});
|
||||
|
||||
public static void register(RegisterCapabilitiesEvent event)
|
||||
{
|
||||
|
||||
@@ -23,8 +23,7 @@ import net.minecraftforge.common.capabilities.*;
|
||||
|
||||
public class CapabilityEnergy
|
||||
{
|
||||
@CapabilityInject(IEnergyStorage.class)
|
||||
public static Capability<IEnergyStorage> ENERGY = null;
|
||||
public static final Capability<IEnergyStorage> ENERGY = CapabilityManager.get(new CapabilityToken<>(){});;
|
||||
|
||||
public static void register(RegisterCapabilitiesEvent event)
|
||||
{
|
||||
|
||||
@@ -20,15 +20,14 @@
|
||||
package net.minecraftforge.fluids.capability;
|
||||
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.CapabilityInject;
|
||||
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||
import net.minecraftforge.common.capabilities.CapabilityToken;
|
||||
import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
|
||||
|
||||
public class CapabilityFluidHandler
|
||||
{
|
||||
@CapabilityInject(IFluidHandler.class)
|
||||
public static Capability<IFluidHandler> FLUID_HANDLER_CAPABILITY = null;
|
||||
@CapabilityInject(IFluidHandlerItem.class)
|
||||
public static Capability<IFluidHandlerItem> FLUID_HANDLER_ITEM_CAPABILITY = null;
|
||||
public static Capability<IFluidHandler> FLUID_HANDLER_CAPABILITY = CapabilityManager.get(new CapabilityToken<>(){});
|
||||
public static Capability<IFluidHandlerItem> FLUID_HANDLER_ITEM_CAPABILITY = CapabilityManager.get(new CapabilityToken<>(){});
|
||||
|
||||
public static void register(RegisterCapabilitiesEvent event)
|
||||
{
|
||||
|
||||
@@ -20,13 +20,13 @@
|
||||
package net.minecraftforge.items;
|
||||
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.CapabilityInject;
|
||||
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||
import net.minecraftforge.common.capabilities.CapabilityToken;
|
||||
import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
|
||||
|
||||
public class CapabilityItemHandler
|
||||
{
|
||||
@CapabilityInject(IItemHandler.class)
|
||||
public static Capability<IItemHandler> ITEM_HANDLER_CAPABILITY = null;
|
||||
public static final Capability<IItemHandler> ITEM_HANDLER_CAPABILITY = CapabilityManager.get(new CapabilityToken<>(){});
|
||||
|
||||
public static void register(RegisterCapabilitiesEvent event)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user