diff --git a/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java b/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java index 553ee5d..334d5bd 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java +++ b/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java @@ -11,10 +11,7 @@ import net.lionarius.skinrestorer.skin.SkinStorage; import net.lionarius.skinrestorer.skin.SkinValue; import net.lionarius.skinrestorer.skin.provider.*; import net.lionarius.skinrestorer.translation.Translation; -import net.lionarius.skinrestorer.util.FileUtils; -import net.lionarius.skinrestorer.util.PlayerUtils; -import net.lionarius.skinrestorer.util.Result; -import net.lionarius.skinrestorer.util.WebUtils; +import net.lionarius.skinrestorer.util.*; import net.minecraft.commands.CommandSourceStack; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; @@ -37,6 +34,7 @@ public final class SkinRestorer { private static SkinStorage skinStorage; private static Path configDir; private static Config config; + private static TickedScheduler tickedScheduler; private SkinRestorer() {} @@ -56,6 +54,10 @@ public final class SkinRestorer { return SkinRestorer.providersRegistry; } + public static TickedScheduler getTickedScheduler() { + return SkinRestorer.tickedScheduler; + } + public static Optional getProvider(String name) { return Optional.ofNullable(SkinRestorer.providersRegistry.get(name)); } @@ -119,6 +121,8 @@ public final class SkinRestorer { PlayerUtils.refreshPlayer(player); acceptedPlayers.add(player); + + SkinRestorer.getTickedScheduler().cancel(player.getUUID()); } return acceptedPlayers; @@ -165,6 +169,8 @@ public final class SkinRestorer { FileUtils.tryMigrateOldSkinDirectory(SkinRestorer.getConfigDir(), worldSkinDirectory); SkinRestorer.skinStorage = new SkinStorage(new SkinIO(worldSkinDirectory)); + SkinRestorer.tickedScheduler = new TickedScheduler(server); + server.addTickable(SkinRestorer.tickedScheduler); } public static void onCommandRegister(CommandDispatcher dispatcher) { diff --git a/common/src/main/java/net/lionarius/skinrestorer/compat/skinshuffle/SkinShuffleCompatibility.java b/common/src/main/java/net/lionarius/skinrestorer/compat/skinshuffle/SkinShuffleCompatibility.java index 5c07460..b535f0f 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/compat/skinshuffle/SkinShuffleCompatibility.java +++ b/common/src/main/java/net/lionarius/skinrestorer/compat/skinshuffle/SkinShuffleCompatibility.java @@ -5,7 +5,6 @@ import net.lionarius.skinrestorer.platform.Services; import net.lionarius.skinrestorer.skin.SkinValue; import net.lionarius.skinrestorer.skin.provider.SkinShuffleSkinProvider; import net.lionarius.skinrestorer.util.PlayerUtils; -import net.lionarius.skinrestorer.util.ServerUtils; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; @@ -41,7 +40,7 @@ public class SkinShuffleCompatibility { if (!property.hasSignature()) return; - ServerUtils.scheduleServerTask(server, () -> { + server.execute(() -> { SkinRestorer.applySkin( server, Collections.singleton(player.getGameProfile()), diff --git a/common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerListMixin.java b/common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerListMixin.java index 1f72781..ccaed02 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerListMixin.java +++ b/common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerListMixin.java @@ -1,7 +1,6 @@ package net.lionarius.skinrestorer.mixin; import net.lionarius.skinrestorer.SkinRestorer; -import net.lionarius.skinrestorer.util.ServerUtils; import net.minecraft.network.Connection; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; @@ -47,11 +46,11 @@ public abstract class PlayerListMixin { skinrestorer$tryApplySkin(server, player); } else { var uuid = player.getUUID(); - ServerUtils.scheduleServerTask(server, () -> { + SkinRestorer.getTickedScheduler().schedule(() -> { var actualPlayer = server.getPlayerList().getPlayer(uuid); if (actualPlayer != null) skinrestorer$tryApplySkin(server, actualPlayer); - }, delay); + }, delay, uuid); } } diff --git a/common/src/main/java/net/lionarius/skinrestorer/util/ServerUtils.java b/common/src/main/java/net/lionarius/skinrestorer/util/ServerUtils.java deleted file mode 100644 index a93de78..0000000 --- a/common/src/main/java/net/lionarius/skinrestorer/util/ServerUtils.java +++ /dev/null @@ -1,37 +0,0 @@ -package net.lionarius.skinrestorer.util; - -import net.minecraft.server.MinecraftServer; - -import java.util.function.Consumer; - -public class ServerUtils { - - private ServerUtils() {} - - public static void scheduleServerTask(MinecraftServer server, Consumer task) { - ServerUtils.scheduleServerTask(server, task, 0); - } - - public static void scheduleServerTask(MinecraftServer server, Runnable task) { - ServerUtils.scheduleServerTask(server, task, 0); - } - - public static void scheduleServerTask(MinecraftServer server, Consumer task, int tickDelay) { - server.execute(new ScheduledTickTask(server, task, server.getTickCount() + tickDelay)); - } - - public static void scheduleServerTask(MinecraftServer server, Runnable task, int tickDelay) { - server.execute(new ScheduledTickTask(server, _server -> task.run(), server.getTickCount() + tickDelay)); - } - - public record ScheduledTickTask(MinecraftServer server, Consumer task, - int tickTarget) implements Runnable { - @Override - public void run() { - if (this.server.getTickCount() >= this.tickTarget) - this.task.accept(this.server); - else - this.server.execute(this); - } - } -} diff --git a/common/src/main/java/net/lionarius/skinrestorer/util/TickedScheduler.java b/common/src/main/java/net/lionarius/skinrestorer/util/TickedScheduler.java new file mode 100644 index 0000000..8085db2 --- /dev/null +++ b/common/src/main/java/net/lionarius/skinrestorer/util/TickedScheduler.java @@ -0,0 +1,60 @@ +package net.lionarius.skinrestorer.util; + +import net.minecraft.server.MinecraftServer; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.PriorityBlockingQueue; + +public class TickedScheduler implements Runnable { + private final MinecraftServer server; + private final Queue queue = new PriorityBlockingQueue<>(); + private final Map idMap = new ConcurrentHashMap<>(); + + public TickedScheduler(MinecraftServer server) { + this.server = server; + } + + public void schedule(Runnable task, int delay) { + this.schedule(task, delay, task); + } + + public void schedule(Runnable task, int delay, Object id) { + var taskId = id.hashCode(); + var serverTick = this.server.getTickCount(); + this.idMap.merge(taskId, serverTick, Integer::max); + this.queue.add(new TickTask(serverTick, serverTick + delay, taskId, task)); + } + + public void cancel(Object id) { + var taskId = id.hashCode(); + this.idMap.remove(taskId); + } + + @Override + public void run() { + TickTask nextTask; + while ((nextTask = this.queue.peek()) != null) { + if (nextTask.runOnTick() > this.server.getTickCount()) + break; + + var tickTask = this.queue.remove(); + var lastTaskScheduledOnTick = this.idMap.get(tickTask.id()); + + if (lastTaskScheduledOnTick != null && lastTaskScheduledOnTick <= tickTask.scheduledOnTick()) { + this.idMap.remove(tickTask.id()); + if (tickTask.task() != null) + tickTask.task().run(); + } + } + } + + private record TickTask(int scheduledOnTick, int runOnTick, int id, Runnable task) implements Comparable { + @Override + public int compareTo(@NotNull TickedScheduler.TickTask other) { + return Integer.compare(this.runOnTick, other.runOnTick); + } + } +}