11 Commits
Author SHA1 Message Date
CPTProgrammer 7ccb661fbd Update README.md 2025-01-26 21:21:28 +08:00
CPTProgrammer f76b670fdc Update README.md 2025-01-25 22:17:30 +08:00
CPTProgrammer 1cd925f192 Update workflow configuration
Upgrade Java version from 17 to 21 in workflow configuration
2025-01-25 20:32:06 +08:00
CPTProgrammer 6a84f79d69 Update mod compatibility fix
Update compatibility fix for mod "Discord Integration" and "Dynmap" to ensure proper functionality.
2025-01-25 20:28:01 +08:00
BloemenpotandCPTProgrammer a94415e427 Update to 1.20.4 (#7)
* Updated Gradle Version

* Updated Loom Version

* Updated GradleProperties to 1.21.4

* Updated needed classes to 1.21.4

* Update mod version

---------

Co-authored-by: CPTProgrammer <46586216+CPTProgrammer@users.noreply.github.com>
2025-01-25 18:35:20 +08:00
CPTProgrammer ed5e4a7ea7 Update gradle.properties 2024-06-25 10:24:29 +08:00
CPTProgrammer 642c890c58 Fixed #5
Fixed incompatibility with mod Dynmap
2024-06-25 08:39:07 +08:00
CPTProgrammer 7ac3c4a802 Details update
- Updated information in fabric.mod.json
- Changed logger prefix
2024-06-25 08:16:10 +08:00
CPTProgrammer ef99035b2d Update README.md 2024-01-25 20:19:21 +08:00
CPTProgrammer a924921a77 Update build.gradle 2024-01-25 20:07:02 +08:00
OffsetMonkey538 886761f03e Update to 1.20.4 (#4)
* Update gradle

* Update loom

* Use 1.20.4 to build

* Update to 1.20.4
2024-01-25 19:52:34 +08:00
9 changed files with 82 additions and 61 deletions
+2 -2
View File
@@ -12,7 +12,7 @@ jobs:
matrix: matrix:
# Use these Java versions # Use these Java versions
java: [ java: [
17, # Current Java LTS & minimum supported by Minecraft 21, # Current Java LTS & minimum supported by Minecraft
] ]
# and run on both Linux and Windows # and run on both Linux and Windows
os: [ubuntu-22.04, windows-2022] os: [ubuntu-22.04, windows-2022]
@@ -33,7 +33,7 @@ jobs:
- name: build - name: build
run: ./gradlew build run: ./gradlew build
- name: capture build artifacts - name: capture build artifacts
if: ${{ runner.os == 'Linux' && matrix.java == '17' }} # Only upload artifacts built from latest java on one OS if: ${{ runner.os == 'Linux' && matrix.java == '21' }} # Only upload artifacts built from latest java on one OS
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: Artifacts name: Artifacts
+11 -3
View File
@@ -1,11 +1,14 @@
# ChatPlus # ChatPlus
![Static Badge](https://img.shields.io/badge/Minecraft_version-1.20.1_%7C_1.20.4_%7C_1.21.4-red?style=flat)
<a href="https://modrinth.com/mod/chatplus">
<img src="https://img.shields.io/badge/Modrinth-Chat_Plus-%234e910e?style=flat" />
</a>
A Simple mod add bukkit style with an "&" and "[item]" display for Fabric Server. A Simple mod add bukkit style with an "&" and "[item]" display for Fabric Server.
It's a **server-side** mod and doesn't need to be installed on the client. It's a **server-side** mod and doesn't need to be installed on the client.
Current Minecraft version: 1.20.1 ## Screenshots
## Screenshot
![image](https://github.com/CPTProgrammer/ChatPlus/assets/46586216/dcd2ca4b-79e9-4692-a8de-02fddfe4a392) ![image](https://github.com/CPTProgrammer/ChatPlus/assets/46586216/dcd2ca4b-79e9-4692-a8de-02fddfe4a392)
**^^^ Display the item in main hand** **^^^ Display the item in main hand**
@@ -14,3 +17,8 @@
![image](https://github.com/CPTProgrammer/ChatPlus/assets/46586216/dc3f0451-cbea-4610-8575-ea5da4e97f0f) ![image](https://github.com/CPTProgrammer/ChatPlus/assets/46586216/dc3f0451-cbea-4610-8575-ea5da4e97f0f)
**^^^ Display items in slots** **^^^ Display items in slots**
## Contributors
<a href="https://github.com/CPTProgrammer/ChatPlus/graphs/contributors">
<img src="https://contrib.rocks/image?repo=CPTProgrammer/ChatPlus" />
</a>
+2 -2
View File
@@ -1,9 +1,9 @@
plugins { plugins {
id 'fabric-loom' version '1.3-SNAPSHOT' id 'fabric-loom' version '1.9-SNAPSHOT'
id 'maven-publish' id 'maven-publish'
} }
version = project.mod_version version = project.mod_version + "-mc" + project.minecraft_version
group = project.maven_group group = project.maven_group
base { base {
+5 -5
View File
@@ -4,14 +4,14 @@ org.gradle.parallel=true
# Fabric Properties # Fabric Properties
# check these on https://fabricmc.net/develop # check these on https://fabricmc.net/develop
minecraft_version=1.20.1 minecraft_version=1.21.4
yarn_mappings=1.20.1+build.9 yarn_mappings=1.21.4+build.8
loader_version=0.14.21 loader_version=0.16.9
# Mod Properties # Mod Properties
mod_version=0.20.2 mod_version=0.21.0
maven_group=cn.revaria.chatplus maven_group=cn.revaria.chatplus
archives_base_name=chat-plus archives_base_name=chat-plus
# Dependencies # Dependencies
fabric_version=0.85.0+1.20.1 fabric_version=0.115.0+1.21.4
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
Vendored Regular → Executable
View File
@@ -1,11 +1,6 @@
package cn.revaria.chatplus; package cn.revaria.chatplus;
import net.fabricmc.api.ModInitializer; import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.message.v1.ServerMessageEvents;
import net.minecraft.network.message.MessageType;
import net.minecraft.network.message.SignedMessage;
import net.minecraft.server.network.ServerPlayerEntity;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -13,7 +8,7 @@ public class ChatPlus implements ModInitializer {
// This logger is used to write text to the console and the log file. // This logger is used to write text to the console and the log file.
// It is considered best practice to use your mod id as the logger's name. // It is considered best practice to use your mod id as the logger's name.
// That way, it's clear which mod wrote info, warnings, and errors. // That way, it's clear which mod wrote info, warnings, and errors.
public static final Logger LOGGER = LoggerFactory.getLogger("chat-plus"); public static final Logger LOGGER = LoggerFactory.getLogger("Chat Plus");
@Override @Override
public void onInitialize() { public void onInitialize() {
@@ -1,33 +1,29 @@
package cn.revaria.chatplus.mixin; package cn.revaria.chatplus.mixin;
import net.minecraft.SharedConstants; import net.minecraft.MinecraftVersion;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.network.message.LastSeenMessageList; import net.minecraft.network.ClientConnection;
import net.minecraft.network.message.MessageChain; import net.minecraft.network.message.*;
import net.minecraft.network.message.MessageType;
import net.minecraft.network.message.SignedMessage;
import net.minecraft.network.message.MessageChainTaskQueue;
import net.minecraft.server.MinecraftServer;
import net.minecraft.network.packet.c2s.play.ChatMessageC2SPacket; import net.minecraft.network.packet.c2s.play.ChatMessageC2SPacket;
import net.minecraft.server.PlayerManager; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.filter.FilteredMessage; import net.minecraft.server.filter.FilteredMessage;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ConnectedClientData;
import net.minecraft.server.network.ServerCommonNetworkHandler;
import net.minecraft.server.network.ServerPlayNetworkHandler; import net.minecraft.server.network.ServerPlayNetworkHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.MutableText; import net.minecraft.text.MutableText;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.text.TextContent; import net.minecraft.util.StringHelper;
import org.slf4j.Logger;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.time.Instant;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Deque; import java.util.Deque;
import java.util.Optional; import java.util.Optional;
@@ -36,25 +32,21 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@Mixin(ServerPlayNetworkHandler.class) @Mixin(ServerPlayNetworkHandler.class)
public abstract class MixinChat { public abstract class MixinChat extends ServerCommonNetworkHandler {
@Shadow @Final public MixinChat(MinecraftServer server, ClientConnection connection, ConnectedClientData clientData) {
static Logger LOGGER; super(server, connection, clientData);
}
@Final
@Shadow
private MinecraftServer server;
@Shadow
public abstract void disconnect(Text reason);
@Final @Final
@Shadow @Shadow
private MessageChainTaskQueue messageChainTaskQueue; private MessageChainTaskQueue messageChainTaskQueue;
@Shadow @Shadow
private ServerPlayerEntity player; public ServerPlayerEntity player;
@Shadow protected abstract Optional<LastSeenMessageList> validateMessage(String message, Instant timestamp, LastSeenMessageList.Acknowledgment acknowledgment); // @Shadow protected abstract Optional<LastSeenMessageList> validateMessage(LastSeenMessageList.Acknowledgment acknowledgment);
@Shadow protected abstract Optional<LastSeenMessageList> validateAcknowledgment(LastSeenMessageList.Acknowledgment acknowledgment);
@Shadow protected abstract SignedMessage getSignedMessage(ChatMessageC2SPacket packet, LastSeenMessageList lastSeenMessages) throws MessageChain.MessageChainException; @Shadow protected abstract SignedMessage getSignedMessage(ChatMessageC2SPacket packet, LastSeenMessageList lastSeenMessages) throws MessageChain.MessageChainException;
@@ -71,7 +63,7 @@ public abstract class MixinChat {
if (hasIllegalCharacter(packet.chatMessage())) { if (hasIllegalCharacter(packet.chatMessage())) {
disconnect(Text.translatable("multiplayer.disconnect.illegal_characters")); disconnect(Text.translatable("multiplayer.disconnect.illegal_characters"));
} else { } else {
Optional<LastSeenMessageList> optional = this.validateMessage(packet.chatMessage(), packet.timestamp(), packet.acknowledgment()); Optional<LastSeenMessageList> optional = this.validateAcknowledgment(packet.acknowledgment());
if (optional.isPresent()) { if (optional.isPresent()) {
if (!packet.chatMessage().startsWith("/")){ if (!packet.chatMessage().startsWith("/")){
@@ -90,7 +82,7 @@ public abstract class MixinChat {
} }
} }
MutableText changedText = MutableText.of(TextContent.EMPTY); MutableText changedText = Text.empty();
for (String message : messages) { for (String message : messages) {
changedText.append(Text.of(message)); changedText.append(Text.of(message));
if (!itemDeque.isEmpty()) { if (!itemDeque.isEmpty()) {
@@ -108,15 +100,42 @@ public abstract class MixinChat {
try { try {
SignedMessage signedMessage = getSignedMessage(packet, (LastSeenMessageList) optional.get()); SignedMessage signedMessage = getSignedMessage(packet, (LastSeenMessageList) optional.get());
server.getPlayerManager().broadcast(signedMessage.withUnsignedContent( server.getPlayerManager().broadcast(signedMessage.withUnsignedContent(
changedText changedText
), player, MessageType.params(MessageType.CHAT, player)); ), player, MessageType.params(MessageType.CHAT, player));
// handleDecoratedMessage(signedMessage.withUnsignedContent(changedText));
// Compatible with mod "Discord Integration"
try { try {
Class<?> DiscordIntegrationMod = Class.forName("de.erdbeerbaerlp.dcintegration.fabric.DiscordIntegrationMod"); Class<?> DiscordIntegrationMod = Class.forName("de.erdbeerbaerlp.dcintegration.architectury.DiscordIntegrationMod");
Method handleChatMessage = DiscordIntegrationMod.getMethod("handleChatMessage", SignedMessage.class, ServerPlayerEntity.class); Method handleChatMessage = DiscordIntegrationMod.getMethod("handleChatMessage", SignedMessage.class, ServerPlayerEntity.class);
handleChatMessage.invoke(null, signedMessage.withUnsignedContent(changedText), player); handleChatMessage.invoke(null, signedMessage.withUnsignedContent(changedText), player);
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException |
IllegalAccessException ignored) { } IllegalAccessException ignored) { }
// Compatible with mod "Dynmap"
String[] minecraftVersion = MinecraftVersion.CURRENT.getName().split("\\.");
int minecraftPatchVersion = minecraftVersion.length == 3 ? Integer.parseInt(minecraftVersion[2]) : 0;
for (int i = minecraftPatchVersion; i >= 0; i--){
String version = minecraftVersion[0] + "." + minecraftVersion[1] + (i == 0 ? "" : ("." + i));
try {
/*
Warning: Using reflection can make the code harder to maintain, debug, and understand.
Statement: DynmapMod.plugin.chathandler.handleChat()
*/
Class<?> DynmapMod = Class.forName("org.dynmap.fabric_" + version.replaceAll("\\.", "_") + ".DynmapMod");
Field pluginField = DynmapMod.getField("plugin");
Object plugin = pluginField.get(null);
Class<?> pluginClass = plugin.getClass();
Field chatHandlerField = pluginClass.getDeclaredField("chathandler");
chatHandlerField.setAccessible(true);
Object chatHandler = chatHandlerField.get(plugin);
Class<?> chatHandlerClass = chatHandler.getClass();
Method handleChat = chatHandlerClass.getMethod("handleChat", ServerPlayerEntity.class, String.class);
handleChat.invoke(chatHandler, player, signedMessage.withUnsignedContent(changedText).getContent().getString());
break;
} catch (ClassNotFoundException | NoSuchMethodException | NoSuchFieldException | NullPointerException |
IllegalAccessException | InvocationTargetException ignored) { }
}
} catch (MessageChain.MessageChainException e) { } catch (MessageChain.MessageChainException e) {
handleMessageChainException(e); handleMessageChainException(e);
} }
@@ -124,19 +143,17 @@ public abstract class MixinChat {
this.server.submit(() -> { this.server.submit(() -> {
SignedMessage signedMessage; SignedMessage signedMessage;
try { try {
signedMessage = this.getSignedMessage(packet, (LastSeenMessageList) optional.get()); signedMessage = this.getSignedMessage(packet, optional.get());
} catch (MessageChain.MessageChainException var6) { } catch (MessageChain.MessageChainException var6) {
handleMessageChainException(var6); handleMessageChainException(var6);
return; return;
} }
CompletableFuture<FilteredMessage> completableFuture = filterText(signedMessage.getSignedContent()); CompletableFuture<FilteredMessage> completableFuture = filterText(signedMessage.getSignedContent());
CompletableFuture<Text> completableFuture2 = this.server.getMessageDecorator().decorate(this.player, signedMessage.getContent()); Text decoratedMessage = this.server.getMessageDecorator().decorate(this.player, signedMessage.getContent());
messageChainTaskQueue.append((executor) -> { messageChainTaskQueue.append(completableFuture, filteredMessage -> {
return CompletableFuture.allOf(completableFuture, completableFuture2).thenAcceptAsync((void_) -> { SignedMessage message = signedMessage.withUnsignedContent(decoratedMessage).withFilterMask(filteredMessage.mask());
SignedMessage signedMessage2 = signedMessage.withUnsignedContent((Text) completableFuture2.join()).withFilterMask(((FilteredMessage) completableFuture.join()).mask()); this.handleDecoratedMessage(message);
handleDecoratedMessage(signedMessage2);
}, executor);
}); });
}); });
} }
@@ -149,7 +166,7 @@ public abstract class MixinChat {
private static boolean hasIllegalCharacter(String message) { private static boolean hasIllegalCharacter(String message) {
for(int i = 0; i < message.length(); ++i) { for(int i = 0; i < message.length(); ++i) {
if (!SharedConstants.isValidChar(message.charAt(i))) { if (!StringHelper.isValidChar(message.charAt(i))) {
return true; return true;
} }
} }
+5 -4
View File
@@ -8,8 +8,9 @@
"Rev_Aria" "Rev_Aria"
], ],
"contact": { "contact": {
"homepage": "https://fabricmc.net/", "homepage": "https://modrinth.com/mod/chatplus",
"sources": "https://github.com/FabricMC/fabric-example-mod" "sources": "https://github.com/CPTProgrammer/ChatPlus/",
"issues": "https://github.com/CPTProgrammer/ChatPlus/issues"
}, },
"license": "CC0-1.0", "license": "CC0-1.0",
"icon": "assets/chat-plus/icon.png", "icon": "assets/chat-plus/icon.png",
@@ -27,8 +28,8 @@
} }
], ],
"depends": { "depends": {
"fabricloader": ">=0.14.21", "fabricloader": ">=0.16.9",
"minecraft": "~1.20.1", "minecraft": "~1.21.4",
"java": ">=17", "java": ">=17",
"fabric-api": "*" "fabric-api": "*"
}, },