From cec5bac6081f29c642e745b3d60436222f7aa0b9 Mon Sep 17 00:00:00 2001 From: lionarius Date: Thu, 27 Mar 2025 17:19:41 +0300 Subject: [PATCH 1/4] fix main thread blocking --- .../lionarius/skinrestorer/SkinRestorer.java | 12 ++++-- .../skinshuffle/SkinShuffleCompatibility.java | 3 +- .../skinrestorer/mixin/PlayerListMixin.java | 3 +- .../skinrestorer/util/ServerUtils.java | 37 ------------------ .../skinrestorer/util/TickedScheduler.java | 39 +++++++++++++++++++ 5 files changed, 49 insertions(+), 45 deletions(-) delete mode 100644 common/src/main/java/net/lionarius/skinrestorer/util/ServerUtils.java create mode 100644 common/src/main/java/net/lionarius/skinrestorer/util/TickedScheduler.java diff --git a/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java b/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java index 46bedfc..6d2da69 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)); } @@ -165,6 +167,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 3485def..3b60331 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..df4c5ef 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,7 +46,7 @@ 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); 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..12954be --- /dev/null +++ b/common/src/main/java/net/lionarius/skinrestorer/util/TickedScheduler.java @@ -0,0 +1,39 @@ +package net.lionarius.skinrestorer.util; + +import com.google.common.collect.Queues; +import net.minecraft.server.MinecraftServer; +import org.jetbrains.annotations.NotNull; + +import java.util.Queue; + +public class TickedScheduler implements Runnable { + private final MinecraftServer server; + private final Queue queue = Queues.newPriorityBlockingQueue(); + + public TickedScheduler(MinecraftServer server) { + this.server = server; + } + + public void schedule(Runnable task, int delay) { + this.queue.add(new TickTask(this.server.getTickCount() + delay, task)); + } + + @Override + public void run() { + TickTask nextTask; + while ((nextTask = this.queue.peek()) != null) { + if (nextTask.tick() > this.server.getTickCount()) + break; + + var tickTask = this.queue.remove(); + tickTask.task().run(); + } + } + + private record TickTask(int tick, Runnable task) implements Comparable { + @Override + public int compareTo(@NotNull TickedScheduler.TickTask other) { + return Integer.compare(this.tick, other.tick); + } + } +} From 364ca540be93ce8ac9bd84772c131bdb7ba681a3 Mon Sep 17 00:00:00 2001 From: lionarius Date: Thu, 27 Mar 2025 18:25:27 +0300 Subject: [PATCH 2/4] make TickedScheduler track task id and schedule time --- .../skinrestorer/mixin/PlayerListMixin.java | 2 +- .../skinrestorer/util/TickedScheduler.java | 26 +++++++++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) 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 df4c5ef..ccaed02 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerListMixin.java +++ b/common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerListMixin.java @@ -50,7 +50,7 @@ public abstract class PlayerListMixin { 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/TickedScheduler.java b/common/src/main/java/net/lionarius/skinrestorer/util/TickedScheduler.java index 12954be..048a452 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/util/TickedScheduler.java +++ b/common/src/main/java/net/lionarius/skinrestorer/util/TickedScheduler.java @@ -4,36 +4,52 @@ import com.google.common.collect.Queues; import net.minecraft.server.MinecraftServer; import org.jetbrains.annotations.NotNull; +import java.util.Map; import java.util.Queue; +import java.util.concurrent.ConcurrentHashMap; public class TickedScheduler implements Runnable { private final MinecraftServer server; private final Queue queue = Queues.newPriorityBlockingQueue(); + private final Map idMap = new ConcurrentHashMap<>(); public TickedScheduler(MinecraftServer server) { this.server = server; } public void schedule(Runnable task, int delay) { - this.queue.add(new TickTask(this.server.getTickCount() + delay, task)); + 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)); } @Override public void run() { TickTask nextTask; while ((nextTask = this.queue.peek()) != null) { - if (nextTask.tick() > this.server.getTickCount()) + if (nextTask.runOnTick() > this.server.getTickCount()) break; var tickTask = this.queue.remove(); - tickTask.task().run(); + 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 tick, Runnable task) implements Comparable { + 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.tick, other.tick); + return Integer.compare(this.runOnTick, other.runOnTick); } } } From 6fbc53e38b30c25bb3db861608f83ae86b7a2121 Mon Sep 17 00:00:00 2001 From: lionarius Date: Thu, 27 Mar 2025 18:25:41 +0300 Subject: [PATCH 3/4] allow canceling tasks by id --- .../main/java/net/lionarius/skinrestorer/SkinRestorer.java | 2 ++ .../net/lionarius/skinrestorer/util/TickedScheduler.java | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java b/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java index 6d2da69..e536312 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java +++ b/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java @@ -121,6 +121,8 @@ public final class SkinRestorer { PlayerUtils.refreshPlayer(player); acceptedPlayers.add(player); + + SkinRestorer.getTickedScheduler().cancel(player.getUUID()); } return acceptedPlayers; diff --git a/common/src/main/java/net/lionarius/skinrestorer/util/TickedScheduler.java b/common/src/main/java/net/lionarius/skinrestorer/util/TickedScheduler.java index 048a452..ea7c5a2 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/util/TickedScheduler.java +++ b/common/src/main/java/net/lionarius/skinrestorer/util/TickedScheduler.java @@ -28,6 +28,11 @@ public class TickedScheduler implements Runnable { 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; From 088ad089ed7e3f2b78026fb2190c78c4787e8184 Mon Sep 17 00:00:00 2001 From: lionarius Date: Thu, 27 Mar 2025 18:28:50 +0300 Subject: [PATCH 4/4] do not use google collections method to create queue --- .../java/net/lionarius/skinrestorer/util/TickedScheduler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/net/lionarius/skinrestorer/util/TickedScheduler.java b/common/src/main/java/net/lionarius/skinrestorer/util/TickedScheduler.java index ea7c5a2..8085db2 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/util/TickedScheduler.java +++ b/common/src/main/java/net/lionarius/skinrestorer/util/TickedScheduler.java @@ -1,16 +1,16 @@ package net.lionarius.skinrestorer.util; -import com.google.common.collect.Queues; 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 = Queues.newPriorityBlockingQueue(); + private final Queue queue = new PriorityBlockingQueue<>(); private final Map idMap = new ConcurrentHashMap<>(); public TickedScheduler(MinecraftServer server) {