9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-20 07:29:17 +00:00

feat(network): 实现阻止玩家在游戏内直接使用自定义默认字体

This commit is contained in:
jhqwqmc
2025-03-23 00:28:52 +08:00
parent 332642d529
commit c09b987733
5 changed files with 142 additions and 30 deletions

View File

@@ -127,6 +127,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener {
registerNMSPacketConsumer(PacketConsumers.SOUND, Reflections.clazz$ClientboundSoundPacket);
registerNMSPacketConsumer(PacketConsumers.CHAT, Reflections.clazz$ServerboundChatPacket);
registerNMSPacketConsumer(PacketConsumers.RENAME_ITEM, Reflections.clazz$ServerboundRenameItemPacket);
registerNMSPacketConsumer(PacketConsumers.SIGN_UPDATE, Reflections.clazz$ServerboundSignUpdatePacket);
registerByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket());
registerByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket());
registerByteBufPacketConsumer(PacketConsumers.LEVEL_PARTICLE, this.packetIds.clientboundLevelParticlesPacket());
@@ -267,6 +268,20 @@ public class BukkitNetworkManager implements NetworkManager, Listener {
this.packetsConsumer.accept(player.serverPlayer(), packet);
}
public void receivePacket(@NotNull NetWorkUser player, Object packet) {
Channel channel = player.nettyChannel();
List<String> handlerNames = channel.pipeline().names();
if (handlerNames.contains("decompress")) {
channel.pipeline().context("decompress").fireChannelRead(packet);
} else {
if (handlerNames.contains("decrypt")) {
channel.pipeline().context("decrypt").fireChannelRead(packet);
} else {
channel.pipeline().context("splitter").fireChannelRead(packet);
}
}
}
private void injectServerChannel(Channel serverChannel) {
ChannelPipeline pipeline = serverChannel.pipeline();
ChannelHandler connectionHandler = pipeline.get(CONNECTION_HANDLER_NAME);

View File

@@ -3,6 +3,8 @@ package net.momirealms.craftengine.bukkit.plugin.network;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.ints.IntList;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture;
import net.momirealms.craftengine.bukkit.api.event.FurnitureBreakEvent;
import net.momirealms.craftengine.bukkit.api.event.FurnitureInteractEvent;
@@ -33,9 +35,8 @@ import org.bukkit.util.RayTraceResult;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.time.Instant;
import java.util.*;
import java.util.function.BiConsumer;
public class PacketConsumers {
@@ -670,17 +671,19 @@ public class PacketConsumers {
if (message != null && !message.isEmpty()) {
ImageManager manager = CraftEngine.instance().imageManager();
if (!manager.isDefaultFontInUse()) return;
char[] chars = message.toCharArray();
try {
int[] codepoints = CharacterUtils.charsToCodePoints(chars);
for (int codepoint : codepoints) {
if (manager.isIllegalCharacter(codepoint)) {
event.setCancelled(true);
return;
}
}
} catch (Exception ignore) {
}
String str = replaceIllegalString(message, manager);
if (message.equals(str)) return;
event.setCancelled(true);
Object newPacket = Reflections.constructor$ServerboundChatPacket.newInstance(
str,
Reflections.field$ServerboundChatPacket$timeStamp.get(packet),
Reflections.field$ServerboundChatPacket$salt.get(packet),
Reflections.field$ServerboundChatPacket$signature.get(packet),
Reflections.field$ServerboundChatPacket$lastSeenMessages.get(packet)
);
user.receivePacket(newPacket);
} catch (Exception ignore) {}
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundChatPacket", e);
@@ -694,25 +697,49 @@ public class PacketConsumers {
if (message != null && !message.isEmpty()) {
ImageManager manager = CraftEngine.instance().imageManager();
if (!manager.isDefaultFontInUse()) return;
char[] chars = message.toCharArray();
try {
int[] codepoints = CharacterUtils.charsToCodePoints(chars);
int[] newCodepoints = new int[codepoints.length];
for (int i = 0; i < codepoints.length; i++) {
int codepoint = codepoints[i];
if (!manager.isIllegalCharacter(codepoint)) {
newCodepoints[i] = codepoint;
} else {
newCodepoints[i] = '*';
}
}
String str = new String(newCodepoints, 0, newCodepoints.length);
String str = replaceIllegalString(message, manager);
Reflections.field$ServerboundRenameItemPacket$name.set(packet, str);
} catch (Exception ignore) {
}
} catch (Exception ignore) {}
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundRenameItemPacket", e);
}
};
// we handle it on packet level to prevent it from being captured by plugins
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> SIGN_UPDATE = (user, event, packet) -> {
try {
String[] lines = (String[]) Reflections.field$ServerboundSignUpdatePacket$lines.get(packet);
ImageManager manager = CraftEngine.instance().imageManager();
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
if (line != null && !line.isEmpty()) {
if (!manager.isDefaultFontInUse()) return;
try {
String str = replaceIllegalString(line, manager);
if (line.equals(str)) continue;
lines[i] = str;
} catch (Exception ignore){}
}
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundSignUpdatePacket", e);
}
};
private static String replaceIllegalString(String string, ImageManager manager) {
char[] chars = string.toCharArray();
int[] codepoints = CharacterUtils.charsToCodePoints(chars);
int[] newCodepoints = new int[codepoints.length];
for (int i = 0; i < codepoints.length; i++) {
int codepoint = codepoints[i];
if (!manager.isIllegalCharacter(codepoint)) {
newCodepoints[i] = codepoint;
} else {
newCodepoints[i] = '*';
}
}
return new String(newCodepoints, 0, newCodepoints.length);
}
}

View File

@@ -231,6 +231,11 @@ public class BukkitServerPlayer extends Player {
this.plugin.networkManager().sendPacket(this, packet, immediately);
}
@Override
public void receivePacket(Object packet) {
this.plugin.networkManager().receivePacket(this, packet);
}
@Override
public ConnectionState decoderState() {
return decoderState;

View File

@@ -7,6 +7,7 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.core.util.ReflectionUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.Location;
@@ -26,6 +27,7 @@ import sun.misc.Unsafe;
import java.io.BufferedReader;
import java.lang.reflect.*;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
@@ -134,9 +136,9 @@ public class Reflections {
);
public static final Constructor<?> constructor$ClientboundSystemChatPacket = requireNonNull(
ReflectionUtils.getConstructor(
clazz$ClientboundSystemChatPacket, clazz$Component, boolean.class
)
VersionHelper.isVersionNewerThan1_20_4()
? ReflectionUtils.getConstructor(clazz$ClientboundSystemChatPacket, clazz$Component, boolean.class)
: ReflectionUtils.getConstructor(clazz$ClientboundSystemChatPacket, Component.class, String.class, boolean.class)
);
public static final Field field$ClientboundSystemChatPacket$overlay = requireNonNull(
@@ -189,6 +191,11 @@ public class Reflections {
clazz$ClientboundSystemChatPacket, clazz$Component, 0
);
public static final Field field$ClientboundSystemChatPacket$adventure$content =
ReflectionUtils.getDeclaredField(
clazz$ClientboundSystemChatPacket, Component.class, 0
);
public static final Field field$ClientboundSystemChatPacket$text =
ReflectionUtils.getDeclaredField(
clazz$ClientboundSystemChatPacket, String.class, 0
@@ -5115,6 +5122,19 @@ public class Reflections {
)
);
public static final Class<?> clazz$MessageSignature = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.chat.MessageSignature")
)
);
public static final Class<?> clazz$LastSeenMessages$Update = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.chat.LastSeenMessages$Update"),
BukkitReflectionUtils.assembleMCClass("network.chat.LastSeenMessages$b")
)
);
public static final Class<?> clazz$ServerboundChatPacket = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.protocol.game.ServerboundChatPacket"),
@@ -5122,12 +5142,42 @@ public class Reflections {
)
);
public static final Constructor<?> constructor$ServerboundChatPacket = requireNonNull(
ReflectionUtils.getConstructor(
clazz$ServerboundChatPacket, String.class, Instant.class, long.class, clazz$MessageSignature, clazz$LastSeenMessages$Update
)
);
public static final Field field$ServerboundChatPacket$message = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ServerboundChatPacket, String.class, 0
)
);
public static final Field field$ServerboundChatPacket$timeStamp = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ServerboundChatPacket, Instant.class, 0
)
);
public static final Field field$ServerboundChatPacket$salt = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ServerboundChatPacket, long.class, 0
)
);
public static final Field field$ServerboundChatPacket$signature = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ServerboundChatPacket, clazz$MessageSignature, 0
)
);
public static final Field field$ServerboundChatPacket$lastSeenMessages = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ServerboundChatPacket, clazz$LastSeenMessages$Update, 0
)
);
public static final Class<?> clazz$ServerboundRenameItemPacket = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.protocol.game.ServerboundRenameItemPacket"),
@@ -5140,4 +5190,17 @@ public class Reflections {
clazz$ServerboundRenameItemPacket, String.class, 0
)
);
public static final Class<?> clazz$ServerboundSignUpdatePacket = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.protocol.game.ServerboundSignUpdatePacket"),
BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayInUpdateSign")
)
);
public static final Field field$ServerboundSignUpdatePacket$lines = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ServerboundSignUpdatePacket, String[].class, 0
)
);
}

View File

@@ -16,6 +16,8 @@ public interface NetWorkUser {
void sendPacket(Object packet, boolean immediately);
void receivePacket(Object packet);
@ApiStatus.Internal
ConnectionState decoderState();