Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.17.6</version>
<version>1.17.5</version>
<scope>provided</scope>
</dependency>

Expand Down
77 changes: 49 additions & 28 deletions src/main/java/net/skullian/listeners/PEOutMessagePacket.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ private void initializeModernPacketHandlers() {
packet -> {
net.kyori.adventure.text.Component nativeComponment = ((WrapperPlayServerDisguisedChat) packet).getMessage();
return new PacketAccessorResult(
nativeComponment != null ? NativeAdventureConverter.componentFromNative(nativeComponment) : Component.empty(),
(nativeComponment != null && nativeComponment.children().size() > 0) ? NativeAdventureConverter.componentFromNative(nativeComponment) : Component.empty(),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Potential NPE when nativeComponment is null

The check nativeComponment != null && nativeComponment.children().size() > 0 could throw NPE if children() returns null. Consider using a more defensive approach.

Apply this safer null check pattern:

-                        (nativeComponment != null && nativeComponment.children().size() > 0) ? NativeAdventureConverter.componentFromNative(nativeComponment) : Component.empty(),
+                        (nativeComponment != null && nativeComponment.children() != null && !nativeComponment.children().isEmpty()) ? NativeAdventureConverter.componentFromNative(nativeComponment) : Component.empty(),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
(nativeComponment != null && nativeComponment.children().size() > 0) ? NativeAdventureConverter.componentFromNative(nativeComponment) : Component.empty(),
(nativeComponment != null
&& nativeComponment.children() != null
&& !nativeComponment.children().isEmpty())
? NativeAdventureConverter.componentFromNative(nativeComponment)
: Component.empty(),
🤖 Prompt for AI Agents
In src/main/java/net/skullian/listeners/PEOutMessagePacket.java at line 65, the
current null check on nativeComponment does not account for the possibility that
nativeComponment.children() might return null, which could cause a
NullPointerException. Update the condition to first check if nativeComponment is
not null, then verify that nativeComponment.children() is not null, and finally
check that nativeComponment.children().size() is greater than 0 before calling
NativeAdventureConverter.componentFromNative. This defensive null check will
prevent potential NPEs.

ChatComponentType.NativeAdventureComponent,
0,
false
Expand All @@ -81,7 +81,9 @@ private void initializeModernPacketHandlers() {
boolean longerThanMaxLength = InteractiveChat.sendOriginalIfTooLong && json.length() > InteractiveChat.packetStringMaxLength;

WrapperPlayServerDisguisedChat chatPacket = (WrapperPlayServerDisguisedChat) packet;
chatPacket.setMessage((net.kyori.adventure.text.Component) type.convertTo(component, legacyRGB));
if (component.children().size() > 0) {
chatPacket.setMessage((net.kyori.adventure.text.Component) type.convertTo(component, legacyRGB));
}
Comment on lines +84 to +86
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Inconsistent empty component handling

The code checks if component.children().size() > 0 but doesn't handle the case when the component itself has content but no children. This might skip processing valid text components.

Consider checking if the component has any content instead:

-                if (component.children().size() > 0) {
-                    chatPacket.setMessage((net.kyori.adventure.text.Component) type.convertTo(component, legacyRGB));
-                }
+                if (!component.equals(Component.empty())) {
+                    chatPacket.setMessage((net.kyori.adventure.text.Component) type.convertTo(component, legacyRGB));
+                }
🤖 Prompt for AI Agents
In src/main/java/net/skullian/listeners/PEOutMessagePacket.java around lines 84
to 86, the current check only processes the component if it has children,
ignoring components with content but no children. Modify the condition to check
if the component itself has any content or text instead of just children,
ensuring all valid text components are processed by chatPacket.setMessage.


sendDebug("PROCESSED DISGUISED_CHAT Packet:" +
"NEW COMPONENT MESSAGE: " + PlainTextComponentSerializer.plainText().serialize(component) +
Expand Down Expand Up @@ -135,7 +137,12 @@ private void initializeCommonPacketHandlers() {
nativeComponent = ((WrapperPlayServerChatMessage) packet).getMessage().getChatContent();
}

return new PacketAccessorResult(NativeAdventureConverter.componentFromNative(nativeComponent), ChatComponentType.NativeAdventureComponent, 0, false);
return new PacketAccessorResult(
(nativeComponent != null && nativeComponent.children().size() > 0) ? NativeAdventureConverter.componentFromNative(nativeComponent) : Component.empty(),
ChatComponentType.NativeAdventureComponent,
0,
false
);
Comment on lines +140 to +145
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Same NPE risk in chat handler

Similar to line 65, this has the same potential NPE issue with the children() check.

Apply the same defensive null check:

-                    return new PacketAccessorResult(
-                            (nativeComponent != null && nativeComponent.children().size() > 0) ? NativeAdventureConverter.componentFromNative(nativeComponent) : Component.empty(),
-                            ChatComponentType.NativeAdventureComponent,
-                            0,
-                            false
-                    );
+                    return new PacketAccessorResult(
+                            (nativeComponent != null && nativeComponent.children() != null && !nativeComponent.children().isEmpty()) ? NativeAdventureConverter.componentFromNative(nativeComponent) : Component.empty(),
+                            ChatComponentType.NativeAdventureComponent,
+                            0,
+                            false
+                    );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return new PacketAccessorResult(
(nativeComponent != null && nativeComponent.children().size() > 0) ? NativeAdventureConverter.componentFromNative(nativeComponent) : Component.empty(),
ChatComponentType.NativeAdventureComponent,
0,
false
);
return new PacketAccessorResult(
(nativeComponent != null
&& nativeComponent.children() != null
&& !nativeComponent.children().isEmpty())
? NativeAdventureConverter.componentFromNative(nativeComponent)
: Component.empty(),
ChatComponentType.NativeAdventureComponent,
0,
false
);
🤖 Prompt for AI Agents
In src/main/java/net/skullian/listeners/PEOutMessagePacket.java around lines 140
to 145, the code checks nativeComponent.children() without ensuring
nativeComponent is not null, risking a NullPointerException. Add a defensive
null check for nativeComponent before calling children(), similar to the fix
applied at line 65, to prevent NPE by verifying nativeComponent is not null
before accessing its children.

},
(packet, component, type, field, sender) -> {
boolean legacyRGB = InteractiveChat.version.isLegacyRGB();
Expand All @@ -154,10 +161,12 @@ private void initializeCommonPacketHandlers() {
"LONGER THAN MAX LENGTH: " + longerThanMaxLength +
"CURRENT MESSAGE: " + net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer.plainText().serialize(chatMessage.getMessage().getChatContent()));

if (InteractiveChat.version.isNewerOrEqualTo(MCVersion.V1_19_3)) {
((ChatMessage_v1_19_3) chatMessage.getMessage()).setUnsignedChatContent((net.kyori.adventure.text.Component) type.convertTo(component, legacyRGB));
} else {
chatMessage.getMessage().setChatContent((net.kyori.adventure.text.Component) type.convertTo(component, legacyRGB));
if (component.children().size() > 0) {
if (InteractiveChat.version.isNewerOrEqualTo(MCVersion.V1_19_3)) {
((ChatMessage_v1_19_3) chatMessage.getMessage()).setUnsignedChatContent((net.kyori.adventure.text.Component) type.convertTo(component, legacyRGB));
} else {
chatMessage.getMessage().setChatContent((net.kyori.adventure.text.Component) type.convertTo(component, legacyRGB));
}
}
Comment on lines +164 to 170
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Repeated pattern of checking children().size() > 0

The same pattern of checking component.children().size() > 0 is repeated multiple times throughout the code. This:

  1. Has the same issue of potentially missing components with content but no children
  2. Creates code duplication
  3. Makes maintenance harder

Extract this logic into a helper method:

private static boolean hasContent(Component component) {
    return component != null && !component.equals(Component.empty());
}

private static boolean hasContent(net.kyori.adventure.text.Component component) {
    return component != null && 
           (component.children() != null && !component.children().isEmpty() || 
            !net.kyori.adventure.text.Component.empty().equals(component));
}

Then use it consistently throughout the code.

Also applies to: 182-184, 194-203, 253-255, 298-307

🤖 Prompt for AI Agents
In src/main/java/net/skullian/listeners/PEOutMessagePacket.java around lines
164-170 and also at 182-184, 194-203, 253-255, and 298-307, the code repeatedly
checks if component.children().size() > 0 to determine if a component has
content, which can miss components with content but no children and causes
duplication. To fix this, create a helper method named hasContent that checks if
the component is not null and not empty, considering both children and content
presence as shown in the suggested method signatures. Replace all repeated
children().size() > 0 checks with calls to this new hasContent method to reduce
duplication and improve correctness.


sendDebug("Processed SERVER_CHAT Packet:" +
Expand All @@ -170,7 +179,9 @@ private void initializeCommonPacketHandlers() {
"LONGER THAN MAX LENGTH: " + longerThanMaxLength +
"CURRENT MESSAGE: " + net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer.plainText().serialize(chatMessage.getMessage()));

chatMessage.setMessage((net.kyori.adventure.text.Component) type.convertTo(component, legacyRGB));
if (component.children().size() > 0) {
chatMessage.setMessage((net.kyori.adventure.text.Component) type.convertTo(component, legacyRGB));
}

sendDebug("Processed SYSTEM_CHAT_MESSAGE Packet:" +
"NEW COMPONENT: " + PlainTextComponentSerializer.plainText().serialize(component));
Expand All @@ -180,13 +191,15 @@ private void initializeCommonPacketHandlers() {
if (packet instanceof WrapperPlayServerChatMessage) {
WrapperPlayServerChatMessage chatMessage = (WrapperPlayServerChatMessage) packet;

if (InteractiveChat.version.isNewerOrEqualTo(MCVersion.V1_19_3)) {
((ChatMessage_v1_19_3) chatMessage.getMessage())
.setUnsignedChatContent((net.kyori.adventure.text.Component) NativeAdventureConverter.componentToNative(component, legacyRGB));
} else {
chatMessage.getMessage().setChatContent(
(net.kyori.adventure.text.Component) NativeAdventureConverter.componentToNative(component, legacyRGB)
);
if (component.children().size() > 0) {
if (InteractiveChat.version.isNewerOrEqualTo(MCVersion.V1_19_3)) {
((ChatMessage_v1_19_3) chatMessage.getMessage())
.setUnsignedChatContent((net.kyori.adventure.text.Component) NativeAdventureConverter.componentToNative(component, legacyRGB));
} else {
chatMessage.getMessage().setChatContent(
(net.kyori.adventure.text.Component) NativeAdventureConverter.componentToNative(component, legacyRGB)
);
}
}
} else {
((WrapperPlayServerSystemChatMessage) packet).setMessageJson(json);
Expand Down Expand Up @@ -237,7 +250,9 @@ private void initializeCommonPacketHandlers() {
"LONGER THAN MAX LENGTH: " + longerThanMaxLength +
"CURRENT TITLE: " + net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer.plainText().serialize(titlePacket.getTitle()));

titlePacket.setTitle((net.kyori.adventure.text.Component) type.convertTo(component, legacyRGB));
if (component.children().size() > 0) {
titlePacket.setTitle((net.kyori.adventure.text.Component) type.convertTo(component, legacyRGB));
}

if (sender == null) sender = UUID_NIL;

Expand Down Expand Up @@ -280,13 +295,15 @@ private PacketHandler createModernTitleHandler() {
"LONGER THAN MAX LENGTH: " + longerThanMaxLength +
"NEW COMPONENT: " + component);

net.kyori.adventure.text.Component nativeComponent = (net.kyori.adventure.text.Component) type.convertTo(component, legacyRGB);
if (packet instanceof WrapperPlayServerSetTitleText) {
((WrapperPlayServerSetTitleText) packet).setTitle(nativeComponent);
} else if (packet instanceof WrapperPlayServerSetTitleSubtitle) {
((WrapperPlayServerSetTitleSubtitle) packet).setSubtitle(nativeComponent);
} else if (packet instanceof WrapperPlayServerActionBar) {
((WrapperPlayServerActionBar) packet).setActionBarText(nativeComponent);
if (component.children().size() > 0) {
net.kyori.adventure.text.Component nativeComponent = (net.kyori.adventure.text.Component) type.convertTo(component, legacyRGB);
if (packet instanceof WrapperPlayServerSetTitleText) {
((WrapperPlayServerSetTitleText) packet).setTitle(nativeComponent);
} else if (packet instanceof WrapperPlayServerSetTitleSubtitle) {
((WrapperPlayServerSetTitleSubtitle) packet).setSubtitle(nativeComponent);
} else if (packet instanceof WrapperPlayServerActionBar) {
((WrapperPlayServerActionBar) packet).setActionBarText(nativeComponent);
}
}

return new PacketWriterResult(longerThanMaxLength, json.length(), sender);
Expand All @@ -311,7 +328,7 @@ private int getChatFieldsSize() {

@Override
public void onPacketSend(PacketSendEvent event) {
System.out.println(event.getPacketType());
//System.out.println(event.getPacketType());

if (!PACKET_HANDLERS.containsKey(event.getPacketType())) return;

Expand All @@ -327,15 +344,19 @@ private void handlePacketSending(PacketSendEvent event) {

InteractiveChat.messagesCounter.getAndIncrement();

// Allow only SystemChat - start
if (event.getPacketType() != PacketType.Play.Server.SYSTEM_CHAT_MESSAGE) {
return;
}
// Allow only SystemChat - end

Player receiver = event.getPlayer();

if (!(event.getLastUsedWrapper() instanceof WrapperPlayServerSystemChatMessage)) {
event.setCancelled(true);
}
event.setCancelled(true);
Comment on lines +347 to +355
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Restrictive packet filtering may cause issues

This change restricts processing to only SYSTEM_CHAT_MESSAGE packets and unconditionally cancels them. This is a significant behavioral change that:

  1. Ignores all other chat message types (CHAT_MESSAGE, DISGUISED_CHAT, etc.)
  2. Always cancels the original event regardless of processing outcome

This aligns with the "temp fix" nature but may break other chat functionality.

Given this is described as a temporary fix for ChatControl compatibility, verify if this restrictive filtering affects other chat features:


🏁 Script executed:

#!/bin/bash
# Search for other usages of the ignored packet types
echo "=== Searching for CHAT_MESSAGE packet usage ==="
rg -A 5 "PacketType\.Play\.Server\.CHAT_MESSAGE" --type java

echo -e "\n=== Searching for DISGUISED_CHAT packet usage ==="
rg -A 5 "PacketType\.Play\.Server\.DISGUISED_CHAT" --type java

echo -e "\n=== Searching for ChatControl plugin integration ==="
fd -e java -e yml | xargs rg -i "chatcontrol" -A 3

Length of output: 5036


Refine chat packet filtering to preserve other message handlers

The current guard in src/main/java/net/skullian/listeners/PEOutMessagePacket.java (lines 347–355)

// Allow only SystemChat
if (event.getPacketType() != PacketType.Play.Server.SYSTEM_CHAT_MESSAGE) {
    return;
}
Player receiver = event.getPlayer();
event.setCancelled(true);

unconditionally skips all non-system chat packets—bypassing existing handlers for:

  • CHAT_MESSAGE (PERedispatchSignedPacket.java & PACKET_HANDLERS in PEOutMessagePacket.java)
  • DISGUISED_CHAT, DELETE_CHAT, CUSTOM_CHAT_COMPLETIONS, DISPLAY_CHAT_PREVIEW, PLAYER_CHAT_HEADER, etc.

To avoid breaking other chat features, narrow the early return and cancellation to only the SystemChat case. For example:

--- a/src/main/java/net/skullian/listeners/PEOutMessagePacket.java
+++ b/src/main/java/net/skullian/listeners/PEOutMessagePacket.java
@@ -346,9 +346,12 @@
-    // Allow only SystemChat
-    if (event.getPacketType() != PacketType.Play.Server.SYSTEM_CHAT_MESSAGE) {
-        return;
-    }
-    Player receiver = event.getPlayer();
-    event.setCancelled(true);
+    switch (event.getPacketType()) {
+        case SYSTEM_CHAT_MESSAGE -> {
+            Player receiver = event.getPlayer();
+            event.setCancelled(true);
+            // existing SystemChat handling…
+        }
+        default -> {
+            // continue so PACKET_HANDLERS for CHAT_MESSAGE, DISGUISED_CHAT, etc. run
+        }
+    }

This ensures other chat packet handlers remain in effect while still isolating the temporary SystemChat fix.

🤖 Prompt for AI Agents
In src/main/java/net/skullian/listeners/PEOutMessagePacket.java around lines 347
to 355, the current code returns early for all non-SystemChat packets, which
prevents other chat packet handlers from running. Modify the logic so that only
SystemChat packets are cancelled and returned early, while other packet types
continue processing normally. This means removing the unconditional return for
non-SystemChat packets and applying cancellation only within the SystemChat
packet check block to preserve handling of other chat message types.


event.markForReEncode(true);
UUID messageUUID = UUID.randomUUID();
ICPlayer determinedSender = ICPlayerFactory.getICPlayer((Player) event.getPlayer());
ICPlayer determinedSender = packetHandler.getDeterminedSenderFunction().apply(event);

PacketSendEvent originalEvent = event.clone();
SCHEDULING_SERVICE.execute(() -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ public ScheduledTask packetSender() {
try {
if (out.getReciever().isOnline() && out.getPacket() != null) {
PacketWrapper<?> wrapper = (PacketWrapper<?>) out.getPacket();
if (wrapper instanceof WrapperPlayServerSystemChatMessage) return;

PacketEvents.getAPI().getPlayerManager().sendPacketSilently(out.getReciever(), wrapper);
}
} catch (Exception e) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ LegacyCommandPacketPriority: HIGH
BungeecordPingPriority: NORMAL

# Enable debug?
Debug: true
Debug: false
2 changes: 1 addition & 1 deletion src/main/resources/plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ libraries:
- net.kyori:adventure-text-minimessage:4.18.0
- org.incendo:cloud-paper:2.0.0-beta.10
- org.incendo:cloud-annotations:2.0.0
- net.bytebuddy:byte-buddy:1.17.6
- net.bytebuddy:byte-buddy:1.17.5