Skip to content

Commit 5cdffb8

Browse files
authored
Add team message command and channel (#35)
2 parents 4390b56 + 7c38d5a commit 5cdffb8

File tree

9 files changed

+241
-0
lines changed

9 files changed

+241
-0
lines changed

src/main/java/pro/cloudnode/smp/cloudnodemsg/CloudnodeMSG.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import pro.cloudnode.smp.cloudnodemsg.command.MainCommand;
99
import pro.cloudnode.smp.cloudnodemsg.command.MessageCommand;
1010
import pro.cloudnode.smp.cloudnodemsg.command.ReplyCommand;
11+
import pro.cloudnode.smp.cloudnodemsg.command.TeamMessageCommand;
1112
import pro.cloudnode.smp.cloudnodemsg.command.UnIgnoreCommand;
1213
import pro.cloudnode.smp.cloudnodemsg.listener.AsyncChatListener;
1314
import pro.cloudnode.smp.cloudnodemsg.command.ToggleMessageCommand;
@@ -35,6 +36,7 @@ public void onEnable() {
3536
Objects.requireNonNull(getCommand("ignore")).setExecutor(new IgnoreCommand());
3637
Objects.requireNonNull(getCommand("unignore")).setExecutor(new UnIgnoreCommand());
3738
Objects.requireNonNull(getCommand("togglemsg")).setExecutor(new ToggleMessageCommand());
39+
Objects.requireNonNull(getCommand("teammsg")).setExecutor(new TeamMessageCommand());
3840

3941
getServer().getPluginManager().registerEvents(new AsyncChatListener(), this);
4042
}

src/main/java/pro/cloudnode/smp/cloudnodemsg/Message.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ else if (player.isOnline())
125125

126126
public static final @NotNull NamespacedKey IGNORED_PLAYERS = new NamespacedKey(CloudnodeMSG.getInstance(), "ignored");
127127
public static final @NotNull NamespacedKey CHANNEL_RECIPIENT = new NamespacedKey(CloudnodeMSG.getInstance(), "channel-recipient");
128+
public static final @NotNull NamespacedKey CHANNEL_TEAM = new NamespacedKey(CloudnodeMSG.getInstance(), "channel-team");
128129

129130
/**
130131
* Get UUID set of ignored players from PDC string
@@ -208,6 +209,7 @@ public static boolean isIncomingEnabled(final @NotNull Player player) {
208209
*/
209210
public static void createChannel(final @NotNull Player player, final @NotNull OfflinePlayer recipient) {
210211
player.getPersistentDataContainer().set(CHANNEL_RECIPIENT, PersistentDataType.STRING, recipient.getUniqueId().toString());
212+
player.getPersistentDataContainer().remove(CHANNEL_TEAM);
211213
}
212214

213215
/**
@@ -239,4 +241,32 @@ public static boolean hasChannel(final @NotNull Player player, final @NotNull Of
239241
final @NotNull Optional<@NotNull OfflinePlayer> channel = getChannel(player);
240242
return channel.isPresent() && channel.get().getUniqueId().equals(recipient.getUniqueId());
241243
}
244+
245+
/**
246+
* Team message channel
247+
*
248+
* @param player The player
249+
*/
250+
public static void createTeamChannel(final @NotNull Player player) {
251+
player.getPersistentDataContainer().set(CHANNEL_TEAM, PersistentDataType.BOOLEAN, true);
252+
player.getPersistentDataContainer().remove(CHANNEL_RECIPIENT);
253+
}
254+
255+
/**
256+
* Exit team message channel
257+
*
258+
* @param player The player
259+
*/
260+
public static void exitTeamChannel(final @NotNull Player player) {
261+
player.getPersistentDataContainer().remove(CHANNEL_TEAM);
262+
}
263+
264+
/**
265+
* Check whether player has a team message channel
266+
*
267+
* @param player The player
268+
*/
269+
public static boolean hasTeamChannel(final @NotNull Player player) {
270+
return player.getPersistentDataContainer().getOrDefault(CHANNEL_TEAM, PersistentDataType.BOOLEAN, false);
271+
}
242272
}

