From 988699b9d54e4891d21ee88c3dee4d210ffd5125 Mon Sep 17 00:00:00 2001 From: petethepossum <47347759+petethepossum@users.noreply.github.com> Date: Mon, 29 Dec 2025 18:30:42 +1300 Subject: [PATCH] Add proxy based friend list and name resolution in strings rather than player or unknown --- service.friend/build.gradle.kts | 1 + .../swofty/service/friend/FriendCache.java | 108 ++++++++++++++++-- .../FriendEventToServiceEndpoint.java | 39 +++++-- 3 files changed, 131 insertions(+), 17 deletions(-) diff --git a/service.friend/build.gradle.kts b/service.friend/build.gradle.kts index f849567d5..b18330edc 100644 --- a/service.friend/build.gradle.kts +++ b/service.friend/build.gradle.kts @@ -25,6 +25,7 @@ repositories { dependencies { implementation(project(":service.generic")) implementation(project(":commons")) + implementation(project(":proxy.api")) implementation("com.github.ben-manes.caffeine:caffeine:3.1.8") implementation("org.tinylog:tinylog-api:2.7.0") implementation("org.tinylog:tinylog-impl:2.7.0") diff --git a/service.friend/src/main/java/net/swofty/service/friend/FriendCache.java b/service.friend/src/main/java/net/swofty/service/friend/FriendCache.java index 7e01c85c7..b2b68dbc8 100644 --- a/service.friend/src/main/java/net/swofty/service/friend/FriendCache.java +++ b/service.friend/src/main/java/net/swofty/service/friend/FriendCache.java @@ -1,17 +1,27 @@ package net.swofty.service.friend; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.model.Filters; import net.swofty.commons.friend.*; import net.swofty.commons.friend.events.*; import net.swofty.commons.friend.events.response.*; import net.swofty.commons.service.FromServiceChannels; +import net.swofty.proxyapi.ProxyPlayer; import net.swofty.service.generic.redis.ServiceToServerManager; import org.json.JSONObject; +import org.bson.Document; +import org.tinylog.Logger; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -276,7 +286,7 @@ public static void handleToggleSettingRequest(FriendToggleSettingRequestEvent ev sendEvent(new FriendSettingToggledResponseEvent(player, settingType, newValue)); } - public static void handleListRequest(FriendListRequestEvent event, Map playerNames, Map onlineStatus) { + public static void handleListRequest(FriendListRequestEvent event) { UUID player = event.getPlayer(); int page = event.getPage(); boolean bestOnly = event.isBestOnly(); @@ -291,9 +301,16 @@ public static void handleListRequest(FriendListRequestEvent event, Map pageFriends = friends.subList(startIndex, endIndex); + Map playerNames = resolvePlayerNames(pageFriends.stream() + .map(Friend::getUuid) + .toList()); + Map onlineStatus = resolveOnlineStatus(pageFriends.stream() + .map(Friend::getUuid) + .toList()); + List entries = new ArrayList<>(); - for (int i = startIndex; i < endIndex; i++) { - Friend friend = friends.get(i); + for (Friend friend : pageFriends) { String name = playerNames.getOrDefault(friend.getUuid(), "Unknown"); boolean isOnline = onlineStatus.getOrDefault(friend.getUuid(), false); entries.add(new FriendListResponseEvent.FriendListEntry( @@ -308,7 +325,7 @@ public static void handleListRequest(FriendListRequestEvent event, Map playerNames) { + public static void handleRequestsListRequest(FriendRequestsListEvent event) { UUID player = event.getPlayer(); int page = event.getPage(); @@ -322,10 +339,14 @@ public static void handleRequestsListRequest(FriendRequestsListEvent event, Map< int startIndex = (page - 1) * FRIENDS_PER_PAGE; int endIndex = Math.min(startIndex + FRIENDS_PER_PAGE, totalRequests); + List pageRequests = requests.subList(startIndex, endIndex); + Map playerNames = resolvePlayerNames(pageRequests.stream() + .map(PendingFriendRequest::getFrom) + .toList()); + List entries = new ArrayList<>(); - for (int i = startIndex; i < endIndex; i++) { - PendingFriendRequest request = requests.get(i); - String senderName = playerNames.getOrDefault(request.getFrom(), "Unknown"); + for (PendingFriendRequest request : pageRequests) { + String senderName = playerNames.getOrDefault(request.getFrom(), request.getFromName()); entries.add(new FriendRequestsListResponseEvent.FriendRequestEntry( request.getFrom(), senderName, @@ -361,6 +382,79 @@ public static void handlePlayerLeave(UUID playerUuid, String playerName) { cachedFriendData.remove(playerUuid); } + public static String getPlayerName(UUID uuid) { + return resolvePlayerNames(List.of(uuid)).getOrDefault(uuid, "Unknown"); + } + + private static Map resolvePlayerNames(Collection uuids) { + Map names = new HashMap<>(); + if (uuids == null || uuids.isEmpty() || FriendDatabase.database == null) return names; + + List idStrings = uuids.stream().map(UUID::toString).toList(); + names.putAll(fetchNamesFromCollection("data", idStrings)); + + Set unresolved = new HashSet<>(uuids); + unresolved.removeAll(names.keySet()); + if (!unresolved.isEmpty()) { + List unresolvedIds = unresolved.stream().map(UUID::toString).toList(); + names.putAll(fetchNamesFromCollection("profiles", unresolvedIds)); + } + + return names; + } + + private static Map resolveOnlineStatus(Collection uuids) { + Map status = new ConcurrentHashMap<>(); + if (uuids == null || uuids.isEmpty()) return status; + + List> futures = new ArrayList<>(); + for (UUID uuid : uuids) { + CompletableFuture future = new ProxyPlayer(uuid) + .isOnline() + .orTimeout(2, TimeUnit.SECONDS) + .exceptionally(e -> false) + .thenAccept(isOnline -> status.put(uuid, isOnline)); + futures.add(future); + } + + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); + return status; + } + + private static Map fetchNamesFromCollection(String collectionName, List ids) { + Map names = new HashMap<>(); + try { + MongoCollection collection = FriendDatabase.database.getCollection(collectionName); + if (collection == null || ids.isEmpty()) return names; + + for (Document doc : collection.find(Filters.in("_id", ids))) { + String id = doc.getString("_id"); + if (id == null) continue; + + String ign = parseStoredName(doc.getString("ign")); + if (ign == null && doc.containsKey("ignLowercase")) { + ign = parseStoredName(doc.getString("ignLowercase")); + } + + if (ign != null) { + names.put(UUID.fromString(id), ign); + } + } + } catch (Exception e) { + Logger.error(e, "Failed to resolve player names from collection {}", collectionName); + } + return names; + } + + private static String parseStoredName(String raw) { + if (raw == null) return null; + raw = raw.trim(); + if (raw.startsWith("\"") && raw.endsWith("\"") && raw.length() >= 2) { + raw = raw.substring(1, raw.length() - 1); + } + return raw.isEmpty() ? null : raw; + } + private static void persistFriendData(UUID playerUuid) { FriendData data = cachedFriendData.get(playerUuid); if (data != null) { diff --git a/service.friend/src/main/java/net/swofty/service/friend/endpoints/FriendEventToServiceEndpoint.java b/service.friend/src/main/java/net/swofty/service/friend/endpoints/FriendEventToServiceEndpoint.java index 94ad551e5..d084e7732 100644 --- a/service.friend/src/main/java/net/swofty/service/friend/endpoints/FriendEventToServiceEndpoint.java +++ b/service.friend/src/main/java/net/swofty/service/friend/endpoints/FriendEventToServiceEndpoint.java @@ -8,8 +8,6 @@ import net.swofty.service.generic.redis.ServiceEndpoint; import org.tinylog.Logger; -import java.util.HashMap; - public class FriendEventToServiceEndpoint implements ServiceEndpoint< SendFriendEventToServiceProtocolObject.SendFriendEventToServiceMessage, SendFriendEventToServiceProtocolObject.SendFriendEventToServiceResponse> { @@ -29,16 +27,37 @@ public SendFriendEventToServiceProtocolObject.SendFriendEventToServiceResponse o System.out.println("Received friend event: " + event.getClass().getSimpleName()); switch (event) { - case FriendAddRequestEvent e -> FriendCache.handleAddRequest(e, "Player", "Player"); - case FriendAcceptRequestEvent e -> FriendCache.handleAcceptRequest(e, "Player", "Player"); - case FriendDenyRequestEvent e -> FriendCache.handleDenyRequest(e, "Player"); - case FriendRemoveRequestEvent e -> FriendCache.handleRemoveRequest(e, "Player", "Player"); + case FriendAddRequestEvent e -> FriendCache.handleAddRequest( + e, + FriendCache.getPlayerName(e.getSender()), + FriendCache.getPlayerName(e.getTarget()) + ); + case FriendAcceptRequestEvent e -> FriendCache.handleAcceptRequest( + e, + FriendCache.getPlayerName(e.getAccepter()), + FriendCache.getPlayerName(e.getRequester()) + ); + case FriendDenyRequestEvent e -> FriendCache.handleDenyRequest( + e, + FriendCache.getPlayerName(e.getDenier()) + ); + case FriendRemoveRequestEvent e -> FriendCache.handleRemoveRequest( + e, + FriendCache.getPlayerName(e.getRemover()), + FriendCache.getPlayerName(e.getTarget()) + ); case FriendRemoveAllRequestEvent e -> FriendCache.handleRemoveAllRequest(e); - case FriendToggleBestRequestEvent e -> FriendCache.handleToggleBestRequest(e, "Player"); - case FriendSetNicknameRequestEvent e -> FriendCache.handleSetNicknameRequest(e, "Player"); + case FriendToggleBestRequestEvent e -> FriendCache.handleToggleBestRequest( + e, + FriendCache.getPlayerName(e.getTarget()) + ); + case FriendSetNicknameRequestEvent e -> FriendCache.handleSetNicknameRequest( + e, + FriendCache.getPlayerName(e.getTarget()) + ); case FriendToggleSettingRequestEvent e -> FriendCache.handleToggleSettingRequest(e); - case FriendListRequestEvent e -> FriendCache.handleListRequest(e, new HashMap<>(), new HashMap<>()); - case FriendRequestsListEvent e -> FriendCache.handleRequestsListRequest(e, new HashMap<>()); + case FriendListRequestEvent e -> FriendCache.handleListRequest(e); + case FriendRequestsListEvent e -> FriendCache.handleRequestsListRequest(e); default -> Logger.warn("Unknown friend event type: " + event.getClass().getSimpleName()); }