mirror of
https://github.com/CPTProgrammer/ChatPlus.git
synced 2026-09-22 00:23:36 +08:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1cd925f192 | ||
|
|
6a84f79d69 | ||
|
|
a94415e427 | ||
|
|
ed5e4a7ea7 | ||
|
|
642c890c58 | ||
|
|
7ac3c4a802 | ||
|
|
ef99035b2d | ||
|
|
a924921a77 | ||
|
|
886761f03e | ||
|
|
7b88d13069 | ||
|
|
3e3e5f9797 |
@@ -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
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
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
|
Currently supported Minecraft versions: 1.20.1, 1.20.4
|
||||||
|
|
||||||
## Screenshot
|
## Screenshot
|
||||||

|

|
||||||
|
|||||||
+2
-2
@@ -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
@@ -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.1
|
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
@@ -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
|
||||||
|
|||||||
@@ -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,75 +1,52 @@
|
|||||||
package cn.revaria.chatplus.mixin;
|
package cn.revaria.chatplus.mixin;
|
||||||
|
|
||||||
import io.netty.util.internal.StringUtil;
|
import net.minecraft.MinecraftVersion;
|
||||||
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
|
|
||||||
import net.minecraft.SharedConstants;
|
|
||||||
import net.minecraft.block.AbstractBlock;
|
|
||||||
import net.minecraft.block.AirBlock;
|
|
||||||
import net.minecraft.block.Block;
|
|
||||||
import net.minecraft.entity.EquipmentSlot;
|
|
||||||
import net.minecraft.item.Item;
|
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.item.Items;
|
import net.minecraft.network.ClientConnection;
|
||||||
import net.minecraft.network.message.LastSeenMessageList;
|
import net.minecraft.network.message.*;
|
||||||
import net.minecraft.network.message.MessageChain;
|
|
||||||
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.network.listener.ServerPlayPacketListener;
|
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.text.LiteralTextContent;
|
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.item.AirBlockItem;
|
import net.minecraft.util.StringHelper;
|
||||||
import net.minecraft.text.TextContent;
|
|
||||||
import net.minecraft.util.Formatting;
|
|
||||||
import net.minecraft.nbt.NbtCompound;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.apache.logging.log4j.core.jmx.Server;
|
|
||||||
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.Mutable;
|
|
||||||
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.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.regex.Matcher;
|
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;
|
||||||
|
|
||||||
@@ -86,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("/")){
|
||||||
|
|
||||||
@@ -105,9 +82,9 @@ public abstract class MixinChat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MutableText changedText = MutableText.of(TextContent.EMPTY);
|
MutableText changedText = Text.empty();
|
||||||
for (int i = 0; i < messages.length; i++) {
|
for (String message : messages) {
|
||||||
changedText.append(Text.of(messages[i]));
|
changedText.append(Text.of(message));
|
||||||
if (!itemDeque.isEmpty()) {
|
if (!itemDeque.isEmpty()) {
|
||||||
ItemStack itemStack;
|
ItemStack itemStack;
|
||||||
if (itemDeque.getFirst() == -1) {
|
if (itemDeque.getFirst() == -1) {
|
||||||
@@ -122,10 +99,43 @@ 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 {
|
||||||
|
Class<?> DiscordIntegrationMod = Class.forName("de.erdbeerbaerlp.dcintegration.architectury.DiscordIntegrationMod");
|
||||||
|
Method handleChatMessage = DiscordIntegrationMod.getMethod("handleChatMessage", SignedMessage.class, ServerPlayerEntity.class);
|
||||||
|
handleChatMessage.invoke(null, signedMessage.withUnsignedContent(changedText), player);
|
||||||
|
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException |
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
@@ -133,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);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -158,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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": "*"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user