package emu.nebula.game; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import emu.nebula.Nebula; import emu.nebula.game.activity.ActivityModule; import emu.nebula.game.ban.BanModule; import emu.nebula.game.gacha.GachaModule; import emu.nebula.game.jointdrill.JointDrillModule; import emu.nebula.game.mall.PayModule; import emu.nebula.game.player.PlayerModule; import emu.nebula.game.scoreboss.ScoreBossModule; import emu.nebula.game.tutorial.TutorialModule; import emu.nebula.net.GameSession; import emu.nebula.util.ResetCycle; import emu.nebula.util.Utils; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import lombok.Getter; @Getter public class GameContext implements Runnable { private final Object2ObjectMap sessions; // Modules private final PlayerModule playerModule; private final GachaModule gachaModule; private final TutorialModule tutorialModule; private final ActivityModule activityModule; private final ScoreBossModule scoreBossModule; private final JointDrillModule jointDrillModule; private final BanModule banModule; private final PayModule payModule; // Game loop private final ScheduledExecutorService scheduler; // Reset-period indexes aligned to the configured daily reset boundary. private long resetDays; private int resetWeeks; private int resetMonths; public GameContext() { // Create session map this.sessions = new Object2ObjectOpenHashMap<>(); // Setup game modules this.playerModule = new PlayerModule(this); this.gachaModule = new GachaModule(this); this.tutorialModule = new TutorialModule(this); this.activityModule = new ActivityModule(this); this.scoreBossModule = new ScoreBossModule(this); this.jointDrillModule = new JointDrillModule(this); this.banModule = new BanModule(this); this.payModule = new PayModule(this); // Run game loop this.scheduler = Executors.newScheduledThreadPool(1); this.scheduler.scheduleAtFixedRate(this, 0, 1, TimeUnit.SECONDS); } public synchronized GameSession getSessionByToken(String token) { return sessions.get(token); } public synchronized void addSession(GameSession session) { this.sessions.put(session.getToken(), session); } public synchronized void generateSessionToken(GameSession session) { // Remove token if (session.getToken() != null) { this.sessions.remove(session.getToken()); } // Generate token String token; do { token = session.generateToken(); } while (this.getSessions().containsKey(token)); // Register session this.sessions.put(session.getToken(), session); } public synchronized void cleanupInactiveSessions() { var it = this.getSessions().entrySet().iterator(); int time = Nebula.getConfig().getServerOptions().sessionTimeout; long timeout = System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(time); while (it.hasNext()) { var session = it.next().getValue(); if (timeout > session.getLastActiveTime() || session.isRemove()) { // Remove from session map it.remove(); // Clear player session.clearPlayer(); } } } @Override public void run() { long lastResetDays = this.resetDays; this.resetDays = Utils.getResetEpochDay(); // Check if the day was changed if (this.resetDays > lastResetDays) { // Update epoch weeks/months this.resetWeeks = Utils.getResetPeriodIndex(ResetCycle.WEEKLY, Nebula.getCurrentServerTime()); this.resetMonths = Utils.getResetPeriodIndex(ResetCycle.MONTHLY, Nebula.getCurrentServerTime()); // Update score boss season this.getScoreBossModule().updateSeason(); // Reset dailies for players this.resetDailies(); } // Clean up any inactive sessions this.cleanupInactiveSessions(); } /** * Resets the daily missions/etc for all players on the server */ public synchronized void resetDailies() { for (var session : this.getSessions().values()) { // Cache var player = session.getPlayer(); // Skip if session doesn't have a player if (player == null) { continue; } player.checkResetDailies(); } } }