Skip to content

Commit

Permalink
Reimplement the player models registry
Browse files Browse the repository at this point in the history
  • Loading branch information
Sollace committed Sep 24, 2023
1 parent 965892e commit 11ffc4f
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@

import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.client.render.block.entity.BlockEntityRenderer;
import net.minecraft.client.render.block.entity.BlockEntityRendererFactory;
import net.minecraft.client.render.entity.EntityRenderer;
import net.minecraft.client.render.entity.EntityRendererFactory;
import net.minecraft.client.render.entity.PlayerEntityRenderer;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.util.Identifier;

import java.util.function.Function;
import java.util.function.Predicate;

/**
* Registry for adding entity and player renderers to the game.
Expand All @@ -19,8 +22,20 @@
public interface EntityRendererRegistry {
/**
* Adds a custom player renderer.
*
* @deprecated Use the more flexible predicate version instead.
*/
<T extends PlayerEntityRenderer> void registerPlayerRenderer(String skinType, Function<EntityRendererFactory.Context, T> constructor);
@Deprecated
default <T extends PlayerEntityRenderer> void registerPlayerRenderer(String skinType, Function<EntityRendererFactory.Context, T> constructor) {
registerPlayerRenderer(new Identifier(skinType), player -> player.method_52814().model().getName().equalsIgnoreCase(skinType), constructor);
}
/**
* Adds a custom player renderer.
*
* @param playerPredicate Predicate to determine which players this renderer should be used for.
* @param constructor The renderer factory
*/
<T extends PlayerEntityRenderer> void registerPlayerRenderer(Identifier skinType, Predicate<AbstractClientPlayerEntity> playerPredicate, Function<EntityRendererFactory.Context, T> constructor);