src/main/java/pro/cloudnode/smp/cloudnodemsg/Permission.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ public final class Permission {
88
*/
99
public final static @NotNull String USE = "cloudnodemsg.use";
1010

11+
/**
12+
* Allow using the team message command (/teammsg)
13+
*/
14+
public final static @NotNull String USE_TEAM = "cloudnodemsg.use.team";
15+
1116
/**
1217
* Allows reloading the plugin
1318
*/

src/main/java/pro/cloudnode/smp/cloudnodemsg/PluginConfig.java

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import net.kyori.adventure.text.minimessage.MiniMessage;
55
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
66
import org.bukkit.configuration.file.FileConfiguration;
7+
import org.bukkit.scoreboard.Team;
78
import org.jetbrains.annotations.NotNull;
89

910
import java.util.Objects;
@@ -57,6 +58,28 @@ public PluginConfig(final @NotNull FileConfiguration config) {
5758
);
5859
}
5960

61+
/**
62+
* Team message
63+
* <p>Uses the vanilla teams from `/team`</p>
64+
* <p>Placeholders:</p>
65+
* <ul>
66+
* <li>{@code <sender>} - the username of the message sender</li>
67+
* <li>{@code <team>} - team</li>
68+
* <li>{@code <message>} - the message text</li>
69+
* </ul>
70+
*
71+
* @param sender The username of the message sender
72+
* @param team The team
73+
* @param message The message text
74+
*/
75+
public @NotNull Component team(final @NotNull String sender, final @NotNull Team team, final @NotNull Component message) {
76+
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("team"))
77+
.replace("<sender>", sender),
78+
Placeholder.component("team", team.displayName()),
79+
Placeholder.component("message", message)
80+
);
81+
}
82+
6083
/**
6184
* Private message format as seen by people with the spy permission and console
6285
* <p>Placeholders:</p>
@@ -78,6 +101,27 @@ public PluginConfig(final @NotNull FileConfiguration config) {
78101
);
79102
}
80103

104+
/**
105+
* Team message format as seen by people with the spy permission and console
106+
* <p>Placeholders:</p>
107+
* <ul>
108+
* <li>{@code <sender>} - the username of the message sender</li>
109+
* <li>{@code <team>} - team</li>
110+
* <li>{@code <message>} - the message text</li>
111+
* </ul>
112+
*
113+
* @param sender The username of the message sender
114+
* @param team The team
115+
* @param message The message text
116+
*/
117+
public @NotNull Component teamSpy(final @NotNull String sender, final @NotNull Team team, final @NotNull Component message) {
118+
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("team-spy"))
119+
.replace("<sender>", sender),
120+
Placeholder.component("team", team.displayName()),
121+
Placeholder.component("message", message)
122+
);
123+
}
124+
81125
/**
82126
* Player has successfully been ignored
83127
* <p>Placeholders:</p>
@@ -161,6 +205,40 @@ public PluginConfig(final @NotNull FileConfiguration config) {
161205
.replace("<recipient>", recipient));
162206
}
163207

208+
/**
209+
* Team chat channel created
210+
* <p>Placeholders:</p>
211+
* <ul>
212+
* <li>{@code <sender>} - the username of the message sender</li>
213+
* <li>{@code <team>} - team</li>
214+
* </ul>
215+
*
216+
* @param sender The username of the message sender
217+
* @param team The team@
218+
*/
219+
public @NotNull Component channelTeamCreated(final @NotNull String sender, final @NotNull Team team) {
220+
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("channel.team-created"))
221+
.replace("<sender>", sender),
222+
Placeholder.component("team", team.displayName()));
223+
}
224+
225+
/**
226+
* Team chat channel closed
227+
* <p>Placeholders:</p>
228+
* <ul>
229+
* <li>{@code <sender>} - the username of the message sender</li>
230+
* <li>{@code <team>} - team</li>
231+
* </ul>
232+
*
233+
* @param sender The username of the message sender
234+
* @param team The team
235+
*/
236+
public @NotNull Component channelTeamClosed(final @NotNull String sender, final @NotNull Team team) {
237+
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("channel.team-closed"))
238+
.replace("<sender>", sender),
239+
Placeholder.component("team", team.displayName()));
240+
}
241+
164242
/**
165243
* Command usage format
166244
* <p>Placeholders:</p>
@@ -333,5 +411,12 @@ public PluginConfig(final @NotNull FileConfiguration config) {
333411
Placeholder.unparsed("player", player)
334412
);
335413
}
414+
415+
/**
416+
* Trying to message a team, but not in one
417+
*/
418+
public @NotNull Component notInTeam() {
419+
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("errors.not-in-team")));
420+
}
336421
}
337422

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package pro.cloudnode.smp.cloudnodemsg.command;
2+
3+
import net.kyori.adventure.text.Component;
4+
import org.bukkit.command.CommandSender;
5+
import org.bukkit.entity.Player;
6+
import org.bukkit.scoreboard.Team;
7+
import org.jetbrains.annotations.NotNull;
8+
import org.jetbrains.annotations.Nullable;
9+
import pro.cloudnode.smp.cloudnodemsg.CloudnodeMSG;
10+
import pro.cloudnode.smp.cloudnodemsg.Message;
11+
import pro.cloudnode.smp.cloudnodemsg.Permission;
12+
import pro.cloudnode.smp.cloudnodemsg.error.NoPermissionError;
13+
import pro.cloudnode.smp.cloudnodemsg.error.NotPlayerError;
14+
15+
import java.util.ArrayList;
16+
import java.util.HashSet;
17+
import java.util.List;
18+
import java.util.Optional;
19+
import java.util.Set;
20+
21+
public class TeamMessageCommand extends Command {
22+
@Override
23+
public boolean run(@NotNull CommandSender sender, @NotNull String label, @NotNull String[] args) {
24+
if (!sender.hasPermission(Permission.USE_TEAM)) return new NoPermissionError().send(sender);
25+
if (!(sender instanceof Player player)) return new NotPlayerError().send(sender);
26+
final @NotNull Optional<@NotNull Team> team = Optional.ofNullable(player.getScoreboard().getPlayerTeam(player));
27+
if (team.isEmpty()) return sendMessage(player, CloudnodeMSG.getInstance().config().notInTeam());
28+
if (args.length == 0) {
29+
if (Message.hasTeamChannel(player)) {
30+
Message.exitTeamChannel(player);
31+
return sendMessage(player, CloudnodeMSG.getInstance().config().channelTeamClosed(player.getName(), team.get()));
32+
}
33+
else {
34+
Message.createTeamChannel(player);
35+
return sendMessage(player, CloudnodeMSG.getInstance().config().channelTeamCreated(player.getName(), team.get()));
36+
}
37+
}
38+
return sendTeamMessage(player, team.get(), Component.text(String.join(" ", args)));
39+
}
40+
41+
@Override
42+
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, org.bukkit.command.@NotNull Command command, @NotNull String label, @NotNull String[] args) {
43+
return new ArrayList<>();
44+
}
45+
46+
/**
47+
* Send message to online team members
48+
*/
49+
public static boolean sendTeamMessage(final @NotNull Player sender, final @NotNull Team team, final @NotNull Component message) {
50+
for (final @NotNull Player player : sender.getServer().getOnlinePlayers()) {
51+
if (Optional.ofNullable(player.getScoreboard().getPlayerTeam(player)).map(t -> t.equals(team)).orElse(false))
52+
sendMessage(player, CloudnodeMSG.getInstance().config().team(sender.getName(), team, message));
53+
else if (player.hasPermission(Permission.SPY)) sendMessage(player, CloudnodeMSG.getInstance().config().teamSpy(sender.getName(), team, message));
54+
}
55+
sender.getServer().getConsoleSender().sendMessage(CloudnodeMSG.getInstance().config().teamSpy(sender.getName(), team, message));
56+
57+
return true;
58+
}
59+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package pro.cloudnode.smp.cloudnodemsg.error;
2+
3+
import pro.cloudnode.smp.cloudnodemsg.CloudnodeMSG;
4+
5+
/**
6+
* Only players can use this command
7+
*/
8+
public final class NotInTeamError extends Error {
9+
/**
10+
* Only players can use this command
11+
*/
12+
public NotInTeamError() {
13+
super(CloudnodeMSG.getInstance().config().notInTeam());
14+
}
15+
}

