package com.disklexar.mmorpg.player; import com.disklexar.mmorpg.MmorpgPlugin; import com.disklexar.mmorpg.core.config.MmorpgConfig; import com.disklexar.mmorpg.core.service.Service; import com.disklexar.mmorpg.persistence.repository.PlayerProfileRepository; import com.disklexar.mmorpg.player.model.PlayerProfile; import com.disklexar.mmorpg.rpg.race.RaceCatalog; import com.hypixel.hytale.logger.HytaleLogger; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.sql.SQLException; import java.util.Map; import java.util.Optional; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; /** * Tracks online player sessions and synchronizes profiles with SQLite. */ public final class PlayerSessionService implements Service { private final MmorpgPlugin plugin; private final MmorpgConfig config; private final PlayerProfileRepository repository; private final HytaleLogger logger; private final Map sessions = new ConcurrentHashMap<>(); private final Map connectedAt = new ConcurrentHashMap<>(); public PlayerSessionService( @Nonnull MmorpgPlugin plugin, @Nonnull MmorpgConfig config, @Nonnull PlayerProfileRepository repository) { this.plugin = plugin; this.config = config; this.repository = repository; this.logger = plugin.getLogger(); } @Override public void start() { try { repository.clearConnectedFlags(); } catch (SQLException e) { logger.at(Level.WARNING).log("Failed to reset connection flags: %s", e.getMessage()); } logger.at(Level.INFO).log("Player session service started"); } @Override public void shutdown() { for (PlayerProfile profile : sessions.values()) { accumulatePlaytime(profile.getUuid(), profile); profile.setConnected(false); saveQuietly(profile); } sessions.clear(); connectedAt.clear(); logger.at(Level.INFO).log("Player session service stopped"); } /** * Loads (or creates) a profile and marks the player connected. */ @Nonnull public PlayerProfile loadOrCreate(@Nonnull UUID uuid, @Nonnull String displayName) throws SQLException { long now = System.currentTimeMillis(); Optional existing = repository.findByUuid(uuid); PlayerProfile profile; if (existing.isPresent()) { profile = existing.get(); profile.setDisplayName(displayName); } else { profile = new PlayerProfile(uuid, displayName, now); profile.setLevel(config.getDefaultLevel()); profile.setRaceId(RaceCatalog.DEFAULT_RACE_ID); logger.at(Level.INFO).log("Created new MMORPG profile for %s", displayName); } profile.setConnected(true); profile.setLastDateConnected(now); profile.touch(); sessions.put(uuid, profile); connectedAt.put(uuid, now); saveQuietly(profile); return profile; } @Nullable public PlayerProfile getSession(@Nonnull UUID uuid) { return sessions.get(uuid); } public void unload(@Nonnull UUID uuid) { PlayerProfile profile = sessions.remove(uuid); if (profile == null) { return; } accumulatePlaytime(uuid, profile); profile.setConnected(false); profile.touch(); saveQuietly(profile); } private void accumulatePlaytime(@Nonnull UUID uuid, @Nonnull PlayerProfile profile) { Long start = connectedAt.remove(uuid); if (start != null) { profile.addTimePlay(System.currentTimeMillis() - start); } } private void saveQuietly(@Nonnull PlayerProfile profile) { try { repository.save(profile); } catch (SQLException e) { logger.at(Level.WARNING).log( "Failed to save profile for %s: %s", profile.getUuid(), e.getMessage()); } } }