Allow @Linked on fields

The value of a @Linked field of a linked class is linked like a linked class
itself, e.g. registered as listener, and unlinked again on unlink. The field
has to be initialised when linking. HullHiderCommand and TPSSystem use it
instead of registering their listeners by hand.
This commit is contained in:
2026-10-31 15:43:35 +01:00
parent a9fbb4cd8d
commit abf44b2306
4 changed files with 44 additions and 4 deletions
@@ -28,6 +28,7 @@ import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -38,6 +39,7 @@ public abstract class AbstractLinker<T> {
protected final T plugin;
private Map<Class<?>, Object> instances = new HashMap<>();
private final List<Object> fieldInstances = new ArrayList<>();
protected AbstractLinker(@NonNull T plugin) {
this.plugin = plugin;
@@ -90,6 +92,7 @@ public abstract class AbstractLinker<T> {
((Enable) any).enable();
}
linkObject(any);
linkFields(any);
});
} catch (SecurityException e) {
Throwable cause = e.getCause();
@@ -119,6 +122,31 @@ public abstract class AbstractLinker<T> {
}
}
private void linkFields(Object any) {
for (Field field : any.getClass().getDeclaredFields()) {
if (field.getAnnotation(Linked.class) == null) continue;
Object value;
try {
field.setAccessible(true);
value = field.get(Modifier.isStatic(field.getModifiers()) ? null : any);
} catch (IllegalAccessException e) {
throw new SecurityException(e.getMessage(), e);
}
if (value == null) {
String message = "Linked field " + any.getClass().getName() + "." + field.getName() + " is not initialised";
throw new SecurityException(message, new IllegalStateException(message));
}
fieldInstances.add(value);
if (value instanceof Enable) {
((Enable) value).enable();
}
linkObject(value);
}
}
public final void unlink() {
instances.forEach((aClass, any) -> {
unlinkObject(any);
@@ -126,6 +154,12 @@ public abstract class AbstractLinker<T> {
((Disable) any).disable();
}
});
fieldInstances.forEach(any -> {
unlinkObject(any);
if (any instanceof Disable) {
((Disable) any).disable();
}
});
}
public final void addLinkableInstance(@NonNull Object instance) {
@@ -26,8 +26,12 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* On a class: the class is instantiated and linked.
* On a field of a linked class: the (already initialised) value of the field is linked as well, e.g. registered as listener.
*/
@IndexAnnotated
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface Linked {
}