Replace /tell with custom implementation
This commit is contained in:
parent
4d2c2cb36f
commit
2fe6498830
11 changed files with 272 additions and 2 deletions
|
@ -49,6 +49,7 @@ dependencies {
|
||||||
include modImplementation("me.lucko:fabric-permissions-api:${project.permissions_api_version}")
|
include modImplementation("me.lucko:fabric-permissions-api:${project.permissions_api_version}")
|
||||||
|
|
||||||
include modImplementation("net.kyori:adventure-platform-fabric:${project.adventure_version}")
|
include modImplementation("net.kyori:adventure-platform-fabric:${project.adventure_version}")
|
||||||
|
modImplementation include("eu.pb4:placeholder-api:${project.placeholderapi_version}")
|
||||||
}
|
}
|
||||||
|
|
||||||
processResources {
|
processResources {
|
||||||
|
|
|
@ -9,7 +9,7 @@ yarn_mappings=1.20.1+build.10
|
||||||
loader_version=0.16.5
|
loader_version=0.16.5
|
||||||
|
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version=1.10.2
|
mod_version=1.11.0
|
||||||
maven_group=cc.reconnected
|
maven_group=cc.reconnected
|
||||||
archives_base_name=rcc-server
|
archives_base_name=rcc-server
|
||||||
|
|
||||||
|
@ -22,3 +22,4 @@ luckpermsapi_version=5.4
|
||||||
permissions_api_version=0.2-SNAPSHOT
|
permissions_api_version=0.2-SNAPSHOT
|
||||||
|
|
||||||
adventure_version=5.9.1
|
adventure_version=5.9.1
|
||||||
|
placeholderapi_version=2.1.3+1.20.1
|
|
@ -1,6 +1,8 @@
|
||||||
package cc.reconnected.server;
|
package cc.reconnected.server;
|
||||||
|
|
||||||
import cc.reconnected.server.commands.AfkCommand;
|
import cc.reconnected.server.commands.AfkCommand;
|
||||||
|
import cc.reconnected.server.commands.ReplyCommand;
|
||||||
|
import cc.reconnected.server.commands.TellCommand;
|
||||||
import cc.reconnected.server.database.PlayerData;
|
import cc.reconnected.server.database.PlayerData;
|
||||||
import cc.reconnected.server.events.PlayerActivityEvents;
|
import cc.reconnected.server.events.PlayerActivityEvents;
|
||||||
import cc.reconnected.server.events.PlayerWelcome;
|
import cc.reconnected.server.events.PlayerWelcome;
|
||||||
|
@ -82,6 +84,8 @@ public class RccServer implements ModInitializer {
|
||||||
LOGGER.info("Starting rcc-server");
|
LOGGER.info("Starting rcc-server");
|
||||||
|
|
||||||
CommandRegistrationCallback.EVENT.register(AfkCommand::register);
|
CommandRegistrationCallback.EVENT.register(AfkCommand::register);
|
||||||
|
CommandRegistrationCallback.EVENT.register(TellCommand::register);
|
||||||
|
CommandRegistrationCallback.EVENT.register(ReplyCommand::register);
|
||||||
|
|
||||||
ServerLifecycleEvents.SERVER_STARTED.register(server -> {
|
ServerLifecycleEvents.SERVER_STARTED.register(server -> {
|
||||||
luckPerms = LuckPermsProvider.get();
|
luckPerms = LuckPermsProvider.get();
|
||||||
|
|
|
@ -11,4 +11,7 @@ public class RccServerConfigModel {
|
||||||
|
|
||||||
public String afkMessage = "<gray><displayname> is now AFK</gray>";
|
public String afkMessage = "<gray><displayname> is now AFK</gray>";
|
||||||
public String afkReturnMessage = "<gray><displayname> is no longer AFK</gray>";
|
public String afkReturnMessage = "<gray><displayname> is no longer AFK</gray>";
|
||||||
|
|
||||||
|
public String tellMessage = "<gold>[</gold><source> <gray>→</gray> <target><gold>]</gold> <message>";
|
||||||
|
public String tellMessageSpy = "\uD83D\uDC41 <gray>[<source> → <target>]</gray> <message>";
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
package cc.reconnected.server.commands;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
import net.minecraft.command.CommandRegistryAccess;
|
||||||
|
import net.minecraft.server.command.CommandManager;
|
||||||
|
import net.minecraft.server.command.ServerCommandSource;
|
||||||
|
import net.minecraft.text.Style;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
import net.minecraft.util.Formatting;
|
||||||
|
|
||||||
|
import static net.minecraft.server.command.CommandManager.*;
|
||||||
|
|
||||||
|
|
||||||
|
public class ReplyCommand {
|
||||||
|
public static void register(CommandDispatcher<ServerCommandSource> dispatcher, CommandRegistryAccess registryAccess, CommandManager.RegistrationEnvironment environment) {
|
||||||
|
var messageNode = dispatcher.register(literal("reply")
|
||||||
|
.then(argument("message", StringArgumentType.greedyString())
|
||||||
|
.executes(ReplyCommand::execute)));
|
||||||
|
|
||||||
|
dispatcher.register(literal("r").redirect(messageNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int execute(CommandContext<ServerCommandSource> context) {
|
||||||
|
var source = context.getSource();
|
||||||
|
var senderName = source.getName();
|
||||||
|
var message = StringArgumentType.getString(context, "message");
|
||||||
|
|
||||||
|
if(!TellCommand.lastSender.containsKey(senderName)) {
|
||||||
|
source.sendFeedback(() -> Text.literal("You have no one to reply to.").setStyle(Style.EMPTY.withColor(Formatting.RED)), false);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetName = TellCommand.lastSender.get(senderName);
|
||||||
|
var playerManager = source.getServer().getPlayerManager();
|
||||||
|
|
||||||
|
TellCommand.sendDirectMessage(targetName, source, message);
|
||||||
|
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
107
src/main/java/cc/reconnected/server/commands/TellCommand.java
Normal file
107
src/main/java/cc/reconnected/server/commands/TellCommand.java
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
package cc.reconnected.server.commands;
|
||||||
|
|
||||||
|
import cc.reconnected.server.RccServer;
|
||||||
|
import cc.reconnected.server.parser.MarkdownParser;
|
||||||
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||||
|
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||||
|
import net.minecraft.command.CommandRegistryAccess;
|
||||||
|
import net.minecraft.command.CommandSource;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.command.CommandManager;
|
||||||
|
import net.minecraft.server.command.ServerCommandSource;
|
||||||
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
|
import net.minecraft.text.Style;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
import net.minecraft.util.Formatting;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import static net.minecraft.server.command.CommandManager.*;
|
||||||
|
|
||||||
|
|
||||||
|
public class TellCommand {
|
||||||
|
public static final HashMap<String, String> lastSender = new HashMap<>();
|
||||||
|
|
||||||
|
public static void register(CommandDispatcher<ServerCommandSource> dispatcher, CommandRegistryAccess registryAccess, CommandManager.RegistrationEnvironment environment) {
|
||||||
|
var messageNode = dispatcher.register(literal("msg")
|
||||||
|
.then(argument("player", StringArgumentType.word())
|
||||||
|
.suggests((context, builder) -> {
|
||||||
|
var playerManager = context.getSource().getServer().getPlayerManager();
|
||||||
|
return CommandSource.suggestMatching(
|
||||||
|
playerManager.getPlayerNames(),
|
||||||
|
builder);
|
||||||
|
})
|
||||||
|
.then(argument("message", StringArgumentType.greedyString())
|
||||||
|
.executes(TellCommand::execute))));
|
||||||
|
|
||||||
|
dispatcher.register(literal("tell").redirect(messageNode));
|
||||||
|
dispatcher.register(literal("w").redirect(messageNode));
|
||||||
|
dispatcher.register(literal("dm").redirect(messageNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int execute(CommandContext<ServerCommandSource> context) {
|
||||||
|
var source = context.getSource();
|
||||||
|
var targetName = StringArgumentType.getString(context, "player");
|
||||||
|
var message = StringArgumentType.getString(context, "message");
|
||||||
|
|
||||||
|
sendDirectMessage(targetName, source, message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendDirectMessage(String targetName, ServerCommandSource source, String message) {
|
||||||
|
Text targetDisplayName;
|
||||||
|
ServerPlayerEntity targetPlayer = null;
|
||||||
|
if (targetName.equalsIgnoreCase("server")) {
|
||||||
|
targetDisplayName = Text.of("Server");
|
||||||
|
} else {
|
||||||
|
targetPlayer = source.getServer().getPlayerManager().getPlayer(targetName);
|
||||||
|
if (targetPlayer == null) {
|
||||||
|
source.sendFeedback(() -> Text.literal("Player \"" + targetName + "\" not found").setStyle(Style.EMPTY.withColor(Formatting.RED)), false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
targetDisplayName = targetPlayer.getDisplayName();
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsedMessage = MarkdownParser.defaultParser.parseNode(message);
|
||||||
|
var text = MiniMessage.miniMessage().deserialize(RccServer.CONFIG.tellMessage(),
|
||||||
|
Placeholder.component("source", source.getDisplayName()),
|
||||||
|
Placeholder.component("target", targetDisplayName),
|
||||||
|
Placeholder.component("message", parsedMessage.toText()));
|
||||||
|
|
||||||
|
lastSender.put(targetName, source.getName());
|
||||||
|
|
||||||
|
if (!source.getName().equals(targetName)) {
|
||||||
|
source.sendMessage(text);
|
||||||
|
}
|
||||||
|
if(targetPlayer != null) {
|
||||||
|
targetPlayer.sendMessage(text);
|
||||||
|
if(source.isExecutedByPlayer()) {
|
||||||
|
source.getServer().sendMessage(text);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// avoid duped message
|
||||||
|
source.getServer().sendMessage(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
var lp = RccServer.getInstance().luckPerms();
|
||||||
|
var playerAdapter = lp.getPlayerAdapter(ServerPlayerEntity.class);
|
||||||
|
var spyText = MiniMessage.miniMessage().deserialize(RccServer.CONFIG.tellMessageSpy(),
|
||||||
|
Placeholder.component("source", source.getDisplayName()),
|
||||||
|
Placeholder.component("target", targetDisplayName),
|
||||||
|
Placeholder.component("message", parsedMessage.toText()));
|
||||||
|
source.getServer().getPlayerManager().getPlayerList().forEach(player -> {
|
||||||
|
var playerName = player.getGameProfile().getName();
|
||||||
|
if(playerName.equals(targetName) || playerName.equals(source.getName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var playerPerms = playerAdapter.getPermissionData(player);
|
||||||
|
if(playerPerms.checkPermission("rcc.tell.spy").asBoolean()) {
|
||||||
|
player.sendMessage(spyText);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package cc.reconnected.server.mixin;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
|
import net.minecraft.server.command.MessageCommand;
|
||||||
|
import net.minecraft.server.command.ServerCommandSource;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Mixin(MessageCommand.class)
|
||||||
|
public class MessageCommandMixin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Alex
|
||||||
|
* @reason Implementing custom tell command
|
||||||
|
*/
|
||||||
|
@Overwrite
|
||||||
|
public static void register(CommandDispatcher<ServerCommandSource> dispatcher) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package cc.reconnected.server.parser;
|
||||||
|
|
||||||
|
import eu.pb4.placeholders.api.node.TextNode;
|
||||||
|
import eu.pb4.placeholders.api.node.parent.ClickActionNode;
|
||||||
|
import eu.pb4.placeholders.api.node.parent.FormattingNode;
|
||||||
|
import eu.pb4.placeholders.api.node.parent.HoverNode;
|
||||||
|
import net.minecraft.text.ClickEvent;
|
||||||
|
import net.minecraft.util.Formatting;
|
||||||
|
|
||||||
|
public class MarkdownComponentParser {
|
||||||
|
public static TextNode spoilerFormatting(TextNode[] textNodes) {
|
||||||
|
var text = TextNode.asSingle(textNodes);
|
||||||
|
return new HoverNode<>(
|
||||||
|
TextNode.array(
|
||||||
|
new FormattingNode(TextNode.array(TextNode.of("\u258C".repeat(text.toText().getString().length()))), Formatting.DARK_GRAY)
|
||||||
|
),
|
||||||
|
HoverNode.Action.TEXT, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextNode quoteFormatting(TextNode[] textNodes) {
|
||||||
|
return new ClickActionNode(
|
||||||
|
TextNode.array(
|
||||||
|
new HoverNode<>(
|
||||||
|
TextNode.array(new FormattingNode(textNodes, Formatting.GRAY)),
|
||||||
|
HoverNode.Action.TEXT, TextNode.of("Click to copy"))
|
||||||
|
),
|
||||||
|
ClickEvent.Action.COPY_TO_CLIPBOARD, TextNode.asSingle(textNodes)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextNode urlFormatting(TextNode[] textNodes, TextNode url) {
|
||||||
|
return new HoverNode<>(TextNode.array(
|
||||||
|
new ClickActionNode(
|
||||||
|
TextNode.array(
|
||||||
|
new FormattingNode(textNodes, Formatting.BLUE, Formatting.UNDERLINE)),
|
||||||
|
ClickEvent.Action.OPEN_URL, url)),
|
||||||
|
HoverNode.Action.TEXT, TextNode.of("Click to open: " + url.toText().getString())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package cc.reconnected.server.parser;
|
||||||
|
|
||||||
|
import eu.pb4.placeholders.api.parsers.MarkdownLiteParserV1;
|
||||||
|
import eu.pb4.placeholders.api.parsers.NodeParser;
|
||||||
|
|
||||||
|
import static eu.pb4.placeholders.api.parsers.MarkdownLiteParserV1.MarkdownFormat;
|
||||||
|
|
||||||
|
|
||||||
|
public class MarkdownParser {
|
||||||
|
public static final MarkdownFormat[] ALL = new MarkdownFormat[] {
|
||||||
|
MarkdownFormat.QUOTE,
|
||||||
|
MarkdownFormat.BOLD,
|
||||||
|
MarkdownFormat.ITALIC,
|
||||||
|
MarkdownFormat.UNDERLINE,
|
||||||
|
MarkdownFormat.STRIKETHROUGH,
|
||||||
|
MarkdownFormat.SPOILER,
|
||||||
|
MarkdownFormat.URL
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final NodeParser defaultParser = createParser(ALL);
|
||||||
|
|
||||||
|
public static NodeParser createParser(MarkdownFormat[] capabilities) {
|
||||||
|
return new MarkdownLiteParserV1(
|
||||||
|
MarkdownComponentParser::spoilerFormatting,
|
||||||
|
MarkdownComponentParser::quoteFormatting,
|
||||||
|
MarkdownComponentParser::urlFormatting,
|
||||||
|
capabilities
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mixins": [
|
"mixins": [
|
||||||
|
"rcc-server.mixins.json"
|
||||||
],
|
],
|
||||||
"depends": {
|
"depends": {
|
||||||
"fabricloader": ">=0.16.0",
|
"fabricloader": ">=0.16.0",
|
||||||
|
|
14
src/main/resources/rcc-server.mixins.json
Normal file
14
src/main/resources/rcc-server.mixins.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"required": true,
|
||||||
|
"minVersion": "0.8",
|
||||||
|
"package": "cc.reconnected.server.mixin",
|
||||||
|
"compatibilityLevel": "JAVA_17",
|
||||||
|
"mixins": [],
|
||||||
|
"client": [],
|
||||||
|
"server": [
|
||||||
|
"MessageCommandMixin"
|
||||||
|
],
|
||||||
|
"injectors": {
|
||||||
|
"defaultRequire": 1
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue