Skip to content

Commit 56c0c44

Browse files
Merge pull request #626 from petethepossum/master
feat: staff chat and staff msgs feat: friend join leave notifs add: friend list formatting
2 parents eb55fa1 + a21d1eb commit 56c0c44

14 files changed

Lines changed: 296 additions & 14 deletions

File tree

service.friend/src/main/java/net/swofty/service/friend/FriendCache.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ public static void handleRequestsListRequest(FriendRequestsListEvent event) {
365365
}
366366

367367
public static void handlePlayerJoin(UUID playerUuid, String playerName) {
368-
PresenceStorage.upsert(new net.swofty.commons.presence.PresenceInfo(
368+
PresenceStorage.upsertPreservingServer(new net.swofty.commons.presence.PresenceInfo(
369369
playerUuid,
370370
true,
371371
null,
@@ -376,15 +376,23 @@ public static void handlePlayerJoin(UUID playerUuid, String playerName) {
376376
FriendData playerData = getFriendData(playerUuid);
377377

378378
for (Friend friend : playerData.getFriends()) {
379+
net.swofty.commons.presence.PresenceInfo friendPresence = PresenceStorage.get(friend.getUuid());
380+
if (friendPresence == null || !friendPresence.isOnline()) continue;
381+
379382
FriendData friendData = cachedFriendData.get(friend.getUuid());
383+
if (friendData == null) {
384+
friendData = getFriendData(friend.getUuid());
385+
cachedFriendData.put(friend.getUuid(), friendData);
386+
}
387+
380388
if (friendData != null && friendData.getSettings().isJoinLeaveNotifications()) {
381389
sendEvent(new FriendJoinNotificationEvent(friend.getUuid(), playerUuid, playerName));
382390
}
383391
}
384392
}
385393

386394
public static void handlePlayerLeave(UUID playerUuid, String playerName) {
387-
PresenceStorage.upsert(new net.swofty.commons.presence.PresenceInfo(
395+
PresenceStorage.upsertPreservingServer(new net.swofty.commons.presence.PresenceInfo(
388396
playerUuid,
389397
false,
390398
null,
@@ -396,7 +404,15 @@ public static void handlePlayerLeave(UUID playerUuid, String playerName) {
396404
if (playerData == null) return;
397405

398406
for (Friend friend : playerData.getFriends()) {
407+
net.swofty.commons.presence.PresenceInfo friendPresence = PresenceStorage.get(friend.getUuid());
408+
if (friendPresence == null || !friendPresence.isOnline()) continue;
409+
399410
FriendData friendData = cachedFriendData.get(friend.getUuid());
411+
if (friendData == null) {
412+
friendData = getFriendData(friend.getUuid());
413+
cachedFriendData.put(friend.getUuid(), friendData);
414+
}
415+
400416
if (friendData != null && friendData.getSettings().isJoinLeaveNotifications()) {
401417
sendEvent(new FriendLeaveNotificationEvent(friend.getUuid(), playerUuid, playerName));
402418
}
@@ -464,6 +480,9 @@ private static String formatServerDisplay(net.swofty.commons.presence.PresenceIn
464480
String type = info.getServerType();
465481
String id = info.getServerId();
466482
if (type == null && id == null) return null;
483+
if (id != null && id.matches("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")) {
484+
return type; // hide raw UUIDs; show type only
485+
}
467486
if (type != null && id != null) return type + " - " + id;
468487
return type != null ? type : id;
469488
}

service.friend/src/main/java/net/swofty/service/friend/PresenceStorage.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,38 @@ public static void upsert(PresenceInfo presence) {
1616
presenceByUuid.put(presence.getUuid(), presence);
1717
}
1818

19+
/**
20+
* Upsert presence and return the previous entry (if any).
21+
*/
22+
public static PresenceInfo upsertAndGetPrevious(PresenceInfo presence) {
23+
return presenceByUuid.put(presence.getUuid(), presence);
24+
}
25+
26+
/**
27+
* Upsert presence while preserving non-null server metadata from previous entries.
28+
*/
29+
public static PresenceInfo upsertPreservingServer(PresenceInfo incoming) {
30+
PresenceInfo previous = presenceByUuid.get(incoming.getUuid());
31+
if (previous == null) {
32+
presenceByUuid.put(incoming.getUuid(), incoming);
33+
return null;
34+
}
35+
36+
String serverType = incoming.getServerType() != null ? incoming.getServerType() : previous.getServerType();
37+
String serverId = incoming.getServerId() != null ? incoming.getServerId() : previous.getServerId();
38+
long lastSeen = incoming.getLastSeen() > 0 ? incoming.getLastSeen() : previous.getLastSeen();
39+
40+
PresenceInfo merged = new PresenceInfo(
41+
incoming.getUuid(),
42+
incoming.isOnline(),
43+
serverType,
44+
serverId,
45+
lastSeen
46+
);
47+
presenceByUuid.put(incoming.getUuid(), merged);
48+
return previous;
49+
}
50+
1951
public static List<PresenceInfo> getBulk(Collection<UUID> uuids) {
2052
if (uuids == null || uuids.isEmpty()) return List.of();
2153
return uuids.stream()

service.friend/src/main/java/net/swofty/service/friend/endpoints/UpdatePresenceEndpoint.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,9 @@ public UpdatePresenceProtocolObject.UpdatePresenceResponse onMessage(
2222
UpdatePresenceProtocolObject.UpdatePresenceMessage messageObject) {
2323

2424
PresenceInfo incoming = messageObject.presence();
25-
PresenceInfo previous = PresenceStorage.get(incoming.getUuid());
25+
PresenceInfo previous = PresenceStorage.upsertPreservingServer(incoming);
2626

27-
// Detect state change to trigger friend join/leave notifications
2827
boolean stateChanged = previous == null || previous.isOnline() != incoming.isOnline();
29-
PresenceStorage.upsert(incoming);
3028

3129
if (stateChanged) {
3230
String playerName = FriendCache.getPlayerName(incoming.getUuid());

type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/events/ActionPlayerChat.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import net.swofty.type.bedwarsgame.game.Game;
99
import net.swofty.type.bedwarsgame.game.GameStatus;
1010
import net.swofty.type.bedwarsgame.user.BedWarsPlayer;
11+
import net.swofty.type.generic.chat.StaffChat;
1112
import net.swofty.type.generic.data.datapoints.DatapointChatType;
1213
import net.swofty.type.generic.data.datapoints.DatapointLeaderboardLong;
1314
import net.swofty.type.generic.data.handlers.BedWarsDataHandler;
@@ -45,6 +46,16 @@ public void run(PlayerChatEvent event) {
4546
String finalMessage = message;
4647

4748
DatapointChatType.Chats chatType = player.getChatType().currentChatType;
49+
if (chatType == DatapointChatType.Chats.STAFF) {
50+
if (!rank.isStaff()) {
51+
player.sendMessage("§cUnknown chat type.");
52+
player.getChatType().switchTo(DatapointChatType.Chats.ALL);
53+
return;
54+
}
55+
StaffChat.sendMessage(player, finalMessage);
56+
return;
57+
}
58+
4859
if (chatType == DatapointChatType.Chats.PARTY) {
4960
if (!PartyManager.isInParty(player)) {
5061
player.sendMessage("§cYou are not in a party and were moved to the ALL channel.");

type.bedwarslobby/src/main/java/net/swofty/type/bedwarslobby/events/ActionPlayerChat.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import net.swofty.commons.bedwars.BedwarsLevelColor;
66
import net.swofty.commons.bedwars.BedwarsLevelUtil;
77
import net.swofty.type.generic.HypixelGenericLoader;
8+
import net.swofty.type.generic.chat.StaffChat;
89
import net.swofty.type.generic.data.HypixelDataHandler;
910
import net.swofty.type.generic.data.datapoints.DatapointChatType;
1011
import net.swofty.type.generic.data.datapoints.DatapointLeaderboardLong;
@@ -44,6 +45,16 @@ public void run(PlayerChatEvent event) {
4445
String finalMessage = message;
4546

4647
DatapointChatType.Chats chatType = player.getChatType().currentChatType;
48+
if (chatType == DatapointChatType.Chats.STAFF) {
49+
if (!rank.isStaff()) {
50+
player.sendMessage("§cUnknown chat type.");
51+
player.getChatType().switchTo(DatapointChatType.Chats.ALL);
52+
return;
53+
}
54+
StaffChat.sendMessage(player, finalMessage);
55+
return;
56+
}
57+
4758
if (chatType == DatapointChatType.Chats.PARTY) {
4859
if (!PartyManager.isInParty(player)) {
4960
player.sendMessage("§cYou are not in a party and were moved to the ALL channel.");
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package net.swofty.type.generic.chat;
2+
3+
import net.swofty.type.generic.HypixelGenericLoader;
4+
import net.swofty.type.generic.command.commands.ChatCommand;
5+
import net.swofty.type.generic.user.HypixelPlayer;
6+
7+
import java.util.List;
8+
import java.util.UUID;
9+
import java.util.stream.Collectors;
10+
11+
public final class StaffChat {
12+
private StaffChat() {}
13+
14+
public static void sendMessage(HypixelPlayer sender, String message) {
15+
String formatted = "§b[STAFF] " + sender.getRank().getPrefix() + sender.getUsername() + "§f: " + message;
16+
broadcast(formatted, sender.getUuid());
17+
}
18+
19+
public static void sendNotification(String message) {
20+
String formatted = "§b[STAFF] §7" + message;
21+
broadcast(formatted, null);
22+
}
23+
24+
private static void broadcast(String message, UUID senderUuid) {
25+
List<HypixelPlayer> viewers = HypixelGenericLoader.getLoadedPlayers().stream()
26+
.filter(player -> player.getRank().isStaff())
27+
.filter(player -> ChatCommand.isStaffViewEnabled(player.getUuid()) || (senderUuid != null && player.getUuid().equals(senderUuid)))
28+
.collect(Collectors.toList());
29+
30+
for (HypixelPlayer viewer : viewers) {
31+
viewer.sendMessage(message);
32+
}
33+
}
34+
}
35+

type.generic/src/main/java/net/swofty/type/generic/command/commands/ChatCommand.java

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package net.swofty.type.generic.command.commands;
22

3-
import net.minestom.server.command.builder.arguments.ArgumentEnum;
43
import net.minestom.server.command.builder.arguments.ArgumentType;
4+
import net.minestom.server.command.builder.arguments.ArgumentWord;
5+
import net.minestom.server.command.builder.suggestion.SuggestionEntry;
56
import net.swofty.type.generic.command.CommandParameters;
67
import net.swofty.type.generic.command.HypixelCommand;
78
import net.swofty.type.generic.data.datapoints.DatapointChatType;
@@ -14,15 +15,52 @@
1415
aliases = "chatmode",
1516
allowsConsole = false)
1617
public class ChatCommand extends HypixelCommand {
18+
private static final java.util.concurrent.ConcurrentHashMap<java.util.UUID, Boolean> staffView = new java.util.concurrent.ConcurrentHashMap<>();
19+
1720
@Override
1821
public void registerUsage(MinestomCommand command) {
19-
ArgumentEnum<PickerChatType> chatType = ArgumentType.Enum("type", PickerChatType.class);
22+
ArgumentWord chatType = ArgumentType.Word("type");
23+
chatType.setSuggestionCallback((sender, context, suggestion) -> {
24+
boolean isStaff = sender instanceof HypixelPlayer hp && hp.getRank().isStaff();
25+
suggestion.addEntry(new SuggestionEntry("p"));
26+
suggestion.addEntry(new SuggestionEntry("party"));
27+
suggestion.addEntry(new SuggestionEntry("a"));
28+
suggestion.addEntry(new SuggestionEntry("all"));
29+
if (isStaff) {
30+
suggestion.addEntry(new SuggestionEntry("s"));
31+
suggestion.addEntry(new SuggestionEntry("staff"));
32+
suggestion.addEntry(new SuggestionEntry("staffview"));
33+
suggestion.addEntry(new SuggestionEntry("sv"));
34+
}
35+
});
2036

2137
command.addSyntax((sender, context) -> {
2238
if (!permissionCheck(sender)) return;
2339

24-
PickerChatType type = context.get(chatType);
2540
HypixelPlayer player = (HypixelPlayer) sender;
41+
String raw = context.get(chatType).toLowerCase();
42+
43+
if ((raw.equals("staffview") || raw.equals("sv"))) {
44+
if (!player.getRank().isStaff()) {
45+
sender.sendMessage("§cUnknown chat type.");
46+
return;
47+
}
48+
boolean enabled = staffView.getOrDefault(player.getUuid(), true);
49+
staffView.put(player.getUuid(), !enabled);
50+
sender.sendMessage("§aStaff chat viewing is now " + (!enabled ? "§aenabled" : "§cdisabled"));
51+
return;
52+
}
53+
54+
PickerChatType type = PickerChatType.fromString(raw);
55+
if (type == null) {
56+
sender.sendMessage("§cUnknown chat type.");
57+
return;
58+
}
59+
60+
if (type.getChatType() == DatapointChatType.Chats.STAFF && !player.getRank().isStaff()) {
61+
sender.sendMessage("§cUnknown chat type.");
62+
return;
63+
}
2664

2765
player.getChatType().switchTo(type.getChatType());
2866
sender.sendMessage("§aYou are now in the §6" + type.chatType.name() + " §achannel");
@@ -33,7 +71,9 @@ enum PickerChatType {
3371
p(DatapointChatType.Chats.PARTY),
3472
party(DatapointChatType.Chats.PARTY),
3573
a(DatapointChatType.Chats.ALL),
36-
all(DatapointChatType.Chats.ALL);
74+
all(DatapointChatType.Chats.ALL),
75+
s(DatapointChatType.Chats.STAFF),
76+
staff(DatapointChatType.Chats.STAFF);
3777

3878
private final DatapointChatType.Chats chatType;
3979

@@ -44,5 +84,16 @@ enum PickerChatType {
4484
public DatapointChatType.Chats getChatType() {
4585
return chatType;
4686
}
87+
88+
public static PickerChatType fromString(String input) {
89+
for (PickerChatType val : values()) {
90+
if (val.name().equalsIgnoreCase(input)) return val;
91+
}
92+
return null;
93+
}
94+
}
95+
96+
public static boolean isStaffViewEnabled(java.util.UUID uuid) {
97+
return staffView.getOrDefault(uuid, true);
4798
}
4899
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package net.swofty.type.generic.command.commands;
2+
3+
import net.minestom.server.command.builder.arguments.ArgumentString;
4+
import net.minestom.server.command.builder.arguments.ArgumentType;
5+
import net.swofty.type.generic.command.CommandParameters;
6+
import net.swofty.type.generic.command.HypixelCommand;
7+
import net.swofty.type.generic.friend.FriendManager;
8+
import net.swofty.type.generic.user.HypixelPlayer;
9+
import net.swofty.type.generic.user.categories.Rank;
10+
11+
@CommandParameters(
12+
aliases = "fl friendlist",
13+
description = "List your friends",
14+
usage = "/fl [page]",
15+
permission = Rank.DEFAULT,
16+
allowsConsole = false
17+
)
18+
public class FriendListCommand extends HypixelCommand {
19+
20+
@Override
21+
public void registerUsage(MinestomCommand command) {
22+
ArgumentString pageArg = ArgumentType.String("page");
23+
24+
// No args -> page 1
25+
command.addSyntax((sender, context) -> {
26+
if (!permissionCheck(sender)) return;
27+
HypixelPlayer player = (HypixelPlayer) sender;
28+
FriendManager.listFriends(player, 1, false);
29+
});
30+
31+
// With page/best
32+
command.addSyntax((sender, context) -> {
33+
if (!permissionCheck(sender)) return;
34+
HypixelPlayer player = (HypixelPlayer) sender;
35+
String arg = context.get(pageArg);
36+
37+
if ("best".equalsIgnoreCase(arg)) {
38+
FriendManager.listFriends(player, 1, true);
39+
return;
40+
}
41+
42+
int page = parsePageNumber(arg);
43+
FriendManager.listFriends(player, page, false);
44+
}, pageArg);
45+
}
46+
47+
private int parsePageNumber(String arg) {
48+
try {
49+
return Math.max(1, Integer.parseInt(arg));
50+
} catch (NumberFormatException e) {
51+
return 1;
52+
}
53+
}
54+
}
55+
56+

type.generic/src/main/java/net/swofty/type/generic/data/datapoints/DatapointChatType.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public void switchTo(Chats chatType) {
4343

4444
public enum Chats {
4545
ALL,
46-
PARTY
46+
PARTY,
47+
STAFF
4748
}
4849
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package net.swofty.type.generic.event.actions;
2+
3+
import net.minestom.server.event.player.PlayerSpawnEvent;
4+
import net.swofty.type.generic.chat.StaffChat;
5+
import net.swofty.type.generic.event.EventNodes;
6+
import net.swofty.type.generic.event.HypixelEvent;
7+
import net.swofty.type.generic.event.HypixelEventClass;
8+
import net.swofty.type.generic.user.HypixelPlayer;
9+
10+
public class ActionStaffJoinNotification implements HypixelEventClass {
11+
12+
@HypixelEvent(node = EventNodes.PLAYER, requireDataLoaded = true)
13+
public void run(PlayerSpawnEvent event) {
14+
if (!event.isFirstSpawn()) return;
15+
HypixelPlayer player = (HypixelPlayer) event.getPlayer();
16+
if (!player.getRank().isStaff()) return;
17+
18+
StaffChat.sendNotification(player.getFullDisplayName() + " §7joined.");
19+
}
20+
}
21+
22+

0 commit comments

Comments
 (0)