Skip to content

Commit

Permalink
chore: fix registrar type safety
Browse files Browse the repository at this point in the history
  • Loading branch information
CallMeEchoCodes committed Sep 5, 2024
1 parent 88e4fec commit 3946b39
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

import org.objectweb.asm.tree.AnnotationNode;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.Optional;

/**
* A bunch of utilities to reduce boilerplate reflection code.
Expand Down Expand Up @@ -125,6 +127,10 @@ public static void setFieldValue(Object instance, Field field, Object value) {
}
}

public static <T extends Annotation> Optional<T> getAnnotation(Field field, Class<T> annotationClass) {
return Optional.ofNullable(field.getAnnotation(annotationClass));
}

@SuppressWarnings("unchecked")
public static <T> T getAnnotationValue(AnnotationNode annotation, String key, T defaultValue) {
if (annotation == null || annotation.values == null) return defaultValue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,9 @@ public interface BlockEntityTypeRegistrar extends MinecraftRegistrar<BlockEntity
default Registry<BlockEntityType<?>> getRegistry() {
return Registries.BLOCK_ENTITY_TYPE;
}

@Override
default Class<BlockEntityType<?>> getObjectType() {
return Registrar.fixGenerics(BlockEntityType.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ default Registry<Block> getRegistry() {
return Registries.BLOCK;
}

@Override
default Class<Block> getObjectType() {
return Block.class;
}

@Override
default void register(String name, String namespace, Block object, Field field) {
Registry.register(getRegistry(), Identifier.of(namespace, name), object);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,9 @@ public interface EntityTypeRegistrar extends MinecraftRegistrar<EntityType<?>> {
default Registry<EntityType<?>> getRegistry() {
return Registries.ENTITY_TYPE;
}

@Override
default Class<EntityType<?>> getObjectType() {
return Registrar.fixGenerics(EntityType.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,9 @@ public interface ItemRegistrar extends MinecraftRegistrar<Item> {
default Registry<Item> getRegistry() {
return Registries.ITEM;
}

@Override
default Class<Item> getObjectType() {
return Item.class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/**
* Automatically run a function on each object using reflection.
Expand All @@ -32,6 +31,15 @@ static <T> void process(Class<? extends Registrar<T>> clazz, String namespace) {
registrar.init(namespace);
}

/**
* Workaround for Java's type erasure.
* Use this if {@link T} has a generic type of ?.
*/
@SuppressWarnings("unchecked")
static <T> Class<T> fixGenerics(Class<?> clazz) {
return (Class<T>) clazz;
}

/**
* Register an object to the registry.
*
Expand All @@ -42,6 +50,14 @@ static <T> void process(Class<? extends Registrar<T>> clazz, String namespace) {
*/
void register(String name, String namespace, T object, Field field);

/**
* Get the type of object to register.
* If {@link T} has a generic type of ?, use {@link Registrar#fixGenerics(Class)} to force the correct type.
*
* @return The type of object to register
*/
Class<T> getObjectType();

/**
* Initialize the registrar and register all objects.
* Do not call this method directly, use {@link Registrar#process(Class, String)} instead.
Expand All @@ -50,20 +66,15 @@ static <T> void process(Class<? extends Registrar<T>> clazz, String namespace) {
*/
@ApiStatus.Internal
default void init(String namespace) {
for (Field field : this.getClass().getDeclaredFields()) {
if (!Modifier.isStatic(field.getModifiers()) || field.isAnnotationPresent(Ignore.class)) continue;
ReflectionHelper.forEachStaticField(this.getClass(), getObjectType(), (value, name, field) -> {
if (field.isAnnotationPresent(Ignore.class)) return;

T value = ReflectionHelper.getFieldValue(field);
if (value == null || !field.getType().isAssignableFrom(value.getClass())) continue;

String name = field.getName().toLowerCase();
if (field.isAnnotationPresent(Name.class)) {
Name nameAnnotation = field.getAnnotation(Name.class);
name = nameAnnotation.value();
}
String objectName = ReflectionHelper.getAnnotation(field, Name.class)
.map(Name::value)
.orElseGet(name::toLowerCase);

register(name, namespace, value, field);
}
register(objectName, namespace, value, field);
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.spiritstudios.specter.api.registry.registration;

import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.sound.SoundEvent;
import net.minecraft.util.Identifier;
Expand All @@ -15,4 +16,14 @@ public interface SoundEventRegistrar extends MinecraftRegistrar<SoundEvent> {
default void register(String name, String namespace, SoundEvent object, Field field) {
Registry.register(getRegistry(), Identifier.of(namespace, name.replace('-', '.')), object);
}

@Override
default Registry<SoundEvent> getRegistry() {
return Registries.SOUND_EVENT;
}

@Override
default Class<SoundEvent> getObjectType() {
return SoundEvent.class;
}
}

0 comments on commit 3946b39

Please sign in to comment.