diff --git a/src/main/java/net/minecraftforge/common/ForgeMod.java b/src/main/java/net/minecraftforge/common/ForgeMod.java index d50376d4ed..80ad5ced44 100644 --- a/src/main/java/net/minecraftforge/common/ForgeMod.java +++ b/src/main/java/net/minecraftforge/common/ForgeMod.java @@ -33,6 +33,7 @@ import net.minecraft.world.level.storage.WorldData; import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.ForgeHooksClient; +import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent; import net.minecraftforge.common.data.ExistingFileHelper; import net.minecraftforge.common.data.ForgeFluidTagsProvider; import net.minecraftforge.common.loot.GlobalLootModifierSerializer; @@ -150,6 +151,7 @@ public class ForgeMod implements WorldPersistenceHooks.WorldPersistenceHook WorldPersistenceHooks.addHook(this); WorldPersistenceHooks.addHook(new FMLWorldPersistenceHook()); final IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); + modEventBus.addListener(this::registerCapabilities); modEventBus.addListener(this::preInit); modEventBus.addListener(this::gatherData); modEventBus.addListener(this::loadComplete); @@ -171,13 +173,16 @@ public class ForgeMod implements WorldPersistenceHooks.WorldPersistenceHook BiomeDictionary.init(); } + public void registerCapabilities(RegisterCapabilitiesEvent event) + { + CapabilityItemHandler.register(event); + CapabilityFluidHandler.register(event); + CapabilityAnimation.register(event); + CapabilityEnergy.register(event); + } + public void preInit(FMLCommonSetupEvent evt) { - CapabilityItemHandler.register(); - CapabilityFluidHandler.register(); - CapabilityAnimation.register(); - CapabilityEnergy.register(); - VersionChecker.startVersionCheck(); registerArgumentTypes(); diff --git a/src/main/java/net/minecraftforge/common/capabilities/CapabilityManager.java b/src/main/java/net/minecraftforge/common/capabilities/CapabilityManager.java index 538dd6b39d..bd82c2276c 100644 --- a/src/main/java/net/minecraftforge/common/capabilities/CapabilityManager.java +++ b/src/main/java/net/minecraftforge/common/capabilities/CapabilityManager.java @@ -24,12 +24,12 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import net.minecraftforge.fml.ModLoader; import net.minecraftforge.forgespi.language.ModFileScanData; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -43,7 +43,7 @@ import static net.minecraftforge.fml.Logging.CAPABILITIES; public enum CapabilityManager { INSTANCE; - private static final Logger LOGGER = LogManager.getLogger(); + static final Logger LOGGER = LogManager.getLogger(); private static final Type CAP_INJECT = Type.getType(CapabilityInject.class); /** @@ -53,7 +53,9 @@ public enum CapabilityManager * This method is safe to call during parallel mod loading. * * @param type The class type to be registered + * @deprecated use {@link RegisterCapabilitiesEvent} */ + @Deprecated(forRemoval = true) public void register(Class type) { Objects.requireNonNull(type,"Attempted to register a capability with invalid type"); @@ -71,7 +73,7 @@ public enum CapabilityManager providers.put(realName, cap); } - callbacks.getOrDefault(realName, Collections.emptyList()).forEach(func -> func.apply(cap)); + fireCallbacks(this.callbacks, List.of(cap)); } // INTERNAL @@ -79,21 +81,36 @@ public enum CapabilityManager private volatile IdentityHashMap, Object>>> callbacks; public void injectCapabilities(List data) { - final List capabilities = data.stream() + final List elementsToInject = data.stream() .map(ModFileScanData::getAnnotations) .flatMap(Collection::stream) .filter(a -> CAP_INJECT.equals(a.annotationType())) .collect(Collectors.toList()); - final IdentityHashMap, Object>>> m = new IdentityHashMap<>(); - capabilities.forEach(entry -> attachCapabilityToMethod(m, entry)); - callbacks = m; + final IdentityHashMap, Object>>> callbacks = new IdentityHashMap<>(); + elementsToInject.forEach(entry -> gatherCallbacks(callbacks, entry)); + + 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 attachCapabilityToMethod(Map, Object>>> cbs, ModFileScanData.AnnotationData entry) + private static void fireCallbacks(Map, Object>>> callbacks, Iterable> caps) { - final String targetClass = entry.clazz().getClassName(); - final String targetName = entry.memberName(); - Type type = (Type)entry.annotationData().get("value"); + for (var cap : caps) + { + callbacks.getOrDefault(cap.getName(), List.of()).forEach(f -> f.apply(cap)); + } + } + + private static void gatherCallbacks(Map, Object>>> callbacks, ModFileScanData.AnnotationData annotationData) + { + final String targetClass = annotationData.clazz().getClassName(); + final String targetName = annotationData.memberName(); + Type type = (Type)annotationData.annotationData().get("value"); if (type == null) { LOGGER.warn(CAPABILITIES,"Unable to inject capability at {}.{} (Invalid Annotation)", targetClass, targetName); @@ -101,9 +118,9 @@ public enum CapabilityManager } final String capabilityName = type.getInternalName().replace('/', '.').intern(); - List, Object>> list = cbs.computeIfAbsent(capabilityName, k -> new ArrayList<>()); + List, Object>> list = callbacks.computeIfAbsent(capabilityName, k -> new ArrayList<>()); - if (entry.memberName().indexOf('(') > 0) + if (annotationData.memberName().indexOf('(') > 0) { list.add(input -> { try diff --git a/src/main/java/net/minecraftforge/common/capabilities/RegisterCapabilitiesEvent.java b/src/main/java/net/minecraftforge/common/capabilities/RegisterCapabilitiesEvent.java new file mode 100644 index 0000000000..ae7fcc604e --- /dev/null +++ b/src/main/java/net/minecraftforge/common/capabilities/RegisterCapabilitiesEvent.java @@ -0,0 +1,60 @@ +/* + * 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; + +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> capabilities = new IdentityHashMap<>(); + + /** + * 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. + * + * @param type The type to be registered + */ + public void register(Class 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); + } + } + + IdentityHashMap> getCapabilities() + { + return this.capabilities; + } + +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/common/model/animation/CapabilityAnimation.java b/src/main/java/net/minecraftforge/common/model/animation/CapabilityAnimation.java index 2b723cd4f7..b86d806ed8 100644 --- a/src/main/java/net/minecraftforge/common/model/animation/CapabilityAnimation.java +++ b/src/main/java/net/minecraftforge/common/model/animation/CapabilityAnimation.java @@ -19,11 +19,10 @@ package net.minecraftforge.common.model.animation; -import net.minecraft.nbt.Tag; 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.RegisterCapabilitiesEvent; import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraftforge.common.util.LazyOptional; @@ -35,9 +34,9 @@ public class CapabilityAnimation @CapabilityInject(IAnimationStateMachine.class) public static Capability ANIMATION_CAPABILITY = null; - public static void register() + public static void register(RegisterCapabilitiesEvent event) { - CapabilityManager.INSTANCE.register(IAnimationStateMachine.class); + event.register(IAnimationStateMachine.class); } public static class DefaultItemAnimationCapabilityProvider implements ICapabilityProvider diff --git a/src/main/java/net/minecraftforge/energy/CapabilityEnergy.java b/src/main/java/net/minecraftforge/energy/CapabilityEnergy.java index e3408764e0..214abfd30f 100644 --- a/src/main/java/net/minecraftforge/energy/CapabilityEnergy.java +++ b/src/main/java/net/minecraftforge/energy/CapabilityEnergy.java @@ -26,8 +26,8 @@ public class CapabilityEnergy @CapabilityInject(IEnergyStorage.class) public static Capability ENERGY = null; - public static void register() + public static void register(RegisterCapabilitiesEvent event) { - CapabilityManager.INSTANCE.register(IEnergyStorage.class); + event.register(IEnergyStorage.class); } } diff --git a/src/main/java/net/minecraftforge/fluids/capability/CapabilityFluidHandler.java b/src/main/java/net/minecraftforge/fluids/capability/CapabilityFluidHandler.java index 4e926761a1..3963c9e6c7 100644 --- a/src/main/java/net/minecraftforge/fluids/capability/CapabilityFluidHandler.java +++ b/src/main/java/net/minecraftforge/fluids/capability/CapabilityFluidHandler.java @@ -21,7 +21,7 @@ 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.RegisterCapabilitiesEvent; public class CapabilityFluidHandler { @@ -30,10 +30,10 @@ public class CapabilityFluidHandler @CapabilityInject(IFluidHandlerItem.class) public static Capability FLUID_HANDLER_ITEM_CAPABILITY = null; - public static void register() + public static void register(RegisterCapabilitiesEvent event) { - CapabilityManager.INSTANCE.register(IFluidHandler.class); + event.register(IFluidHandler.class); - CapabilityManager.INSTANCE.register(IFluidHandlerItem.class); + event.register(IFluidHandlerItem.class); } } diff --git a/src/main/java/net/minecraftforge/items/CapabilityItemHandler.java b/src/main/java/net/minecraftforge/items/CapabilityItemHandler.java index df27b70959..4229dc9079 100644 --- a/src/main/java/net/minecraftforge/items/CapabilityItemHandler.java +++ b/src/main/java/net/minecraftforge/items/CapabilityItemHandler.java @@ -21,16 +21,16 @@ 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.RegisterCapabilitiesEvent; public class CapabilityItemHandler { @CapabilityInject(IItemHandler.class) public static Capability ITEM_HANDLER_CAPABILITY = null; - public static void register() + public static void register(RegisterCapabilitiesEvent event) { - CapabilityManager.INSTANCE.register(IItemHandler.class); + event.register(IItemHandler.class); } }