diff --git a/src/main/java/dev/plex/HTTPDModule.java b/src/main/java/dev/plex/HTTPDModule.java index 463452d..daccdff 100644 --- a/src/main/java/dev/plex/HTTPDModule.java +++ b/src/main/java/dev/plex/HTTPDModule.java @@ -3,7 +3,6 @@ package dev.plex; import dev.plex.assets.MinecraftAssetsManager; import dev.plex.authentication.AuthenticationManager; import dev.plex.cache.FileCache; -import dev.plex.api.PlexApi; import dev.plex.config.ModuleConfig; import dev.plex.logging.Log; import dev.plex.module.PlexModule; @@ -33,35 +32,41 @@ import java.util.concurrent.atomic.AtomicReference; public class HTTPDModule extends PlexModule { - public static ServletContextHandler context; + @Getter + private ServletContextHandler context; private Thread serverThread; - private AtomicReference atomicServer = new AtomicReference<>(); - - public static ModuleConfig moduleConfig; - private static PlexApi plexApi; - - public static PlexApi plexApi() - { - return plexApi; - } - - public static final FileCache fileCache = new FileCache(); - - public static final String template = AbstractServlet.readFileReal(HTTPDModule.class.getResourceAsStream("/httpd/template.html")); + private final AtomicReference atomicServer = new AtomicReference<>(); @Getter - private static AuthenticationManager authenticationManager; + private ModuleConfig moduleConfig; @Getter - private static File accessLogFile; + private final FileCache fileCache = new FileCache(); @Getter - private static MinecraftAssetsManager minecraftAssetsManager; + private final String template = AbstractServlet.readFileReal(HTTPDModule.class.getResourceAsStream("/httpd/template.html")); + + @Getter + private AuthenticationManager authenticationManager; + + @Getter + private File accessLogFile; + + @Getter + private MinecraftAssetsManager minecraftAssetsManager; + + @Getter + private StatsBroadcaster statsBroadcaster; + + @Getter + private PlayersBroadcaster playersBroadcaster; + + @Getter + private PlayerInventoryBroadcaster playerInventoryBroadcaster; @Override public void load() { - plexApi = api(); // Move it from /httpd/config.yml to /plugins/Plex/modules/Plex-HTTPD/config.yml moduleConfig = new ModuleConfig(this, "httpd/config.yml", "config.yml"); } @@ -70,17 +75,18 @@ public class HTTPDModule extends PlexModule public void enable() { moduleConfig.load(); - HTTPDModule.plexApi().logging().debug("HTTPD Module Port: {0}", moduleConfig.getInt("server.port")); + api().logging().debug("HTTPD Module Port: {0}", moduleConfig.getInt("server.port")); accessLogFile = new File(getDataFolder(), moduleConfig.getString("server.logging.file-path", "httpd.log")); + Log.configure(moduleConfig, accessLogFile); - minecraftAssetsManager = new MinecraftAssetsManager(getDataFolder().toPath()); + minecraftAssetsManager = new MinecraftAssetsManager(getDataFolder().toPath(), api()); minecraftAssetsManager.refreshAsync(); - authenticationManager = new AuthenticationManager(); + authenticationManager = new AuthenticationManager(this); if (authenticationManager.provider() == null) { - HTTPDModule.plexApi().logging().debug("Authentication is disabled or misconfigured"); + api().logging().debug("Authentication is disabled or misconfigured"); } @@ -110,33 +116,37 @@ public class HTTPDModule extends PlexModule connector.setIdleTimeout(moduleConfig.getLong("server.limits.idle-timeout-ms", 15_000L)); connector.setAcceptQueueSize(moduleConfig.getInt("server.limits.accept-queue", 32)); - context.addFilter(new FilterHolder(new RateLimitFilter()), "/*", EnumSet.of(DispatcherType.REQUEST)); + context.addFilter(new FilterHolder(new RateLimitFilter(moduleConfig)), "/*", EnumSet.of(DispatcherType.REQUEST)); - StatsBroadcaster.get().start(); - PlayersBroadcaster.get().start(); - PlayerInventoryBroadcaster.get().start(); + statsBroadcaster = new StatsBroadcaster(this); + playersBroadcaster = new PlayersBroadcaster(this); + playerInventoryBroadcaster = new PlayerInventoryBroadcaster(this); + statsBroadcaster.start(); + playersBroadcaster.start(); + playerInventoryBroadcaster.start(); - new IndefBansEndpoint(); - new IndexEndpoint(); - new ListEndpoint(); - new PunishmentsEndpoint(); - new CommandsEndpoint(); - new SchematicDownloadEndpoint(); - new SchematicUploadEndpoint(); - new PlayersEndpoint(); - new PlayerAdminEndpoint(); - new AssetsEndpoint(); - new PunishmentsUIEndpoint(); - new IndefBansUIEndpoint(); - new AuthenticationEndpoint(); + new IndefBansEndpoint(this); + new IndexEndpoint(this); + new ListEndpoint(this); + new PunishmentsEndpoint(this); + new CommandsEndpoint(this); + new SchematicDownloadEndpoint(this); + new SchematicUploadEndpoint(this); + new PlayersEndpoint(this); + new PlayerAdminEndpoint(this); + new AssetsEndpoint(this); + new PunishmentsUIEndpoint(this); + new IndefBansUIEndpoint(this); + new AuthenticationEndpoint(this); - HTTPDModule.context.addServlet(StatsStreamServlet.class, "/api/stats/stream"); - HTTPDModule.context.addServlet(PlayersStreamServlet.class, "/api/players/stream"); - HTTPDModule.context.addServlet(StaffPlayersStreamServlet.class, "/api/players/stream/staff"); - HTTPDModule.context.addServlet(PlayerActionServlet.class, "/api/admin/action"); - HTTPDModule.context.addServlet(PlayerInventoryStreamServlet.class, "/api/player/inventory/stream"); + context.addServlet(new ServletHolder(new StatsStreamServlet(statsBroadcaster)), "/api/stats/stream"); + context.addServlet(new ServletHolder(new PlayersStreamServlet(playersBroadcaster)), "/api/players/stream"); + context.addServlet(new ServletHolder(new StaffPlayersStreamServlet(this, playersBroadcaster)), "/api/players/stream/staff"); + context.addServlet(new ServletHolder(new PlayerActionServlet(this)), "/api/admin/action"); + context.addServlet(new ServletHolder(new PlayerInventoryStreamServlet(this, playerInventoryBroadcaster)), "/api/player/inventory/stream"); - ServletHolder uploadHolder = HTTPDModule.context.addServlet(SchematicUploadServlet.class, "/api/schematics/uploading"); + ServletHolder uploadHolder = new ServletHolder(new SchematicUploadServlet(this)); + context.addServlet(uploadHolder, "/api/schematics/uploading"); File uploadLoc = new File(System.getProperty("java.io.tmpdir"), "schematic-temp-dir"); if (!uploadLoc.exists()) @@ -160,16 +170,19 @@ public class HTTPDModule extends PlexModule } }, "Jetty-Server"); serverThread.start(); - HTTPDModule.plexApi().logging().info("Starting Jetty server on port " + moduleConfig.getInt("server.port")); + api().logging().info("Starting Jetty server on port " + moduleConfig.getInt("server.port")); } @Override public void disable() { - HTTPDModule.plexApi().logging().debug("Stopping Jetty server"); + api().logging().debug("Stopping Jetty server"); try { - StatsBroadcaster.get().shutdown(); + if (statsBroadcaster != null) + { + statsBroadcaster.shutdown(); + } } catch (Throwable t) { @@ -177,7 +190,10 @@ public class HTTPDModule extends PlexModule } try { - PlayersBroadcaster.get().shutdown(); + if (playersBroadcaster != null) + { + playersBroadcaster.shutdown(); + } } catch (Throwable t) { @@ -185,7 +201,10 @@ public class HTTPDModule extends PlexModule } try { - PlayerInventoryBroadcaster.get().shutdown(); + if (playerInventoryBroadcaster != null) + { + playerInventoryBroadcaster.shutdown(); + } } catch (Throwable t) { @@ -193,8 +212,12 @@ public class HTTPDModule extends PlexModule } try { - atomicServer.get().stop(); - atomicServer.get().destroy(); + Server server = atomicServer.get(); + if (server != null) + { + server.stop(); + server.destroy(); + } } catch (Exception e) { diff --git a/src/main/java/dev/plex/assets/MinecraftAssetsManager.java b/src/main/java/dev/plex/assets/MinecraftAssetsManager.java index 07b25f0..8ba8003 100644 --- a/src/main/java/dev/plex/assets/MinecraftAssetsManager.java +++ b/src/main/java/dev/plex/assets/MinecraftAssetsManager.java @@ -1,6 +1,6 @@ package dev.plex.assets; -import dev.plex.HTTPDModule; +import dev.plex.api.PlexApi; import org.bukkit.Bukkit; import org.json.JSONArray; import org.json.JSONObject; @@ -35,12 +35,14 @@ public class MinecraftAssetsManager private final AtomicBoolean ready = new AtomicBoolean(false); private final AtomicBoolean refreshStarted = new AtomicBoolean(false); private final String minecraftVersion; + private final PlexApi api; - public MinecraftAssetsManager(Path dataFolder) + public MinecraftAssetsManager(Path dataFolder, PlexApi api) { this.root = dataFolder.resolve("minecraft-assets"); this.versionFile = root.resolve("version.txt"); this.minecraftVersion = detectMinecraftVersion(); + this.api = api; this.client = HttpClient.newBuilder() .followRedirects(HttpClient.Redirect.NORMAL) .connectTimeout(Duration.ofSeconds(20)) @@ -63,7 +65,7 @@ public class MinecraftAssetsManager } catch (Exception e) { - HTTPDModule.plexApi().logging().info("Unable to download Minecraft assets for HTTPD inventory view: " + e.getMessage()); + api.logging().info("Unable to download Minecraft assets for HTTPD inventory view: " + e.getMessage()); e.printStackTrace(); } }); @@ -90,13 +92,13 @@ public class MinecraftAssetsManager String cachedVersion = Files.exists(versionFile) ? Files.readString(versionFile).trim() : ""; if (minecraftVersion.equals(cachedVersion) && hasAssets()) { - HTTPDModule.plexApi().logging().debug("HTTPD Minecraft assets are already cached for {0}", minecraftVersion); + api.logging().debug("HTTPD Minecraft assets are already cached for {0}", minecraftVersion); return; } if (!cachedVersion.isEmpty() && !minecraftVersion.equals(cachedVersion)) { - HTTPDModule.plexApi().logging().info("Minecraft version changed from " + cachedVersion + " to " + minecraftVersion + "; recreating HTTPD asset cache"); + api.logging().info("Minecraft version changed from " + cachedVersion + " to " + minecraftVersion + "; recreating HTTPD asset cache"); } recreateCache(); } @@ -114,7 +116,7 @@ public class MinecraftAssetsManager deleteDirectory(root); Files.createDirectories(root); - HTTPDModule.plexApi().logging().info("Downloading Minecraft " + minecraftVersion + " client assets for HTTPD inventory view"); + api.logging().info("Downloading Minecraft " + minecraftVersion + " client assets for HTTPD inventory view"); JSONObject version = findVersionJson(); String clientUrl = version.getJSONObject("downloads").getJSONObject("client").getString("url"); @@ -142,7 +144,7 @@ public class MinecraftAssetsManager } Files.writeString(versionFile, minecraftVersion + System.lineSeparator()); - HTTPDModule.plexApi().logging().info("HTTPD Minecraft assets cached for " + minecraftVersion); + api.logging().info("HTTPD Minecraft assets cached for " + minecraftVersion); } private JSONObject findVersionJson() throws IOException, InterruptedException diff --git a/src/main/java/dev/plex/authentication/AuthenticationManager.java b/src/main/java/dev/plex/authentication/AuthenticationManager.java index 82a5224..e10333f 100644 --- a/src/main/java/dev/plex/authentication/AuthenticationManager.java +++ b/src/main/java/dev/plex/authentication/AuthenticationManager.java @@ -7,17 +7,17 @@ public class AuthenticationManager { private final OAuth2Provider provider; - public AuthenticationManager() + public AuthenticationManager(HTTPDModule module) { - final boolean enabled = HTTPDModule.moduleConfig.getBoolean("authentication.enabled", false); + final boolean enabled = module.getModuleConfig().getBoolean("authentication.enabled", false); if (!enabled) { provider = null; return; } - HTTPDModule.plexApi().logging().info("[HTTPD] XenForo OAuth2 authentication is enabled"); - provider = new XenForoOAuth2Provider(); + module.api().logging().info("[HTTPD] XenForo OAuth2 authentication is enabled"); + provider = new XenForoOAuth2Provider(module); } public OAuth2Provider provider() diff --git a/src/main/java/dev/plex/authentication/impl/XenForoOAuth2Provider.java b/src/main/java/dev/plex/authentication/impl/XenForoOAuth2Provider.java index 8ab0bc6..20c6402 100644 --- a/src/main/java/dev/plex/authentication/impl/XenForoOAuth2Provider.java +++ b/src/main/java/dev/plex/authentication/impl/XenForoOAuth2Provider.java @@ -49,19 +49,21 @@ public class XenForoOAuth2Provider implements OAuth2Provider private final String clientSecret; private final String redirectUri; private final Duration sessionTtl; + private final HTTPDModule module; - public XenForoOAuth2Provider() + public XenForoOAuth2Provider(HTTPDModule module) { - String domain = HTTPDModule.moduleConfig.getString("authentication.provider.xenforo.domain", ""); - this.clientId = HTTPDModule.moduleConfig.getString("authentication.provider.xenforo.clientId", ""); - this.clientSecret = HTTPDModule.moduleConfig.getString("authentication.provider.xenforo.clientSecret", ""); - this.redirectUri = HTTPDModule.moduleConfig.getString("authentication.provider.redirectUri", ""); - long ttlMinutes = HTTPDModule.moduleConfig.getLong("authentication.provider.xenforo.sessionMinutes", 1440L); + this.module = module; + String domain = module.getModuleConfig().getString("authentication.provider.xenforo.domain", ""); + this.clientId = module.getModuleConfig().getString("authentication.provider.xenforo.clientId", ""); + this.clientSecret = module.getModuleConfig().getString("authentication.provider.xenforo.clientSecret", ""); + this.redirectUri = module.getModuleConfig().getString("authentication.provider.redirectUri", ""); + long ttlMinutes = module.getModuleConfig().getLong("authentication.provider.xenforo.sessionMinutes", 1440L); this.sessionTtl = Duration.ofMinutes(Math.max(ttlMinutes, 1L)); if (domain.isEmpty() || clientId.isEmpty() || clientSecret.isEmpty() || redirectUri.isEmpty()) { - HTTPDModule.plexApi().logging().error("XenForo OAuth2 misconfigured: domain, clientId, clientSecret, redirectUri are all required."); + module.api().logging().error("XenForo OAuth2 misconfigured: domain, clientId, clientSecret, redirectUri are all required."); } String base = "https://" + domain.replaceFirst("^https?://", "").replaceAll("/+$", ""); @@ -285,7 +287,7 @@ public class XenForoOAuth2Provider implements OAuth2Provider } catch (Exception e) { - HTTPDModule.plexApi().logging().debug("Failed to revoke XenForo token: {0}", e.getMessage()); + module.api().logging().debug("Failed to revoke XenForo token: {0}", e.getMessage()); } } diff --git a/src/main/java/dev/plex/logging/Log.java b/src/main/java/dev/plex/logging/Log.java index 5e0045a..bdd43a0 100644 --- a/src/main/java/dev/plex/logging/Log.java +++ b/src/main/java/dev/plex/logging/Log.java @@ -1,6 +1,6 @@ package dev.plex.logging; -import dev.plex.HTTPDModule; +import dev.plex.config.ModuleConfig; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; @@ -11,19 +11,30 @@ import java.io.FileWriter; import java.io.IOException; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.util.function.BooleanSupplier; public class Log { private static final DateTimeFormatter STAMP = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS z"); + private static BooleanSupplier consoleLoggingEnabled = () -> false; + private static BooleanSupplier fileLoggingEnabled = () -> false; + private static File accessLogFile; private static BufferedWriter writer; private static File writerTarget; + public static synchronized void configure(ModuleConfig moduleConfig, File target) + { + consoleLoggingEnabled = () -> moduleConfig.getBoolean("server.logging.console", false); + fileLoggingEnabled = () -> moduleConfig.getBoolean("server.logging.file", true); + accessLogFile = target; + } + public static void log(String message, Object... strings) { String formatted = format(message, strings); writeFile(formatted); - if (HTTPDModule.moduleConfig != null && HTTPDModule.moduleConfig.getBoolean("server.logging.console", false)) + if (consoleLoggingEnabled.getAsBoolean()) { Bukkit.getConsoleSender().sendMessage(Component.text("[Plex HTTPD] ").color(NamedTextColor.DARK_AQUA).append(Component.text(formatted).color(NamedTextColor.GRAY))); } @@ -42,6 +53,9 @@ public class Log writer = null; writerTarget = null; } + consoleLoggingEnabled = () -> false; + fileLoggingEnabled = () -> false; + accessLogFile = null; } private static String format(String message, Object... strings) @@ -59,9 +73,8 @@ public class Log private static synchronized void writeFile(String formatted) { - if (HTTPDModule.moduleConfig == null) return; - if (!HTTPDModule.moduleConfig.getBoolean("server.logging.file", true)) return; - File target = HTTPDModule.getAccessLogFile(); + if (!fileLoggingEnabled.getAsBoolean()) return; + File target = accessLogFile; if (target == null) return; if (writer == null || !target.equals(writerTarget)) { diff --git a/src/main/java/dev/plex/ratelimit/RateLimitFilter.java b/src/main/java/dev/plex/ratelimit/RateLimitFilter.java index dd242a8..bd9366c 100644 --- a/src/main/java/dev/plex/ratelimit/RateLimitFilter.java +++ b/src/main/java/dev/plex/ratelimit/RateLimitFilter.java @@ -1,6 +1,6 @@ package dev.plex.ratelimit; -import dev.plex.HTTPDModule; +import dev.plex.config.ModuleConfig; import dev.plex.logging.Log; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; @@ -28,14 +28,14 @@ public class RateLimitFilter implements Filter private final ConcurrentHashMap ipBuckets = new ConcurrentHashMap<>(); private final AtomicLong nextEvictMillis = new AtomicLong(System.currentTimeMillis() + EVICT_INTERVAL_MILLIS); - public RateLimitFilter() + public RateLimitFilter(ModuleConfig config) { - this.enabled = HTTPDModule.moduleConfig.getBoolean("rate-limit.enabled", true); - double globalCapacity = HTTPDModule.moduleConfig.getDouble("rate-limit.global.capacity", 200.0); - double globalRate = HTTPDModule.moduleConfig.getDouble("rate-limit.global.per-second", 100.0); + this.enabled = config.getBoolean("rate-limit.enabled", true); + double globalCapacity = config.getDouble("rate-limit.global.capacity", 200.0); + double globalRate = config.getDouble("rate-limit.global.per-second", 100.0); this.globalBucket = new TokenBucket(globalCapacity, globalRate); - this.ipCapacity = HTTPDModule.moduleConfig.getDouble("rate-limit.per-ip.capacity", 30.0); - this.ipRefillPerSecond = HTTPDModule.moduleConfig.getDouble("rate-limit.per-ip.per-second", 10.0); + this.ipCapacity = config.getDouble("rate-limit.per-ip.capacity", 30.0); + this.ipRefillPerSecond = config.getDouble("rate-limit.per-ip.per-second", 10.0); } @Override diff --git a/src/main/java/dev/plex/request/AbstractServlet.java b/src/main/java/dev/plex/request/AbstractServlet.java index 51d899b..c8f51bd 100644 --- a/src/main/java/dev/plex/request/AbstractServlet.java +++ b/src/main/java/dev/plex/request/AbstractServlet.java @@ -28,9 +28,11 @@ import org.eclipse.jetty.ee10.servlet.ServletHolder; public class AbstractServlet extends HttpServlet { private final List GET_MAPPINGS = Lists.newArrayList(); + protected final HTTPDModule module; - public AbstractServlet() + public AbstractServlet(HTTPDModule module) { + this.module = module; for (Method declaredMethod : this.getClass().getDeclaredMethods()) { declaredMethod.setAccessible(true); @@ -46,7 +48,7 @@ public class AbstractServlet extends HttpServlet ServletHolder holder = new ServletHolder(this); String endpoint = getMapping.endpoint(); String pattern = endpoint.endsWith("/") ? endpoint + "*" : endpoint; - HTTPDModule.context.addServlet(holder, pattern); + module.getContext().addServlet(holder, pattern); } } } @@ -63,11 +65,6 @@ public class AbstractServlet extends HttpServlet String requestPath = getRequestPath(req); Log.log(ipAddress + " visited endpoint " + requestPath); - /*Enumeration headerz = req.getHeaderNames(); - while (headerz.hasMoreElements()) { - String header = headerz.nextElement(); - HTTPDModule.plexApi().logging().debug("Header: {0} Value {1}", header, req.getHeader(header)); - }*/ GET_MAPPINGS.stream().filter(mapping -> endpointMatchesRequest(mapping.getMapping().endpoint(), requestPath)).forEach(mapping -> { resp.setCharacterEncoding("UTF-8"); @@ -137,27 +134,42 @@ public class AbstractServlet extends HttpServlet return requestPath.isEmpty() ? "/" : requestPath; } - public static AuthenticatedUser currentUser(HttpServletRequest request) + protected AuthenticatedUser currentUser(HttpServletRequest request) { - AuthenticationManager manager = HTTPDModule.getAuthenticationManager(); + return currentUser(module, request); + } + + public static AuthenticatedUser currentUser(HTTPDModule module, HttpServletRequest request) + { + AuthenticationManager manager = module.getAuthenticationManager(); if (manager == null) return null; OAuth2Provider provider = manager.provider(); if (provider == null) return null; return provider.lookup(request); } - public static AuthenticatedUser currentStaff(HttpServletRequest request) + protected AuthenticatedUser currentStaff(HttpServletRequest request) { - AuthenticatedUser user = currentUser(request); + return currentStaff(module, request); + } + + public static AuthenticatedUser currentStaff(HTTPDModule module, HttpServletRequest request) + { + AuthenticatedUser user = currentUser(module, request); return (user != null && user.staff()) ? user : null; } - public static String signInPrompt(String action) + protected String signInPrompt(String action) { return signInPrompt(null, action); } - public static String signInPrompt(HttpServletRequest request, String action) + protected String signInPrompt(HttpServletRequest request, String action) + { + return signInPrompt(module, request, action); + } + + public static String signInPrompt(HTTPDModule module, HttpServletRequest request, String action) { String href = "/oauth2/login"; if (request != null) @@ -170,9 +182,14 @@ public class AbstractServlet extends HttpServlet return "You must sign in as staff " + action + "."; } - public static String readFile(InputStream filename) + protected String readFile(InputStream filename) { - String base = HTTPDModule.template; + return readFile(module, filename); + } + + public static String readFile(HTTPDModule module, InputStream filename) + { + String base = module.getTemplate(); String page = readFileReal(filename); String[] info = page.split("\\r?\\n", 3); String title = info.length > 0 ? info[0] : ""; diff --git a/src/main/java/dev/plex/request/PlayerActionServlet.java b/src/main/java/dev/plex/request/PlayerActionServlet.java index f81071e..455edca 100644 --- a/src/main/java/dev/plex/request/PlayerActionServlet.java +++ b/src/main/java/dev/plex/request/PlayerActionServlet.java @@ -27,12 +27,18 @@ public class PlayerActionServlet extends HttpServlet private static final List PERMANENT_ACTIONS = List.of("ban", "mute"); private static final List TEMP_ACTIONS = List.of("tempban", "tempmute", "freeze"); private static final List INVENTORY_ACTIONS = List.of("clear-inventory", "clear-selected"); + private final HTTPDModule module; + + public PlayerActionServlet(HTTPDModule module) + { + this.module = module; + } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - AuthenticatedUser staff = AbstractServlet.currentStaff(request); + AuthenticatedUser staff = AbstractServlet.currentStaff(module, request); if (staff == null) { response.setStatus(HttpServletResponse.SC_FORBIDDEN); @@ -71,7 +77,7 @@ public class PlayerActionServlet extends HttpServlet return; } - PlexPlayerView target = HTTPDModule.plexApi().players().byUuid(uuid).orElse(null); + PlexPlayerView target = module.api().players().byUuid(uuid).orElse(null); if (target == null) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); @@ -119,11 +125,11 @@ public class PlayerActionServlet extends HttpServlet final boolean kick = action.equals("ban") || action.equals("tempban"); final PunishmentRequest toApply = punishment; - HTTPDModule.plexApi().scheduler().runGlobal(() -> + module.api().scheduler().runGlobal(() -> { try { - HTTPDModule.plexApi().punishments().punish(target, toApply); + module.api().punishments().punish(target, toApply); } catch (Throwable t) { @@ -135,7 +141,7 @@ public class PlayerActionServlet extends HttpServlet Player online = Bukkit.getPlayer(uuid); if (online != null) { - HTTPDModule.plexApi().scheduler().runEntity(online, () -> + module.api().scheduler().runEntity(online, () -> { try { online.kick(Component.text("You have been banned: " + toApply.reason())); } catch (Throwable t) { t.printStackTrace(); } @@ -147,7 +153,7 @@ public class PlayerActionServlet extends HttpServlet response.sendRedirect("/player/" + uuid); } - private static void handleInventoryAction(HttpServletRequest request, HttpServletResponse response, AuthenticatedUser staff, UUID uuid, PlexPlayerView target, String action, String slot) + private void handleInventoryAction(HttpServletRequest request, HttpServletResponse response, AuthenticatedUser staff, UUID uuid, PlexPlayerView target, String action, String slot) throws IOException { String ipAddress = request.getRemoteAddr(); @@ -159,11 +165,11 @@ 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().runGlobal(() -> + module.api().scheduler().runGlobal(() -> { Player online = Bukkit.getPlayer(uuid); if (online == null) return; - HTTPDModule.plexApi().scheduler().runEntity(online, () -> + module.api().scheduler().runEntity(online, () -> { PlayerInventory inv = online.getInventory(); if ("clear-inventory".equals(action)) diff --git a/src/main/java/dev/plex/request/PlayerInventoryStreamServlet.java b/src/main/java/dev/plex/request/PlayerInventoryStreamServlet.java index 399aceb..5d1d8c0 100644 --- a/src/main/java/dev/plex/request/PlayerInventoryStreamServlet.java +++ b/src/main/java/dev/plex/request/PlayerInventoryStreamServlet.java @@ -1,5 +1,6 @@ package dev.plex.request; +import dev.plex.HTTPDModule; import dev.plex.logging.Log; import dev.plex.request.impl.PlayerInventoryBroadcaster; import jakarta.servlet.AsyncContext; @@ -16,11 +17,20 @@ import java.util.UUID; public class PlayerInventoryStreamServlet extends HttpServlet { + private final HTTPDModule module; + private final PlayerInventoryBroadcaster broadcaster; + + public PlayerInventoryStreamServlet(HTTPDModule module, PlayerInventoryBroadcaster broadcaster) + { + this.module = module; + this.broadcaster = broadcaster; + } + @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - if (AbstractServlet.currentStaff(request) == null) + if (AbstractServlet.currentStaff(module, request) == null) { response.setStatus(HttpServletResponse.SC_FORBIDDEN); return; @@ -51,7 +61,6 @@ public class PlayerInventoryStreamServlet extends HttpServlet } Log.log(ipAddress + " opened inventory stream for " + uuid); - PlayerInventoryBroadcaster broadcaster = PlayerInventoryBroadcaster.get(); if (broadcaster.atCapacity()) { response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE); diff --git a/src/main/java/dev/plex/request/PlayersStreamServlet.java b/src/main/java/dev/plex/request/PlayersStreamServlet.java index d2cb3d1..d75806b 100644 --- a/src/main/java/dev/plex/request/PlayersStreamServlet.java +++ b/src/main/java/dev/plex/request/PlayersStreamServlet.java @@ -15,6 +15,13 @@ import java.io.PrintWriter; public class PlayersStreamServlet extends HttpServlet { + private final PlayersBroadcaster broadcaster; + + public PlayersStreamServlet(PlayersBroadcaster broadcaster) + { + this.broadcaster = broadcaster; + } + @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException @@ -27,7 +34,6 @@ public class PlayersStreamServlet extends HttpServlet } Log.log(ipAddress + " opened SSE stream /api/players/stream"); - PlayersBroadcaster broadcaster = PlayersBroadcaster.get(); if (broadcaster.atCapacity()) { response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE); diff --git a/src/main/java/dev/plex/request/SchematicUploadServlet.java b/src/main/java/dev/plex/request/SchematicUploadServlet.java index 22fb978..d69c884 100644 --- a/src/main/java/dev/plex/request/SchematicUploadServlet.java +++ b/src/main/java/dev/plex/request/SchematicUploadServlet.java @@ -22,14 +22,20 @@ import java.util.regex.Pattern; public class SchematicUploadServlet extends HttpServlet { private static final Pattern schemNameMatcher = Pattern.compile("^[a-z0-9'!,_ -]{1,30}\\.schem(atic)?$", Pattern.CASE_INSENSITIVE); + private final HTTPDModule module; + + public SchematicUploadServlet(HTTPDModule module) + { + this.module = module; + } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - AuthenticatedUser user = AbstractServlet.currentStaff(request); + AuthenticatedUser user = AbstractServlet.currentStaff(module, request); if (user == null) { - response.getWriter().println(schematicUploadBadHTML(AbstractServlet.signInPrompt(request, "to upload schematics"))); + response.getWriter().println(schematicUploadBadHTML(AbstractServlet.signInPrompt(module, request, "to upload schematics"))); return; } File worldeditFolder = HTTPDModule.getWorldeditFolder(); @@ -67,7 +73,7 @@ public class SchematicUploadServlet extends HttpServlet ClipboardFormat schematicFormat = ClipboardFormats.findByFile(schematicFile); if (schematicFormat == null) { - HTTPDModule.plexApi().logging().info(user.username() + " FAILED to upload schematic with filename: " + filename); + module.api().logging().info(user.username() + " FAILED to upload schematic with filename: " + filename); Log.log("{0} (xf:{1}) FAILED to upload schematic {2}", user.username(), user.userId(), filename); response.getWriter().println(schematicUploadBadHTML("Schematic is not a valid format.")); FileUtils.deleteQuietly(schematicFile); @@ -79,7 +85,7 @@ public class SchematicUploadServlet extends HttpServlet } catch (IOException e) { - HTTPDModule.plexApi().logging().info(user.username() + " FAILED to upload schematic with filename: " + filename); + module.api().logging().info(user.username() + " FAILED to upload schematic with filename: " + filename); Log.log("{0} (xf:{1}) FAILED to upload schematic {2}", user.username(), user.userId(), filename); response.getWriter().println(schematicUploadBadHTML("Schematic is not a valid format.")); FileUtils.deleteQuietly(schematicFile); @@ -87,20 +93,20 @@ public class SchematicUploadServlet extends HttpServlet } inputStream.close(); response.getWriter().println(schematicUploadGoodHTML("Successfully uploaded " + filename + ".")); - HTTPDModule.plexApi().logging().info(user.username() + " uploaded schematic with filename: " + filename); + module.api().logging().info(user.username() + " uploaded schematic with filename: " + filename); Log.log("{0} (xf:{1}) uploaded schematic {2}", user.username(), user.userId(), filename); } private String schematicUploadBadHTML(String message) { - String file = AbstractServlet.readFile(this.getClass().getResourceAsStream("/httpd/schematic_upload_bad.html")); + String file = AbstractServlet.readFile(module, this.getClass().getResourceAsStream("/httpd/schematic_upload_bad.html")); file = file.replace("${MESSAGE}", message); return file; } private String schematicUploadGoodHTML(String message) { - String file = AbstractServlet.readFile(this.getClass().getResourceAsStream("/httpd/schematic_upload_good.html")); + String file = AbstractServlet.readFile(module, this.getClass().getResourceAsStream("/httpd/schematic_upload_good.html")); file = file.replace("${MESSAGE}", message); return file; } diff --git a/src/main/java/dev/plex/request/StaffPlayersStreamServlet.java b/src/main/java/dev/plex/request/StaffPlayersStreamServlet.java index f0ad190..d264d12 100644 --- a/src/main/java/dev/plex/request/StaffPlayersStreamServlet.java +++ b/src/main/java/dev/plex/request/StaffPlayersStreamServlet.java @@ -1,5 +1,6 @@ package dev.plex.request; +import dev.plex.HTTPDModule; import dev.plex.logging.Log; import dev.plex.request.impl.PlayersBroadcaster; import jakarta.servlet.AsyncContext; @@ -15,11 +16,20 @@ import java.io.PrintWriter; public class StaffPlayersStreamServlet extends HttpServlet { + private final HTTPDModule module; + private final PlayersBroadcaster broadcaster; + + public StaffPlayersStreamServlet(HTTPDModule module, PlayersBroadcaster broadcaster) + { + this.module = module; + this.broadcaster = broadcaster; + } + @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - if (AbstractServlet.currentStaff(request) == null) + if (AbstractServlet.currentStaff(module, request) == null) { response.setStatus(HttpServletResponse.SC_FORBIDDEN); return; @@ -33,7 +43,6 @@ public class StaffPlayersStreamServlet extends HttpServlet } Log.log(ipAddress + " opened SSE stream /api/players/stream/staff"); - PlayersBroadcaster broadcaster = PlayersBroadcaster.get(); if (broadcaster.atCapacity()) { response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE); diff --git a/src/main/java/dev/plex/request/StatsStreamServlet.java b/src/main/java/dev/plex/request/StatsStreamServlet.java index 81bd232..3944a4c 100644 --- a/src/main/java/dev/plex/request/StatsStreamServlet.java +++ b/src/main/java/dev/plex/request/StatsStreamServlet.java @@ -15,6 +15,13 @@ import java.io.PrintWriter; public class StatsStreamServlet extends HttpServlet { + private final StatsBroadcaster broadcaster; + + public StatsStreamServlet(StatsBroadcaster broadcaster) + { + this.broadcaster = broadcaster; + } + @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException @@ -27,7 +34,6 @@ public class StatsStreamServlet extends HttpServlet } Log.log(ipAddress + " opened SSE stream /api/stats/stream"); - StatsBroadcaster broadcaster = StatsBroadcaster.get(); if (broadcaster.atCapacity()) { response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE); diff --git a/src/main/java/dev/plex/request/impl/AssetsEndpoint.java b/src/main/java/dev/plex/request/impl/AssetsEndpoint.java index e9c5e73..bee9e45 100644 --- a/src/main/java/dev/plex/request/impl/AssetsEndpoint.java +++ b/src/main/java/dev/plex/request/impl/AssetsEndpoint.java @@ -20,6 +20,10 @@ public class AssetsEndpoint extends AbstractServlet private static final Pattern MODEL_PATH = Pattern.compile("(item|block)/[a-z0-9_]+\\.json"); private static final Pattern ITEM_DEF_PATH = Pattern.compile("[a-z0-9_]+\\.json"); + public AssetsEndpoint(HTTPDModule module) + { + super(module); + } @GetMapping(endpoint = "/assets/dashboard.js") @MappingHeaders(headers = {"content-type;application/javascript; charset=utf-8", "cache-control;public, max-age=300"}) @@ -97,7 +101,7 @@ public class AssetsEndpoint extends AbstractServlet return null; } - private static void servePathUnder(HttpServletRequest request, HttpServletResponse response, String urlPrefix, Pattern allowed, String cacheCategory, String resourcePrefix) + private void servePathUnder(HttpServletRequest request, HttpServletResponse response, String urlPrefix, Pattern allowed, String cacheCategory, String resourcePrefix) { String uri = request.getRequestURI(); if (!uri.startsWith(urlPrefix)) @@ -123,13 +127,13 @@ public class AssetsEndpoint extends AbstractServlet serveResource(resourcePrefix + rest, response); } - private static boolean serveCached(String category, String relativePath, HttpServletResponse response) + private boolean serveCached(String category, String relativePath, HttpServletResponse response) { - if (HTTPDModule.getMinecraftAssetsManager() == null) + if (module.getMinecraftAssetsManager() == null) { return false; } - Path path = HTTPDModule.getMinecraftAssetsManager().resolve(category, relativePath); + Path path = module.getMinecraftAssetsManager().resolve(category, relativePath); if (path == null) { return false; diff --git a/src/main/java/dev/plex/request/impl/AuthenticationEndpoint.java b/src/main/java/dev/plex/request/impl/AuthenticationEndpoint.java index 7a093a3..9432ab3 100644 --- a/src/main/java/dev/plex/request/impl/AuthenticationEndpoint.java +++ b/src/main/java/dev/plex/request/impl/AuthenticationEndpoint.java @@ -21,10 +21,15 @@ public class AuthenticationEndpoint extends AbstractServlet { private static final String RETURN_TO_COOKIE = "plex_return_to"; + public AuthenticationEndpoint(HTTPDModule module) + { + super(module); + } + @GetMapping(endpoint = "/oauth2/login") public String login(HttpServletRequest request, HttpServletResponse response) throws IOException { - OAuth2Provider provider = HTTPDModule.getAuthenticationManager().provider(); + OAuth2Provider provider = module.getAuthenticationManager().provider(); if (provider == null) { response.sendError(HttpServletResponse.SC_NOT_FOUND, "Authentication is not enabled."); @@ -51,7 +56,7 @@ public class AuthenticationEndpoint extends AbstractServlet @GetMapping(endpoint = "/oauth2/callback") public String callback(HttpServletRequest request, HttpServletResponse response) throws IOException { - OAuth2Provider provider = HTTPDModule.getAuthenticationManager().provider(); + OAuth2Provider provider = module.getAuthenticationManager().provider(); if (provider == null) { response.sendError(HttpServletResponse.SC_NOT_FOUND, "Authentication is not enabled."); @@ -63,7 +68,7 @@ public class AuthenticationEndpoint extends AbstractServlet } catch (AuthenticationException e) { - HTTPDModule.plexApi().logging().error("OAuth2 callback failed: " + e.getMessage()); + module.api().logging().error("OAuth2 callback failed: " + e.getMessage()); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.setContentType("text/html; charset=UTF-8"); return "Sign-in failed" @@ -84,7 +89,7 @@ public class AuthenticationEndpoint extends AbstractServlet @GetMapping(endpoint = "/oauth2/logout") public String logout(HttpServletRequest request, HttpServletResponse response) throws IOException { - OAuth2Provider provider = HTTPDModule.getAuthenticationManager().provider(); + OAuth2Provider provider = module.getAuthenticationManager().provider(); if (provider == null) { response.sendRedirect("/"); @@ -106,7 +111,7 @@ public class AuthenticationEndpoint extends AbstractServlet @MappingHeaders(headers = "content-type;application/json") public String me(HttpServletRequest request, HttpServletResponse response) { - OAuth2Provider provider = HTTPDModule.getAuthenticationManager().provider(); + OAuth2Provider provider = module.getAuthenticationManager().provider(); if (provider == null) { return "{\"authenticated\":false,\"reason\":\"disabled\"}"; diff --git a/src/main/java/dev/plex/request/impl/CommandsEndpoint.java b/src/main/java/dev/plex/request/impl/CommandsEndpoint.java index 7c69f0d..5019a29 100644 --- a/src/main/java/dev/plex/request/impl/CommandsEndpoint.java +++ b/src/main/java/dev/plex/request/impl/CommandsEndpoint.java @@ -22,6 +22,11 @@ public class CommandsEndpoint extends AbstractServlet { private String cachedHtml; + public CommandsEndpoint(HTTPDModule module) + { + super(module); + } + @GetMapping(endpoint = "/api/commands/") public String getCommands(HttpServletRequest request, HttpServletResponse response) { @@ -34,12 +39,12 @@ public class CommandsEndpoint extends AbstractServlet return file; } - private static String buildSections() + private String buildSections() { final SortedMap> commandMap = new TreeMap<>(); List plexCommands = commandMap.computeIfAbsent("Plex", k -> new ArrayList<>()); - for (PlexCommand command : HTTPDModule.plexApi().commands().registeredCommands()) + for (PlexCommand command : module.api().commands().registeredCommands()) { plexCommands.add(CommandInfo.from(command)); } diff --git a/src/main/java/dev/plex/request/impl/IndefBansEndpoint.java b/src/main/java/dev/plex/request/impl/IndefBansEndpoint.java index 63478a2..8bc13bf 100644 --- a/src/main/java/dev/plex/request/impl/IndefBansEndpoint.java +++ b/src/main/java/dev/plex/request/impl/IndefBansEndpoint.java @@ -10,6 +10,11 @@ import jakarta.servlet.http.HttpServletResponse; public class IndefBansEndpoint extends AbstractServlet { + public IndefBansEndpoint(HTTPDModule module) + { + super(module); + } + @GetMapping(endpoint = "/api/indefbans/") public String getBans(HttpServletRequest request, HttpServletResponse response) { @@ -20,7 +25,7 @@ public class IndefBansEndpoint extends AbstractServlet } response.setHeader("content-type", "application/json"); - return new GsonBuilder().setPrettyPrinting().create().toJson(HTTPDModule.plexApi().punishments().indefiniteBans()); + return new GsonBuilder().setPrettyPrinting().create().toJson(module.api().punishments().indefiniteBans()); } private String indefbansHTML(String message) diff --git a/src/main/java/dev/plex/request/impl/IndefBansUIEndpoint.java b/src/main/java/dev/plex/request/impl/IndefBansUIEndpoint.java index 5869e5e..064a2a4 100644 --- a/src/main/java/dev/plex/request/impl/IndefBansUIEndpoint.java +++ b/src/main/java/dev/plex/request/impl/IndefBansUIEndpoint.java @@ -13,6 +13,11 @@ import java.util.UUID; public class IndefBansUIEndpoint extends AbstractServlet { + public IndefBansUIEndpoint(HTTPDModule module) + { + super(module); + } + @GetMapping(endpoint = "/indefbans/") public String getBans(HttpServletRequest request, HttpServletResponse response) { @@ -22,7 +27,7 @@ public class IndefBansUIEndpoint extends AbstractServlet return errorHTML(signInPrompt(request, "to view this page")); } - List bans = HTTPDModule.plexApi().punishments().indefiniteBans(); + List bans = module.api().punishments().indefiniteBans(); return listHTML(bans); } diff --git a/src/main/java/dev/plex/request/impl/IndexEndpoint.java b/src/main/java/dev/plex/request/impl/IndexEndpoint.java index c5a312f..433d099 100644 --- a/src/main/java/dev/plex/request/impl/IndexEndpoint.java +++ b/src/main/java/dev/plex/request/impl/IndexEndpoint.java @@ -1,5 +1,6 @@ package dev.plex.request.impl; +import dev.plex.HTTPDModule; import dev.plex.request.AbstractServlet; import dev.plex.request.GetMapping; import jakarta.servlet.http.HttpServletRequest; @@ -7,6 +8,11 @@ import jakarta.servlet.http.HttpServletResponse; public class IndexEndpoint extends AbstractServlet { + public IndexEndpoint(HTTPDModule module) + { + super(module); + } + @GetMapping(endpoint = "//") public String getIndex(HttpServletRequest request, HttpServletResponse response) { diff --git a/src/main/java/dev/plex/request/impl/ListEndpoint.java b/src/main/java/dev/plex/request/impl/ListEndpoint.java index c34c307..6fdf2c2 100644 --- a/src/main/java/dev/plex/request/impl/ListEndpoint.java +++ b/src/main/java/dev/plex/request/impl/ListEndpoint.java @@ -1,6 +1,7 @@ package dev.plex.request.impl; import com.google.gson.GsonBuilder; +import dev.plex.HTTPDModule; import dev.plex.request.AbstractServlet; import dev.plex.request.GetMapping; import dev.plex.request.MappingHeaders; @@ -14,6 +15,11 @@ import org.bukkit.entity.Player; public class ListEndpoint extends AbstractServlet { + public ListEndpoint(HTTPDModule module) + { + super(module); + } + @GetMapping(endpoint = "/api/list/") @MappingHeaders(headers = "content-type;application/json") public String getOnlinePlayers(HttpServletRequest request, HttpServletResponse response) diff --git a/src/main/java/dev/plex/request/impl/PlayerAdminEndpoint.java b/src/main/java/dev/plex/request/impl/PlayerAdminEndpoint.java index 5827816..6ba0075 100644 --- a/src/main/java/dev/plex/request/impl/PlayerAdminEndpoint.java +++ b/src/main/java/dev/plex/request/impl/PlayerAdminEndpoint.java @@ -23,6 +23,11 @@ public class PlayerAdminEndpoint extends AbstractServlet { private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z"); + public PlayerAdminEndpoint(HTTPDModule module) + { + super(module); + } + @GetMapping(endpoint = "/player/") public String getPlayer(HttpServletRequest request, HttpServletResponse response) { @@ -54,15 +59,15 @@ public class PlayerAdminEndpoint extends AbstractServlet return file; } - private static PlexPlayerView lookupPlayer(String query) + private PlexPlayerView lookupPlayer(String query) { try { - return HTTPDModule.plexApi().players().byUuid(UUID.fromString(query)).orElse(null); + return module.api().players().byUuid(UUID.fromString(query)).orElse(null); } catch (IllegalArgumentException ignored) { - return HTTPDModule.plexApi().players().byName(query).orElse(null); + return module.api().players().byName(query).orElse(null); } } diff --git a/src/main/java/dev/plex/request/impl/PlayerInventoryBroadcaster.java b/src/main/java/dev/plex/request/impl/PlayerInventoryBroadcaster.java index 8bff426..225db90 100644 --- a/src/main/java/dev/plex/request/impl/PlayerInventoryBroadcaster.java +++ b/src/main/java/dev/plex/request/impl/PlayerInventoryBroadcaster.java @@ -44,7 +44,6 @@ import org.bukkit.persistence.PersistentDataContainer; */ public final class PlayerInventoryBroadcaster { - private static final PlayerInventoryBroadcaster INSTANCE = new PlayerInventoryBroadcaster(); private static final long REFRESH_TICKS = 20L; // 1 second private static final int MAX_NAME_CHARS = 256; private static final int MAX_LORE_LINES = 20; @@ -53,11 +52,7 @@ public final class PlayerInventoryBroadcaster private static final int MAX_PDC_KEYS = 64; private static final int MAX_PDC_KEY_CHARS = 128; - public static PlayerInventoryBroadcaster get() - { - return INSTANCE; - } - + private final HTTPDModule module; private final Map> subscribers = new ConcurrentHashMap<>(); private final Map cachedPayloads = new ConcurrentHashMap<>(); private final AtomicInteger subscriberCount = new AtomicInteger(); @@ -66,14 +61,17 @@ public final class PlayerInventoryBroadcaster private ScheduledTask refreshTask; private int maxConnections = 32; - private PlayerInventoryBroadcaster() {} + public PlayerInventoryBroadcaster(HTTPDModule module) + { + this.module = module; + } public synchronized void start() { if (executor != null) return; - maxConnections = HTTPDModule.moduleConfig.getInt("server.sse.max-connections", 32); - int threads = Math.max(1, HTTPDModule.moduleConfig.getInt("server.sse.threads", 2)); + maxConnections = module.getModuleConfig().getInt("server.sse.max-connections", 32); + int threads = Math.max(1, module.getModuleConfig().getInt("server.sse.threads", 2)); executor = Executors.newScheduledThreadPool(threads, r -> { @@ -84,11 +82,11 @@ public final class PlayerInventoryBroadcaster try { - refreshTask = HTTPDModule.plexApi().scheduler().runGlobalTimer(this::tick, 1L, REFRESH_TICKS); + refreshTask = module.api().scheduler().runGlobalTimer(this::tick, 1L, REFRESH_TICKS); } catch (Throwable t) { - HTTPDModule.plexApi().logging().debug("PlayerInventoryBroadcaster: could not register refresh task: " + t.getMessage()); + module.api().logging().debug("PlayerInventoryBroadcaster: could not register refresh task: " + t.getMessage()); } try @@ -97,7 +95,7 @@ public final class PlayerInventoryBroadcaster } catch (Throwable t) { - HTTPDModule.plexApi().logging().debug("PlayerInventoryBroadcaster: NBT-API preload failed: " + t.getMessage()); + module.api().logging().debug("PlayerInventoryBroadcaster: NBT-API preload failed: " + t.getMessage()); } } @@ -181,7 +179,7 @@ public final class PlayerInventoryBroadcaster } try { - ScheduledTask task = HTTPDModule.plexApi().scheduler().runEntity(player, () -> + ScheduledTask task = module.api().scheduler().runEntity(player, () -> { String json; try diff --git a/src/main/java/dev/plex/request/impl/PlayersBroadcaster.java b/src/main/java/dev/plex/request/impl/PlayersBroadcaster.java index 05bbe32..9478da6 100644 --- a/src/main/java/dev/plex/request/impl/PlayersBroadcaster.java +++ b/src/main/java/dev/plex/request/impl/PlayersBroadcaster.java @@ -36,14 +36,9 @@ import java.util.concurrent.atomic.AtomicInteger; */ public final class PlayersBroadcaster { - private static final PlayersBroadcaster INSTANCE = new PlayersBroadcaster(); private static final long REFRESH_TICKS = 100L; // 5 seconds at 20 TPS - public static PlayersBroadcaster get() - { - return INSTANCE; - } - + private final HTTPDModule module; private final Set subscribers = ConcurrentHashMap.newKeySet(); private final AtomicInteger subscriberCount = new AtomicInteger(); private final AtomicBoolean refreshScheduled = new AtomicBoolean(false); @@ -56,14 +51,17 @@ public final class PlayersBroadcaster private Listener listener; private int maxConnections = 32; - private PlayersBroadcaster() {} + public PlayersBroadcaster(HTTPDModule module) + { + this.module = module; + } public synchronized void start() { if (executor != null) return; - maxConnections = HTTPDModule.moduleConfig.getInt("server.sse.max-connections", 32); - int threads = Math.max(1, HTTPDModule.moduleConfig.getInt("server.sse.threads", 2)); + maxConnections = module.getModuleConfig().getInt("server.sse.max-connections", 32); + int threads = Math.max(1, module.getModuleConfig().getInt("server.sse.threads", 2)); executor = Executors.newScheduledThreadPool(threads, r -> { @@ -75,20 +73,20 @@ public final class PlayersBroadcaster listener = new PlayersListener(); try { - HTTPDModule.plexApi().listeners().register(listener); + module.api().listeners().register(listener); } catch (Throwable t) { - HTTPDModule.plexApi().logging().debug("PlayersBroadcaster: could not register Bukkit listener: " + t.getMessage()); + module.api().logging().debug("PlayersBroadcaster: could not register Bukkit listener: " + t.getMessage()); } try { - refreshTask = HTTPDModule.plexApi().scheduler().runGlobalTimer(this::refreshAndBroadcast, 1L, REFRESH_TICKS); + refreshTask = module.api().scheduler().runGlobalTimer(this::refreshAndBroadcast, 1L, REFRESH_TICKS); } catch (Throwable t) { - HTTPDModule.plexApi().logging().debug("PlayersBroadcaster: could not register refresh task: " + t.getMessage()); + module.api().logging().debug("PlayersBroadcaster: could not register refresh task: " + t.getMessage()); } } @@ -173,7 +171,7 @@ public final class PlayersBroadcaster Player player = online.get(i); try { - ScheduledTask task = HTTPDModule.plexApi().scheduler().runEntity(player, () -> + ScheduledTask task = module.api().scheduler().runEntity(player, () -> { try { @@ -276,7 +274,7 @@ public final class PlayersBroadcaster if (!refreshScheduled.compareAndSet(false, true)) return; try { - HTTPDModule.plexApi().scheduler().runGlobalLater(() -> + module.api().scheduler().runGlobalLater(() -> { refreshScheduled.set(false); refreshAndBroadcast(); diff --git a/src/main/java/dev/plex/request/impl/PlayersEndpoint.java b/src/main/java/dev/plex/request/impl/PlayersEndpoint.java index e3365e6..f27bfe5 100644 --- a/src/main/java/dev/plex/request/impl/PlayersEndpoint.java +++ b/src/main/java/dev/plex/request/impl/PlayersEndpoint.java @@ -1,5 +1,6 @@ package dev.plex.request.impl; +import dev.plex.HTTPDModule; import dev.plex.request.AbstractServlet; import dev.plex.request.GetMapping; import jakarta.servlet.http.HttpServletRequest; @@ -7,6 +8,11 @@ import jakarta.servlet.http.HttpServletResponse; public class PlayersEndpoint extends AbstractServlet { + public PlayersEndpoint(HTTPDModule module) + { + super(module); + } + @GetMapping(endpoint = "/players/") public String getPlayers(HttpServletRequest request, HttpServletResponse response) { diff --git a/src/main/java/dev/plex/request/impl/PunishmentsEndpoint.java b/src/main/java/dev/plex/request/impl/PunishmentsEndpoint.java index e5a0d63..7390902 100644 --- a/src/main/java/dev/plex/request/impl/PunishmentsEndpoint.java +++ b/src/main/java/dev/plex/request/impl/PunishmentsEndpoint.java @@ -16,6 +16,11 @@ import java.util.UUID; public class PunishmentsEndpoint extends AbstractServlet { + public PunishmentsEndpoint(HTTPDModule module) + { + super(module); + } + @GetMapping(endpoint = "/api/punishments/") public String getPunishments(HttpServletRequest request, HttpServletResponse response) { @@ -28,11 +33,11 @@ public class PunishmentsEndpoint extends AbstractServlet try { UUID pathUUID = UUID.fromString(request.getPathInfo().replace("/", "")); - punishedPlayer = HTTPDModule.plexApi().players().byUuid(pathUUID).orElse(null); + punishedPlayer = module.api().players().byUuid(pathUUID).orElse(null); } catch (IllegalArgumentException ignored) { - punishedPlayer = HTTPDModule.plexApi().players().byName(request.getPathInfo().replace("/", "")).orElse(null); + punishedPlayer = module.api().players().byName(request.getPathInfo().replace("/", "")).orElse(null); } if (punishedPlayer == null) diff --git a/src/main/java/dev/plex/request/impl/PunishmentsUIEndpoint.java b/src/main/java/dev/plex/request/impl/PunishmentsUIEndpoint.java index 0077bc3..1ccd62f 100644 --- a/src/main/java/dev/plex/request/impl/PunishmentsUIEndpoint.java +++ b/src/main/java/dev/plex/request/impl/PunishmentsUIEndpoint.java @@ -18,6 +18,11 @@ public class PunishmentsUIEndpoint extends AbstractServlet { private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z"); + public PunishmentsUIEndpoint(HTTPDModule module) + { + super(module); + } + @GetMapping(endpoint = "/punishments/") public String getPunishments(HttpServletRequest request, HttpServletResponse response) { @@ -49,15 +54,15 @@ public class PunishmentsUIEndpoint extends AbstractServlet return resultsHTML(punished, punishments, showIps); } - private static PlexPlayerView lookupPlayer(String query) + private PlexPlayerView lookupPlayer(String query) { try { - return HTTPDModule.plexApi().players().byUuid(UUID.fromString(query)).orElse(null); + return module.api().players().byUuid(UUID.fromString(query)).orElse(null); } catch (IllegalArgumentException ignored) { - return HTTPDModule.plexApi().players().byName(query).orElse(null); + return module.api().players().byName(query).orElse(null); } } diff --git a/src/main/java/dev/plex/request/impl/SchematicDownloadEndpoint.java b/src/main/java/dev/plex/request/impl/SchematicDownloadEndpoint.java index 006d126..ff2c048 100644 --- a/src/main/java/dev/plex/request/impl/SchematicDownloadEndpoint.java +++ b/src/main/java/dev/plex/request/impl/SchematicDownloadEndpoint.java @@ -19,6 +19,11 @@ public class SchematicDownloadEndpoint extends AbstractServlet { List files = new ArrayList<>(); + public SchematicDownloadEndpoint(HTTPDModule module) + { + super(module); + } + @GetMapping(endpoint = "/api/schematics/download/") public String downloadSchematic(HttpServletRequest request, HttpServletResponse response) { @@ -57,7 +62,7 @@ public class SchematicDownloadEndpoint extends AbstractServlet { try { - byte[] schemData = HTTPDModule.fileCache.getFile(schemFile); + byte[] schemData = module.getFileCache().getFile(schemFile); if (schemData != null) { outputStream.write(schemData); @@ -71,11 +76,11 @@ public class SchematicDownloadEndpoint extends AbstractServlet } } - private static void logDownload(HttpServletRequest request, File schemFile) + private void logDownload(HttpServletRequest request, File schemFile) { AuthenticatedUser user = currentUser(request); String who = user != null ? user.username() + " (xf:" + user.userId() + ")" : request.getRemoteAddr(); - HTTPDModule.plexApi().logging().info("{0} downloaded schematic {1}", who, schemFile.getName()); + module.api().logging().info("{0} downloaded schematic {1}", who, schemFile.getName()); Log.log("{0} downloaded schematic {1}", who, schemFile.getName()); } @@ -118,7 +123,7 @@ public class SchematicDownloadEndpoint extends AbstractServlet { if (fileEntry.isDirectory()) { - HTTPDModule.plexApi().logging().debug("Found directory"); + module.api().logging().debug("Found directory"); listFilesForFolder(fileEntry); } else diff --git a/src/main/java/dev/plex/request/impl/SchematicUploadEndpoint.java b/src/main/java/dev/plex/request/impl/SchematicUploadEndpoint.java index 210fd91..0e93b2a 100644 --- a/src/main/java/dev/plex/request/impl/SchematicUploadEndpoint.java +++ b/src/main/java/dev/plex/request/impl/SchematicUploadEndpoint.java @@ -1,5 +1,6 @@ package dev.plex.request.impl; +import dev.plex.HTTPDModule; import dev.plex.authentication.AuthenticatedUser; import dev.plex.request.AbstractServlet; import dev.plex.request.GetMapping; @@ -8,6 +9,11 @@ import jakarta.servlet.http.HttpServletResponse; public class SchematicUploadEndpoint extends AbstractServlet { + public SchematicUploadEndpoint(HTTPDModule module) + { + super(module); + } + @GetMapping(endpoint = "/api/schematics/upload/") public String uploadSchematic(HttpServletRequest request, HttpServletResponse response) { diff --git a/src/main/java/dev/plex/request/impl/StatsBroadcaster.java b/src/main/java/dev/plex/request/impl/StatsBroadcaster.java index f5c3f84..f504a75 100644 --- a/src/main/java/dev/plex/request/impl/StatsBroadcaster.java +++ b/src/main/java/dev/plex/request/impl/StatsBroadcaster.java @@ -27,13 +27,7 @@ import java.util.concurrent.atomic.AtomicInteger; */ public final class StatsBroadcaster { - private static final StatsBroadcaster INSTANCE = new StatsBroadcaster(); - - public static StatsBroadcaster get() - { - return INSTANCE; - } - + private final HTTPDModule module; private final Set subscribers = ConcurrentHashMap.newKeySet(); private final AtomicInteger subscriberCount = new AtomicInteger(); @@ -58,15 +52,18 @@ public final class StatsBroadcaster private int maxConnections = 32; private long broadcastIntervalMs = 2000L; - private StatsBroadcaster() {} + public StatsBroadcaster(HTTPDModule module) + { + this.module = module; + } public synchronized void start() { if (executor != null) return; - maxConnections = HTTPDModule.moduleConfig.getInt("server.sse.max-connections", 32); - broadcastIntervalMs = HTTPDModule.moduleConfig.getLong("server.sse.broadcast-interval-ms", 2000L); - int threads = Math.max(1, HTTPDModule.moduleConfig.getInt("server.sse.threads", 2)); + maxConnections = module.getModuleConfig().getInt("server.sse.max-connections", 32); + broadcastIntervalMs = module.getModuleConfig().getLong("server.sse.broadcast-interval-ms", 2000L); + int threads = Math.max(1, module.getModuleConfig().getInt("server.sse.threads", 2)); executor = Executors.newScheduledThreadPool(threads, r -> { @@ -77,11 +74,11 @@ public final class StatsBroadcaster try { - bukkitTask = HTTPDModule.plexApi().scheduler().runGlobalTimer(this::sampleBukkit, 1L, 40L); + bukkitTask = module.api().scheduler().runGlobalTimer(this::sampleBukkit, 1L, 40L); } catch (Throwable t) { - HTTPDModule.plexApi().logging().debug("StatsBroadcaster: could not register Bukkit sampling task: " + t.getMessage()); + module.api().logging().debug("StatsBroadcaster: could not register Bukkit sampling task: " + t.getMessage()); } broadcastTask = executor.scheduleAtFixedRate(