This commit is contained in:
parent
1876f11fc2
commit
2a0e89dfb7
|
|
@ -6,7 +6,6 @@ import net.neoforged.neoforge.common.ModConfigSpec;
|
||||||
public final class Config {
|
public final class Config {
|
||||||
private static final ModConfigSpec.Builder BUILDER = new ModConfigSpec.Builder();
|
private static final ModConfigSpec.Builder BUILDER = new ModConfigSpec.Builder();
|
||||||
|
|
||||||
// API настройки
|
|
||||||
private static final ModConfigSpec.ConfigValue<String> API_BASE_URL = BUILDER
|
private static final ModConfigSpec.ConfigValue<String> API_BASE_URL = BUILDER
|
||||||
.comment("Base URL for whitelist API")
|
.comment("Base URL for whitelist API")
|
||||||
.define("apiBaseUrl", "http://localhost:8080/api/v1/whitelist");
|
.define("apiBaseUrl", "http://localhost:8080/api/v1/whitelist");
|
||||||
|
|
@ -29,7 +28,7 @@ public final class Config {
|
||||||
|
|
||||||
static final ModConfigSpec SPEC = BUILDER.build();
|
static final ModConfigSpec SPEC = BUILDER.build();
|
||||||
|
|
||||||
// кэш значений
|
|
||||||
public static String apiBaseUrl;
|
public static String apiBaseUrl;
|
||||||
public static String apiKey;
|
public static String apiKey;
|
||||||
public static int requestTimeout;
|
public static int requestTimeout;
|
||||||
|
|
@ -38,7 +37,6 @@ public final class Config {
|
||||||
|
|
||||||
private Config() {}
|
private Config() {}
|
||||||
|
|
||||||
// подписка идёт через modEventBus.addListener(Config::onLoad) — без @EventBusSubscriber и без устаревшего 'bus'
|
|
||||||
static void onLoad(final ModConfigEvent event) {
|
static void onLoad(final ModConfigEvent event) {
|
||||||
if (event.getConfig().getSpec() != SPEC) return;
|
if (event.getConfig().getSpec() != SPEC) return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,19 +18,10 @@ public class Whitelist {
|
||||||
|
|
||||||
private static final Logger LOGGER = LogUtils.getLogger();
|
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) {
|
public Whitelist(IEventBus modEventBus, ModContainer modContainer) {
|
||||||
modEventBus.addListener(this::commonSetup);
|
modEventBus.addListener(this::commonSetup);
|
||||||
modEventBus.addListener(this::addCreative);
|
|
||||||
modEventBus.addListener(Config::onLoad);
|
modEventBus.addListener(Config::onLoad);
|
||||||
|
|
||||||
BLOCKS.register(modEventBus);
|
|
||||||
ITEMS.register(modEventBus);
|
|
||||||
CREATIVE_MODE_TABS.register(modEventBus);
|
|
||||||
|
|
||||||
modContainer.registerConfig(ModConfig.Type.COMMON, Config.SPEC);
|
modContainer.registerConfig(ModConfig.Type.COMMON, Config.SPEC);
|
||||||
}
|
}
|
||||||
|
|
@ -39,6 +30,4 @@ public class Whitelist {
|
||||||
LOGGER.info("whitelist initialized");
|
LOGGER.info("whitelist initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addCreative(BuildCreativeModeTabContentsEvent event) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package org.itqop.whitelist;
|
package org.itqop.whitelist;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParser;
|
import com.google.gson.JsonParser;
|
||||||
import com.mojang.logging.LogUtils;
|
import com.mojang.logging.LogUtils;
|
||||||
|
|
@ -12,154 +13,178 @@ import java.net.http.HttpRequest;
|
||||||
import java.net.http.HttpResponse;
|
import java.net.http.HttpResponse;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.UUID;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class WhitelistApiClient {
|
public class WhitelistApiClient {
|
||||||
private static final Logger LOGGER = LogUtils.getLogger();
|
private static final Logger LOGGER = LogUtils.getLogger();
|
||||||
private static final Gson GSON = new Gson();
|
private static final Gson GSON = new Gson();
|
||||||
|
private final HttpClient http;
|
||||||
private final HttpClient httpClient;
|
private volatile String baseUrl;
|
||||||
private final String baseUrl;
|
|
||||||
private final String apiKey;
|
|
||||||
private final int timeoutSeconds;
|
|
||||||
private final boolean enableLogging;
|
|
||||||
|
|
||||||
public WhitelistApiClient() {
|
public WhitelistApiClient() {
|
||||||
this.baseUrl = Config.apiBaseUrl;
|
this.http = HttpClient.newBuilder()
|
||||||
this.apiKey = Config.apiKey;
|
.version(HttpClient.Version.HTTP_1_1)
|
||||||
this.timeoutSeconds = Config.requestTimeout;
|
.connectTimeout(Duration.ofSeconds(Math.max(1, Config.requestTimeout)))
|
||||||
this.enableLogging = Config.enableLogging;
|
|
||||||
|
|
||||||
this.httpClient = HttpClient.newBuilder()
|
|
||||||
.connectTimeout(Duration.ofSeconds(timeoutSeconds))
|
|
||||||
.build();
|
.build();
|
||||||
|
this.baseUrl = normalizeBaseUrl(Config.apiBaseUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<Boolean> addPlayer(String playerName, UUID playerUuid, String addedBy, String reason) {
|
public void refreshFromConfig() {
|
||||||
JsonObject requestBody = new JsonObject();
|
this.baseUrl = normalizeBaseUrl(Config.apiBaseUrl);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return makeRequest("POST", "/add", requestBody.toString())
|
private static String normalizeBaseUrl(String url) {
|
||||||
.thenApply(response -> {
|
if (url == null || url.isBlank()) return "";
|
||||||
if (response.statusCode() == 201) {
|
String u = url.trim();
|
||||||
if (enableLogging) {
|
while (u.endsWith("/")) u = u.substring(0, u.length() - 1);
|
||||||
LOGGER.info("Player {} successfully added to whitelist", playerName);
|
return u;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
} else {
|
public CompletableFuture<Boolean> addPlayer(String playerName) {
|
||||||
LOGGER.error("Error adding player {}: HTTP {}", playerName, response.statusCode());
|
return addPlayer(playerName, null, null, null);
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
})
|
public CompletableFuture<Boolean> addPlayer(String playerName, String reason) {
|
||||||
.exceptionally(throwable -> {
|
return addPlayer(playerName, null, null, null);
|
||||||
LOGGER.error("Error adding player {}: {}", playerName, throwable.getMessage());
|
}
|
||||||
|
|
||||||
|
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;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<Boolean> removePlayer(String playerName) {
|
public CompletableFuture<Boolean> removePlayer(String playerName) {
|
||||||
JsonObject requestBody = new JsonObject();
|
Objects.requireNonNull(playerName, "playerName");
|
||||||
requestBody.addProperty("player_name", playerName);
|
Instant t0 = Instant.now();
|
||||||
|
if (Config.enableLogging) LOGGER.info("[REMOVE] start -> player={}", playerName);
|
||||||
return makeRequest("POST", "/remove", requestBody.toString())
|
JsonObject body = new JsonObject();
|
||||||
.thenApply(response -> {
|
body.addProperty("player_name", playerName);
|
||||||
if (response.statusCode() == 204) {
|
return makeRequest("POST", "/remove", body)
|
||||||
if (enableLogging) {
|
.orTimeout(Config.requestTimeout, TimeUnit.SECONDS)
|
||||||
LOGGER.info("Player {} successfully removed from whitelist", playerName);
|
.thenApply(resp -> handleBooleanResponse(resp, "REMOVE", playerName, t0))
|
||||||
}
|
.exceptionally(ex -> {
|
||||||
return true;
|
long ms = Duration.between(t0, Instant.now()).toMillis();
|
||||||
} else {
|
LOGGER.error("[REMOVE] fail player={} in {} ms: {}", playerName, ms, ex.toString());
|
||||||
LOGGER.error("Error removing player {}: HTTP {}", playerName, response.statusCode());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.exceptionally(throwable -> {
|
|
||||||
LOGGER.error("Error removing player {}: {}", playerName, throwable.getMessage());
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<WhitelistCheckResult> checkPlayer(String playerName) {
|
public CompletableFuture<CheckResponse> checkPlayer(String playerName) {
|
||||||
JsonObject requestBody = new JsonObject();
|
Objects.requireNonNull(playerName, "playerName");
|
||||||
requestBody.addProperty("player_name", playerName);
|
Instant t0 = Instant.now();
|
||||||
|
if (Config.enableLogging) LOGGER.info("[CHECK] start -> player={}", playerName);
|
||||||
return makeRequest("POST", "/check", requestBody.toString())
|
JsonObject body = new JsonObject();
|
||||||
.thenApply(response -> {
|
body.addProperty("player_name", playerName);
|
||||||
if (response.statusCode() == 200) {
|
return makeRequest("POST", "/check", body)
|
||||||
try {
|
.orTimeout(Config.requestTimeout, TimeUnit.SECONDS)
|
||||||
JsonObject jsonResponse = JsonParser.parseString(response.body()).getAsJsonObject();
|
.thenApply(resp -> {
|
||||||
boolean isWhitelisted = jsonResponse.get("is_whitelisted").getAsBoolean();
|
long ms = Duration.between(t0, Instant.now()).toMillis();
|
||||||
String playerUuid = jsonResponse.has("player_uuid") && !jsonResponse.get("player_uuid").isJsonNull()
|
int code = resp.statusCode();
|
||||||
? jsonResponse.get("player_uuid").getAsString()
|
String bodyStr = resp.body();
|
||||||
: null;
|
if (code / 100 != 2) {
|
||||||
|
LOGGER.warn("[CHECK] non-2xx code={} for player={}, {} ms; body={}",
|
||||||
if (enableLogging) {
|
code, playerName, ms, HttpUtils.abbreviate(bodyStr, 512));
|
||||||
LOGGER.info("Player {} check: {}", playerName, isWhitelisted ? "whitelisted" : "not whitelisted");
|
return new CheckResponse(false, false, null);
|
||||||
}
|
}
|
||||||
|
if (bodyStr == null || bodyStr.isBlank()) return new CheckResponse(true, false, null);
|
||||||
return new WhitelistCheckResult(true, isWhitelisted, playerUuid);
|
try {
|
||||||
} catch (Exception e) {
|
JsonObject json = JsonParser.parseString(bodyStr).getAsJsonObject();
|
||||||
LOGGER.error("Error parsing response for player {}: {}", playerName, e.getMessage());
|
boolean isWhitelisted = json.has("is_whitelisted") && json.get("is_whitelisted").getAsBoolean();
|
||||||
return new WhitelistCheckResult(false, false, null);
|
String uuid = json.has("player_uuid") && !json.get("player_uuid").isJsonNull()
|
||||||
}
|
? json.get("player_uuid").getAsString()
|
||||||
} else {
|
: null;
|
||||||
LOGGER.error("Error checking player {}: HTTP {}", playerName, response.statusCode());
|
if (Config.enableLogging) LOGGER.info("[CHECK] ok player={} -> isWhitelisted={}, uuid={}, {} ms",
|
||||||
return new WhitelistCheckResult(false, false, null);
|
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 -> {
|
.exceptionally(ex -> {
|
||||||
LOGGER.error("Error checking player {}: {}", playerName, throwable.getMessage());
|
long ms = Duration.between(t0, Instant.now()).toMillis();
|
||||||
return new WhitelistCheckResult(false, false, null);
|
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) {
|
private CompletableFuture<HttpResponse<String>> makeRequest(String method, String path, JsonObject body) {
|
||||||
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
|
String url = baseUrl + path;
|
||||||
.uri(URI.create(baseUrl + endpoint))
|
String payload = body == null ? "{}" : GSON.toJson(body);
|
||||||
.header("X-API-Key", apiKey)
|
if (Config.enableLogging) LOGGER.info("[HTTP] {} {} payload_len={}", method, url, payload.length());
|
||||||
.header("Content-Type", "application/json")
|
HttpRequest.Builder b = HttpRequest.newBuilder()
|
||||||
.timeout(Duration.ofSeconds(timeoutSeconds));
|
.uri(URI.create(url))
|
||||||
|
.timeout(Duration.ofSeconds(Math.max(1, Config.requestTimeout)))
|
||||||
if ("POST".equals(method)) {
|
.header("Accept", "application/json");
|
||||||
requestBuilder.POST(HttpRequest.BodyPublishers.ofString(body));
|
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 {
|
} else {
|
||||||
requestBuilder.GET();
|
throw new IllegalArgumentException("Unsupported method: " + method);
|
||||||
}
|
}
|
||||||
|
return http.sendAsync(b.build(), HttpResponse.BodyHandlers.ofString());
|
||||||
HttpRequest request = requestBuilder.build();
|
|
||||||
|
|
||||||
return httpClient.sendAsync(request, 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 success;
|
||||||
private final boolean isWhitelisted;
|
private final boolean isWhitelisted;
|
||||||
private final String playerUuid;
|
private final String playerUuid;
|
||||||
|
public CheckResponse(boolean success, boolean isWhitelisted, String playerUuid) {
|
||||||
public WhitelistCheckResult(boolean success, boolean isWhitelisted, String playerUuid) {
|
|
||||||
this.success = success;
|
this.success = success;
|
||||||
this.isWhitelisted = isWhitelisted;
|
this.isWhitelisted = isWhitelisted;
|
||||||
this.playerUuid = playerUuid;
|
this.playerUuid = playerUuid;
|
||||||
}
|
}
|
||||||
|
public boolean isSuccess() { return success; }
|
||||||
|
public boolean isWhitelisted() { return isWhitelisted; }
|
||||||
|
public String getPlayerUuid() { return playerUuid; }
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isSuccess() {
|
private static final class HttpUtils {
|
||||||
return success;
|
static String abbreviate(String s, int max) {
|
||||||
}
|
if (s == null) return null;
|
||||||
|
if (s.length() <= max) return s;
|
||||||
public boolean isWhitelisted() {
|
return s.substring(0, Math.max(0, max - 3)) + "...";
|
||||||
return isWhitelisted;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPlayerUuid() {
|
|
||||||
return playerUuid;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,186 +7,185 @@ import com.mojang.logging.LogUtils;
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
import net.minecraft.commands.Commands;
|
import net.minecraft.commands.Commands;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import org.slf4j.Logger;
|
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.UUID;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import net.neoforged.fml.loading.FMLPaths;
|
||||||
|
|
||||||
public class WhitelistCommands {
|
public class WhitelistCommands {
|
||||||
private static final Logger LOGGER = LogUtils.getLogger();
|
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) {
|
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
||||||
apiClient = new WhitelistApiClient();
|
dispatcher.register(Commands.literal("wl")
|
||||||
|
.requires(src -> src.hasPermission(3))
|
||||||
dispatcher.register(Commands.literal("whitelist")
|
.then(Commands.literal("on").executes(WhitelistCommands::enableWhitelist))
|
||||||
.then(Commands.literal("on")
|
.then(Commands.literal("off").executes(WhitelistCommands::disableWhitelist))
|
||||||
.executes(WhitelistCommands::enableWhitelist))
|
.then(Commands.literal("status").executes(WhitelistCommands::whitelistStatus))
|
||||||
.then(Commands.literal("off")
|
|
||||||
.executes(WhitelistCommands::disableWhitelist))
|
|
||||||
.then(Commands.literal("status")
|
|
||||||
.executes(WhitelistCommands::whitelistStatus))
|
|
||||||
.then(Commands.literal("add")
|
.then(Commands.literal("add")
|
||||||
.then(Commands.argument("player", StringArgumentType.string())
|
.then(Commands.argument("player", StringArgumentType.string())
|
||||||
.executes(WhitelistCommands::addPlayer)
|
.executes(WhitelistCommands::addPlayer)
|
||||||
.then(Commands.argument("reason", StringArgumentType.greedyString())
|
.then(Commands.argument("reason", StringArgumentType.greedyString())
|
||||||
.executes(WhitelistCommands::addPlayerWithReason))))
|
.executes(WhitelistCommands::addPlayer))))
|
||||||
.then(Commands.literal("remove")
|
.then(Commands.literal("remove")
|
||||||
.then(Commands.argument("player", StringArgumentType.string())
|
.then(Commands.argument("player", StringArgumentType.string())
|
||||||
.executes(WhitelistCommands::removePlayer)))
|
.executes(WhitelistCommands::removePlayer)))
|
||||||
.then(Commands.literal("check")
|
.then(Commands.literal("check")
|
||||||
.then(Commands.argument("player", StringArgumentType.string())
|
.then(Commands.argument("player", StringArgumentType.string())
|
||||||
.executes(WhitelistCommands::checkPlayer)))
|
.executes(WhitelistCommands::checkPlayer)))
|
||||||
.then(Commands.literal("list")
|
);
|
||||||
.executes(WhitelistCommands::listPlayers))
|
|
||||||
.then(Commands.literal("count")
|
|
||||||
.executes(WhitelistCommands::countPlayers)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isWhitelistEnabled() {
|
private static int enableWhitelist(CommandContext<CommandSourceStack> ctx) {
|
||||||
return Config.enableWhitelist;
|
boolean changed = setEnableWhitelistPersisted(ctx, true);
|
||||||
}
|
if (!changed) return 0;
|
||||||
|
sendOnServer(ctx, Component.literal("Whitelist enabled"), true);
|
||||||
private static int enableWhitelist(CommandContext<CommandSourceStack> context) {
|
|
||||||
Config.enableWhitelist = true;
|
|
||||||
context.getSource().sendSuccess(() ->
|
|
||||||
Component.literal("Whitelist enabled"), true);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int disableWhitelist(CommandContext<CommandSourceStack> context) {
|
private static int disableWhitelist(CommandContext<CommandSourceStack> ctx) {
|
||||||
Config.enableWhitelist = false;
|
boolean changed = setEnableWhitelistPersisted(ctx, false);
|
||||||
context.getSource().sendSuccess(() ->
|
if (!changed) return 0;
|
||||||
Component.literal("Whitelist disabled"), true);
|
sendOnServer(ctx, Component.literal("Whitelist disabled"), true);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int whitelistStatus(CommandContext<CommandSourceStack> context) {
|
private static int whitelistStatus(CommandContext<CommandSourceStack> ctx) {
|
||||||
String status = isWhitelistEnabled() ? "enabled" : "disabled";
|
boolean enabled = Config.enableWhitelist;
|
||||||
context.getSource().sendSuccess(() ->
|
sendOnServer(ctx, Component.literal("Whitelist status: " + (enabled ? "ON" : "OFF")), false);
|
||||||
Component.literal("Whitelist is " + status), true);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int addPlayer(CommandContext<CommandSourceStack> context) {
|
private static int addPlayer(CommandContext<CommandSourceStack> ctx) {
|
||||||
return addPlayerWithReason(context);
|
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) {
|
private static int removePlayer(CommandContext<CommandSourceStack> ctx) {
|
||||||
String playerName = StringArgumentType.getString(context, "player");
|
final String player = StringArgumentType.getString(ctx, "player");
|
||||||
String reason = "";
|
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 {
|
try {
|
||||||
reason = StringArgumentType.getString(context, "reason");
|
Config.enableWhitelist = value;
|
||||||
} catch (Exception ignored) {
|
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) {
|
private static String replaceEnableWhitelist(String toml, boolean value) {
|
||||||
String playerName = StringArgumentType.getString(context, "player");
|
Pattern p = Pattern.compile("(?m)^\\s*enableWhitelist\\s*=\\s*(true|false)\\s*$");
|
||||||
|
Matcher m = p.matcher(toml);
|
||||||
context.getSource().sendSuccess(() ->
|
String replacement = "enableWhitelist = " + (value ? "true" : "false");
|
||||||
Component.literal("Removing player " + playerName + " from whitelist..."), false);
|
if (m.find()) {
|
||||||
|
return m.replaceFirst(replacement);
|
||||||
apiClient.removePlayer(playerName)
|
}
|
||||||
.thenAccept(success -> {
|
String sep = toml.endsWith("\n") ? "" : "\n";
|
||||||
if (success) {
|
return toml + sep + replacement + "\n";
|
||||||
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 int checkPlayer(CommandContext<CommandSourceStack> context) {
|
private static String getSourceName(CommandContext<CommandSourceStack> ctx) {
|
||||||
String playerName = StringArgumentType.getString(context, "player");
|
try {
|
||||||
|
if (ctx.getSource().getEntity() instanceof ServerPlayer sp) {
|
||||||
context.getSource().sendSuccess(() ->
|
return sp.getGameProfile().getName();
|
||||||
Component.literal("Checking player " + playerName + " status..."), false);
|
}
|
||||||
|
} catch (Exception ignored) {}
|
||||||
apiClient.checkPlayer(playerName)
|
return "server";
|
||||||
.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 int listPlayers(CommandContext<CommandSourceStack> context) {
|
private static String resolveUuid(MinecraftServer srv, String player) {
|
||||||
context.getSource().sendSuccess(() ->
|
try {
|
||||||
Component.literal("Getting whitelist..."), false);
|
ServerPlayer sp = srv.getPlayerList().getPlayerByName(player);
|
||||||
|
if (sp != null) return sp.getUUID().toString();
|
||||||
// TODO: Implement full list retrieval
|
} catch (Exception ignored) {}
|
||||||
context.getSource().sendSuccess(() ->
|
UUID offline = UUID.nameUUIDFromBytes(("OfflinePlayer:" + player).getBytes(StandardCharsets.UTF_8));
|
||||||
Component.literal("List functionality will be implemented in the next version"), true);
|
return offline.toString();
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue