This commit is contained in:
		
							parent
							
								
									8749276420
								
							
						
					
					
						commit
						e409c0d907
					
				| 
						 | 
				
			
			@ -15,12 +15,16 @@ import java.time.Duration;
 | 
			
		|||
import java.time.Instant;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import java.util.concurrent.CompletableFuture;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
 | 
			
		||||
public class WhitelistApiClient {
 | 
			
		||||
    private static final WhitelistApiClient INSTANCE = new WhitelistApiClient();
 | 
			
		||||
    public static WhitelistApiClient get() { return INSTANCE; }
 | 
			
		||||
    public static void refreshConfigFromSpec() { INSTANCE.refreshFromConfig(); }
 | 
			
		||||
 | 
			
		||||
    private static final Logger LOGGER = LogUtils.getLogger();
 | 
			
		||||
    private static final Gson GSON = new Gson();
 | 
			
		||||
    private final HttpClient http;
 | 
			
		||||
 | 
			
		||||
    private volatile HttpClient http;
 | 
			
		||||
    private volatile String baseUrl;
 | 
			
		||||
 | 
			
		||||
    public WhitelistApiClient() {
 | 
			
		||||
| 
						 | 
				
			
			@ -38,12 +42,16 @@ public class WhitelistApiClient {
 | 
			
		|||
    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);
 | 
			
		||||
        if (u.endsWith("/")) u = u.substring(0, u.length() - 1);
 | 
			
		||||
        return u;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public CompletableFuture<Boolean> addPlayer(String playerName) {
 | 
			
		||||
        return addPlayer(playerName, null, null, null);
 | 
			
		||||
    private HttpRequest.Builder baseBuilder(String path) {
 | 
			
		||||
        return HttpRequest.newBuilder(URI.create(baseUrl + path))
 | 
			
		||||
                .timeout(Duration.ofSeconds(Math.max(1, Config.requestTimeout)))
 | 
			
		||||
                .header("Content-Type", "application/json")
 | 
			
		||||
                .header("Accept", "application/json")
 | 
			
		||||
                .header("X-API-Key", Config.apiKey);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public CompletableFuture<Boolean> addPlayer(String playerName, String reason) {
 | 
			
		||||
| 
						 | 
				
			
			@ -52,120 +60,59 @@ public class WhitelistApiClient {
 | 
			
		|||
 | 
			
		||||
    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);
 | 
			
		||||
        if (Config.enableLogging) LOGGER.info("[ADD] player={}", 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 makeRequest("POST", "/add", HttpRequest.BodyPublishers.ofString(GSON.toJson(body)))
 | 
			
		||||
                .thenApply(resp -> resp != null && resp.statusCode() / 100 == 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public CompletableFuture<Boolean> removePlayer(String playerName) {
 | 
			
		||||
        Objects.requireNonNull(playerName, "playerName");
 | 
			
		||||
        Instant t0 = Instant.now();
 | 
			
		||||
        if (Config.enableLogging) LOGGER.info("[REMOVE] start -> player={}", playerName);
 | 
			
		||||
        if (Config.enableLogging) LOGGER.info("[REMOVE] 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;
 | 
			
		||||
                });
 | 
			
		||||
        return makeRequest("POST", "/remove", HttpRequest.BodyPublishers.ofString(GSON.toJson(body)))
 | 
			
		||||
                .thenApply(resp -> resp != null && resp.statusCode() / 100 == 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public CompletableFuture<CheckResponse> checkPlayer(String playerName) {
 | 
			
		||||
        Objects.requireNonNull(playerName, "playerName");
 | 
			
		||||
        Instant t0 = Instant.now();
 | 
			
		||||
        if (Config.enableLogging) LOGGER.info("[CHECK] start -> player={}", playerName);
 | 
			
		||||
        if (Config.enableLogging) LOGGER.info("[CHECK] 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);
 | 
			
		||||
        return makeRequest("POST", "/check", HttpRequest.BodyPublishers.ofString(GSON.toJson(body)))
 | 
			
		||||
                .thenApply(response -> {
 | 
			
		||||
                    if (response == null) return new CheckResponse(false, false, null);
 | 
			
		||||
                    try {
 | 
			
		||||
                        JsonObject json = JsonParser.parseString(bodyStr).getAsJsonObject();
 | 
			
		||||
                        String bodyStr = response.body();
 | 
			
		||||
                        JsonElement el = JsonParser.parseString(bodyStr);
 | 
			
		||||
                        JsonObject json = el.getAsJsonObject();
 | 
			
		||||
                        boolean ok = json.has("ok") && json.get("ok").getAsBoolean();
 | 
			
		||||
                        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);
 | 
			
		||||
                        return new CheckResponse(ok, 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(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);
 | 
			
		||||
                });
 | 
			
		||||
                .exceptionally(ex -> new CheckResponse(false, false, null));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 {
 | 
			
		||||
            throw new IllegalArgumentException("Unsupported method: " + method);
 | 
			
		||||
        }
 | 
			
		||||
    private CompletableFuture<HttpResponse<String>> makeRequest(String method, String path, HttpRequest.BodyPublisher body) {
 | 
			
		||||
        HttpRequest.Builder b = baseBuilder(path);
 | 
			
		||||
        if ("POST".equalsIgnoreCase(method)) b.POST(body);
 | 
			
		||||
        else if ("PUT".equalsIgnoreCase(method)) b.PUT(body);
 | 
			
		||||
        else if ("DELETE".equalsIgnoreCase(method)) b.method("DELETE", body);
 | 
			
		||||
        else b.method(method, body);
 | 
			
		||||
        return http.sendAsync(b.build(), HttpResponse.BodyHandlers.ofString());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
| 
						 | 
				
			
			@ -179,12 +126,4 @@ public class WhitelistApiClient {
 | 
			
		|||
        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)) + "...";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,29 +3,32 @@ package org.itqop.whitelist;
 | 
			
		|||
import com.mojang.brigadier.CommandDispatcher;
 | 
			
		||||
import com.mojang.brigadier.arguments.StringArgumentType;
 | 
			
		||||
import com.mojang.brigadier.context.CommandContext;
 | 
			
		||||
import com.mojang.logging.LogUtils;
 | 
			
		||||
import com.mojang.brigadier.suggestion.SuggestionProvider;
 | 
			
		||||
import net.minecraft.commands.CommandSourceStack;
 | 
			
		||||
import net.minecraft.commands.Commands;
 | 
			
		||||
import net.minecraft.commands.SharedSuggestionProvider;
 | 
			
		||||
import net.minecraft.network.chat.Component;
 | 
			
		||||
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.List;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
import java.util.regex.Matcher;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
 | 
			
		||||
import net.neoforged.fml.loading.FMLPaths;
 | 
			
		||||
import java.util.concurrent.CompletableFuture;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
public class WhitelistCommands {
 | 
			
		||||
    private static final Logger LOGGER = LogUtils.getLogger();
 | 
			
		||||
    private static final WhitelistApiClient API = new WhitelistApiClient();
 | 
			
		||||
 | 
			
		||||
    private static final SuggestionProvider<CommandSourceStack> ONLINE_PLAYER_SUGGESTIONS = (ctx, builder) -> {
 | 
			
		||||
        List<String> names = ctx.getSource().getServer().getPlayerList().getPlayers()
 | 
			
		||||
                .stream()
 | 
			
		||||
                .map(p -> p.getGameProfile().getName())
 | 
			
		||||
                .collect(Collectors.toList());
 | 
			
		||||
        return SharedSuggestionProvider.suggest(names, builder);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
 | 
			
		||||
        dispatcher.register(Commands.literal("wl")
 | 
			
		||||
                .requires(src -> src.hasPermission(3))
 | 
			
		||||
| 
						 | 
				
			
			@ -34,152 +37,134 @@ public class WhitelistCommands {
 | 
			
		|||
                .then(Commands.literal("status").executes(WhitelistCommands::whitelistStatus))
 | 
			
		||||
                .then(Commands.literal("add")
 | 
			
		||||
                        .then(Commands.argument("player", StringArgumentType.string())
 | 
			
		||||
                                .suggests(ONLINE_PLAYER_SUGGESTIONS)
 | 
			
		||||
                                .executes(WhitelistCommands::addPlayer)
 | 
			
		||||
                                .then(Commands.argument("reason", StringArgumentType.greedyString())
 | 
			
		||||
                                        .executes(WhitelistCommands::addPlayer))))
 | 
			
		||||
                .then(Commands.literal("remove")
 | 
			
		||||
                        .then(Commands.argument("player", StringArgumentType.string())
 | 
			
		||||
                                .suggests(ONLINE_PLAYER_SUGGESTIONS)
 | 
			
		||||
                                .executes(WhitelistCommands::removePlayer)))
 | 
			
		||||
                .then(Commands.literal("check")
 | 
			
		||||
                        .then(Commands.argument("player", StringArgumentType.string())
 | 
			
		||||
                                .suggests(ONLINE_PLAYER_SUGGESTIONS)
 | 
			
		||||
                                .executes(WhitelistCommands::checkPlayer)))
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static int enableWhitelist(CommandContext<CommandSourceStack> ctx) {
 | 
			
		||||
        boolean changed = setEnableWhitelistPersisted(ctx, true);
 | 
			
		||||
        if (!changed) return 0;
 | 
			
		||||
        sendOnServer(ctx, Component.literal("Whitelist enabled"), true);
 | 
			
		||||
        if (!changed) {
 | 
			
		||||
            ctx.getSource().sendFailure(Component.literal("Whitelist already enabled"));
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        ctx.getSource().sendSuccess(() -> Component.literal("Whitelist enabled"), true);
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static int disableWhitelist(CommandContext<CommandSourceStack> ctx) {
 | 
			
		||||
        boolean changed = setEnableWhitelistPersisted(ctx, false);
 | 
			
		||||
        if (!changed) return 0;
 | 
			
		||||
        sendOnServer(ctx, Component.literal("Whitelist disabled"), true);
 | 
			
		||||
        if (!changed) {
 | 
			
		||||
            ctx.getSource().sendFailure(Component.literal("Whitelist already disabled"));
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        ctx.getSource().sendSuccess(() -> Component.literal("Whitelist disabled"), true);
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static int whitelistStatus(CommandContext<CommandSourceStack> ctx) {
 | 
			
		||||
        boolean enabled = Config.enableWhitelist;
 | 
			
		||||
        sendOnServer(ctx, Component.literal("Whitelist status: " + (enabled ? "ON" : "OFF")), false);
 | 
			
		||||
        ctx.getSource().sendSuccess(() -> Component.literal("Whitelist is " + (Config.enableWhitelist ? "ON" : "OFF")), false);
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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"));
 | 
			
		||||
        String player = StringArgumentType.getString(ctx, "player");
 | 
			
		||||
        String input = ctx.getInput();
 | 
			
		||||
        String reason = null;
 | 
			
		||||
        int idx = input.indexOf(" add ");
 | 
			
		||||
        if (idx >= 0) {
 | 
			
		||||
            String tail = input.substring(idx + 5);
 | 
			
		||||
            int sp = tail.indexOf(' ');
 | 
			
		||||
            if (sp >= 0) reason = tail.substring(sp + 1).trim();
 | 
			
		||||
        }
 | 
			
		||||
                }))
 | 
			
		||||
                .exceptionally(ex -> {
 | 
			
		||||
                    dispatchBack(ctx, () -> ctx.getSource().sendFailure(Component.literal("Error adding player: " + ex.getMessage())));
 | 
			
		||||
                    return null;
 | 
			
		||||
                });
 | 
			
		||||
        String addedBy = getSourceName(ctx);
 | 
			
		||||
        String addedAt = Instant.now().toString();
 | 
			
		||||
        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"), false);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        ctx.getSource().sendFailure(Component.literal("Failed to add " + player));
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
        );
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static int removePlayer(CommandContext<CommandSourceStack> ctx) {
 | 
			
		||||
        final String player = StringArgumentType.getString(ctx, "player");
 | 
			
		||||
        String player = StringArgumentType.getString(ctx, "player");
 | 
			
		||||
        sendOnServer(ctx, Component.literal("Removing player from whitelist: " + player), false);
 | 
			
		||||
        API.removePlayer(player)
 | 
			
		||||
                .thenAccept(success -> dispatchBack(ctx, () -> {
 | 
			
		||||
        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"));
 | 
			
		||||
                        ctx.getSource().sendSuccess(() -> Component.literal("Player " + player + " removed"), false);
 | 
			
		||||
                        ServerPlayer sp = ctx.getSource().getServer().getPlayerList().getPlayerByName(player);
 | 
			
		||||
                        if (sp != null) {
 | 
			
		||||
                            sp.connection.disconnect(Component.literal("Removed from whitelist"));
 | 
			
		||||
                        }
 | 
			
		||||
                }))
 | 
			
		||||
                .exceptionally(ex -> {
 | 
			
		||||
                    dispatchBack(ctx, () -> ctx.getSource().sendFailure(Component.literal("Error removing player: " + ex.getMessage())));
 | 
			
		||||
                    return null;
 | 
			
		||||
                });
 | 
			
		||||
                    } else {
 | 
			
		||||
                        ctx.getSource().sendFailure(Component.literal("Failed to remove " + player));
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
        );
 | 
			
		||||
        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 player = StringArgumentType.getString(ctx, "player");
 | 
			
		||||
        API.checkPlayer(player).thenAccept(resp ->
 | 
			
		||||
                dispatchBack(ctx, () -> {
 | 
			
		||||
                    if (resp != null && resp.isSuccess()) {
 | 
			
		||||
                        String msg = resp.isWhitelisted() ? "is in whitelist" : "is NOT in whitelist";
 | 
			
		||||
                        ctx.getSource().sendSuccess(() -> Component.literal(player + " " + msg), false);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        ctx.getSource().sendFailure(Component.literal("Check failed for " + player));
 | 
			
		||||
                    }
 | 
			
		||||
                    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 {
 | 
			
		||||
            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;
 | 
			
		||||
    private static void sendOnServer(CommandContext<CommandSourceStack> ctx, Component msg, boolean broadcast) {
 | 
			
		||||
        if (broadcast) {
 | 
			
		||||
            ctx.getSource().getServer().getPlayerList().broadcastSystemMessage(msg, false);
 | 
			
		||||
        } else {
 | 
			
		||||
            ctx.getSource().sendSuccess(() -> msg, false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 void dispatchBack(CommandContext<CommandSourceStack> ctx, Runnable task) {
 | 
			
		||||
        ctx.getSource().getServer().execute(task);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String getSourceName(CommandContext<CommandSourceStack> ctx) {
 | 
			
		||||
        try {
 | 
			
		||||
            if (ctx.getSource().getEntity() instanceof ServerPlayer sp) {
 | 
			
		||||
                return sp.getGameProfile().getName();
 | 
			
		||||
            }
 | 
			
		||||
            String s = ctx.getSource().getDisplayName().getString();
 | 
			
		||||
            if (!s.isEmpty()) return s;
 | 
			
		||||
        } catch (Exception ignored) {}
 | 
			
		||||
        return "server";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static boolean setEnableWhitelistPersisted(CommandContext<CommandSourceStack> ctx, boolean enabled) {
 | 
			
		||||
        boolean changed = Config.enableWhitelist != enabled;
 | 
			
		||||
        Config.enableWhitelist = enabled;
 | 
			
		||||
        WhitelistApiClient.refreshConfigFromSpec();
 | 
			
		||||
        ctx.getSource().getServer().submit(() -> {}).join();
 | 
			
		||||
        return changed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String resolveUuid(MinecraftServer srv, String player) {
 | 
			
		||||
        try {
 | 
			
		||||
            ServerPlayer sp = srv.getPlayerList().getPlayerByName(player);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,18 +1,43 @@
 | 
			
		|||
package org.itqop.whitelist;
 | 
			
		||||
 | 
			
		||||
import com.mojang.logging.LogUtils;
 | 
			
		||||
import net.neoforged.bus.api.SubscribeEvent;
 | 
			
		||||
import net.neoforged.fml.common.EventBusSubscriber;
 | 
			
		||||
import net.neoforged.neoforge.event.RegisterCommandsEvent;
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
 | 
			
		||||
import net.minecraft.network.chat.ClickEvent;
 | 
			
		||||
import net.minecraft.network.chat.Component;
 | 
			
		||||
import net.minecraft.server.level.ServerPlayer;
 | 
			
		||||
 | 
			
		||||
@EventBusSubscriber(modid = Whitelist.MODID)
 | 
			
		||||
public class WhitelistEventHandler {
 | 
			
		||||
    private static final Logger LOGGER = LogUtils.getLogger();
 | 
			
		||||
 | 
			
		||||
    @SubscribeEvent
 | 
			
		||||
    public static void registerCommands(RegisterCommandsEvent event) {
 | 
			
		||||
        WhitelistCommands.register(event.getDispatcher());
 | 
			
		||||
        LOGGER.info("Whitelist commands registered");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SubscribeEvent
 | 
			
		||||
    public static void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) {
 | 
			
		||||
        if (!Config.enableWhitelist) return;
 | 
			
		||||
        if (!(event.getEntity() instanceof ServerPlayer sp)) return;
 | 
			
		||||
        String name = sp.getGameProfile().getName();
 | 
			
		||||
        WhitelistApiClient.get().checkPlayer(name).thenAccept(resp -> {
 | 
			
		||||
            if (resp != null && resp.isSuccess() && !resp.isWhitelisted()) {
 | 
			
		||||
                sp.server.execute(() -> {
 | 
			
		||||
                    Component msg = Component.literal("Вас нету в белом списке сервера.\n")
 | 
			
		||||
                            .append(
 | 
			
		||||
                                    Component.literal("Для входа необходимо приобрести проходку на сайте ")
 | 
			
		||||
                                            .append(
 | 
			
		||||
                                                    Component.literal("hubmc.org/shop")
 | 
			
		||||
                                                            .withStyle(style -> style
 | 
			
		||||
                                                                    .withUnderlined(true)
 | 
			
		||||
                                                                    .withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://hubmc.org/shop"))
 | 
			
		||||
                                                            )
 | 
			
		||||
                                            )
 | 
			
		||||
                            );
 | 
			
		||||
                    sp.connection.disconnect(msg);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue