Skip to content

Commit ebec077

Browse files
authored
Add /message and /cloudnodemsg commands (#7)
2 parents 841cfac + c6a36b7 commit ebec077

File tree

14 files changed

+447
-1
lines changed

14 files changed

+447
-1
lines changed
Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,39 @@
11
package pro.cloudnode.smp.cloudnodemsg;
22

33
import org.bukkit.plugin.java.JavaPlugin;
4+
import org.jetbrains.annotations.NotNull;
5+
import pro.cloudnode.smp.cloudnodemsg.command.MainCommand;
6+
import pro.cloudnode.smp.cloudnodemsg.command.MessageCommand;
7+
8+
import java.util.Objects;
49

510
public final class CloudnodeMSG extends JavaPlugin {
11+
public static @NotNull CloudnodeMSG getInstance() {
12+
return getPlugin(CloudnodeMSG.class);
13+
}
14+
15+
public void reload() {
16+
getInstance().reloadConfig();
17+
getInstance().config.config = getInstance().getConfig();
18+
}
619

720
@Override
821
public void onEnable() {
9-
// Plugin startup logic
22+
saveDefaultConfig();
23+
reload();
1024

25+
Objects.requireNonNull(getCommand("cloudnodemsg")).setExecutor(new MainCommand());
26+
Objects.requireNonNull(getCommand("message")).setExecutor(new MessageCommand());
1127
}
1228

1329
@Override
1430
public void onDisable() {
1531
// Plugin shutdown logic
1632
}
33+
34+
private final @NotNull PluginConfig config = new PluginConfig(getConfig());
35+
36+
public @NotNull PluginConfig config() {
37+
return config;
38+
}
1739
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package pro.cloudnode.smp.cloudnodemsg;
2+
3+
import org.jetbrains.annotations.NotNull;
4+
5+
public final class Permission {
6+
/**
7+
* Allows using the /msg and /r commands
8+
*/
9+
public final static @NotNull String USE = "cloudnodemsg.use";
10+
11+
/**
12+
* Allows reloading the plugin
13+
*/
14+
public final static @NotNull String RELOAD = "cloudnodemsg.reload";
15+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package pro.cloudnode.smp.cloudnodemsg;
2+
3+
import net.kyori.adventure.text.Component;
4+
import net.kyori.adventure.text.minimessage.MiniMessage;
5+
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
6+
import org.bukkit.configuration.file.FileConfiguration;
7+
import org.jetbrains.annotations.NotNull;
8+
9+
import java.util.Objects;
10+
11+
public final class PluginConfig {
12+
public @NotNull FileConfiguration config;
13+
14+
public PluginConfig(final @NotNull FileConfiguration config) {
15+
this.config = config;
16+
}
17+
18+
/**
19+
* Incoming message format (recipient's point of view)
20+
* <p>Placeholders:</p>
21+
* <ul>
22+
* <li>{@code <sender>} - the username of the message sender</li>
23+
* <li>{@code <recipient>} - the username of the message recipient</li>
24+
* <li>{@code <message>} - the message text</li>
25+
* </ul>
26+
*
27+
* @param sender The username of the message sender
28+
* @param recipient The username of the message recipient
29+
* @param message The message text
30+
*/
31+
public @NotNull Component incoming(final @NotNull String sender, final @NotNull String recipient, final @NotNull String message) {
32+
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("incoming"))
33+
.replace("<sender>", sender)
34+
.replace("<recipient>", recipient),
35+
Placeholder.unparsed("message", message)
36+
);
37+
}
38+
39+
/**
40+
* Outgoing message format (sender's point of view)
41+
* <p>Placeholders:</p>
42+
* <ul>
43+
* <li>{@code <sender>} - the username of the message sender</li>
44+
* <li>{@code <recipient>} - the username of the message recipient</li>
45+
* <li>{@code <message>} - the message text</li>
46+
* </ul>
47+
*
48+
* @param sender The username of the message sender
49+
* @param recipient The username of the message recipient
50+
* @param message The message text
51+
*/
52+
public @NotNull Component outgoing(final @NotNull String sender, final @NotNull String recipient, final @NotNull String message) {
53+
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("outgoing"))
54+
.replace("<sender>", sender)
55+
.replace("<recipient>", recipient),
56+
Placeholder.unparsed("message", message)
57+
);
58+
}
59+
60+
/**
61+
* Command usage format
62+
* <p>Placeholders:</p>
63+
* <ul>
64+
* <li>{@code <command>} - the command name</li>
65+
* <li>{@code <usage>} - the command usage parameters</li>
66+
* </ul>
67+
*
68+
* @param label Command label
69+
* @param usage Command usage parameters
70+
*/
71+
public @NotNull Component usage(final @NotNull String label, final @NotNull String usage) {
72+
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("usage")),
73+
Placeholder.unparsed("command", label),
74+
Placeholder.unparsed("usage", usage)
75+
);
76+
}
77+
78+
/**
79+
* Plugin reloaded
80+
*/
81+
public @NotNull Component reloaded() {
82+
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("reloaded")));
83+
}
84+
85+
/**
86+
* Name for console/server that should appear as {@code <sender>} or {@code <recipient>} in messages
87+
*/
88+
public @NotNull String consoleName() {
89+
return Objects.requireNonNull(config.getString("console-name"));
90+
}
91+
92+
/**
93+
* No permission
94+
*/
95+
public @NotNull Component noPermission() {
96+
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("errors.no-permission")));
97+
}
98+
99+
/**
100+
* Player has no username (somehow)
101+
*/
102+
public @NotNull Component invalidPlayer() {
103+
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("errors.invalid-player")));
104+
}
105+
106+
/**
107+
* Player not found
108+
* <p>Placeholders:</p>
109+
* <ul><li>{@code <player>} - the player's username</li></ul>
110+
*
111+
* @param player The player's username
112+
*/
113+
public @NotNull Component playerNotFound(final @NotNull String player) {
114+
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("errors.player-not-found")),
115+
Placeholder.unparsed("player", player)
116+
);
117+
}
118+
119+
public @NotNull Component messageYourself() {
120+
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("errors.message-yourself")));
121+
}
122+
}
123+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package pro.cloudnode.smp.cloudnodemsg.command;
2+
3+
import net.kyori.adventure.audience.Audience;
4+
import net.kyori.adventure.text.Component;
5+
import org.bukkit.command.CommandExecutor;
6+
import org.bukkit.command.TabCompleter;
7+
import org.jetbrains.annotations.NotNull;
8+
9+
public abstract class Command implements TabCompleter, CommandExecutor {
10+
public boolean sendMessage(final @NotNull Audience recipient, final @NotNull Component message) {
11+
recipient.sendMessage(message);
12+
return true;
13+
}
14+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package pro.cloudnode.smp.cloudnodemsg.command;
2+
3+
import io.papermc.paper.plugin.configuration.PluginMeta;
4+
import net.kyori.adventure.text.minimessage.MiniMessage;
5+
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
6+
import org.bukkit.command.CommandSender;
7+
import org.jetbrains.annotations.NotNull;
8+
import pro.cloudnode.smp.cloudnodemsg.CloudnodeMSG;
9+
import pro.cloudnode.smp.cloudnodemsg.Permission;
10+
import pro.cloudnode.smp.cloudnodemsg.error.NoPermissionError;
11+
12+
import java.util.ArrayList;
13+
import java.util.List;
14+
15+
public final class MainCommand extends Command {
16+
17+
@Override
18+
public boolean onCommand(final @NotNull CommandSender sender, final @NotNull org.bukkit.command.Command command, final @NotNull String label, @NotNull String @NotNull [] args) {
19+
if (args.length == 1) switch (args[0]) {
20+
case "reload", "rl" -> {
21+
return reload(sender);
22+
}
23+
}
24+
return info(sender);
25+
}
26+
27+
private boolean info(final @NotNull CommandSender sender) {
28+
final @NotNull PluginMeta meta = CloudnodeMSG.getInstance().getPluginMeta();
29+
return sendMessage(sender, MiniMessage.miniMessage().deserialize("<green><name> by <author></green><newline><green>Version: <white><version></white></green>",
30+
Placeholder.unparsed("name", meta.getName()),
31+
Placeholder.unparsed("author", String.join(", ", meta.getAuthors())),
32+
Placeholder.unparsed("version", meta.getVersion())
33+
));
34+
}
35+
36+
private boolean reload(final @NotNull CommandSender sender) {
37+
if (!sender.hasPermission(Permission.RELOAD)) return new NoPermissionError().send(sender);
38+
CloudnodeMSG.getInstance().reload();
39+
return sendMessage(sender, CloudnodeMSG.getInstance().config().reloaded());
40+
}
41+
42+
@Override
43+
public @NotNull List<@NotNull String> onTabComplete(@NotNull CommandSender sender, @NotNull org.bukkit.command.Command command, @NotNull String label, @NotNull String @NotNull [] args) {
44+
final @NotNull List<@NotNull String> completions = new ArrayList<>();
45+
if (args.length == 1)
46+
if (sender.hasPermission(Permission.RELOAD) && "reload".startsWith(args[0].toLowerCase())) completions.add("reload");
47+
return completions;
48+
}
49+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package pro.cloudnode.smp.cloudnodemsg.command;
2+
3+
import org.bukkit.command.CommandSender;
4+
import org.bukkit.entity.Player;
5+
import org.jetbrains.annotations.NotNull;
6+
import pro.cloudnode.smp.cloudnodemsg.CloudnodeMSG;
7+
import pro.cloudnode.smp.cloudnodemsg.Permission;
8+
import pro.cloudnode.smp.cloudnodemsg.error.InvalidPlayerError;
9+
import pro.cloudnode.smp.cloudnodemsg.error.MessageYourselfError;
10+
import pro.cloudnode.smp.cloudnodemsg.error.NoPermissionError;
11+
import pro.cloudnode.smp.cloudnodemsg.error.PlayerNotFoundError;
12+
import pro.cloudnode.smp.cloudnodemsg.message.Message;
13+
14+
import java.util.ArrayList;
15+
import java.util.Arrays;
16+
import java.util.List;
17+
import java.util.Optional;
18+
19+
public final class MessageCommand extends Command {
20+
public static final @NotNull String usage = "<player> <message>";
21+
22+
@Override
23+
public boolean onCommand(final @NotNull CommandSender sender, final @NotNull org.bukkit.command.Command command, final @NotNull String label, @NotNull String @NotNull [] args) {
24+
if (!sender.hasPermission(Permission.USE)) return new NoPermissionError().send(sender);
25+
if (args.length == 0) return sendMessage(sender, CloudnodeMSG.getInstance().config().usage(label, usage));
26+
if (args.length == 1) return sendMessage(sender, CloudnodeMSG.getInstance().config()
27+
.usage(label, usage.replace("<player>", args[0])));
28+
29+
final @NotNull Optional<@NotNull Player> recipient = Optional.ofNullable(CloudnodeMSG.getInstance().getServer()
30+
.getPlayer(args[0]));
31+
if (recipient.isEmpty()) return new PlayerNotFoundError(args[0]).send(sender);
32+
if (sender instanceof final @NotNull Player player && recipient.get().getUniqueId().equals(player.getUniqueId()))
33+
return new MessageYourselfError().send(sender);
34+
35+
try {
36+
new Message(Message.offlinePlayer(sender), recipient.get(), String.join(" ", Arrays.copyOfRange(args, 1, args.length))).send();
37+
return true;
38+
}
39+
catch (final @NotNull InvalidPlayerError e) {
40+
return e.log().send(sender);
41+
}
42+
}
43+
44+
@Override
45+
public @NotNull List<@NotNull String> onTabComplete(final @NotNull CommandSender sender, final @NotNull org.bukkit.command.Command command, final @NotNull String label, final @NotNull String @NotNull [] args) {
46+
if (!sender.hasPermission(Permission.USE)) return new ArrayList<>();
47+
final @NotNull List<@NotNull String> completions = new ArrayList<>();
48+
if (args.length == 1) completions.addAll(CloudnodeMSG.getInstance().getServer().getOnlinePlayers().stream()
49+
.map(Player::getName)
50+
.filter(n -> n.toLowerCase().startsWith(args[0].toLowerCase()))
51+
.toList());
52+
return completions;
53+
}
54+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package pro.cloudnode.smp.cloudnodemsg.error;
2+
3+
import net.kyori.adventure.audience.Audience;
4+
import net.kyori.adventure.text.Component;
5+
import net.kyori.adventure.text.minimessage.MiniMessage;
6+
import org.jetbrains.annotations.NotNull;
7+
import pro.cloudnode.smp.cloudnodemsg.CloudnodeMSG;
8+
9+
import java.util.logging.Level;
10+
11+
public abstract class Error extends Throwable {
12+
public final @NotNull Component component;
13+
14+
protected Error(final @NotNull Component component) {
15+
this.component = component;
16+
}
17+
18+
public boolean send(final @NotNull Audience recipient) {
19+
recipient.sendMessage(component);
20+
return true;
21+
}
22+
23+
public @NotNull Error log() {
24+
CloudnodeMSG.getInstance().getLogger().log(Level.SEVERE, MiniMessage.miniMessage().serialize(component), this);
25+
return this;
26+
}
27+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package pro.cloudnode.smp.cloudnodemsg.error;
2+
3+
import pro.cloudnode.smp.cloudnodemsg.CloudnodeMSG;
4+
5+
/**
6+
* Player has no username (somehow)
7+
*/
8+
public final class InvalidPlayerError extends Error {
9+
public InvalidPlayerError() {
10+
super(CloudnodeMSG.getInstance().config().invalidPlayer());
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package pro.cloudnode.smp.cloudnodemsg.error;
2+
3+
import pro.cloudnode.smp.cloudnodemsg.CloudnodeMSG;
4+
5+
/**
6+
* Player has no username (somehow)
7+
*/
8+
public final class MessageYourselfError extends Error {
9+
public MessageYourselfError() {
10+
super(CloudnodeMSG.getInstance().config().messageYourself());
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package pro.cloudnode.smp.cloudnodemsg.error;
2+
3+
import pro.cloudnode.smp.cloudnodemsg.CloudnodeMSG;
4+
5+
/**
6+
* Player has no username (somehow)
7+
*/
8+
public final class NoPermissionError extends Error {
9+
public NoPermissionError() {
10+
super(CloudnodeMSG.getInstance().config().noPermission());
11+
}
12+
}

0 commit comments

Comments
 (0)