forked from SteamWar/SteamWar
Merge pull request 'Hider core' (#519) from HiderCore into main
Reviewed-on: SteamWar/SteamWar#519 Reviewed-by: FessmLP <72+fessmlp@noreply.localhost>
This commit is contained in:
@@ -22,12 +22,18 @@ package de.steamwar.linkage;
|
||||
import de.steamwar.linkage.api.Disable;
|
||||
import de.steamwar.linkage.api.Enable;
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.experimental.StandardException;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -38,6 +44,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 +97,7 @@ public abstract class AbstractLinker<T> {
|
||||
((Enable) any).enable();
|
||||
}
|
||||
linkObject(any);
|
||||
linkFields(any);
|
||||
});
|
||||
} catch (SecurityException e) {
|
||||
Throwable cause = e.getCause();
|
||||
@@ -117,6 +125,70 @@ public abstract class AbstractLinker<T> {
|
||||
Throwable cause = e.getCause();
|
||||
throw new LinkException(cause.getMessage(), cause);
|
||||
}
|
||||
|
||||
// After the injection, as timers may run immediately on another thread
|
||||
for (Object any : instances.values()) {
|
||||
scheduleTimers(any);
|
||||
}
|
||||
for (Object any : fieldInstances) {
|
||||
scheduleTimers(any);
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleTimers(Object any) throws LinkException {
|
||||
for (Method method : any.getClass().getDeclaredMethods()) {
|
||||
LinkedTimer timer = method.getAnnotation(LinkedTimer.class);
|
||||
if (timer == null) continue;
|
||||
|
||||
String name = any.getClass().getName() + "." + method.getName();
|
||||
if (method.getParameterCount() != 0) {
|
||||
throw new LinkException("Linked timer " + name + " must not have parameters");
|
||||
}
|
||||
|
||||
MethodHandle handle;
|
||||
try {
|
||||
handle = MethodHandles.privateLookupIn(method.getDeclaringClass(), MethodHandles.lookup()).unreflect(method);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new LinkException("Could not access linked timer " + name, e);
|
||||
}
|
||||
if (!Modifier.isStatic(method.getModifiers())) {
|
||||
handle = handle.bindTo(any);
|
||||
}
|
||||
// Discards the return value
|
||||
MethodHandle timerHandle = handle.asType(MethodType.methodType(void.class));
|
||||
|
||||
scheduleTimer(() -> runTimer(timerHandle), timer.delay(), timer.period(), timer.unit());
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private static void runTimer(MethodHandle timer) {
|
||||
timer.invokeExact();
|
||||
}
|
||||
|
||||
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() {
|
||||
@@ -126,6 +198,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) {
|
||||
@@ -152,6 +230,14 @@ public abstract class AbstractLinker<T> {
|
||||
protected void linkObject(Object any) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the runnable repeatedly for a {@link LinkedTimer}.
|
||||
*
|
||||
* @param delay time until the first run, in unit
|
||||
* @param period time between two runs, in unit
|
||||
*/
|
||||
protected abstract void scheduleTimer(Runnable runnable, long delay, long period, LinkedTimer.Unit unit);
|
||||
|
||||
/**
|
||||
* There is no need in calling {@link Disable#disable()} ()} by this method.
|
||||
*/
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.linkage;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Runs the annotated method of a linked class (or of the value of a {@link Linked} field) repeatedly.
|
||||
* The method must not have parameters, its return value is ignored. It may be static.
|
||||
* The timer is not stopped on unlink, as the server stops anyway.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface LinkedTimer {
|
||||
|
||||
/**
|
||||
* Time until the first run, in {@link #unit()}.
|
||||
*/
|
||||
long delay();
|
||||
|
||||
/**
|
||||
* Time between two runs, in {@link #unit()}.
|
||||
*/
|
||||
long period();
|
||||
|
||||
Unit unit() default Unit.TICKS;
|
||||
|
||||
@AllArgsConstructor
|
||||
enum Unit {
|
||||
/**
|
||||
* 1 tick = 50 ms
|
||||
*/
|
||||
TICKS(50, TimeUnit.MILLISECONDS),
|
||||
MILLISECONDS(1, TimeUnit.MILLISECONDS),
|
||||
SECONDS(1, TimeUnit.SECONDS),
|
||||
MINUTES(1, TimeUnit.MINUTES),
|
||||
HOURS(1, TimeUnit.HOURS);
|
||||
|
||||
private final long factor;
|
||||
@Getter
|
||||
private final TimeUnit timeUnit;
|
||||
|
||||
/**
|
||||
* @return the value in ticks, rounded down
|
||||
*/
|
||||
public long toTicks(long value) {
|
||||
return timeUnit.toMillis(value * factor) / 50;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the value in {@link #getTimeUnit()}
|
||||
*/
|
||||
public long toTimeUnit(long value) {
|
||||
return value * factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user