9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-19 15:09:15 +00:00

添加玩家聊天标签解析

This commit is contained in:
jhqwqmc
2025-11-10 06:43:42 +08:00
parent 9a0695c6b4
commit 8a6574f2d3
9 changed files with 231 additions and 1 deletions

View File

@@ -121,6 +121,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@@ -433,6 +434,11 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
new SetObjectiveListener1_20(), new SetObjectiveListener1_20(),
this.packetIds.clientboundSetObjectivePacket(), "ClientboundSetObjectivePacket" this.packetIds.clientboundSetObjectivePacket(), "ClientboundSetObjectivePacket"
); );
registerS2CGamePacketListener(
VersionHelper.isOrAbove1_20_3() ?
new PlayerChatListener_1_20_3() :
new PlayerChatListener_1_20(),
this.packetIds.clientboundPlayerChatPacket(), "ClientboundPlayerChatPacket");
} }
@EventHandler(priority = EventPriority.LOWEST) @EventHandler(priority = EventPriority.LOWEST)
@@ -4206,4 +4212,193 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
} }
} }
} }
public static class PlayerChatListener_1_20 implements ByteBufferPacketListener {
public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) {
if (!Config.interceptPlayerChat()) return;
FriendlyByteBuf buf = event.getBuffer();
boolean changed = false;
UUID sender = buf.readUUID();
int index = buf.readVarInt();
byte @Nullable [] messageSignature = buf.readNullable(b -> {
byte[] bs = new byte[256];
buf.readBytes(bs);
return bs;
});
// SignedMessageBody.Packed start
String content = buf.readUtf(256);
Instant timeStamp = buf.readInstant();
long salt = buf.readLong();
// LastSeenMessages.Packed start
ArrayList<Pair<Integer, byte[]>> lastSeen = buf.readCollection(FriendlyByteBuf.limitValue(ArrayList::new, 20), b -> {
int i = b.readVarInt() - 1;
if (i == -1) {
byte[] bs = new byte[256];
buf.readBytes(bs);
return Pair.of(-1, bs);
} else {
return Pair.of(i, null);
}
});
// LastSeenMessages.Packed end
// SignedMessageBody.Packed end
@Nullable String unsignedContent = buf.readNullable(FriendlyByteBuf::readUtf);
if (unsignedContent != null) {
Map<String, ComponentProvider> unsignedContentTokens = CraftEngine.instance().fontManager().matchTags(unsignedContent);
if (!unsignedContentTokens.isEmpty()) {
Tag tag = MRegistryOps.JSON.convertTo(MRegistryOps.SPARROW_NBT, GsonHelper.get().fromJson(unsignedContent, JsonElement.class));
Component component = AdventureHelper.nbtToComponent(tag);
component = AdventureHelper.replaceText(component, unsignedContentTokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user));
unsignedContent = MRegistryOps.SPARROW_NBT.convertTo(MRegistryOps.JSON, AdventureHelper.componentToNbt(component)).toString();
changed = true;
}
}
// FilterMask start
int type = buf.readVarInt();
BitSet mask = type == 2 /* PARTIALLY_FILTERED */ ? buf.readBitSet() : null;
// FilterMask end
// ChatType.BoundNetwork start
int chatType = buf.readVarInt();
String name = buf.readUtf();
Map<String, ComponentProvider> nameTokens = CraftEngine.instance().fontManager().matchTags(name);
if (!nameTokens.isEmpty()) {
Tag tag = MRegistryOps.JSON.convertTo(MRegistryOps.SPARROW_NBT, GsonHelper.get().fromJson(name, JsonElement.class));
Component component = AdventureHelper.nbtToComponent(tag);
component = AdventureHelper.replaceText(component, nameTokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user));
name = MRegistryOps.SPARROW_NBT.convertTo(MRegistryOps.JSON, AdventureHelper.componentToNbt(component)).toString();
changed = true;
}
@Nullable String targetName = buf.readNullable(FriendlyByteBuf::readUtf);
if (targetName != null) {
Map<String, ComponentProvider> targetNameTokens = CraftEngine.instance().fontManager().matchTags(targetName);
if (!targetNameTokens.isEmpty()) {
Tag tag = MRegistryOps.JSON.convertTo(MRegistryOps.SPARROW_NBT, GsonHelper.get().fromJson(targetName, JsonElement.class));
Component component = AdventureHelper.nbtToComponent(tag);
component = AdventureHelper.replaceText(component, targetNameTokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user));
targetName = MRegistryOps.SPARROW_NBT.convertTo(MRegistryOps.JSON, AdventureHelper.componentToNbt(component)).toString();
changed = true;
}
}
// ChatType.BoundNetwork end
if (changed) {
event.setChanged(true);
buf.clear();
buf.writeVarInt(event.packetID());
buf.writeUUID(sender);
buf.writeVarInt(index);
buf.writeNullable(messageSignature, (b, bs) -> buf.writeBytes(bs));
buf.writeUtf(content);
buf.writeInstant(timeStamp);
buf.writeLong(salt);
buf.writeCollection(lastSeen, (b, pair) -> {
b.writeVarInt(pair.left() + 1);
if (pair.right() != null) {
b.writeBytes(pair.right());
}
});
buf.writeNullable(unsignedContent, FriendlyByteBuf::writeUtf);
buf.writeVarInt(type);
if (type == 2) buf.writeBitSet(mask);
buf.writeVarInt(chatType);
buf.writeUtf(name);
buf.writeNullable(targetName, FriendlyByteBuf::writeUtf);
}
}
}
public static class PlayerChatListener_1_20_3 implements ByteBufferPacketListener {
public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) {
if (!Config.interceptPlayerChat()) return;
FriendlyByteBuf buf = event.getBuffer();
boolean changed = false;
int globalIndex = VersionHelper.isOrAbove1_21_5() ? buf.readVarInt() : -1;
UUID sender = buf.readUUID();
int index = buf.readVarInt();
byte @Nullable [] messageSignature = buf.readNullable(b -> {
byte[] bs = new byte[256];
buf.readBytes(bs);
return bs;
});
// SignedMessageBody.Packed start
String content = buf.readUtf(256);
Instant timeStamp = buf.readInstant();
long salt = buf.readLong();
// LastSeenMessages.Packed start
ArrayList<Pair<Integer, byte[]>> lastSeen = buf.readCollection(FriendlyByteBuf.limitValue(ArrayList::new, 20), b -> {
int i = b.readVarInt() - 1;
if (i == -1) {
byte[] bs = new byte[256];
buf.readBytes(bs);
return Pair.of(-1, bs);
} else {
return Pair.of(i, null);
}
});
// LastSeenMessages.Packed end
// SignedMessageBody.Packed end
@Nullable Tag unsignedContent = buf.readNullable(b -> b.readNbt(false));
if (unsignedContent != null) {
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(unsignedContent);
if (!tokens.isEmpty()) {
Component component = AdventureHelper.tagToComponent(unsignedContent);
component = AdventureHelper.replaceText(component, tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user));
unsignedContent = AdventureHelper.componentToTag(component);
changed = true;
}
}
// FilterMask start
int type = buf.readVarInt();
BitSet mask = type == 2 /* PARTIALLY_FILTERED */ ? buf.readBitSet() : null;
// FilterMask end
// ChatType.Bound start
int chatType = buf.readVarInt();
Tag name = buf.readNbt(false);
if (name != null) {
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(name);
if (!tokens.isEmpty()) {
Component component = AdventureHelper.tagToComponent(name);
component = AdventureHelper.replaceText(component, tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user));
name = AdventureHelper.componentToTag(component);
changed = true;
}
}
@Nullable Tag targetName = buf.readNullable(b -> b.readNbt(false));
if (targetName != null) {
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(targetName);
if (!tokens.isEmpty()) {
Component component = AdventureHelper.tagToComponent(targetName);
component = AdventureHelper.replaceText(component, tokens, NetworkTextReplaceContext.of((BukkitServerPlayer) user));
targetName = AdventureHelper.componentToTag(component);
changed = true;
}
}
// ChatType.Bound end
if (changed) {
event.setChanged(true);
buf.clear();
buf.writeVarInt(event.packetID());
if (VersionHelper.isOrAbove1_21_5()) buf.writeVarInt(globalIndex);
buf.writeUUID(sender);
buf.writeVarInt(index);
buf.writeNullable(messageSignature, (b, bs) -> buf.writeBytes(bs));
buf.writeUtf(content);
buf.writeInstant(timeStamp);
buf.writeLong(salt);
buf.writeCollection(lastSeen, (b, pair) -> {
b.writeVarInt(pair.left() + 1);
if (pair.right() != null) {
b.writeBytes(pair.right());
}
});
buf.writeNullable(unsignedContent, (b, tag) -> b.writeNbt(tag, false));
buf.writeVarInt(type);
if (type == 2) buf.writeBitSet(mask);
buf.writeVarInt(chatType);
buf.writeNbt(name, false);
buf.writeNullable(targetName, (b, tag) -> b.writeNbt(tag, false));
}
}
}
} }

