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("net.kyori:adventure-platform-fabric:${project.adventure_version}")
|
||||
modImplementation include("eu.pb4:placeholder-api:${project.placeholderapi_version}")
|
||||
}
|
||||
|
||||
processResources {
|
||||
|
|
|
@ -9,7 +9,7 @@ yarn_mappings=1.20.1+build.10
|
|||
loader_version=0.16.5
|
||||
|
||||
# Mod Properties
|
||||
mod_version=1.10.2
|
||||
mod_version=1.11.0
|
||||
maven_group=cc.reconnected
|
||||
archives_base_name=rcc-server
|
||||
|
||||
|
@ -21,4 +21,5 @@ owo_version=0.11.2+1.20
|
|||
luckpermsapi_version=5.4
|
||||
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;
|
||||
|
||||
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.events.PlayerActivityEvents;
|
||||
import cc.reconnected.server.events.PlayerWelcome;
|
||||
|
@ -82,6 +84,8 @@ public class RccServer implements ModInitializer {
|
|||
LOGGER.info("Starting rcc-server");
|
||||
|
||||
CommandRegistrationCallback.EVENT.register(AfkCommand::register);
|
||||
CommandRegistrationCallback.EVENT.register(TellCommand::register);
|
||||
CommandRegistrationCallback.EVENT.register(ReplyCommand::register);
|
||||
|
||||
ServerLifecycleEvents.SERVER_STARTED.register(server -> {
|
||||
luckPerms = LuckPermsProvider.get();
|
||||
|
|
|
@ -11,4 +11,7 @@ public class RccServerConfigModel {
|
|||
|
||||
public String afkMessage = "<gray><displayname> is now 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": [
|
||||
"rcc-server.mixins.json"
|
||||
],
|
||||
"depends": {
|
||||
"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