/**
* Adds a custom entity renderer.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@

import com.minelittlepony.mson.impl.key.ReflectedModelKey;

import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;

@SuppressWarnings("deprecation")
public interface InstanceCreator<T> {
InstanceCreator<ModelPart> DEFAULT = InstanceCreator.ofFunction(ModelPart.class, Function.identity());

Expand All @@ -28,15 +28,15 @@ public static <T> InstanceCreator<T> ofType(Class<T> type) {
}

public static <T> InstanceCreator<T> ofFunction(Class<T> type, Function<ModelPart, T> function) {
return new ReflectedModelKey<>(null, function, type);
return new ReflectedModelKey<>(Optional.empty(), Optional.of(function), type);
}

public static <T> InstanceCreator<T> ofFactory(Class<T> type, Function<ModelContext, T> factory) {
return new ReflectedModelKey<>(factory, null, type);
return new ReflectedModelKey<>(Optional.of(factory), Optional.empty(), type);
}

public static <T> InstanceCreator<T> ofSupplier(Class<T> type, Supplier<T> supplier) {
return new ReflectedModelKey<>(ctx -> supplier.get(), tree -> supplier.get(), type);
return new ReflectedModelKey<>(Optional.of(ctx -> supplier.get()), Optional.of(tree -> supplier.get()), type);
}

@Nullable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.client.render.block.entity.BlockEntityRenderer;
import net.minecraft.client.render.block.entity.BlockEntityRendererFactory;
import net.minecraft.client.render.entity.EntityRenderer;
import net.minecraft.client.render.entity.EntityRendererFactory;
import net.minecraft.client.render.entity.EntityRendererFactory.Context;
import net.minecraft.client.render.entity.PlayerEntityRenderer;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
Expand All @@ -19,28 +21,27 @@
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Predicate;

public final class PendingEntityRendererRegistry implements EntityRendererRegistry {

public final RendererList<
String,
EntityRendererFactory.Context,
PlayerEntityRenderer
> player = new RendererList<>(new Identifier("mson", "renderers/player"), EntityRendererRegistry::registerPlayerRenderer);
public final RendererList<
public final PendingRegistrations<
Identifier,
Map.Entry<Predicate<AbstractClientPlayerEntity>, Function<EntityRendererFactory.Context, ? extends PlayerEntityRenderer>>
> player = new PendingRegistrations<>(new Identifier("mson", "renderers/player"), (registry, key, entry) -> {
registry.registerPlayerRenderer(key, entry.getKey(), entry.getValue());
});
public final PendingRegistrations<
EntityType<?>,
EntityRendererFactory.Context,
EntityRenderer<?>
> entity = new RendererList<>(new Identifier("mson", "renderers/entity"), EntityRendererRegistry::registerEntityRenderer);
public final RendererList<
Function<EntityRendererFactory.Context, ? extends EntityRenderer<?>>
> entity = new PendingRegistrations<>(new Identifier("mson", "renderers/entity"), EntityRendererRegistry::registerEntityRenderer);
public final PendingRegistrations<
BlockEntityType<?>,
BlockEntityRendererFactory.Context,
BlockEntityRenderer<?>
> block = new RendererList<>(new Identifier("mson", "renderers/block"), EntityRendererRegistry::registerBlockRenderer);
Function<BlockEntityRendererFactory.Context, ? extends BlockEntityRenderer<?>>
> block = new PendingRegistrations<>(new Identifier("mson", "renderers/block"), EntityRendererRegistry::registerBlockRenderer);

@Override
public <T extends PlayerEntityRenderer> void registerPlayerRenderer(String skinType, Function<EntityRendererFactory.Context, T> constructor) {
player.register(skinType, constructor);
public <T extends PlayerEntityRenderer> void registerPlayerRenderer(Identifier skinType, Predicate<AbstractClientPlayerEntity> playerPredicate, Function<Context, T> constructor) {
player.register(skinType, Map.entry(playerPredicate, constructor));
}

@Override
Expand All @@ -59,10 +60,10 @@ void initialize() {
block.reload();
}

public class RendererList<Type, Dispatcher, Renderer> {
private final HashMap<? extends Type, ? extends Function<Dispatcher, Renderer>> entries = new HashMap<>();
public class PendingRegistrations<Key, Entry> {
private final HashMap<Key, Entry> entries = new HashMap<>();

private final RegisterAction<Type, Dispatcher, Renderer> runtimeAdd;
private final Registerable<Key, Entry> registerable;

private boolean waiting;

Expand All @@ -71,16 +72,15 @@ public class RendererList<Type, Dispatcher, Renderer> {

private final Identifier registryId;

public RendererList(Identifier registryId, RegisterAction<Type, Dispatcher, Renderer> runtimeAdd) {
public PendingRegistrations(Identifier registryId, Registerable<Key, Entry> registerable) {
this.registryId = registryId;
this.runtimeAdd = runtimeAdd;
this.registerable = registerable;
}

@SuppressWarnings("unchecked")
public <T extends Type, R extends Renderer> void register(T type, Function<Dispatcher, R> constructor) {
((Map<T, Function<Dispatcher, R>>)(Object)entries).put(type, constructor);
public void register(Key key, Entry entry) {
entries.put(key, entry);
if (runtimeRegistry != null && !waiting) {
((RegisterAction<T, Dispatcher, R>)runtimeAdd).call(runtimeRegistry, type, constructor);
registerable.register(runtimeRegistry, key, entry);
}
}

Expand All @@ -89,7 +89,7 @@ void reload() {
waiting = false;
if (runtimeRegistry != null) {
MsonImpl.LOGGER.info(delayed ? "Running delayed initialization for registry '{}'" : "Initializing registry '{}'", registryId);
entries.forEach((k, v) -> runtimeAdd.call(runtimeRegistry, k, v));
entries.forEach((k, v) -> registerable.register(runtimeRegistry, k, v));
} else {
MsonImpl.LOGGER.info("Registry '{}' queued for delayed initialization", registryId);
waiting = true;
Expand All @@ -103,9 +103,9 @@ public void publish(EntityRendererRegistry runtimeRegistry) {
reload();
}
}
}

interface RegisterAction<Type, Dispatcher, Renderer> {
void call(EntityRendererRegistry registry, Type type, Function<Dispatcher, Renderer> constructor);
interface Registerable<Key, Entry> {
void register(EntityRendererRegistry registry, Key key, Entry entry);
}
}
}
6 changes: 3 additions & 3 deletions src/main/java/com/minelittlepony/mson/impl/Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@

final class Test {
static void init() {
var RAYMAN = playerRendererFactor(Mson.getInstance().registerModel(new Identifier("mson_test", "planar_cube"), MsonPlayer::new));
var ID = new Identifier("mson_test", "planar_cube");
var RAYMAN = playerRendererFactor(Mson.getInstance().registerModel(ID, MsonPlayer::new));
//var PLANE = playerRendererFactor(Mson.getInstance().registerModel(new Identifier("mson_test", "plane"), MsonPlayer::new));

Mson.getInstance().getEntityRendererRegistry().registerPlayerRenderer("default", RAYMAN);
Mson.getInstance().getEntityRendererRegistry().registerPlayerRenderer("slim", RAYMAN);
Mson.getInstance().getEntityRendererRegistry().registerPlayerRenderer(ID, player -> true, RAYMAN);
}

static void exportVanillaModels() {
Expand Down
25 changes: 11 additions & 14 deletions src/main/java/com/minelittlepony/mson/impl/key/MethodHandles.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,42 @@

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import java.lang.invoke.MethodHandles.Lookup;

@Deprecated
final class MethodHandles {
private static final Lookup LOOKUP = java.lang.invoke.MethodHandles.lookup();

public static <Ctx, T> MethodHandle findConstructor(Class<T> owner, Class<?>... parameters) {
public static <Ctx, T> Optional<MethodHandle> findConstructor(Class<T> owner, Class<?>... parameters) {
try {
MethodType constrType = MethodType.methodType(void.class, parameters);

// privateLookupIn (since Java9) lets us access private methods and fields as if we were inside the same class.
Lookup lookup = java.lang.invoke.MethodHandles.privateLookupIn(owner, LOOKUP);
return lookup.findConstructor(owner, constrType);
} catch (Throwable e) {
throw new RuntimeException(e);
}
return Optional.ofNullable(lookup.findConstructor(owner, constrType));
} catch (Throwable e) {}
return Optional.empty();
}

public static <T> Supplier<T> createInstanceSupplier(Class<T> owner) {
final MethodHandle constr = findConstructor(owner);
return () -> {
public static <T> Optional<Supplier<T>> createInstanceSupplier(Class<T> owner) {
return findConstructor(owner).map(constr -> () -> {
try {
return (T)constr.invoke();
} catch (Throwable e) {
throw new RuntimeException(e);
}
};
});
}

public static <Ctx, T> Function<Ctx, T> createInstanceFactory(Class<T> owner, Class<Ctx> firstParam) {
final MethodHandle constr = findConstructor(owner, firstParam);
return ctx -> {
public static <Ctx, T> Optional<Function<Ctx, T>> createInstanceFactory(Class<T> owner, Class<Ctx> firstParam) {
return findConstructor(owner, firstParam).map(constr -> ctx -> {
try {
return (T)constr.invoke(ctx);
} catch (Throwable e) {
throw new RuntimeException(e);
}
};
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@
import com.minelittlepony.mson.api.ModelView;
import com.minelittlepony.mson.api.MsonModel;

import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;

@Deprecated
public record ReflectedModelKey<T> (
@Nullable Function<ModelContext, T> contextFactory,
@Nullable Function<ModelPart, T> partFactory,
Optional<Function<ModelContext, T>> contextFactory,
Optional<Function<ModelPart, T>> partFactory,
@Nullable Class<T> type) implements InstanceCreator<T> {
private static final Function<String, InstanceCreator<?>> NAME_LOOKUP = Util.memoize(className -> {
if (className.endsWith("ModelPart")) {
Expand All @@ -34,30 +33,13 @@ public record ReflectedModelKey<T> (
return InstanceCreator.ofPart();
}

Supplier<Object> supplier = null;

Function<ModelPart, Object> treeFactory = null;
Function<ModelContext, Object> contextFactory = null;

try {
supplier = MethodHandles.createInstanceSupplier(type);
} catch (Error | Exception ignored) { }

try {
treeFactory = MethodHandles.createInstanceFactory(type, ModelPart.class);
} catch (Error | Exception ignored) { }

try {
contextFactory = MethodHandles.createInstanceFactory(type, ModelContext.class);
} catch (Error | Exception ignored) { }

final Supplier<Object> supplierCopy = supplier;
var key = new ReflectedModelKey<Object>(
contextFactory == null ? supplierCopy == null ? null : ctx -> supplierCopy.get() : contextFactory,
treeFactory == null ? supplierCopy == null ? null : tree -> supplierCopy.get() : treeFactory,
var supplier = MethodHandles.createInstanceSupplier(type);
var key = new ReflectedModelKey<>(
MethodHandles.createInstanceFactory(type, ModelContext.class).or(() -> supplier.map(c -> ctx -> c.get())),
MethodHandles.createInstanceFactory(type, ModelPart.class).or(() -> supplier.map(c -> tree -> c.get())),
type
);
if (key.contextFactory == null && key.partFactory == null) {
if (key.contextFactory().isEmpty() && key.partFactory().isEmpty()) {
throw new RuntimeException("Could not locate constructors for type " + type);
}
return key;
Expand All @@ -80,21 +62,18 @@ public boolean isCompatible(Class<?> toType) {

@Override
public T createInstance(ModelContext context) {
if (contextFactory == null) {
if (partFactory != null) {
return initInstance(partFactory.apply(context.toTree()), context);
}
throw new JsonParseException("The generated lamba cannot be used with a model context");
}
return contextFactory.apply(context);
return contextFactory.map(factory -> factory.apply(context))
.orElseGet(() -> {
return partFactory.map(factory -> initInstance(factory.apply(context.toTree()), context))
.orElseThrow(() -> new JsonParseException("The generated lamba cannot be used with a model context"));
});
}

@Override
public T createInstance(ModelContext context, Function<ModelContext, ModelPart> converter) {
if (partFactory != null) {
return initInstance(partFactory.apply(converter.apply(context)), context);
}
return createInstance(context);
return partFactory
.map(factory -> initInstance(factory.apply(converter.apply(context)), context))
.orElseGet(() -> createInstance(context));
}

private T initInstance(T instance, ModelView view) {
Expand Down
Loading

0 comments on commit 11ffc4f

Please sign in to comment.