compiles again

This commit is contained in:
2026-05-19 14:13:29 -04:00
parent 5f4f91ddfe
commit 5763ba27cf
6 changed files with 185 additions and 101 deletions
+7 -2
View File
@@ -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")
@@ -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);
@@ -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<UUID, Set<Subscriber>> subscribers = new ConcurrentHashMap<>();
private final Map<UUID, String> 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<Subscriber> 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<Subscriber> 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);
}
}
}
@@ -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<Player> 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<Map<String, Object>> publicPlayers = new AtomicReferenceArray<>(online.size());
AtomicReferenceArray<Map<String, Object>> 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<Map<String, Object>> publicPlayers, List<Map<String, Object>> 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<Map<String, Object>> compact(AtomicReferenceArray<Map<String, Object>> players)
{
List<Map<String, Object>> result = new ArrayList<>(players.length());
for (int i = 0; i < players.length(); i++)
{
Map<String, Object> 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<Player> online, int max)
private String buildPayload(List<Map<String, Object>> players, int max)
{
List<Map<String, Object>> players = new ArrayList<>();
for (Player p : online)
{
Map<String, Object> 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<String, Object> root = new LinkedHashMap<>();
root.put("players", players);
root.put("max", max);
return new GsonBuilder().serializeNulls().create().toJson(root);
}
private String buildStaffPayload(List<Player> online, int max)
private Map<String, Object> buildPublicPlayer(Player p)
{
List<Map<String, Object>> players = new ArrayList<>();
for (Player p : online)
{
Map<String, Object> 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<String, Object> root = new LinkedHashMap<>();
root.put("players", players);
root.put("max", max);
return new GsonBuilder().serializeNulls().create().toJson(root);
Map<String, Object> 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<String, Object> buildStaffPlayer(Player p)
{
Map<String, Object> 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
@@ -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)
{
+1 -1
View File
@@ -1,5 +1,5 @@
name: Module-HTTPD
version: 1.7
version: 2.0
description: HTTPD server for Plex
main: dev.plex.HTTPDModule
apiCompatibility: 1