From 2a0e89dfb744bc040d70a6db12740ac5fe11832f Mon Sep 17 00:00:00 2001 From: itqop Date: Sat, 18 Oct 2025 18:48:58 +0300 Subject: [PATCH] 3 --- src/main/java/org/itqop/whitelist/Config.java | 4 +- .../java/org/itqop/whitelist/Whitelist.java | 11 - .../itqop/whitelist/WhitelistApiClient.java | 247 ++++++++------- .../itqop/whitelist/WhitelistCommands.java | 281 +++++++++--------- 4 files changed, 277 insertions(+), 266 deletions(-) diff --git a/src/main/java/org/itqop/whitelist/Config.java b/src/main/java/org/itqop/whitelist/Config.java index c7c8083..5cad88c 100644 --- a/src/main/java/org/itqop/whitelist/Config.java +++ b/src/main/java/org/itqop/whitelist/Config.java @@ -6,7 +6,6 @@ import net.neoforged.neoforge.common.ModConfigSpec; public final class Config { private static final ModConfigSpec.Builder BUILDER = new ModConfigSpec.Builder(); - // API настройки private static final ModConfigSpec.ConfigValue API_BASE_URL = BUILDER .comment("Base URL for whitelist API") .define("apiBaseUrl", "http://localhost:8080/api/v1/whitelist"); @@ -29,7 +28,7 @@ public final class Config { static final ModConfigSpec SPEC = BUILDER.build(); - // кэш значений + public static String apiBaseUrl; public static String apiKey; public static int requestTimeout; @@ -38,7 +37,6 @@ public final class Config { private Config() {} - // подписка идёт через modEventBus.addListener(Config::onLoad) — без @EventBusSubscriber и без устаревшего 'bus' static void onLoad(final ModConfigEvent event) { if (event.getConfig().getSpec() != SPEC) return; diff --git a/src/main/java/org/itqop/whitelist/Whitelist.java b/src/main/java/org/itqop/whitelist/Whitelist.java index 615e064..8b1b7f9 100644 --- a/src/main/java/org/itqop/whitelist/Whitelist.java +++ b/src/main/java/org/itqop/whitelist/Whitelist.java @@ -18,19 +18,10 @@ public class Whitelist { private static final Logger LOGGER = LogUtils.getLogger(); - public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(MODID); - public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(MODID); - public static final DeferredRegister CREATIVE_MODE_TABS = - DeferredRegister.create(Registries.CREATIVE_MODE_TAB, MODID); - public Whitelist(IEventBus modEventBus, ModContainer modContainer) { modEventBus.addListener(this::commonSetup); - modEventBus.addListener(this::addCreative); modEventBus.addListener(Config::onLoad); - BLOCKS.register(modEventBus); - ITEMS.register(modEventBus); - CREATIVE_MODE_TABS.register(modEventBus); modContainer.registerConfig(ModConfig.Type.COMMON, Config.SPEC); } @@ -39,6 +30,4 @@ public class Whitelist { LOGGER.info("whitelist initialized"); } - private void addCreative(BuildCreativeModeTabContentsEvent event) { - } } diff --git a/src/main/java/org/itqop/whitelist/WhitelistApiClient.java b/src/main/java/org/itqop/whitelist/WhitelistApiClient.java index 98e06b0..6f2aefe 100644 --- a/src/main/java/org/itqop/whitelist/WhitelistApiClient.java +++ b/src/main/java/org/itqop/whitelist/WhitelistApiClient.java @@ -1,6 +1,7 @@ package org.itqop.whitelist; import com.google.gson.Gson; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.mojang.logging.LogUtils; @@ -12,154 +13,178 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.Duration; import java.time.Instant; -import java.util.UUID; +import java.util.Objects; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; public class WhitelistApiClient { private static final Logger LOGGER = LogUtils.getLogger(); private static final Gson GSON = new Gson(); - - private final HttpClient httpClient; - private final String baseUrl; - private final String apiKey; - private final int timeoutSeconds; - private final boolean enableLogging; + private final HttpClient http; + private volatile String baseUrl; public WhitelistApiClient() { - this.baseUrl = Config.apiBaseUrl; - this.apiKey = Config.apiKey; - this.timeoutSeconds = Config.requestTimeout; - this.enableLogging = Config.enableLogging; - - this.httpClient = HttpClient.newBuilder() - .connectTimeout(Duration.ofSeconds(timeoutSeconds)) + this.http = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_1_1) + .connectTimeout(Duration.ofSeconds(Math.max(1, Config.requestTimeout))) .build(); + this.baseUrl = normalizeBaseUrl(Config.apiBaseUrl); } - public CompletableFuture addPlayer(String playerName, UUID playerUuid, String addedBy, String reason) { - JsonObject requestBody = new JsonObject(); - requestBody.addProperty("player_name", playerName); - requestBody.addProperty("player_uuid", playerUuid.toString()); - requestBody.addProperty("added_by", addedBy); - requestBody.addProperty("added_at", Instant.now().toString()); - requestBody.addProperty("is_active", true); - if (reason != null && !reason.isEmpty()) { - requestBody.addProperty("reason", reason); - } + public void refreshFromConfig() { + this.baseUrl = normalizeBaseUrl(Config.apiBaseUrl); + } - return makeRequest("POST", "/add", requestBody.toString()) - .thenApply(response -> { - if (response.statusCode() == 201) { - if (enableLogging) { - LOGGER.info("Player {} successfully added to whitelist", playerName); - } - return true; - } else { - LOGGER.error("Error adding player {}: HTTP {}", playerName, response.statusCode()); - return false; - } - }) - .exceptionally(throwable -> { - LOGGER.error("Error adding player {}: {}", playerName, throwable.getMessage()); + private static String normalizeBaseUrl(String url) { + if (url == null || url.isBlank()) return ""; + String u = url.trim(); + while (u.endsWith("/")) u = u.substring(0, u.length() - 1); + return u; + } + + public CompletableFuture addPlayer(String playerName) { + return addPlayer(playerName, null, null, null); + } + + public CompletableFuture addPlayer(String playerName, String reason) { + return addPlayer(playerName, null, null, null); + } + + public CompletableFuture addPlayer(String playerName, String playerUuid, String addedBy, String addedAt) { + Objects.requireNonNull(playerName, "playerName"); + Instant t0 = Instant.now(); + if (Config.enableLogging) LOGGER.info("[ADD] start -> player={}, reason=null", playerName); + JsonObject body = new JsonObject(); + body.addProperty("player_name", playerName); + if (playerUuid != null && !playerUuid.isBlank()) body.addProperty("player_uuid", playerUuid); + if (addedBy != null && !addedBy.isBlank()) body.addProperty("added_by", addedBy); + if (addedAt != null && !addedAt.isBlank()) body.addProperty("added_at", addedAt); + return makeRequest("POST", "/add", body) + .orTimeout(Config.requestTimeout, TimeUnit.SECONDS) + .thenApply(resp -> handleBooleanResponse(resp, "ADD", playerName, t0)) + .exceptionally(ex -> { + long ms = Duration.between(t0, Instant.now()).toMillis(); + LOGGER.error("[ADD] fail player={} in {} ms: {}", playerName, ms, ex.toString()); return false; }); } public CompletableFuture removePlayer(String playerName) { - JsonObject requestBody = new JsonObject(); - requestBody.addProperty("player_name", playerName); - - return makeRequest("POST", "/remove", requestBody.toString()) - .thenApply(response -> { - if (response.statusCode() == 204) { - if (enableLogging) { - LOGGER.info("Player {} successfully removed from whitelist", playerName); - } - return true; - } else { - LOGGER.error("Error removing player {}: HTTP {}", playerName, response.statusCode()); - return false; - } - }) - .exceptionally(throwable -> { - LOGGER.error("Error removing player {}: {}", playerName, throwable.getMessage()); + Objects.requireNonNull(playerName, "playerName"); + Instant t0 = Instant.now(); + if (Config.enableLogging) LOGGER.info("[REMOVE] start -> player={}", playerName); + JsonObject body = new JsonObject(); + body.addProperty("player_name", playerName); + return makeRequest("POST", "/remove", body) + .orTimeout(Config.requestTimeout, TimeUnit.SECONDS) + .thenApply(resp -> handleBooleanResponse(resp, "REMOVE", playerName, t0)) + .exceptionally(ex -> { + long ms = Duration.between(t0, Instant.now()).toMillis(); + LOGGER.error("[REMOVE] fail player={} in {} ms: {}", playerName, ms, ex.toString()); return false; }); } - public CompletableFuture checkPlayer(String playerName) { - JsonObject requestBody = new JsonObject(); - requestBody.addProperty("player_name", playerName); - - return makeRequest("POST", "/check", requestBody.toString()) - .thenApply(response -> { - if (response.statusCode() == 200) { - try { - JsonObject jsonResponse = JsonParser.parseString(response.body()).getAsJsonObject(); - boolean isWhitelisted = jsonResponse.get("is_whitelisted").getAsBoolean(); - String playerUuid = jsonResponse.has("player_uuid") && !jsonResponse.get("player_uuid").isJsonNull() - ? jsonResponse.get("player_uuid").getAsString() - : null; - - if (enableLogging) { - LOGGER.info("Player {} check: {}", playerName, isWhitelisted ? "whitelisted" : "not whitelisted"); - } - - return new WhitelistCheckResult(true, isWhitelisted, playerUuid); - } catch (Exception e) { - LOGGER.error("Error parsing response for player {}: {}", playerName, e.getMessage()); - return new WhitelistCheckResult(false, false, null); - } - } else { - LOGGER.error("Error checking player {}: HTTP {}", playerName, response.statusCode()); - return new WhitelistCheckResult(false, false, null); + public CompletableFuture checkPlayer(String playerName) { + Objects.requireNonNull(playerName, "playerName"); + Instant t0 = Instant.now(); + if (Config.enableLogging) LOGGER.info("[CHECK] start -> player={}", playerName); + JsonObject body = new JsonObject(); + body.addProperty("player_name", playerName); + return makeRequest("POST", "/check", body) + .orTimeout(Config.requestTimeout, TimeUnit.SECONDS) + .thenApply(resp -> { + long ms = Duration.between(t0, Instant.now()).toMillis(); + int code = resp.statusCode(); + String bodyStr = resp.body(); + if (code / 100 != 2) { + LOGGER.warn("[CHECK] non-2xx code={} for player={}, {} ms; body={}", + code, playerName, ms, HttpUtils.abbreviate(bodyStr, 512)); + return new CheckResponse(false, false, null); + } + if (bodyStr == null || bodyStr.isBlank()) return new CheckResponse(true, false, null); + try { + JsonObject json = JsonParser.parseString(bodyStr).getAsJsonObject(); + boolean isWhitelisted = json.has("is_whitelisted") && json.get("is_whitelisted").getAsBoolean(); + String uuid = json.has("player_uuid") && !json.get("player_uuid").isJsonNull() + ? json.get("player_uuid").getAsString() + : null; + if (Config.enableLogging) LOGGER.info("[CHECK] ok player={} -> isWhitelisted={}, uuid={}, {} ms", + playerName, isWhitelisted, uuid, ms); + return new CheckResponse(true, isWhitelisted, uuid); + } catch (Exception e) { + LOGGER.error("[CHECK] parse error player={} in {} ms: {}\nbody={}", + playerName, ms, e.toString(), HttpUtils.abbreviate(bodyStr, 512)); + return new CheckResponse(false, false, null); } }) - .exceptionally(throwable -> { - LOGGER.error("Error checking player {}: {}", playerName, throwable.getMessage()); - return new WhitelistCheckResult(false, false, null); + .exceptionally(ex -> { + long ms = Duration.between(t0, Instant.now()).toMillis(); + LOGGER.error("[CHECK] fail player={} in {} ms: {}", playerName, ms, ex.toString()); + return new CheckResponse(false, false, null); }); } - private CompletableFuture> makeRequest(String method, String endpoint, String body) { - HttpRequest.Builder requestBuilder = HttpRequest.newBuilder() - .uri(URI.create(baseUrl + endpoint)) - .header("X-API-Key", apiKey) - .header("Content-Type", "application/json") - .timeout(Duration.ofSeconds(timeoutSeconds)); - - if ("POST".equals(method)) { - requestBuilder.POST(HttpRequest.BodyPublishers.ofString(body)); + private CompletableFuture> makeRequest(String method, String path, JsonObject body) { + String url = baseUrl + path; + String payload = body == null ? "{}" : GSON.toJson(body); + if (Config.enableLogging) LOGGER.info("[HTTP] {} {} payload_len={}", method, url, payload.length()); + HttpRequest.Builder b = HttpRequest.newBuilder() + .uri(URI.create(url)) + .timeout(Duration.ofSeconds(Math.max(1, Config.requestTimeout))) + .header("Accept", "application/json"); + if (Config.apiKey != null && !Config.apiKey.isBlank()) b.header("X-API-Key", Config.apiKey); + if ("GET".equals(method)) { + b.GET(); + } else if ("POST".equals(method)) { + b.header("Content-Type", "application/json"); + b.POST(HttpRequest.BodyPublishers.ofString(payload)); } else { - requestBuilder.GET(); + throw new IllegalArgumentException("Unsupported method: " + method); } - - HttpRequest request = requestBuilder.build(); - - return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()); + return http.sendAsync(b.build(), HttpResponse.BodyHandlers.ofString()); } - public static class WhitelistCheckResult { + private boolean handleBooleanResponse(HttpResponse resp, String op, String playerName, Instant t0) { + long ms = Duration.between(t0, Instant.now()).toMillis(); + int code = resp.statusCode(); + String body = resp.body(); + if (code / 100 != 2) { + LOGGER.warn("[{}] non-2xx code={} player={} {} ms; body={}", op, code, playerName, ms, HttpUtils.abbreviate(body, 512)); + return false; + } + if (body == null || body.isBlank()) return true; + try { + JsonElement el = JsonParser.parseString(body); + if (el.isJsonObject()) { + JsonObject json = el.getAsJsonObject(); + if (json.has("success")) return json.get("success").getAsBoolean(); + } + return true; + } catch (Exception e) { + return true; + } + } + + public static final class CheckResponse { private final boolean success; private final boolean isWhitelisted; private final String playerUuid; - - public WhitelistCheckResult(boolean success, boolean isWhitelisted, String playerUuid) { + public CheckResponse(boolean success, boolean isWhitelisted, String playerUuid) { this.success = success; this.isWhitelisted = isWhitelisted; this.playerUuid = playerUuid; } + public boolean isSuccess() { return success; } + public boolean isWhitelisted() { return isWhitelisted; } + public String getPlayerUuid() { return playerUuid; } + } - public boolean isSuccess() { - return success; - } - - public boolean isWhitelisted() { - return isWhitelisted; - } - - public String getPlayerUuid() { - return playerUuid; + private static final class HttpUtils { + static String abbreviate(String s, int max) { + if (s == null) return null; + if (s.length() <= max) return s; + return s.substring(0, Math.max(0, max - 3)) + "..."; } } } diff --git a/src/main/java/org/itqop/whitelist/WhitelistCommands.java b/src/main/java/org/itqop/whitelist/WhitelistCommands.java index 3dfb6f1..f783c8c 100644 --- a/src/main/java/org/itqop/whitelist/WhitelistCommands.java +++ b/src/main/java/org/itqop/whitelist/WhitelistCommands.java @@ -7,186 +7,185 @@ import com.mojang.logging.LogUtils; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; import org.slf4j.Logger; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Instant; +import java.util.Locale; import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import net.neoforged.fml.loading.FMLPaths; public class WhitelistCommands { private static final Logger LOGGER = LogUtils.getLogger(); - private static WhitelistApiClient apiClient; + private static final WhitelistApiClient API = new WhitelistApiClient(); public static void register(CommandDispatcher dispatcher) { - apiClient = new WhitelistApiClient(); - - dispatcher.register(Commands.literal("whitelist") - .then(Commands.literal("on") - .executes(WhitelistCommands::enableWhitelist)) - .then(Commands.literal("off") - .executes(WhitelistCommands::disableWhitelist)) - .then(Commands.literal("status") - .executes(WhitelistCommands::whitelistStatus)) + dispatcher.register(Commands.literal("wl") + .requires(src -> src.hasPermission(3)) + .then(Commands.literal("on").executes(WhitelistCommands::enableWhitelist)) + .then(Commands.literal("off").executes(WhitelistCommands::disableWhitelist)) + .then(Commands.literal("status").executes(WhitelistCommands::whitelistStatus)) .then(Commands.literal("add") .then(Commands.argument("player", StringArgumentType.string()) .executes(WhitelistCommands::addPlayer) .then(Commands.argument("reason", StringArgumentType.greedyString()) - .executes(WhitelistCommands::addPlayerWithReason)))) + .executes(WhitelistCommands::addPlayer)))) .then(Commands.literal("remove") .then(Commands.argument("player", StringArgumentType.string()) .executes(WhitelistCommands::removePlayer))) .then(Commands.literal("check") .then(Commands.argument("player", StringArgumentType.string()) .executes(WhitelistCommands::checkPlayer))) - .then(Commands.literal("list") - .executes(WhitelistCommands::listPlayers)) - .then(Commands.literal("count") - .executes(WhitelistCommands::countPlayers))); + ); } - private static boolean isWhitelistEnabled() { - return Config.enableWhitelist; - } - - private static int enableWhitelist(CommandContext context) { - Config.enableWhitelist = true; - context.getSource().sendSuccess(() -> - Component.literal("Whitelist enabled"), true); + private static int enableWhitelist(CommandContext ctx) { + boolean changed = setEnableWhitelistPersisted(ctx, true); + if (!changed) return 0; + sendOnServer(ctx, Component.literal("Whitelist enabled"), true); return 1; } - private static int disableWhitelist(CommandContext context) { - Config.enableWhitelist = false; - context.getSource().sendSuccess(() -> - Component.literal("Whitelist disabled"), true); + private static int disableWhitelist(CommandContext ctx) { + boolean changed = setEnableWhitelistPersisted(ctx, false); + if (!changed) return 0; + sendOnServer(ctx, Component.literal("Whitelist disabled"), true); return 1; } - private static int whitelistStatus(CommandContext context) { - String status = isWhitelistEnabled() ? "enabled" : "disabled"; - context.getSource().sendSuccess(() -> - Component.literal("Whitelist is " + status), true); + private static int whitelistStatus(CommandContext ctx) { + boolean enabled = Config.enableWhitelist; + sendOnServer(ctx, Component.literal("Whitelist status: " + (enabled ? "ON" : "OFF")), false); return 1; } - private static int addPlayer(CommandContext context) { - return addPlayerWithReason(context); + private static int addPlayer(CommandContext ctx) { + final String player = StringArgumentType.getString(ctx, "player"); + final String reason = ctx.getInput().contains(" add ") && ctx.getInput().split(" add ").length > 1 && ctx.getInput().split(" add ")[1].contains(" ") + ? ctx.getInput().substring(ctx.getInput().indexOf(" add ") + 5 + player.length()).trim() + : null; + final String addedBy = getSourceName(ctx); + final String addedAt = Instant.now().toString(); + final String uuid = resolveUuid(ctx.getSource().getServer(), player); + sendOnServer(ctx, Component.literal("Adding player to whitelist: " + player), false); + API.addPlayer(player, uuid, addedBy, addedAt) + .thenAccept(success -> dispatchBack(ctx, () -> { + if (success) { + ctx.getSource().sendSuccess(() -> Component.literal("Player " + player + " added to whitelist"), true); + } else { + ctx.getSource().sendFailure(Component.literal("Failed to add player " + player + " to whitelist")); + } + })) + .exceptionally(ex -> { + dispatchBack(ctx, () -> ctx.getSource().sendFailure(Component.literal("Error adding player: " + ex.getMessage()))); + return null; + }); + return 1; } - private static int addPlayerWithReason(CommandContext context) { - String playerName = StringArgumentType.getString(context, "player"); - String reason = ""; - + private static int removePlayer(CommandContext ctx) { + final String player = StringArgumentType.getString(ctx, "player"); + sendOnServer(ctx, Component.literal("Removing player from whitelist: " + player), false); + API.removePlayer(player) + .thenAccept(success -> dispatchBack(ctx, () -> { + if (success) { + ctx.getSource().sendSuccess(() -> Component.literal("Player " + player + " removed from whitelist"), true); + } else { + ctx.getSource().sendFailure(Component.literal("Failed to remove player " + player + " from whitelist")); + } + })) + .exceptionally(ex -> { + dispatchBack(ctx, () -> ctx.getSource().sendFailure(Component.literal("Error removing player: " + ex.getMessage()))); + return null; + }); + return 1; + } + + private static int checkPlayer(CommandContext ctx) { + final String player = StringArgumentType.getString(ctx, "player"); + sendOnServer(ctx, Component.literal("Checking whitelist status: " + player), false); + API.checkPlayer(player) + .thenAccept(res -> dispatchBack(ctx, () -> { + if (!res.isSuccess()) { + ctx.getSource().sendFailure(Component.literal("Check failed for player " + player)); + return; + } + String msg = String.format(Locale.ROOT, + "Player %s -> whitelisted=%s%s", + player, + res.isWhitelisted(), + res.getPlayerUuid() != null ? ", uuid=" + res.getPlayerUuid() : ""); + ctx.getSource().sendSuccess(() -> Component.literal(msg), false); + })) + .exceptionally(ex -> { + dispatchBack(ctx, () -> ctx.getSource().sendFailure(Component.literal("Error checking player: " + ex.getMessage()))); + return null; + }); + return 1; + } + + private static void sendOnServer(CommandContext ctx, Component msg, boolean broadcastToAdmins) { + dispatchBack(ctx, () -> ctx.getSource().sendSuccess(() -> msg, broadcastToAdmins)); + } + + private static void dispatchBack(CommandContext ctx, Runnable action) { + MinecraftServer srv = ctx.getSource().getServer(); + srv.execute(action); + } + + private static boolean setEnableWhitelistPersisted(CommandContext ctx, boolean value) { try { - reason = StringArgumentType.getString(context, "reason"); - } catch (Exception ignored) { + Config.enableWhitelist = value; + Path cfgPath = FMLPaths.CONFIGDIR.get().resolve(Whitelist.MODID + "-common.toml"); + if (Files.exists(cfgPath)) { + String content = Files.readString(cfgPath); + String updated = replaceEnableWhitelist(content, value); + if (!content.equals(updated)) { + Files.writeString(cfgPath, updated); + } + } + API.refreshFromConfig(); + return true; + } catch (Exception e) { + dispatchBack(ctx, () -> ctx.getSource().sendFailure(Component.literal("Failed to persist config: " + e.getMessage()))); + LOGGER.error("Persist enableWhitelist error", e); + return false; } - - String addedBy = context.getSource().getTextName(); - - MinecraftServer server = context.getSource().getServer(); - ServerPlayer player = server.getPlayerList().getPlayerByName(playerName); - UUID playerUuid = player != null ? player.getUUID() : null; - - context.getSource().sendSuccess(() -> - Component.literal("Adding player " + playerName + " to whitelist..."), false); - - assert playerUuid != null; - apiClient.addPlayer(playerName, playerUuid, addedBy, reason) - .thenAccept(success -> { - if (success) { - context.getSource().sendSuccess(() -> - Component.literal("Player " + playerName + " successfully added to whitelist"), true); - } else { - context.getSource().sendFailure( - Component.literal("Error adding player " + playerName + " to whitelist")); - } - }) - .exceptionally(throwable -> { - LOGGER.error("Error adding player: ", throwable); - context.getSource().sendFailure( - Component.literal("Error adding player " + playerName + ": " + throwable.getMessage())); - return null; - }); - - return 1; } - private static int removePlayer(CommandContext context) { - String playerName = StringArgumentType.getString(context, "player"); - - context.getSource().sendSuccess(() -> - Component.literal("Removing player " + playerName + " from whitelist..."), false); - - apiClient.removePlayer(playerName) - .thenAccept(success -> { - if (success) { - context.getSource().sendSuccess(() -> - Component.literal("Player " + playerName + " successfully removed from whitelist"), true); - } else { - context.getSource().sendFailure( - Component.literal("Error removing player " + playerName + " from whitelist")); - } - }) - .exceptionally(throwable -> { - LOGGER.error("Error removing player: ", throwable); - context.getSource().sendFailure( - Component.literal("Error removing player " + playerName + ": " + throwable.getMessage())); - return null; - }); - - return 1; + private static String replaceEnableWhitelist(String toml, boolean value) { + Pattern p = Pattern.compile("(?m)^\\s*enableWhitelist\\s*=\\s*(true|false)\\s*$"); + Matcher m = p.matcher(toml); + String replacement = "enableWhitelist = " + (value ? "true" : "false"); + if (m.find()) { + return m.replaceFirst(replacement); + } + String sep = toml.endsWith("\n") ? "" : "\n"; + return toml + sep + replacement + "\n"; } - private static int checkPlayer(CommandContext context) { - String playerName = StringArgumentType.getString(context, "player"); - - context.getSource().sendSuccess(() -> - Component.literal("Checking player " + playerName + " status..."), false); - - apiClient.checkPlayer(playerName) - .thenAccept(result -> { - if (result.isSuccess()) { - String status = result.isWhitelisted() ? "whitelisted" : "not whitelisted"; - String uuidInfo = result.getPlayerUuid() != null ? - " (UUID: " + result.getPlayerUuid() + ")" : ""; - - context.getSource().sendSuccess(() -> - Component.literal("Player " + playerName + " is " + status + uuidInfo), true); - } else { - context.getSource().sendFailure( - Component.literal("Error checking player " + playerName + " status")); - } - }) - .exceptionally(throwable -> { - LOGGER.error("Error checking player: ", throwable); - context.getSource().sendFailure( - Component.literal("Error checking player " + playerName + ": " + throwable.getMessage())); - return null; - }); - - return 1; + private static String getSourceName(CommandContext ctx) { + try { + if (ctx.getSource().getEntity() instanceof ServerPlayer sp) { + return sp.getGameProfile().getName(); + } + } catch (Exception ignored) {} + return "server"; } - private static int listPlayers(CommandContext context) { - context.getSource().sendSuccess(() -> - Component.literal("Getting whitelist..."), false); - - // TODO: Implement full list retrieval - context.getSource().sendSuccess(() -> - Component.literal("List functionality will be implemented in the next version"), true); - - return 1; - } - - private static int countPlayers(CommandContext context) { - context.getSource().sendSuccess(() -> - Component.literal("Counting players in whitelist..."), false); - - // TODO: Implement player counting - context.getSource().sendSuccess(() -> - Component.literal("Count functionality will be implemented in the next version"), true); - - return 1; + private static String resolveUuid(MinecraftServer srv, String player) { + try { + ServerPlayer sp = srv.getPlayerList().getPlayerByName(player); + if (sp != null) return sp.getUUID().toString(); + } catch (Exception ignored) {} + UUID offline = UUID.nameUUIDFromBytes(("OfflinePlayer:" + player).getBytes(StandardCharsets.UTF_8)); + return offline.toString(); } }