src/main/java/pro/cloudnode/smp/cloudnodemsg/listener/AsyncChatListener.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
import org.bukkit.event.EventHandler;
99
import org.bukkit.event.EventPriority;
1010
import org.bukkit.event.Listener;
11+
import org.bukkit.scoreboard.Team;
1112
import org.jetbrains.annotations.NotNull;
1213
import pro.cloudnode.smp.cloudnodemsg.CloudnodeMSG;
1314
import pro.cloudnode.smp.cloudnodemsg.Permission;
15+
import pro.cloudnode.smp.cloudnodemsg.command.TeamMessageCommand;
1416
import pro.cloudnode.smp.cloudnodemsg.error.InvalidPlayerError;
1517
import pro.cloudnode.smp.cloudnodemsg.Message;
1618

@@ -57,4 +59,18 @@ public void channels(final @NotNull AsyncChatEvent event) {
5759
e.log().send(sender);
5860
}
5961
}
62+
63+
@EventHandler (priority = EventPriority.HIGHEST)
64+
public void teamChannel(final @NotNull AsyncChatEvent event) {
65+
final @NotNull Player sender = event.getPlayer();
66+
if (!Message.hasTeamChannel(sender)) return;
67+
event.setCancelled(true);
68+
final @NotNull Optional<@NotNull Team> team = Optional.ofNullable(sender.getScoreboard().getPlayerTeam(sender));
69+
if (team.isEmpty()) {
70+
Message.exitTeamChannel(sender);
71+
sender.sendMessage(CloudnodeMSG.getInstance().config().notInTeam());
72+
return;
73+
}
74+
TeamMessageCommand.sendTeamMessage(sender, team.get(), event.message());
75+
}
6076
}

