Skip to content

Commit

Permalink
feat(spigot): translate signed chat packet on 1.19+ (#353)
Browse files Browse the repository at this point in the history
Behind `chat.signed-enabled` config option, which is ~~disabled~~
ENABLED by default in v4 (versus disabled in v3).

Closes #350

Port of 1d7afbd to v4
  • Loading branch information
diogotcorreia committed Nov 28, 2023
1 parent e56dff9 commit 31115c1
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public class MainConfig implements TritonConfig {
private List<String> commandAliases;
private String disabledLine;
private boolean chat;
private boolean signedChat;
private FeatureSyntax chatSyntax;
private boolean actionbars;
private FeatureSyntax actionbarSyntax;
Expand Down Expand Up @@ -233,6 +234,7 @@ private void setupLanguageCreation(Configuration section) {

Configuration chat = section.getSection("chat");
this.chat = chat.getBoolean("enabled", true);
this.signedChat = chat.getBoolean("signed-enabled", true);
this.chatSyntax = FeatureSyntax.fromSection(chat);
this.chatSyntax.interactive = true;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import com.rexcantor64.triton.spigot.wrappers.WrappedClientConfiguration;
import com.rexcantor64.triton.utils.ComponentUtils;
import com.rexcantor64.triton.utils.ReflectionUtils;
import com.rexcantor64.triton.wrappers.WrappedPlayerChatMessage;
import lombok.SneakyThrows;
import lombok.val;
import net.kyori.adventure.text.Component;
Expand Down Expand Up @@ -137,11 +138,10 @@ private void setupPacketHandlers() {
// Removed in 1.19.3
packetHandlers.put(PacketType.Play.Server.CHAT_PREVIEW, asAsync(this::handleChatPreview));
}
} else {
// In 1.19+, this packet is signed, so we cannot edit it.
// It's sent by the player anyway, so there's nothing to translate.
packetHandlers.put(PacketType.Play.Server.CHAT, asAsync(this::handleChat));
}
// In 1.19+, this packet is signed, but we can still edit it, since it might contain
// formatting from chat plugins.
packetHandlers.put(PacketType.Play.Server.CHAT, asAsync(this::handleChat));
if (main.getMcVersion() >= 17) {
// Title packet split on 1.17
packetHandlers.put(PacketType.Play.Server.SET_TITLE_TEXT, asAsync(this::handleTitle));
Expand Down Expand Up @@ -185,22 +185,38 @@ private void setupPacketHandlers() {
/* PACKET HANDLERS */

private void handleChat(PacketEvent packet, SpigotLanguagePlayer languagePlayer) {
boolean ab = isActionbar(packet.getPacket());
boolean isSigned = MinecraftVersion.WILD_UPDATE.atOrAbove(); // MC 1.19+
if (isSigned && !main.getConfig().isSignedChat()) return;
// action bars are not sent here on 1.19+ anymore
boolean ab = !isSigned && isActionbar(packet.getPacket());

// Don't bother parsing anything else if it's disabled on config
if ((ab && !main.getConfig().isActionbars()) || (!ab && !main.getConfig().isChat())) return;

val chatModifier = packet.getPacket().getChatComponents();
val baseComponentModifier = packet.getPacket().getSpecificModifier(BASE_COMPONENT_ARRAY_CLASS);
val adventureModifier = packet.getPacket().getSpecificModifier(ADVENTURE_COMPONENT_CLASS);
boolean hasPlayerChatMessageRecord = isSigned && !MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove(); // MC 1.19-1.19.2
WrappedPlayerChatMessage wrappedPlayerChatMessage = null;

Component message = null;

if (adventureModifier.readSafely(0) != null) {
if (hasPlayerChatMessageRecord) {
// The message is wrapped in a PlayerChatMessage record
val playerChatModifier = packet.getPacket().getModifier().withType(WrappedPlayerChatMessage.getWrappedClass(), WrappedPlayerChatMessage.CONVERTER);
wrappedPlayerChatMessage = playerChatModifier.readSafely(0);
if (wrappedPlayerChatMessage != null) {
Optional<WrappedChatComponent> msg = wrappedPlayerChatMessage.getMessage();
if (msg.isPresent()) {
message = WrappedComponentUtils.deserialize(msg.get());
}
}
} else if (adventureModifier.readSafely(0) != null) {
message = adventureModifier.readSafely(0);
} else if (baseComponentModifier.readSafely(0) != null) {
message = BaseComponentUtils.deserialize(baseComponentModifier.readSafely(0));
} else {
val msg = packet.getPacket().getChatComponents().readSafely(0);
val msg = chatModifier.readSafely(0);
if (msg != null) {
message = WrappedComponentUtils.deserialize(msg);
}
Expand All @@ -212,6 +228,7 @@ private void handleChat(PacketEvent packet, SpigotLanguagePlayer languagePlayer)
}

// Translate the message
val wrappedPlayerChatMessageFinal = wrappedPlayerChatMessage;
parser()
.translateComponent(
message,
Expand All @@ -222,6 +239,12 @@ private void handleChat(PacketEvent packet, SpigotLanguagePlayer languagePlayer)
if (adventureModifier.size() > 0) {
// On a Paper or fork, so we can directly set the Adventure Component
adventureModifier.writeSafely(0, result);
} else if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) { // MC 1.19.3+
// While chat is signed, we can still mess around with formatting and prefixes
chatModifier.writeSafely(0, WrappedComponentUtils.serialize(result));
} else if (hasPlayerChatMessageRecord) { // MC 1.19-1.19.2
// While chat is signed, we can still mess around with formatting and prefixes
wrappedPlayerChatMessageFinal.setMessage(Optional.of(WrappedComponentUtils.serialize(result)));
} else {
BaseComponent[] resultComponent;
if (ab && !MinecraftVersion.EXPLORATION_UPDATE.atOrAbove()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.rexcantor64.triton.wrappers;

import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.AbstractWrapper;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.Converters;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;

import java.util.Optional;

/**
* Custom ProtocolLib Wrapper of NMS' PlayerChatMessage (added to NMS in 1.19 and removed in 1.19.3)
* Tested with 1.19.2
*/
public class WrappedPlayerChatMessage extends AbstractWrapper {

private static Class<?> PLAYER_CHAT_MESSAGE = MinecraftReflection.getMinecraftClass("network.chat.PlayerChatMessage");
private static FuzzyReflection FUZZY_REFLECTION = FuzzyReflection.fromClass(PLAYER_CHAT_MESSAGE, true);
private static FieldAccessor CHAT_COMPONENT = Accessors.getFieldAccessor(FUZZY_REFLECTION.getParameterizedField(Optional.class, MinecraftReflection.getIChatBaseComponentClass()));
private static EquivalentConverter<Optional<WrappedChatComponent>> CHAT_COMPONENT_CONVERTER = Converters.optional(BukkitConverters.getWrappedChatComponentConverter());

public static final EquivalentConverter<WrappedPlayerChatMessage> CONVERTER = Converters.ignoreNull(Converters.handle(WrappedPlayerChatMessage::getHandle, WrappedPlayerChatMessage::fromHandle, WrappedPlayerChatMessage.class));

/**
* Construct a new PlayerChatMessage wrapper.
*/
private WrappedPlayerChatMessage(Object handle) {
super(getWrappedClass());
setHandle(handle);
}

public Optional<WrappedChatComponent> getMessage() {
return CHAT_COMPONENT_CONVERTER.getSpecific(CHAT_COMPONENT.get(handle));
}

@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public void setMessage(Optional<WrappedChatComponent> message) {
CHAT_COMPONENT.set(handle, CHAT_COMPONENT_CONVERTER.getGeneric(message));
}

/**
* Construct a player chat message from a native NMS object.
*
* @param handle - the native object.
* @return The wrapped player chat message object.
*/
@Contract("_ -> new")
public static @NotNull WrappedPlayerChatMessage fromHandle(Object handle) {
return new WrappedPlayerChatMessage(handle);
}

public static Class<?> getWrappedClass() {
return PLAYER_CHAT_MESSAGE;
}
}
2 changes: 2 additions & 0 deletions triton-spigot/src/main/resources/config_spigot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ language-creation:
chat:
# Should the plugin check for placeholders in chat messages?
enabled: true
# Should the plugin check for placeholders in signed chat messages (MC 1.19+)? Requires the above option to be enabled as well.
signed-enabled: true
syntax-lang: lang
syntax-args: args
syntax-arg: arg
Expand Down

0 comments on commit 31115c1

Please sign in to comment.