package cat.nyaa.playtimetracker; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; import java.time.DayOfWeek; import java.time.Duration; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalAdjusters; import java.util.*; import java.util.stream.Collectors; /** * Communicate with DatabaseManager * and maintains pre-session online time info. */ public class RecordManager { public class SessionedRecord { private SessionedRecord(UUID id) { this.id = id; this.dbRec = db.getRecord(id); } private final UUID id; public final DatabaseRecord dbRec; public long getSessionTime() { if (sessionTimeMap.containsKey(id)) { return sessionTimeMap.get(id); } else { return -1; } } public void setSessionTime(long time) { if (sessionTimeMap.containsKey(id)) { sessionTimeMap.put(id, time); } } public Set<String> getCompletedSessionMissions() { if (sessionRewardMap.containsKey(id)) { return sessionRewardMap.get(id); } else { return sessionRewardMap.put(id, new HashSet<>()); } } public void setCompletedSessionMissions(Set<String> set) { sessionRewardMap.put(id, set); } } private final DatabaseManager db; private final Map<UUID, Long> sessionTimeMap = new HashMap<>(); private final Map<UUID, Set<String>> sessionRewardMap = new HashMap<>(); public RecordManager(DatabaseManager db) { this.db = db; } public Set<UUID> updateAllOnlinePlayers() { Set<UUID> tmp = Bukkit.getServer().getOnlinePlayers().stream() .map(Player::getUniqueId) .map(this::updateAccumulative) .filter(u -> u != null) .collect(Collectors.toSet()); db.save(); return tmp; } public void updateSingle(OfflinePlayer p) { updateAccumulative(p.getUniqueId()); db.save(); } /** * Update the time statistic for single uuid * database not flushed * fallback to updateNonAccumulative if id is in AFK state * prerequisite: player is online * * @param id the uuid to be updated * @return id if changed, null if not */ private UUID updateAccumulative(UUID id) { if (id == null) return null; if (Main.isAFK(id)) { Main.debug("updateNonAccumulative due player AFK: " + id.toString()); return updateNonAccumulative(id); } Main.debug("updateAccumulative: " + id.toString()); DatabaseRecord rec = db.getRecord(id); if (rec == null) { db.createRecord(id, ZonedDateTime.now()); } else { ZonedDateTime currentTime = ZonedDateTime.now(); ZonedDateTime lastSeen = rec.lastSeen; rec.lastSeen = currentTime; long duration = Duration.between(lastSeen, currentTime).toMillis(); if (duration <= 0) return null; Main.debug(String.format("Time duration: %d (%s ~ %s)", duration, lastSeen.toString(), currentTime.toString())); ZonedDateTime startOfToday = currentTime.truncatedTo(ChronoUnit.DAYS); ZonedDateTime startOfWeek = currentTime.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).truncatedTo(ChronoUnit.DAYS); ZonedDateTime startOfMonth = currentTime.with(TemporalAdjusters.firstDayOfMonth()).truncatedTo(ChronoUnit.DAYS); if (startOfToday.isAfter(lastSeen)) { Main.debug("Daily time reset: " + id.toString()); rec.completedDailyMissions = new HashSet<>(); rec.dailyTime = Duration.between(startOfToday, currentTime).toMillis(); } else { rec.dailyTime += duration; } if (startOfWeek.isAfter(lastSeen)) { Main.debug("Weekly time reset: " + id.toString()); rec.completedWeeklyMissions = new HashSet<>(); rec.weeklyTime = 0; } else { rec.weeklyTime += duration; } if (startOfMonth.isAfter(lastSeen)) { Main.debug("Daily time reset: " + id.toString()); rec.completedMonthlyMissions = new HashSet<>(); rec.monthlyTime = 0; } else { rec.monthlyTime += duration; } rec.totalTime += duration; // update recurrence records if (db.recurrenceMap.containsKey(id)) { Map<String, Long> tmp = db.recurrenceMap.get(id); for (String n : tmp.keySet()) { tmp.put(n, tmp.get(n) + duration); } } } return id; } private UUID updateNonAccumulative(UUID id) { if (id == null) return null; Main.debug("updateNonAccumulative: " + id.toString()); DatabaseRecord rec = db.getRecord(id); if (rec == null) { db.createRecord(id, ZonedDateTime.now()); } else { ZonedDateTime currentTime = ZonedDateTime.now(); ZonedDateTime lastSeen = rec.lastSeen; rec.lastSeen = currentTime; long duration = Duration.between(lastSeen, currentTime).toMillis(); if (duration <= 0) return null; ZonedDateTime startOfToday = currentTime.truncatedTo(ChronoUnit.DAYS); ZonedDateTime startOfWeek = currentTime.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).truncatedTo(ChronoUnit.DAYS); ZonedDateTime startOfMonth = currentTime.with(TemporalAdjusters.firstDayOfMonth()).truncatedTo(ChronoUnit.DAYS); if (startOfToday.isAfter(lastSeen)) { Main.debug("Daily time reset: " + id.toString()); rec.completedDailyMissions = new HashSet<>(); rec.dailyTime = 0; } if (startOfWeek.isAfter(lastSeen)) { Main.debug("Weekly time reset: " + id.toString()); rec.completedWeeklyMissions = new HashSet<>(); rec.weeklyTime = 0; } if (startOfMonth.isAfter(lastSeen)) { Main.debug("Monthly time reset: " + id.toString()); rec.completedMonthlyMissions = new HashSet<>(); rec.monthlyTime = 0; } } return id; } public SessionedRecord getFullRecord(UUID id) { return new SessionedRecord(id); } public void resetAllStatistic() { Set<UUID> ids = new HashSet<>(db.getAllRecords().keySet()); for (UUID id : ids) { db.createRecord(id, ZonedDateTime.now()); } db.recurrenceMap.clear(); db.save(); sessionRewardMap.clear(); sessionTimeMap.clear(); } public void resetSingleStatistic(UUID id) { if (id == null) return; Main.log(String.format("Statistic reset for %s, old record: %s", id.toString(), db.getRecord(id))); db.createRecord(id, ZonedDateTime.now()); db.recurrenceMap.remove(id); db.save(); sessionRewardMap.remove(id); sessionTimeMap.remove(id); } public void sessionStart(UUID id) { updateNonAccumulative(id); sessionTimeMap.put(id, 0L); sessionRewardMap.put(id, new HashSet<>()); db.save(); } public void sessionEnd(UUID id) { updateAccumulative(id); sessionTimeMap.remove(id); sessionRewardMap.remove(id); db.save(); } public void markRuleAsApplied(UUID id, Rule rule) { if (rule.period == Rule.PeriodType.SESSION) { sessionRewardMap.get(id).add(rule.name); return; } DatabaseRecord rec = db.getRecord(id); if (rec == null) return; switch (rule.period) { case DAY: { rec.completedDailyMissions.add(rule.name); break; } case WEEK: { rec.completedWeeklyMissions.add(rule.name); break; } case MONTH: { rec.completedMonthlyMissions.add(rule.name); break; } case DISPOSABLE: { rec.completedLifetimeMissions.add(rule.name); if (db.recurrenceMap.containsKey(id)) { db.recurrenceMap.get(id).remove(rule.name); } break; } } db.save(); } }