src/main/resources/config.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,22 @@ incoming: '<click:suggest_command:/msg <sender> ><dark_gray>[<#60a5fa><sender></
99
# Same placeholders as incoming
1010
outgoing: '<click:suggest_command:/msg <recipient> ><dark_gray>[<#93c5fd>me</#93c5fd> <white>-></white> <#60a5fa><recipient></#60a5fa>]</dark_gray></click> <reset><#dbeafe><message></#dbeafe>'
1111

12+
# Team message
13+
# Uses the vanilla teams from `/team`
14+
# Placeholders:
15+
# <sender> - the username of the message sender
16+
# <team> - team display name
17+
# <message> - the message text
18+
team: '<click:suggest_command:/tm ><dark_gray>[<white><team></white>] <gray><sender></gray>:</dark_gray> <white><message></white></click>'
19+
1220
# Private message format as seen by people with the spy permission and console
1321
# Same placeholders as incoming
1422
spy: '<dark_gray>[SPY] [<click:suggest_command:/msg <sender> ><gray><sender></gray></click> -> <click:suggest_command:/msg <recipient> ><gray><recipient></gray></click>] <gray><message></gray></dark_gray>'
1523

24+
# Team message format as seen by people with the spy permission and console
25+
# Same placeholders as team
26+
team-spy: '<dark_gray>[SPY]</dark_gray> <dark_gray>[<white><team></white>] <gray><click:suggest_command:/msg <sender> ><sender></click></gray>:</dark_gray> <gray><message></gray>'
27+
1628
# Player has successfully been ignored
1729
# Placeholders:
1830
# <player> - the player's username
@@ -40,6 +52,16 @@ channel:
4052
# <recipient> - the username of the message recipient
4153
offline: <red>(!) Player <gray><recipient></gray> is offline. Your chat messages have been switched back to <gray>public</gray>.</red>
4254

55+
# Team chat channel created
56+
# Placeholders:
57+
# <sender> - the username of the message sender
58+
# <team> - team display name
59+
team-created: <green>(!) Your chat messages will now be sent as private team messages in <gray><team></gray>. To re-enable public messages, run <click:suggest_command:/teammsg><gray>/teammsg</gray></click></green>
60+
61+
# Team chat channel closed
62+
# Same placeholders as created
63+
team-closed: <yellow>(!) Your chat messages will now be <gray>public</gray>.</yellow>
64+
4365
# Name for console/server that should appear as <sender> or <recipient> in messages
4466
console-name: "Server"
4567

@@ -110,3 +132,6 @@ errors:
110132
# Placeholders:
111133
# <player> - the player's username
112134
incoming-disabled: "<red>(!) You cannot message <gray><player></gray> because they have disabled private messages.</red>"
135+
136+
# Trying to message a team, but not in one
137+
not-in-team: "<red>(!) You are not in a team.</red>"

src/main/resources/plugin.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,7 @@ commands:
2727
description: Toggles private messages from a player
2828
usage: /<command> <player>
2929
aliases: [ "toggledms", "togglepms" ]
30+
teammsg:
31+
description: Send a private message to your teammates
32+
usage: /<command> <message>
33+
aliases: [ "tmsg", "teamchat" , "tm" ]

0 commit comments

Comments
 (0)