Move to session-java 0.2.0 (Gitea package registry) and rework ServiceClient around a single connect loop: - Reconnect with exponential backoff (1 s -> 60 s) when the connection drops, e.g. on every server redeploy; give up only when retrying cannot help (no Minecraft session, or the server rejects the token) - Publish the session only after authentication succeeds, and replace (rather than append to) owned cosmetics and clear cached players on reconnect - Time out every request after 10 s and connecting after 10 s, so a lost response can no longer hang the player-lookup worker forever - Register onClientStopping once, and keep a local session reference in request callbacks - Allow -Dsaturn.serverUri to point the client at a local server Refs saturnclientmc/saturnclient#4, saturnclientmc/saturnclient#5 Co-Authored-By: Claude Opus 5.5 <[email protected]>
132 lines
4.0 KiB
Java
132 lines
4.0 KiB
Java
package org.saturnclient.client.player;
|
|
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.ExecutorService;
|
|
import java.util.concurrent.Executors;
|
|
|
|
import org.saturnclient.client.ServiceClient;
|
|
import org.saturnclient.common.provider.Providers;
|
|
|
|
public class SaturnPlayer {
|
|
/** Players known to be using Saturn. */
|
|
private static final Map<UUID, SaturnPlayer> PLAYERS = new ConcurrentHashMap<>();
|
|
|
|
/**
|
|
* Earliest time (ms) a player without a Saturn entry may be looked up again,
|
|
* so non-Saturn players aren't re-requested every frame.
|
|
*/
|
|
private static final Map<UUID, Long> NEXT_LOOKUP = new ConcurrentHashMap<>();
|
|
|
|
/** Players queued or being looked up. */
|
|
private static final Set<UUID> PENDING = ConcurrentHashMap.newKeySet();
|
|
|
|
private static final long NOT_SATURN_RECHECK_MS = 60_000;
|
|
private static final long FAILED_LOOKUP_RETRY_MS = 5_000;
|
|
|
|
private static final ExecutorService LOOKUPS = Executors.newSingleThreadExecutor(runnable -> {
|
|
Thread thread = new Thread(runnable, "SaturnPlayer-Worker");
|
|
thread.setDaemon(true);
|
|
return thread;
|
|
});
|
|
|
|
public volatile String cloak = "";
|
|
public volatile String hat = "";
|
|
public final UUID uuid;
|
|
public final String name;
|
|
|
|
public SaturnPlayer(UUID uuid, String name, String cloak, String hat) {
|
|
this.cloak = cloak;
|
|
this.hat = hat;
|
|
this.uuid = uuid;
|
|
this.name = name;
|
|
}
|
|
|
|
public static SaturnPlayer get() {
|
|
UUID self = ServiceClient.uuid;
|
|
if (self == null) {
|
|
return null;
|
|
}
|
|
return PLAYERS.get(self);
|
|
}
|
|
|
|
/**
|
|
* Returns the player if they are known to use Saturn, otherwise null. An
|
|
* unknown player is looked up in the background, at most once at a time and
|
|
* no more often than {@link #NEXT_LOOKUP} allows. Safe to call every frame.
|
|
*/
|
|
public static SaturnPlayer get(String name, UUID uuid) {
|
|
if (uuid == null)
|
|
return null;
|
|
|
|
SaturnPlayer player = PLAYERS.get(uuid);
|
|
|
|
if (player == null) {
|
|
requestLookup(name, uuid);
|
|
}
|
|
|
|
return player;
|
|
}
|
|
|
|
public static SaturnPlayer get(UUID uuid) {
|
|
return get(Providers.saturn.getClient().getPlayerListEntry(uuid), uuid);
|
|
}
|
|
|
|
public static SaturnPlayer get(String name) {
|
|
return get(name, Providers.saturn.getClient().getPlayerListEntryUUID(name));
|
|
}
|
|
|
|
/** Forgets every cached player, e.g. after reconnecting to the server. */
|
|
public static void clear() {
|
|
PLAYERS.clear();
|
|
NEXT_LOOKUP.clear();
|
|
}
|
|
|
|
public static void set(SaturnPlayer player) {
|
|
PLAYERS.put(player.uuid, player);
|
|
NEXT_LOOKUP.remove(player.uuid);
|
|
}
|
|
|
|
public static String[] getExternalUUIDAsString() {
|
|
UUID self = ServiceClient.uuid;
|
|
|
|
return PLAYERS.keySet().stream().filter(id -> self == null || !id.equals(self))
|
|
.map(UUID::toString)
|
|
.toArray(String[]::new);
|
|
}
|
|
|
|
private static void requestLookup(String name, UUID uuid) {
|
|
if (!ServiceClient.isConnected()) {
|
|
return;
|
|
}
|
|
|
|
Long next = NEXT_LOOKUP.get(uuid);
|
|
if (next != null && System.currentTimeMillis() < next) {
|
|
return;
|
|
}
|
|
|
|
if (!PENDING.add(uuid)) {
|
|
return;
|
|
}
|
|
|
|
LOOKUPS.execute(() -> {
|
|
try {
|
|
SaturnPlayer player = ServiceClient.getPlayer(uuid, name);
|
|
|
|
if (player != null) {
|
|
set(player);
|
|
} else {
|
|
NEXT_LOOKUP.put(uuid, System.currentTimeMillis() + NOT_SATURN_RECHECK_MS);
|
|
}
|
|
} catch (Exception e) {
|
|
Providers.saturn.logError("Failed to fetch player " + uuid, e);
|
|
NEXT_LOOKUP.put(uuid, System.currentTimeMillis() + FAILED_LOOKUP_RETRY_MS);
|
|
} finally {
|
|
PENDING.remove(uuid);
|
|
}
|
|
});
|
|
}
|
|
}
|