From 5763ba27cf78ae156acab9a36a77fe7cb6b5b808 Mon Sep 17 00:00:00 2001 From: Telesphoreo Date: Tue, 19 May 2026 14:13:29 -0400 Subject: [PATCH] compiles again --- build.gradle.kts | 11 +- .../dev/plex/request/PlayerActionServlet.java | 40 +++-- .../impl/PlayerInventoryBroadcaster.java | 71 +++++--- .../plex/request/impl/PlayersBroadcaster.java | 156 ++++++++++++------ .../plex/request/impl/StatsBroadcaster.java | 6 +- src/main/resources/module.yml | 2 +- 6 files changed, 185 insertions(+), 101 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 882aa55..56d8736 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,11 +2,11 @@ plugins { java `maven-publish` idea - id("dev.plex.module") version "1.1" + id("dev.plex.module") version "1.2" } group = "dev.plex" -version = "1.7" +version = "2.0" description = "Module-HTTPD" repositories { @@ -25,10 +25,15 @@ repositories { } maven { + name = "codemc" url = uri("https://repo.codemc.io/repository/maven-public/") } } +plexModule { + includeRepository("codemc") +} + dependencies { implementation("org.projectlombok:lombok:1.18.46") annotationProcessor("org.projectlombok:lombok:1.18.46") @@ -84,4 +89,4 @@ publishing { from(components["java"]) } } -} \ No newline at end of file +} diff --git a/src/main/java/dev/plex/request/PlayerActionServlet.java b/src/main/java/dev/plex/request/PlayerActionServlet.java index f2bf6a5..f81071e 100644 --- a/src/main/java/dev/plex/request/PlayerActionServlet.java +++ b/src/main/java/dev/plex/request/PlayerActionServlet.java @@ -119,7 +119,7 @@ public class PlayerActionServlet extends HttpServlet final boolean kick = action.equals("ban") || action.equals("tempban"); final PunishmentRequest toApply = punishment; - HTTPDModule.plexApi().scheduler().runSync(() -> + HTTPDModule.plexApi().scheduler().runGlobal(() -> { try { @@ -135,8 +135,11 @@ public class PlayerActionServlet extends HttpServlet Player online = Bukkit.getPlayer(uuid); if (online != null) { - try { online.kick(Component.text("You have been banned: " + toApply.reason())); } - catch (Throwable t) { t.printStackTrace(); } + HTTPDModule.plexApi().scheduler().runEntity(online, () -> + { + try { online.kick(Component.text("You have been banned: " + toApply.reason())); } + catch (Throwable t) { t.printStackTrace(); } + }); } } }); @@ -156,24 +159,27 @@ public class PlayerActionServlet extends HttpServlet Log.log(ipAddress + " (xf:" + staff.username() + ") issued " + action + " on " + target.name() + " (" + uuid + ")" + (slot == null || slot.isBlank() ? "" : " slot " + slot)); - HTTPDModule.plexApi().scheduler().runSync(() -> + HTTPDModule.plexApi().scheduler().runGlobal(() -> { Player online = Bukkit.getPlayer(uuid); if (online == null) return; - PlayerInventory inv = online.getInventory(); - if ("clear-inventory".equals(action)) + HTTPDModule.plexApi().scheduler().runEntity(online, () -> { - inv.clear(); - inv.setArmorContents(null); - inv.setItemInOffHand(null); - online.updateInventory(); - return; - } - if ("clear-selected".equals(action)) - { - clearSlot(inv, slot); - online.updateInventory(); - } + PlayerInventory inv = online.getInventory(); + if ("clear-inventory".equals(action)) + { + inv.clear(); + inv.setArmorContents(null); + inv.setItemInOffHand(null); + online.updateInventory(); + return; + } + if ("clear-selected".equals(action)) + { + clearSlot(inv, slot); + online.updateInventory(); + } + }); }); response.sendRedirect("/player/" + uuid); diff --git a/src/main/java/dev/plex/request/impl/PlayerInventoryBroadcaster.java b/src/main/java/dev/plex/request/impl/PlayerInventoryBroadcaster.java index fed07e1..118fecc 100644 --- a/src/main/java/dev/plex/request/impl/PlayerInventoryBroadcaster.java +++ b/src/main/java/dev/plex/request/impl/PlayerInventoryBroadcaster.java @@ -3,6 +3,7 @@ package dev.plex.request.impl; import com.google.gson.GsonBuilder; import dev.plex.HTTPDModule; import jakarta.servlet.AsyncContext; +import io.papermc.paper.threadedregions.scheduler.ScheduledTask; import org.bukkit.Bukkit; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; @@ -10,7 +11,6 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import org.bukkit.inventory.meta.Damageable; import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.scheduler.BukkitTask; import java.io.PrintWriter; import java.util.ArrayList; @@ -60,10 +60,11 @@ public final class PlayerInventoryBroadcaster } private final Map> subscribers = new ConcurrentHashMap<>(); + private final Map cachedPayloads = new ConcurrentHashMap<>(); private final AtomicInteger subscriberCount = new AtomicInteger(); private ScheduledExecutorService executor; - private BukkitTask refreshTask; + private ScheduledTask refreshTask; private int maxConnections = 32; private PlayerInventoryBroadcaster() {} @@ -84,7 +85,7 @@ public final class PlayerInventoryBroadcaster try { - refreshTask = (BukkitTask)HTTPDModule.plexApi().scheduler().runTimer(this::tick, 0L, REFRESH_TICKS); + refreshTask = HTTPDModule.plexApi().scheduler().runGlobalTimer(this::tick, 1L, REFRESH_TICKS); } catch (Throwable t) { @@ -121,6 +122,7 @@ public final class PlayerInventoryBroadcaster } } subscribers.clear(); + cachedPayloads.clear(); subscriberCount.set(0); } @@ -160,12 +162,10 @@ public final class PlayerInventoryBroadcaster public String currentPayload(UUID uuid) { - Player p = Bukkit.getPlayer(uuid); - if (p == null) return "{\"online\":false}"; - return buildPayload(p); + return cachedPayloads.getOrDefault(uuid, "{\"online\":false}"); } - // Runs on the Bukkit main thread. + // Runs on the global region and schedules per-player snapshots on entity schedulers. private void tick() { if (subscribers.isEmpty()) return; @@ -174,29 +174,54 @@ public final class PlayerInventoryBroadcaster Set set = entry.getValue(); if (set.isEmpty()) continue; UUID uuid = entry.getKey(); - String json; + Player player = Bukkit.getPlayer(uuid); + if (player == null) + { + publish(uuid, set, "{\"online\":false}"); + continue; + } try { - Player p = Bukkit.getPlayer(uuid); - json = (p == null) ? "{\"online\":false}" : buildPayload(p); + ScheduledTask task = HTTPDModule.plexApi().scheduler().runEntity(player, () -> + { + String json; + try + { + json = buildPayload(player); + } + catch (Throwable t) + { + json = "{\"online\":false}"; + } + publish(uuid, set, json); + }); + if (task == null) + { + publish(uuid, set, "{\"online\":false}"); + } } catch (Throwable t) { - json = "{\"online\":false}"; + publish(uuid, set, "{\"online\":false}"); } - final String frame = "data: " + json + "\n\n"; - ScheduledExecutorService exec = executor; - if (exec == null) return; - for (Subscriber sub : set) + } + } + + private void publish(UUID uuid, Set set, String json) + { + cachedPayloads.put(uuid, json); + final String frame = "data: " + json + "\n\n"; + ScheduledExecutorService exec = executor; + if (exec == null) return; + for (Subscriber sub : set) + { + try { - try - { - exec.execute(() -> writeFrame(uuid, sub, frame)); - } - catch (Throwable t) - { - drop(uuid, sub); - } + exec.execute(() -> writeFrame(uuid, sub, frame)); + } + catch (Throwable t) + { + drop(uuid, sub); } } } diff --git a/src/main/java/dev/plex/request/impl/PlayersBroadcaster.java b/src/main/java/dev/plex/request/impl/PlayersBroadcaster.java index efacde0..05bbe32 100644 --- a/src/main/java/dev/plex/request/impl/PlayersBroadcaster.java +++ b/src/main/java/dev/plex/request/impl/PlayersBroadcaster.java @@ -3,6 +3,7 @@ package dev.plex.request.impl; import com.google.gson.GsonBuilder; import dev.plex.HTTPDModule; import jakarta.servlet.AsyncContext; +import io.papermc.paper.threadedregions.scheduler.ScheduledTask; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -11,7 +12,6 @@ import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerChangedWorldEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.scheduler.BukkitTask; import java.io.PrintWriter; import java.util.ArrayList; @@ -22,6 +22,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.atomic.AtomicReferenceArray; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -51,7 +52,7 @@ public final class PlayersBroadcaster private volatile String cachedStaffFrame = "{\"players\":[],\"max\":0}"; private ScheduledExecutorService executor; - private BukkitTask refreshTask; + private ScheduledTask refreshTask; private Listener listener; private int maxConnections = 32; @@ -83,7 +84,7 @@ public final class PlayersBroadcaster try { - refreshTask = (BukkitTask)HTTPDModule.plexApi().scheduler().runTimer(this::refreshAndBroadcast, 0L, REFRESH_TICKS); + refreshTask = HTTPDModule.plexApi().scheduler().runGlobalTimer(this::refreshAndBroadcast, 1L, REFRESH_TICKS); } catch (Throwable t) { @@ -157,32 +158,94 @@ public final class PlayersBroadcaster { List online = new ArrayList<>(Bukkit.getOnlinePlayers()); int max = Bukkit.getMaxPlayers(); - String publicJson = buildPublicPayload(online, max); - String staffJson = buildStaffPayload(online, max); - cachedPublicFrame = publicJson; - cachedStaffFrame = staffJson; - - ScheduledExecutorService exec = executor; - if (exec == null || subscribers.isEmpty()) return; - - final String publicFrame = "data: " + publicJson + "\n\n"; - final String staffFrame = "data: " + staffJson + "\n\n"; - for (Subscriber sub : subscribers) + if (online.isEmpty()) { - final String frame = sub.staff ? staffFrame : publicFrame; + publish(List.of(), List.of(), max); + return; + } + + AtomicReferenceArray> publicPlayers = new AtomicReferenceArray<>(online.size()); + AtomicReferenceArray> staffPlayers = new AtomicReferenceArray<>(online.size()); + AtomicInteger remaining = new AtomicInteger(online.size()); + for (int i = 0; i < online.size(); i++) + { + final int index = i; + Player player = online.get(i); try { - exec.execute(() -> writeFrame(sub, frame)); + ScheduledTask task = HTTPDModule.plexApi().scheduler().runEntity(player, () -> + { + try + { + publicPlayers.set(index, buildPublicPlayer(player)); + staffPlayers.set(index, buildStaffPlayer(player)); + } + catch (Throwable ignored) {} + finally + { + if (remaining.decrementAndGet() == 0) + { + publish(compact(publicPlayers), compact(staffPlayers), max); + } + } + }); + if (task == null && remaining.decrementAndGet() == 0) + { + publish(compact(publicPlayers), compact(staffPlayers), max); + } } - catch (Throwable t) + catch (Throwable ignored) { - dropSubscriber(sub); + if (remaining.decrementAndGet() == 0) + { + publish(compact(publicPlayers), compact(staffPlayers), max); + } } } } catch (Throwable ignored) {} } + private void publish(List> publicPlayers, List> staffPlayers, int max) + { + String publicJson = buildPayload(publicPlayers, max); + String staffJson = buildPayload(staffPlayers, max); + cachedPublicFrame = publicJson; + cachedStaffFrame = staffJson; + + ScheduledExecutorService exec = executor; + if (exec == null || subscribers.isEmpty()) return; + + final String publicFrame = "data: " + publicJson + "\n\n"; + final String staffFrame = "data: " + staffJson + "\n\n"; + for (Subscriber sub : subscribers) + { + final String frame = sub.staff ? staffFrame : publicFrame; + try + { + exec.execute(() -> writeFrame(sub, frame)); + } + catch (Throwable t) + { + dropSubscriber(sub); + } + } + } + + private static List> compact(AtomicReferenceArray> players) + { + List> result = new ArrayList<>(players.length()); + for (int i = 0; i < players.length(); i++) + { + Map player = players.get(i); + if (player != null) + { + result.add(player); + } + } + return result; + } + private void writeFrame(Subscriber sub, String frame) { try @@ -213,7 +276,7 @@ public final class PlayersBroadcaster if (!refreshScheduled.compareAndSet(false, true)) return; try { - HTTPDModule.plexApi().scheduler().runLater(() -> + HTTPDModule.plexApi().scheduler().runGlobalLater(() -> { refreshScheduled.set(false); refreshAndBroadcast(); @@ -225,49 +288,34 @@ public final class PlayersBroadcaster } } - private String buildPublicPayload(List online, int max) + private String buildPayload(List> players, int max) { - List> players = new ArrayList<>(); - for (Player p : online) - { - Map m = new LinkedHashMap<>(); - m.put("uuid", p.getUniqueId().toString()); - m.put("name", p.getName()); - try { m.put("world", p.getWorld() != null ? p.getWorld().getName() : ""); } - catch (Throwable ignored) { m.put("world", ""); } - int ping = 0; - try { ping = p.getPing(); } catch (Throwable ignored) {} - m.put("ping", ping); - players.add(m); - } Map root = new LinkedHashMap<>(); root.put("players", players); root.put("max", max); return new GsonBuilder().serializeNulls().create().toJson(root); } - private String buildStaffPayload(List online, int max) + private Map buildPublicPlayer(Player p) { - List> players = new ArrayList<>(); - for (Player p : online) - { - Map m = new LinkedHashMap<>(); - m.put("uuid", p.getUniqueId().toString()); - m.put("name", p.getName()); - try { m.put("world", p.getWorld() != null ? p.getWorld().getName() : ""); } - catch (Throwable ignored) { m.put("world", ""); } - try { m.put("op", p.isOp()); } catch (Throwable ignored) { m.put("op", false); } - try { m.put("gamemode", p.getGameMode().name()); } - catch (Throwable ignored) { m.put("gamemode", ""); } - int ping = 0; - try { ping = p.getPing(); } catch (Throwable ignored) {} - m.put("ping", ping); - players.add(m); - } - Map root = new LinkedHashMap<>(); - root.put("players", players); - root.put("max", max); - return new GsonBuilder().serializeNulls().create().toJson(root); + Map m = new LinkedHashMap<>(); + m.put("uuid", p.getUniqueId().toString()); + m.put("name", p.getName()); + try { m.put("world", p.getWorld() != null ? p.getWorld().getName() : ""); } + catch (Throwable ignored) { m.put("world", ""); } + int ping = 0; + try { ping = p.getPing(); } catch (Throwable ignored) {} + m.put("ping", ping); + return m; + } + + private Map buildStaffPlayer(Player p) + { + Map m = buildPublicPlayer(p); + try { m.put("op", p.isOp()); } catch (Throwable ignored) { m.put("op", false); } + try { m.put("gamemode", p.getGameMode().name()); } + catch (Throwable ignored) { m.put("gamemode", ""); } + return m; } private final class PlayersListener implements Listener diff --git a/src/main/java/dev/plex/request/impl/StatsBroadcaster.java b/src/main/java/dev/plex/request/impl/StatsBroadcaster.java index d3c265e..f5c3f84 100644 --- a/src/main/java/dev/plex/request/impl/StatsBroadcaster.java +++ b/src/main/java/dev/plex/request/impl/StatsBroadcaster.java @@ -3,9 +3,9 @@ package dev.plex.request.impl; import com.google.gson.GsonBuilder; import dev.plex.HTTPDModule; import jakarta.servlet.AsyncContext; +import io.papermc.paper.threadedregions.scheduler.ScheduledTask; import org.bukkit.Bukkit; import org.bukkit.World; -import org.bukkit.scheduler.BukkitTask; import java.io.PrintWriter; import java.lang.management.ManagementFactory; @@ -52,7 +52,7 @@ public final class StatsBroadcaster System.currentTimeMillis() - ManagementFactory.getRuntimeMXBean().getUptime(); private ScheduledExecutorService executor; - private BukkitTask bukkitTask; + private ScheduledTask bukkitTask; private ScheduledFuture broadcastTask; private int maxConnections = 32; @@ -77,7 +77,7 @@ public final class StatsBroadcaster try { - bukkitTask = (BukkitTask)HTTPDModule.plexApi().scheduler().runTimer(this::sampleBukkit, 0L, 40L); + bukkitTask = HTTPDModule.plexApi().scheduler().runGlobalTimer(this::sampleBukkit, 1L, 40L); } catch (Throwable t) { diff --git a/src/main/resources/module.yml b/src/main/resources/module.yml index f38ab0f..8ae7dd0 100644 --- a/src/main/resources/module.yml +++ b/src/main/resources/module.yml @@ -1,5 +1,5 @@ name: Module-HTTPD -version: 1.7 +version: 2.0 description: HTTPD server for Plex main: dev.plex.HTTPDModule apiCompatibility: 1 \ No newline at end of file