Stop per-frame player lookups and make shared state thread-safe
SaturnPlayer stored non-Saturn players as null, which looked the same as "not fetched yet", so every render re-queued them and flooded the server with get_player requests. Remember negative lookups and re-check them after 60 s, retry failed lookups after 5 s, queue each player at most once, skip lookups while disconnected, and replace the hand-rolled worker thread with a single-thread executor. getPlayer now returns null only when the server says the player isn't on Saturn and throws on failure, so failures are retried instead of cached. Make state shared between network callbacks and the render thread safe: ConcurrentHashMap for players, CopyOnWriteArrayList for owned cosmetics, volatile session/uuid and cosmetic fields, and run emote notifications on the client thread. Refs saturnclientmc/saturnclient#2, saturnclientmc/saturnclient#6 Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -13,8 +13,8 @@ import org.saturnclient.cosmetics.Hats;
|
||||
import dev.selimaj.session.Session;
|
||||
|
||||
public class ServiceClient {
|
||||
private static Session session;
|
||||
public static UUID uuid;
|
||||
private static volatile Session session;
|
||||
public static volatile UUID uuid;
|
||||
|
||||
public static void initialize() {
|
||||
new Thread(() -> {
|
||||
@@ -28,6 +28,10 @@ public class ServiceClient {
|
||||
}).start();
|
||||
}
|
||||
|
||||
public static boolean isConnected() {
|
||||
return session != null;
|
||||
}
|
||||
|
||||
public static boolean connectTimeout() {
|
||||
try {
|
||||
session = Session.connect("wss://saturn-server.selimaj.dev", 2, TimeUnit.MINUTES);
|
||||
@@ -220,11 +224,10 @@ public class ServiceClient {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.emote() != null && !data.emote().isEmpty()) {
|
||||
Providers.saturn.playEmote(from, data.emote());
|
||||
} else {
|
||||
Providers.saturn.playEmote(from, null);
|
||||
}
|
||||
String emote = data.emote() != null && !data.emote().isEmpty() ? data.emote() : null;
|
||||
|
||||
// Notifications arrive on a background thread.
|
||||
Providers.saturn.getClient().executeOnThread(() -> Providers.saturn.playEmote(from, emote));
|
||||
});
|
||||
|
||||
session.onNotification(ServiceMethods.Player, (player) -> {
|
||||
@@ -233,21 +236,21 @@ public class ServiceClient {
|
||||
});
|
||||
}
|
||||
|
||||
public static SaturnPlayer getPlayer(UUID uuid, String name) {
|
||||
/**
|
||||
* Returns the player's Saturn data, or null if the server says they aren't
|
||||
* using Saturn. Throws if the lookup itself fails, so callers can retry.
|
||||
*/
|
||||
public static SaturnPlayer getPlayer(UUID uuid, String name) throws Exception {
|
||||
Session session = ServiceClient.session;
|
||||
|
||||
if (session == null)
|
||||
throw new IllegalStateException("Not connected to the Saturn server");
|
||||
|
||||
ServiceMethods.Types.Player player = session.request(ServiceMethods.GetPlayer, uuid.toString()).join();
|
||||
|
||||
if (player == null)
|
||||
return null;
|
||||
|
||||
try {
|
||||
ServiceMethods.Types.Player player = session.request(ServiceMethods.GetPlayer, uuid.toString()).join();
|
||||
|
||||
if (player == null)
|
||||
return null;
|
||||
|
||||
return player.toSaturnPlayer(uuid, name);
|
||||
} catch (Exception e) {
|
||||
Providers.saturn.logError("Failed to get player", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
return player.toSaturnPlayer(uuid, name);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user