[1.13] Make Caps, TESR, Entity renderers, and keybinds thread safe to call during parallel init (#5359)
This commit is contained in:
parent
2c8ab76240
commit
fa939a890c
4 changed files with 49 additions and 40 deletions
|
@ -28,17 +28,23 @@ import java.lang.annotation.*;
|
|||
* of 'Capability'
|
||||
*
|
||||
* Example:
|
||||
* @CapabilityInject(IExampleCapability.class)
|
||||
* <pre>
|
||||
* {@literal @}CapabilityInject(IExampleCapability.class)
|
||||
* private static final Capability<IExampleCapability> TEST_CAP = null;
|
||||
* </pre>
|
||||
*
|
||||
* When placed on a METHOD, the method will be invoked once the
|
||||
* capability is registered. This allows you to have a 'enable features'
|
||||
* callback. It MUST have one parameter of type 'Capability;
|
||||
*
|
||||
* Example:
|
||||
* @CapabilityInject(IExampleCapability.class)
|
||||
* <pre>
|
||||
* {@literal @}CapabilityInject(IExampleCapability.class)
|
||||
* private static void capRegistered(Capability<IExampleCapability> cap) {}
|
||||
* </pre>
|
||||
*
|
||||
* <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.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
|
|
|
@ -27,6 +27,7 @@ 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 java.util.concurrent.Callable;
|
||||
|
||||
|
@ -50,6 +51,7 @@ public enum CapabilityManager
|
|||
* 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 method is safe to call during parallel mod loading.
|
||||
*
|
||||
* @param type The Interface to be registered
|
||||
* @param storage A default implementation of the storage handler.
|
||||
|
@ -61,20 +63,25 @@ public enum CapabilityManager
|
|||
Objects.requireNonNull(storage,"Attempted to register a capability with no storage implementation");
|
||||
Objects.requireNonNull(factory,"Attempted to register a capability with no default implementation factory");
|
||||
String realName = type.getName().intern();
|
||||
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);
|
||||
}
|
||||
|
||||
Capability<T> cap = new Capability<>(realName, storage, factory);
|
||||
cap = new Capability<>(realName, storage, factory);
|
||||
providers.put(realName, cap);
|
||||
}
|
||||
|
||||
callbacks.getOrDefault(realName, Collections.emptyList()).forEach(func -> func.apply(cap));
|
||||
}
|
||||
|
||||
// INTERNAL
|
||||
private IdentityHashMap<String, Capability<?>> providers = new IdentityHashMap<>();
|
||||
private IdentityHashMap<String, List<Function<Capability<?>, Object>>> callbacks = new IdentityHashMap<>();
|
||||
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> capabilities = data.stream()
|
||||
|
@ -82,10 +89,12 @@ public enum CapabilityManager
|
|||
.flatMap(Collection::stream)
|
||||
.filter(a -> CAP_INJECT.equals(a.getAnnotationType()))
|
||||
.collect(Collectors.toList());
|
||||
capabilities.forEach(this::attachCapabilityToMethod);
|
||||
final IdentityHashMap<String, List<Function<Capability<?>, Object>>> m = new IdentityHashMap<>();
|
||||
capabilities.forEach(entry -> attachCapabilityToMethod(m, entry));
|
||||
callbacks = m;
|
||||
}
|
||||
|
||||
private void attachCapabilityToMethod(ModFileScanData.AnnotationData entry)
|
||||
private static void attachCapabilityToMethod(Map<String, List<Function<Capability<?>, Object>>> cbs, ModFileScanData.AnnotationData entry)
|
||||
{
|
||||
final String targetClass = entry.getClassType().getClassName();
|
||||
final String targetName = entry.getMemberName();
|
||||
|
@ -97,7 +106,7 @@ public enum CapabilityManager
|
|||
}
|
||||
final String capabilityName = type.getInternalName().replace('/', '.').intern();
|
||||
|
||||
List<Function<Capability<?>, Object>> list = callbacks.computeIfAbsent(capabilityName, k -> new ArrayList<>());
|
||||
List<Function<Capability<?>, Object>> list = cbs.computeIfAbsent(capabilityName, k -> new ArrayList<>());
|
||||
|
||||
if (entry.getMemberName().indexOf('(') > 0)
|
||||
{
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
package net.minecraftforge.fml.client.registry;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
@ -29,35 +28,31 @@ import net.minecraft.client.Minecraft;
|
|||
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
|
||||
import net.minecraft.client.settings.KeyBinding;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraftforge.fml.common.registry.GameRegistry;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ClientRegistry
|
||||
{
|
||||
private static Map<Class<? extends Entity>, ResourceLocation> entityShaderMap = Maps.newHashMap();
|
||||
private static Map<Class<? extends Entity>, ResourceLocation> entityShaderMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
*
|
||||
* Utility method for registering a tile entity and it's renderer at once - generally you should register them separately
|
||||
*
|
||||
* @param tileEntityClass
|
||||
* @param id
|
||||
* @param specialRenderer
|
||||
* / TODO GameRegistry
|
||||
public static <T extends TileEntity> void registerTileEntity(Class<T> tileEntityClass, String id, TileEntityRenderer<? super T> specialRenderer)
|
||||
{
|
||||
GameRegistry.registerTileEntity(tileEntityClass, id);
|
||||
bindTileEntitySpecialRenderer(tileEntityClass, specialRenderer);
|
||||
}
|
||||
* Registers a Tile Entity renderer.
|
||||
* Call this during {@link net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent}.
|
||||
* This method is safe to call during parallel mod loading.
|
||||
*/
|
||||
public static <T extends TileEntity> void bindTileEntitySpecialRenderer(Class<T> tileEntityClass, TileEntityRenderer<? super T> specialRenderer)
|
||||
public static synchronized <T extends TileEntity> void bindTileEntitySpecialRenderer(Class<T> tileEntityClass, TileEntityRenderer<? super T> specialRenderer)
|
||||
{
|
||||
TileEntityRendererDispatcher.instance.renderers.put(tileEntityClass, specialRenderer);
|
||||
specialRenderer.setRendererDispatcher(TileEntityRendererDispatcher.instance);
|
||||
}
|
||||
|
||||
public static void registerKeyBinding(KeyBinding key)
|
||||
/**
|
||||
* Registers a KeyBinding.
|
||||
* Call this during {@link net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent}.
|
||||
* This method is safe to call during parallel mod loading.
|
||||
*/
|
||||
public static synchronized void registerKeyBinding(KeyBinding key)
|
||||
{
|
||||
Minecraft.getInstance().gameSettings.keyBindings = ArrayUtils.add(Minecraft.getInstance().gameSettings.keyBindings, key);
|
||||
}
|
||||
|
@ -65,9 +60,8 @@ public class ClientRegistry
|
|||
/**
|
||||
* Register a shader for an entity. This shader gets activated when a spectator begins spectating an entity.
|
||||
* Vanilla examples of this are the green effect for creepers and the invert effect for endermen.
|
||||
*
|
||||
* @param entityClass
|
||||
* @param shader
|
||||
* Call this during {@link net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent}.
|
||||
* This method is safe to call during parallel mod loading.
|
||||
*/
|
||||
public static void registerEntityShader(Class<? extends Entity> entityClass, ResourceLocation shader)
|
||||
{
|
||||
|
|
|
@ -20,23 +20,23 @@
|
|||
package net.minecraftforge.fml.client.registry;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.minecraft.client.renderer.entity.Render;
|
||||
import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraft.entity.Entity;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
public class RenderingRegistry
|
||||
{
|
||||
private static final RenderingRegistry INSTANCE = new RenderingRegistry();
|
||||
|
||||
private Map<Class<? extends Entity>, IRenderFactory<? extends Entity>> entityRenderers = Maps.newHashMap();
|
||||
private final Map<Class<? extends Entity>, IRenderFactory<? extends Entity>> entityRenderers = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Register an entity rendering handler. This will, after mod initialization, be inserted into the main
|
||||
* render map for entities.
|
||||
* Call this during Preinitialization phase.
|
||||
* Call this during {@link net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent}.
|
||||
* This method is safe to call during parallel mod loading.
|
||||
*/
|
||||
public static <T extends Entity> void registerEntityRenderingHandler(Class<T> entityClass, IRenderFactory<? super T> renderFactory)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue