This commit is contained in:
itqop 2025-10-18 18:48:58 +03:00
parent 1876f11fc2
commit 2a0e89dfb7
4 changed files with 277 additions and 266 deletions

View File

@ -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<String> 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;

View File

@ -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<CreativeModeTab> 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) {
}
}

View File

@ -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<Boolean> 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<Boolean> addPlayer(String playerName) {
return addPlayer(playerName, null, null, null);
}
public CompletableFuture<Boolean> addPlayer(String playerName, String reason) {
return addPlayer(playerName, null, null, null);
}
public CompletableFuture<Boolean> 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<Boolean> 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<WhitelistCheckResult> 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<CheckResponse> 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<HttpResponse<String>> 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<HttpResponse<String>> 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<String> 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)) + "...";
}
}
}

View File

@ -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<CommandSourceStack> 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<CommandSourceStack> context) {
Config.enableWhitelist = true;
context.getSource().sendSuccess(() ->
Component.literal("Whitelist enabled"), true);
private static int enableWhitelist(CommandContext<CommandSourceStack> ctx) {
boolean changed = setEnableWhitelistPersisted(ctx, true);
if (!changed) return 0;
sendOnServer(ctx, Component.literal("Whitelist enabled"), true);
return 1;
}
private static int disableWhitelist(CommandContext<CommandSourceStack> context) {
Config.enableWhitelist = false;
context.getSource().sendSuccess(() ->
Component.literal("Whitelist disabled"), true);
private static int disableWhitelist(CommandContext<CommandSourceStack> ctx) {
boolean changed = setEnableWhitelistPersisted(ctx, false);
if (!changed) return 0;
sendOnServer(ctx, Component.literal("Whitelist disabled"), true);
return 1;
}
private static int whitelistStatus(CommandContext<CommandSourceStack> context) {
String status = isWhitelistEnabled() ? "enabled" : "disabled";
context.getSource().sendSuccess(() ->
Component.literal("Whitelist is " + status), true);
private static int whitelistStatus(CommandContext<CommandSourceStack> ctx) {
boolean enabled = Config.enableWhitelist;
sendOnServer(ctx, Component.literal("Whitelist status: " + (enabled ? "ON" : "OFF")), false);
return 1;
}
private static int addPlayer(CommandContext<CommandSourceStack> context) {
return addPlayerWithReason(context);
private static int addPlayer(CommandContext<CommandSourceStack> 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<CommandSourceStack> context) {
String playerName = StringArgumentType.getString(context, "player");
String reason = "";
private static int removePlayer(CommandContext<CommandSourceStack> 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<CommandSourceStack> 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<CommandSourceStack> ctx, Component msg, boolean broadcastToAdmins) {
dispatchBack(ctx, () -> ctx.getSource().sendSuccess(() -> msg, broadcastToAdmins));
}
private static void dispatchBack(CommandContext<CommandSourceStack> ctx, Runnable action) {
MinecraftServer srv = ctx.getSource().getServer();
srv.execute(action);
}
private static boolean setEnableWhitelistPersisted(CommandContext<CommandSourceStack> 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<CommandSourceStack> 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<CommandSourceStack> 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<CommandSourceStack> ctx) {
try {
if (ctx.getSource().getEntity() instanceof ServerPlayer sp) {
return sp.getGameProfile().getName();
}
} catch (Exception ignored) {}
return "server";
}
private static int listPlayers(CommandContext<CommandSourceStack> 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<CommandSourceStack> 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();
}
}