View File

@@ -75,4 +75,6 @@ public interface PacketIds {
int clientboundForgetLevelChunkPacket(); int clientboundForgetLevelChunkPacket();
int serverboundCustomPayloadPacket(); int serverboundCustomPayloadPacket();
int clientboundPlayerChatPacket();
} }

View File

@@ -190,4 +190,9 @@ public class PacketIds1_20 implements PacketIds {
public int serverboundCustomPayloadPacket() { public int serverboundCustomPayloadPacket() {
return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ServerboundCustomPayloadPacket, PacketFlow.SERVERBOUND); return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ServerboundCustomPayloadPacket, PacketFlow.SERVERBOUND);
} }
@Override
public int clientboundPlayerChatPacket() {
return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundPlayerChatPacket, PacketFlow.CLIENTBOUND);
}
} }

View File

@@ -189,4 +189,9 @@ public class PacketIds1_20_5 implements PacketIds {
public int serverboundCustomPayloadPacket() { public int serverboundCustomPayloadPacket() {
return PlayPacketIdHelper.byName("minecraft:custom_payload", PacketFlow.SERVERBOUND); return PlayPacketIdHelper.byName("minecraft:custom_payload", PacketFlow.SERVERBOUND);
} }
@Override
public int clientboundPlayerChatPacket() {
return PlayPacketIdHelper.byName("minecraft:player_chat", PacketFlow.CLIENTBOUND);
}
} }

