feat: first mixins attempt

This commit is contained in:
itqop 2025-11-09 15:54:20 +03:00
parent be901a31d8
commit 4e5682c6b1
4 changed files with 61 additions and 21 deletions

View File

@ -25,6 +25,7 @@ public class WhitelistEventHandler {
WhitelistCommands.register(event.getDispatcher()); WhitelistCommands.register(event.getDispatcher());
} }
// Check whitelist when player logs in
@SubscribeEvent(priority = EventPriority.HIGHEST) @SubscribeEvent(priority = EventPriority.HIGHEST)
public static void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) { public static void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) {
if (!Config.enableWhitelist) return; if (!Config.enableWhitelist) return;
@ -47,35 +48,24 @@ public class WhitelistEventHandler {
) )
.join(); .join();
// If check failed or player not whitelisted, kick immediately // If check failed or player not whitelisted, disconnect immediately
if (!response.isSuccess() || !response.isWhitelisted()) { if (!response.isSuccess() || !response.isWhitelisted()) {
kickPlayer(player, createKickMessage(response.isSuccess())); disconnectPlayer(player, createKickMessage(response.isSuccess()));
} }
} catch (Exception e) { } catch (Exception e) {
// On any error, deny access // On any error, deny access
LOGGER.error("Whitelist check error for '{}': {}", playerName, e.getMessage()); LOGGER.error("Whitelist check error for '{}': {}", playerName, e.getMessage());
kickPlayer(player, Component.literal("Whitelist service error. Please contact administrator.")); disconnectPlayer(player, Component.literal("Whitelist service error. Please contact administrator."));
} }
} }
private static void kickPlayer(ServerPlayer player, Component message) { private static void disconnectPlayer(ServerPlayer player, Component message) {
if (player.server == null || player.connection == null) return; if (player.connection == null) return;
// Send disconnect message with delay to ensure client receives it // Disconnect player - mixin will suppress all join/leave messages
// Without delay, rapid reconnects may fail to show the message player.connection.disconnect(message);
player.server.execute(() -> { LOGGER.info("Player '{}' denied access (not in whitelist)", player.getGameProfile().getName());
if (player.connection != null) {
try {
// Small delay to ensure message packet is sent before connection close
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
player.connection.disconnect(message);
LOGGER.info("Player '{}' kicked from whitelist", player.getGameProfile().getName());
}
});
} }
private static Component createKickMessage(boolean apiAvailable) { private static Component createKickMessage(boolean apiAvailable) {

View File

@ -0,0 +1,37 @@
package org.itqop.whitelist.mixin;
import net.minecraft.network.chat.Component;
import net.minecraft.server.players.PlayerList;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
@Mixin(PlayerList.class)
public class PlayerListMixin {
/**
* Suppress ALL player join/leave messages
* Intercepts ALL calls to broadcastSystemMessage and filters out join/leave messages
*/
@ModifyArg(method = "*",
at = @At(value = "INVOKE",
target = "Lnet/minecraft/server/players/PlayerList;broadcastSystemMessage(Lnet/minecraft/network/chat/Component;Z)V"),
index = 0)
private Component suppressJoinLeaveMessages(Component original) {
String messageString = original.getString();
// Check if this is a join or leave message
// Common patterns: "Player joined the game", "Player left the game"
// Russian: "Игрок присоединился к игре", "Игрок покинул игру"
if (messageString.contains("joined the game") ||
messageString.contains("left the game") ||
messageString.contains("присоединился") ||
messageString.contains("покинул")) {
// Return empty component to suppress the message
return Component.empty();
}
// For all other messages, return the original
return original;
}
}

View File

@ -0,0 +1,13 @@
{
"required": true,
"minVersion": "0.8",
"package": "org.itqop.whitelist.mixin",
"compatibilityLevel": "JAVA_21",
"refmap": "whitelist.refmap.json",
"mixins": [
"PlayerListMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -35,8 +35,8 @@ authors = "${mod_authors}" #optional
description = '''${mod_description}''' description = '''${mod_description}'''
# The [[mixins]] block allows you to declare your mixin config to FML so that it gets loaded. # The [[mixins]] block allows you to declare your mixin config to FML so that it gets loaded.
#[[mixins]] [[mixins]]
#config="${mod_id}.mixins.json" config="${mod_id}.mixins.json"
# The [[accessTransformers]] block allows you to declare where your AT file is. # The [[accessTransformers]] block allows you to declare where your AT file is.
# If this block is omitted, a fallback attempt will be made to load an AT from META-INF/accesstransformer.cfg # If this block is omitted, a fallback attempt will be made to load an AT from META-INF/accesstransformer.cfg