Skip to content
Open
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@
import java.util.regex.PatternSyntaxException;

public class BetterChat extends Module {
private final int FIRST_USABLE_ASCII = (int) ' '; // 32
private final int LAST_USABLE_ASCII = (int) '~'; // 126
private final int NUM_USABLE_ASCII = LAST_USABLE_ASCII - FIRST_USABLE_ASCII + 1;

private final SettingGroup sgGeneral = settings.getDefaultGroup();
private final SettingGroup sgFilter = settings.createGroup("Filter");
private final SettingGroup sgLongerChat = settings.createGroup("Longer Chat");
Expand Down Expand Up @@ -104,6 +108,25 @@ public class BetterChat extends Module {
.build()
);

private final Setting<Boolean> encrypt = sgGeneral.add(new BoolSetting.Builder()
.name("encrypt")
.description("Applies an encryption routine to chat messages")
.defaultValue(false)
.build()
);

private final Setting<Integer> cypherOffset = sgFilter.add(new IntSetting.Builder()
Comment thread
Adog64 marked this conversation as resolved.
Outdated
.name("cypher-offset")
.description("Ceasar cypher offset used for encrypting messages")
.defaultValue(20)
.min(0)
.sliderMin(0)
.max(NUM_USABLE_ASCII - 1)
.sliderMax(NUM_USABLE_ASCII - 1)
Comment thread
Adog64 marked this conversation as resolved.
Outdated
.visible(encrypt::get)
.build()
);

// Filter

private final Setting<Boolean> antiSpam = sgFilter.add(new BoolSetting.Builder()
Expand Down Expand Up @@ -257,6 +280,10 @@ public BetterChat() {
private void onMessageReceive(ReceiveMessageEvent event) {
Component message = event.getMessage();

// Always decrypt messages regardless of whether we are encrypting them
String decryptedMessage = applyDecryption(message.getString());
message = Component.empty().append(decryptedMessage);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

i'm not sure whether always decrypting is the behaviour we want, but regardless the message should only be replaced if the text actually contained a caesar cipher encoded message so that:

  1. we dont allocate a redundant Component
  2. we dont override the styling information held in message unless we actually replace the text it contains

@Adog64 Adog64 Aug 5, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I updated the applyDecryption method to have an Optional return type, so the caller has information as to whether decryption actually occurred or not.


if (filterRegex.get()) {
String messageString = message.getString();
for (Pattern pattern : filterRegexList) {
Expand Down Expand Up @@ -322,6 +349,8 @@ private void onMessageSend(SendMessageEvent event) {
return;
}

if (encrypt.get()) message = applyEncryption(message);

event.message = message;
}

Expand Down Expand Up @@ -511,6 +540,76 @@ private String applyAnnoy(String message) {
return message;
}

// Encrypt

private String applyEncryption(String message) {
int msgLen = message.length();
int offset = cypherOffset.get();
StringBuilder modString = new StringBuilder();
Comment thread
Adog64 marked this conversation as resolved.
Outdated

// Encode offset into message with char for validation
modString.append((char) (FIRST_USABLE_ASCII + offset));
modString.append(encryptChar('%', offset));

for (int i = 0; i < msgLen; ++i) {
char chr = message.charAt(i);
chr = encryptChar(chr, offset);
modString.append(chr);
}
return modString.toString();
}

private char encryptChar(char chr, int offset) {
int ascii = (int) chr;
if (ascii > FIRST_USABLE_ASCII && ascii <= LAST_USABLE_ASCII) {
ascii -= FIRST_USABLE_ASCII;
ascii += offset;
ascii %= NUM_USABLE_ASCII;
ascii += FIRST_USABLE_ASCII;
chr = (char) ascii;
}
return chr;
}

// Decrypt

private String applyDecryption(String message) {
int msgLen = message.length();
StringBuilder modString = new StringBuilder();
int msgStart = message.indexOf("> ") + 2;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
StringBuilder modString = new StringBuilder();
int msgStart = message.indexOf("> ") + 2;
int msgStart = message.indexOf("> ") + 2;
StringBuilder modString = new StringBuilder(msgLen - (msgStart + 2));

needs double check, i mathed in my head

@Adog64 Adog64 Aug 5, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Your math looks correct to me

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this code incorrectly assumes all incoming chat messages will be decodable, which isn't true (ex. system messages)
specifically, message.indexOf("> ") may return -1 and cause the second character of the message to be read as the cipher's offset which will result in gibberish

additionally, this function should never cause an exception to be thrown otherwise players would be able to remotely crash our users, so please double check everything

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Added some more robust logic here. Going to do more extensive testing in multiplayer in a prod env to make sure there are no unexpected crashes. This feels like a really good example of something I should make unit tests for though.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ideally yeah i guess, but we have no unit test setup in this project and frankly it’d be too much work

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yeah as useful as they are, they are a pain to write.


int offset = (int)(message.charAt(msgStart)) - FIRST_USABLE_ASCII;
boolean isEncrypted = decryptChar(message.charAt(msgStart+1), offset) == '%';

if (isEncrypted) {
for (int i = 0; i < msgLen; ++i) {
char chr = message.charAt(i);
if (i >= msgStart + 2) {
chr = decryptChar(chr, offset);
}
else if (i >= msgStart) {
continue;
}
modString.append(chr);
}
return modString.toString();
}
return message;
}

private char decryptChar(char chr, int offset) {
int ascii = (int) chr;
if (ascii > FIRST_USABLE_ASCII && ascii <= LAST_USABLE_ASCII) {
ascii -= FIRST_USABLE_ASCII;
ascii -= offset;
ascii += NUM_USABLE_ASCII;
ascii %= NUM_USABLE_ASCII;
ascii += FIRST_USABLE_ASCII;
chr = (char) ascii;
}
return chr;
}

// Fancy

private String applyFancy(String message) {
Expand Down