View File

@@ -1726,4 +1726,10 @@ public final class NetworkReflections {
clazz$ClientboundUpdateTagsPacket, Map.class, 0 clazz$ClientboundUpdateTagsPacket, Map.class, 0
) )
); );
public static final Class<?> clazz$ClientboundPlayerChatPacket = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundPlayerChatPacket")
)
);
} }

View File

@@ -403,6 +403,7 @@ network:
text-display: true # Modern Holograms text-display: true # Modern Holograms
item: true item: true
advancement: true advancement: true
player-chat: true
recipe: recipe:
# Master switch for custom recipes # Master switch for custom recipes

View File

@@ -175,6 +175,7 @@ public class Config {
protected boolean network$intercept_packets$set_score; protected boolean network$intercept_packets$set_score;
protected boolean network$intercept_packets$item; protected boolean network$intercept_packets$item;
protected boolean network$intercept_packets$advancement; protected boolean network$intercept_packets$advancement;
protected boolean network$intercept_packets$player_chat;
protected boolean network$disable_item_operations; protected boolean network$disable_item_operations;
protected boolean item$client_bound_model; protected boolean item$client_bound_model;
@@ -546,6 +547,7 @@ public class Config {
network$intercept_packets$set_score = config.getBoolean("network.intercept-packets.set-score", true); network$intercept_packets$set_score = config.getBoolean("network.intercept-packets.set-score", true);
network$intercept_packets$item = config.getBoolean("network.intercept-packets.item", true); network$intercept_packets$item = config.getBoolean("network.intercept-packets.item", true);
network$intercept_packets$advancement = config.getBoolean("network.intercept-packets.advancement", true); network$intercept_packets$advancement = config.getBoolean("network.intercept-packets.advancement", true);
network$intercept_packets$player_chat = config.getBoolean("network.intercept-packets.player-chat", true);
// emoji // emoji
emoji$contexts$chat = config.getBoolean("emoji.contexts.chat", true); emoji$contexts$chat = config.getBoolean("emoji.contexts.chat", true);
@@ -980,6 +982,10 @@ public class Config {
return instance.network$intercept_packets$advancement; return instance.network$intercept_packets$advancement;
} }
public static boolean interceptPlayerChat() {
return instance.network$intercept_packets$player_chat;
}
public static boolean predictBreaking() { public static boolean predictBreaking() {
return instance.block$predict_breaking; return instance.block$predict_breaking;
} }

View File

@@ -74,6 +74,16 @@ public class FriendlyByteBuf extends ByteBuf {
this.writeLong(instant.toEpochMilli()); this.writeLong(instant.toEpochMilli());
} }
public static <T> IntFunction<T> limitValue(IntFunction<T> applier, int max) {
return (j) -> {
if (j > max) {
throw new DecoderException("Value " + j + " is larger than limit " + max);
} else {
return applier.apply(j);
}
};
}
public <T, C extends Collection<T>> C readCollection(IntFunction<C> collectionFactory, Reader<T> reader) { public <T, C extends Collection<T>> C readCollection(IntFunction<C> collectionFactory, Reader<T> reader) {
int i = this.readVarInt(); int i = this.readVarInt();
C collection = collectionFactory.apply(i); C collection = collectionFactory.apply(i);

View File

@@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx1G
# Project settings # Project settings
# Rule: [major update].[feature update].[bug fix] # Rule: [major update].[feature update].[bug fix]
project_version=0.0.65.6 project_version=0.0.65.6
config_version=53 config_version=54
lang_version=38 lang_version=38
project_group=net.momirealms project_group=net.momirealms
latest_supported_version=1.21.10 latest_supported_version=1.21.10