forked from SteamWar/SteamWar
Add @LinkedTimer
A method of a linked class annotated with @LinkedTimer(delay, period) is run repeatedly by the linker, on Spigot and Velocity. The method must not have parameters, its return value is ignored. It is called through a MethodHandle bound once when linking instead of reflection; LambdaMetafactory cannot be used as the linker and the linked classes are in different class loaders. The unit defaults to ticks and can be set to milliseconds up to hours; the Unit enum converts it to ticks for Spigot and to a TimeUnit for Velocity. HullHiderCommand uses it for its tick task.
This commit is contained in:
+2
-2
@@ -32,6 +32,7 @@ import de.steamwar.hullhider.HullRegion;
|
||||
import de.steamwar.hullhider.Viewers;
|
||||
import de.steamwar.linkage.Linked;
|
||||
import de.steamwar.linkage.LinkedInstance;
|
||||
import de.steamwar.linkage.LinkedTimer;
|
||||
import de.steamwar.techhider.ObfuscationConfig;
|
||||
import net.md_5.bungee.api.ChatMessageType;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -55,8 +56,6 @@ public class HullHiderCommand extends SWCommand implements Listener {
|
||||
|
||||
public HullHiderCommand() {
|
||||
super("hullhider");
|
||||
|
||||
Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), this::onTick, 1, 1);
|
||||
}
|
||||
|
||||
@Linked
|
||||
@@ -140,6 +139,7 @@ public class HullHiderCommand extends SWCommand implements Listener {
|
||||
});
|
||||
}
|
||||
|
||||
@LinkedTimer(delay = 1, period = 1)
|
||||
private void onTick() {
|
||||
if (hidden.isEmpty()) return;
|
||||
|
||||
|
||||
@@ -22,11 +22,16 @@ 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;
|
||||
@@ -120,6 +125,45 @@ 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) {
|
||||
@@ -186,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.
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,11 @@ public class SpigotLinker extends AbstractLinker<JavaPlugin> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void scheduleTimer(Runnable runnable, long delay, long period, LinkedTimer.Unit unit) {
|
||||
Bukkit.getScheduler().runTaskTimer(plugin, runnable, unit.toTicks(delay), unit.toTicks(period));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unlinkObject(Object any) {
|
||||
if (any instanceof Listener) {
|
||||
|
||||
@@ -32,6 +32,7 @@ import de.steamwar.command.*;
|
||||
import de.steamwar.linkage.AbstractLinker;
|
||||
import de.steamwar.linkage.EventMode;
|
||||
import de.steamwar.linkage.LinkedInstance;
|
||||
import de.steamwar.linkage.LinkedTimer;
|
||||
import de.steamwar.messages.Chatter;
|
||||
import de.steamwar.network.packets.PacketHandler;
|
||||
import de.steamwar.persistent.ReloadablePlugin;
|
||||
@@ -164,6 +165,11 @@ public class VelocityCore implements ReloadablePlugin {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void scheduleTimer(Runnable runnable, long delay, long period, LinkedTimer.Unit unit) {
|
||||
schedule(runnable).delay(unit.toTimeUnit(delay), unit.getTimeUnit()).repeat(unit.toTimeUnit(period), unit.getTimeUnit()).schedule();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unlinkObject(Object any) {
|
||||
if (any instanceof PacketHandler) {
|
||||
|
||||
Reference in New Issue
Block a user