Initial commit

This commit is contained in:
CPTProgrammer
2023-07-07 09:55:43 +08:00
commit bbe2d03767
17 changed files with 1465 additions and 0 deletions
@@ -0,0 +1,26 @@
package cn.revaria.chatplus;
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.LoggerFactory;
public class ChatPlus implements ModInitializer {
// 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.
// That way, it's clear which mod wrote info, warnings, and errors.
public static final Logger LOGGER = LoggerFactory.getLogger("chat-plus");
@Override
public void onInitialize() {
// This code runs as soon as Minecraft is in a mod-load-ready state.
// However, some things (like resources) may still be uninitialized.
// Proceed with mild caution.
LOGGER.info("§2ChatPlus Loaded!");
}
}
@@ -0,0 +1,167 @@
package cn.revaria.chatplus.mixin;
import io.netty.util.internal.StringUtil;
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.Items;
import net.minecraft.network.message.LastSeenMessageList;
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.listener.ServerPlayPacketListener;
import net.minecraft.server.filter.FilteredMessage;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.network.ServerPlayNetworkHandler;
import net.minecraft.text.LiteralTextContent;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.item.AirBlockItem;
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.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.time.Instant;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Mixin(ServerPlayNetworkHandler.class)
public abstract class MixinChat {
@Shadow @Final
static Logger LOGGER;
@Final
@Shadow
private MinecraftServer server;
@Shadow
public abstract void disconnect(Text reason);
@Final
@Shadow
private MessageChainTaskQueue messageChainTaskQueue;
@Shadow
private ServerPlayerEntity player;
@Shadow protected abstract Optional<LastSeenMessageList> validateMessage(String message, Instant timestamp, LastSeenMessageList.Acknowledgment acknowledgment);
@Shadow protected abstract SignedMessage getSignedMessage(ChatMessageC2SPacket packet, LastSeenMessageList lastSeenMessages) throws MessageChain.MessageChainException;
@Shadow protected abstract void handleMessageChainException(MessageChain.MessageChainException exception);
@Shadow protected abstract void handleDecoratedMessage(SignedMessage message);
@Shadow protected abstract CompletableFuture<FilteredMessage> filterText(String text);
@Inject(method = "onChatMessage", at = @At("HEAD"), cancellable = true)
public void onChatMessage(ChatMessageC2SPacket packet, CallbackInfo ci) {
// LOGGER.info("CHAT_MESSAGE: " + packet.chatMessage());
if (hasIllegalCharacter(packet.chatMessage())) {
disconnect(Text.translatable("multiplayer.disconnect.illegal_characters"));
} else {
Optional<LastSeenMessageList> optional = this.validateMessage(packet.chatMessage(), packet.timestamp(), packet.acknowledgment());
if (optional.isPresent()) {
if (!packet.chatMessage().startsWith("/")){
String changedMessage = packet.chatMessage().replace('&', '§');
String regex = "\\[item(?:=([1-9]))?\\]";
String[] messages = changedMessage.split(regex, -1);
Deque<Integer> itemDeque = new ArrayDeque<>();
Matcher matcher = Pattern.compile(regex).matcher(changedMessage);
while (matcher.find()){
String digit = matcher.group(1);
if (digit == null){
itemDeque.addLast(-1);
}else {
itemDeque.addLast(Integer.parseInt(digit));
}
}
MutableText changedText = MutableText.of(TextContent.EMPTY);
for (int i = 0; i < messages.length; i++) {
changedText.append(Text.of(messages[i]));
if (!itemDeque.isEmpty()) {
ItemStack itemStack;
if (itemDeque.getFirst() == -1) {
itemStack = player.getMainHandStack();
} else {
itemStack = player.getInventory().getStack(itemDeque.getFirst() - 1);
}
changedText.append(itemStack.toHoverableText());
itemDeque.removeFirst();
}
}
try {
SignedMessage signedMessage = getSignedMessage(packet, (LastSeenMessageList) optional.get());
server.getPlayerManager().broadcast(signedMessage.withUnsignedContent(
changedText
), player, MessageType.params(MessageType.CHAT, player));
} catch (MessageChain.MessageChainException e) {
handleMessageChainException(e);
}
} else {
this.server.submit(() -> {
SignedMessage signedMessage;
try {
signedMessage = this.getSignedMessage(packet, (LastSeenMessageList) optional.get());
} catch (MessageChain.MessageChainException var6) {
handleMessageChainException(var6);
return;
}
CompletableFuture<FilteredMessage> completableFuture = filterText(signedMessage.getSignedContent());
CompletableFuture<Text> completableFuture2 = this.server.getMessageDecorator().decorate(this.player, signedMessage.getContent());
messageChainTaskQueue.append((executor) -> {
return CompletableFuture.allOf(completableFuture, completableFuture2).thenAcceptAsync((void_) -> {
SignedMessage signedMessage2 = signedMessage.withUnsignedContent((Text) completableFuture2.join()).withFilterMask(((FilteredMessage) completableFuture.join()).mask());
handleDecoratedMessage(signedMessage2);
}, executor);
});
});
}
}
}
ci.cancel();
}
private static boolean hasIllegalCharacter(String message) {
for(int i = 0; i < message.length(); ++i) {
if (!SharedConstants.isValidChar(message.charAt(i))) {
return true;
}
}
return false;
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 453 B

+11
View File
@@ -0,0 +1,11 @@
{
"required": true,
"package": "cn.revaria.chatplus.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
"MixinChat"
],
"injectors": {
"defaultRequire": 1
}
}
+38
View File
@@ -0,0 +1,38 @@
{
"schemaVersion": 1,
"id": "chat-plus",
"version": "${version}",
"name": "Chat Plus",
"description": "Add bukkit style with an & and [item] for Fabric Server",
"authors": [
"Rev_Aria"
],
"contact": {
"homepage": "https://fabricmc.net/",
"sources": "https://github.com/FabricMC/fabric-example-mod"
},
"license": "CC0-1.0",
"icon": "assets/chat-plus/icon.png",
"environment": "*",
"entrypoints": {
"main": [
"cn.revaria.chatplus.ChatPlus"
]
},
"mixins": [
"chat-plus.mixins.json",
{
"config": "chat-plus.client.mixins.json",
"environment": "client"
}
],
"depends": {
"fabricloader": ">=0.14.21",
"minecraft": "~1.20.1",
"java": ">=17",
"fabric-api": "*"
},
"suggests": {
"another-mod": "*"
}
}