-
Notifications
You must be signed in to change notification settings - Fork 0
Player Profiles
PrisonCore tracks one persistent profile per player and one transient session per online player. Modules read and write their own per-player state through the profile's metadata map, or through their own storage if the data is large or has structure the metadata model doesn't fit.
The platform owns persistence. You don't manage profile files yourself.
com.github.frosxt.prisoncore.player.api.PlayerProfileService
public interface PlayerProfileService {
CompletableFuture<PlayerProfile> loadOrCreate(UUID playerId, String username);
CompletableFuture<Void> save(PlayerProfile profile);
Optional<PlayerSession> getSession(UUID playerId);
PlayerSession createSession(UUID playerId, PlayerProfile profile);
void destroySession(UUID playerId);
Optional<PlayerView> getView(UUID playerId);
}Resolved through the service container:
final PlayerProfileService players = context.services().resolve(PlayerProfileService.class);loadOrCreate(UUID, String) loads an existing profile or creates a fresh one if none exists. Asynchronous — use the returned future. The platform calls this on join automatically; you only call it directly if you need a profile for a player who isn't online yet.
save(PlayerProfile) persists a profile asynchronously. Safe to call from the main thread. The default profile service uses an io executor and a background flush.
getSession(UUID) returns the active session for an online player, or empty if they're offline.
createSession and destroySession are normally driven by the platform's join/quit listener. You almost certainly do not call these yourself.
getView(UUID) returns an immutable snapshot of the player's current profile state. Use this for UI, placeholder resolution, and any read-heavy code path where you want a stable view that won't change under you.
com.github.frosxt.prisoncore.player.api.PlayerProfile
The persistent identity record:
public UUID id();
public String username();
public Instant firstSeen();
public Instant lastSeen();
public Map<String, Object> metadata();
public <T> T getMetadata(String key, Class<T> type);
public void setMetadata(String key, Object value);
public void setUsername(String username);
public void setLastSeen(Instant lastSeen);metadata() is the per-player open-ended key/value store. Use it for small, infrequent data: a setting toggle, a counter, a last-known location id. Don't use it for thousands of records, that's what StorageRegistry is for.
getMetadata(key, type) is a typed convenience that casts on your behalf. It returns null if the key is absent.
setMetadata(key, value) writes a value. The profile is not auto-saved. Call players.save(profile) after a meaningful change.
The username and lastSeen setters exist because the platform needs to refresh them on every login.
com.github.frosxt.prisoncore.player.api.PlayerSession
The transient per-login wrapper:
public UUID playerId();
public PlayerProfile profile();
public Instant loginTime();
public <T> T getData(String key, Class<T> type);
public void setData(String key, Object value);
public void removeData(String key);A session exists from join to quit. Its loginTime is when this specific session started. profile() returns the persisted profile attached to the session.
getData / setData / removeData operate on a separate per-session map that is not persisted. Use this for data that should reset every login (a temporary buff, an in-progress menu state, a trade-in-flight). When the player quits, the session and all its session data are dropped.
If you want data that survives login, use profile().setMetadata(...) and save the profile.
com.github.frosxt.prisoncore.player.api.PlayerView
An immutable snapshot of a profile:
public UUID id();
public String username();
public Instant firstSeen();
public Instant lastSeen();
public Map<String, Object> metadata();Constructed by the platform from a live PlayerProfile. The metadata map is wrapped in Collections.unmodifiableMap and copied at construction, so changes to the source profile after the view is taken don't affect the view.
Use a view when you need to read profile data on an io thread or another non-main thread. The live profile is intended to be touched from the main thread; the view is the safe read shape elsewhere.
public final class HelloModule extends AbstractPlatformModule {
private PlayerProfileService players;
@Override
protected void onPrepare(final ModuleContext context) {
players = context.services().resolve(PlayerProfileService.class);
}
public void incrementGreetCount(final Player player) {
players.getSession(player.getUniqueId()).ifPresent(session -> {
final PlayerProfile profile = session.profile();
final Integer current = profile.getMetadata("hello.count", Integer.class);
profile.setMetadata("hello.count", current == null ? 1 : current + 1);
players.save(profile);
});
}
public Integer getGreetCount(final UUID playerId) {
return players.getView(playerId)
.map(view -> (Integer) view.metadata().get("hello.count"))
.orElse(0);
}
}The profile service uses whichever storage backend core.yml selects. JSON, SQLite, SQL, and Mongo are all supported. The platform picks once at boot and the profile service uses that choice for every profile.
If your module needs a backend instance for its own data, ask the StorageRegistry for one. The profile service and your module's storage can share the same backend instance under different scope tags, or they can be entirely independent.
This wiki documents the public API surface module authors are expected to touch. Kernel internals are intentionally omitted. Spot something wrong? Open an issue.
Start here
Architecture
Subsystems
- Commands
- Menus
- Messages and Placeholders
- Scheduler
- Storage
- Events and Listeners
- Configuration
- Player Profiles
Utilities