From 90f4be862fe9279645e7fff0fe51aa59d53c6aa7 Mon Sep 17 00:00:00 2001 From: Melledy <121644117+Melledy@users.noreply.github.com> Date: Sun, 7 Dec 2025 20:00:22 -0800 Subject: [PATCH] Implement daily check in --- src/main/java/emu/nebula/data/GameData.java | 81 ++++++++++--------- .../emu/nebula/data/resources/SignInDef.java | 20 +++++ .../java/emu/nebula/game/GameContext.java | 8 +- .../java/emu/nebula/game/player/Player.java | 54 ++++++++++++- src/main/java/emu/nebula/util/Utils.java | 22 ++++- 5 files changed, 145 insertions(+), 40 deletions(-) create mode 100644 src/main/java/emu/nebula/data/resources/SignInDef.java diff --git a/src/main/java/emu/nebula/data/GameData.java b/src/main/java/emu/nebula/data/GameData.java index 865243d..f961f85 100644 --- a/src/main/java/emu/nebula/data/GameData.java +++ b/src/main/java/emu/nebula/data/GameData.java @@ -18,7 +18,7 @@ import lombok.Getter; @SuppressWarnings("unused") public class GameData { - // Characters + // ===== Characters ===== @Getter private static DataTable CharacterDataTable = new DataTable<>(); @Getter private static DataTable CharacterAdvanceDataTable = new DataTable<>(); @Getter private static DataTable CharacterSkillUpgradeDataTable = new DataTable<>(); @@ -28,40 +28,43 @@ public class GameData { @Getter private static DataTable TalentGroupDataTable = new DataTable<>(); @Getter private static DataTable TalentDataTable = new DataTable<>(); - // Character emblems + // Characters: Emblems @Getter private static DataTable CharGemDataTable = new DataTable<>(); @Getter private static DataTable CharGemSlotControlDataTable = new DataTable<>(); @Getter private static DataTable CharGemAttrGroupDataTable = new DataTable<>(); @Getter private static DataTable CharGemAttrValueDataTable = new DataTable<>(); - // Character affinity + // Characters: Affinity @Getter private static DataTable AffinityLevelDataTable = new DataTable<>(); @Getter private static DataTable AffinityGiftDataTable = new DataTable<>(); @Getter private static DataTable PlotDataTable = new DataTable<>(); + // Characters: Phone @Getter private static DataTable ChatDataTable = new DataTable<>(); + // Characters: Dating @Getter private static DataTable DatingLandmarkDataTable = new DataTable<>(); @Getter private static DataTable DatingLandmarkEventDataTable = new DataTable<>(); @Getter private static DataTable DatingCharacterEventDataTable = new DataTable<>(); - // Discs + // ===== Discs ===== @Getter private static DataTable DiscDataTable = new DataTable<>(); @Getter private static DataTable DiscStrengthenDataTable = new DataTable<>(); @Getter private static DataTable DiscItemExpDataTable = new DataTable<>(); @Getter private static DataTable DiscPromoteDataTable = new DataTable<>(); @Getter private static DataTable DiscPromoteLimitDataTable = new DataTable<>(); + // Discs: Melody items @Getter private static DataTable SecondarySkillDataTable = new DataTable<>(); - // Items + // ===== Items ===== @Getter private static DataTable ItemDataTable = new DataTable<>(); @Getter private static DataTable ProductionDataTable = new DataTable<>(); @Getter private static DataTable PlayerHeadDataTable = new DataTable<>(); @Getter private static DataTable titleDataTable = new DataTable<>(); @Getter private static DataTable honorDataTable = new DataTable<>(); - // Shops + // ===== Shops ===== @Getter private static DataTable MallMonthlyCardDataTable = new DataTable<>(); @Getter private static DataTable MallPackageDataTable = new DataTable<>(); @Getter private static DataTable MallShopDataTable = new DataTable<>(); @@ -70,20 +73,38 @@ public class GameData { @Getter private static DataTable ResidentShopDataTable = new DataTable<>(); @Getter private static DataTable ResidentGoodsDataTable = new DataTable<>(); - // Battle Pass + // ===== Battle Pass ===== @Getter private static DataTable BattlePassDataTable = new DataTable<>(); @Getter private static DataTable BattlePassLevelDataTable = new DataTable<>(); @Getter private static DataTable BattlePassQuestDataTable = new DataTable<>(); @Getter private static DataTable BattlePassRewardDataTable = new DataTable<>(); - // Commissions + // ===== Commissions ===== @Getter private static DataTable AgentDataTable = new DataTable<>(); - // Dictionary + // ===== Dictionary ===== @Getter private static DataTable DictionaryTabDataTable = new DataTable<>(); @Getter private static DataTable DictionaryEntryDataTable = new DataTable<>(); - // Instances + // ===== Gacha ===== + @Getter private static DataTable GachaDataTable = new DataTable<>(); + @Getter private static DataTable GachaStorageDataTable = new DataTable<>(); + + // ===== Story ===== + @Getter private static DataTable StoryDataTable = new DataTable<>(); + @Getter private static DataTable StorySetSectionDataTable = new DataTable<>(); + + // ===== Daily Quests ===== + @Getter private static DataTable DailyQuestDataTable = new DataTable<>(); + @Getter private static DataTable DailyQuestActiveDataTable = new DataTable<>(); + + // ===== Achievements ===== + @Getter private static DataTable AchievementDataTable = new DataTable<>(); + + // ===== Tutorials ===== + @Getter private static DataTable TutorialLevelDataTable = new DataTable<>(); + + // ===== Instances ===== @Getter private static DataTable DailyInstanceDataTable = new DataTable<>(); @Getter private static DataTable DailyInstanceRewardGroupDataTable = new DataTable<>(); @Getter private static DataTable RegionBossLevelDataTable = new DataTable<>(); @@ -91,26 +112,7 @@ public class GameData { @Getter private static DataTable CharGemInstanceDataTable = new DataTable<>(); @Getter private static DataTable WeekBossLevelDataTable = new DataTable<>(); - @Getter private static DataTable GachaDataTable = new DataTable<>(); - @Getter private static DataTable GachaStorageDataTable = new DataTable<>(); - - @Getter private static DataTable WorldClassDataTable = new DataTable<>(); - @Getter private static DataTable GuideGroupDataTable = new DataTable<>(); - @Getter private static DataTable HandbookDataTable = new DataTable<>(); - @Getter private static DataTable StoryDataTable = new DataTable<>(); - @Getter private static DataTable StorySetSectionDataTable = new DataTable<>(); - - // Daily quests - @Getter private static DataTable DailyQuestDataTable = new DataTable<>(); - @Getter private static DataTable DailyQuestActiveDataTable = new DataTable<>(); - - // Achievements - @Getter private static DataTable AchievementDataTable = new DataTable<>(); - - // Tutorial - @Getter private static DataTable TutorialLevelDataTable = new DataTable<>(); - - // Star tower + // ===== Star Tower ===== @Getter private static DataTable StarTowerDataTable = new DataTable<>(); @Getter private static DataTable StarTowerStageDataTable = new DataTable<>(); @Getter private static DataTable StarTowerGrowthNodeDataTable = new DataTable<>(); @@ -126,24 +128,31 @@ public class GameData { @Getter private static DataTable StarTowerBookFateCardQuestDataTable = new DataTable<>(); @Getter private static DataTable StarTowerBookFateCardDataTable = new DataTable<>(); @Getter private static DataTable FateCardDataTable = new DataTable<>(); - - // Infinity Tower + + // ===== Infinity Tower ===== @Getter private static DataTable InfinityTowerLevelDataTable = new DataTable<>(); - // Vampire survivor + // ===== Vampire Survivor ===== @Getter private static DataTable VampireSurvivorDataTable = new DataTable<>(); @Getter private static DataTable VampireTalentDataTable = new DataTable<>(); - // Score boss + // ===== Score Boss ===== @Getter private static DataTable ScoreBossControlDataTable = new DataTable<>(); @Getter private static DataTable ScoreBossRewardDataTable = new DataTable<>(); - // Activity + // ===== Misc ===== + @Getter private static DataTable WorldClassDataTable = new DataTable<>(); + @Getter private static DataTable GuideGroupDataTable = new DataTable<>(); + @Getter private static DataTable HandbookDataTable = new DataTable<>(); + @Getter private static DataTable SignInDataTable = new DataTable<>(); + + // ===== Activity ===== @Getter private static DataTable ActivityDataTable = new DataTable<>(); - // Tower defense + // Activity: Tower Defense @Getter private static DataTable TowerDefenseLevelDataTable = new DataTable<>(); + // Activity: Trials @Getter private static DataTable TrialControlDataTable = new DataTable<>(); @Getter private static DataTable TrialGroupDataTable = new DataTable<>(); } \ No newline at end of file diff --git a/src/main/java/emu/nebula/data/resources/SignInDef.java b/src/main/java/emu/nebula/data/resources/SignInDef.java new file mode 100644 index 0000000..8d5d49b --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/SignInDef.java @@ -0,0 +1,20 @@ +package emu.nebula.data.resources; + +import emu.nebula.data.BaseDef; +import emu.nebula.data.ResourceType; + +import lombok.Getter; + +@Getter +@ResourceType(name = "SignIn.json") +public class SignInDef extends BaseDef { + private int Group; + private int Day; + private int ItemId; + private int ItemQty; + + @Override + public int getId() { + return (this.Group << 16) + this.Day; + } +} diff --git a/src/main/java/emu/nebula/game/GameContext.java b/src/main/java/emu/nebula/game/GameContext.java index a5eaac6..cc2e3b4 100644 --- a/src/main/java/emu/nebula/game/GameContext.java +++ b/src/main/java/emu/nebula/game/GameContext.java @@ -37,6 +37,7 @@ public class GameContext implements Runnable { // Daily private long epochDays; private int epochWeeks; + private int epochMonths; public GameContext() { this.sessions = new Object2ObjectOpenHashMap<>(); @@ -105,12 +106,17 @@ public class GameContext implements Runnable { var instant = Instant.now().plusSeconds(offset); var date = LocalDate.ofInstant(instant, GameConstants.UTC_ZONE); + // Update epoch days long lastEpochDays = this.epochDays; this.epochDays = date.toEpochDay(); - this.epochWeeks = Utils.getWeeks(this.epochDays); // Check if the day was changed if (this.epochDays > lastEpochDays) { + // Update epoch weeks/months + this.epochWeeks = Utils.getWeeks(this.epochDays); + this.epochMonths = Utils.getMonths(this.epochDays); + + // Reset dailies for players this.resetDailies(); } diff --git a/src/main/java/emu/nebula/game/player/Player.java b/src/main/java/emu/nebula/game/player/Player.java index 445ea29..72335bf 100644 --- a/src/main/java/emu/nebula/game/player/Player.java +++ b/src/main/java/emu/nebula/game/player/Player.java @@ -36,6 +36,7 @@ import emu.nebula.game.vampire.VampireSurvivorManager; import emu.nebula.net.GameSession; import emu.nebula.net.NetMsgId; import emu.nebula.net.NetMsgPacket; +import emu.nebula.proto.Notify.SigninRewardUpdate; import emu.nebula.proto.PlayerData.DictionaryEntry; import emu.nebula.proto.PlayerData.DictionaryTab; import emu.nebula.proto.PlayerData.PlayerInfo; @@ -85,6 +86,8 @@ public class Player implements GameDatabaseObject { private int energy; private long energyLastUpdate; + private int signInIndex; + private long lastEpochDay; private long lastLogin; private long createTime; @@ -613,6 +616,13 @@ public class Player implements GameDatabaseObject { public void checkResetDailies() { // Sanity check to make sure daily reset isnt being triggered wrong if (Nebula.getGameContext().getEpochDays() <= this.getLastEpochDay()) { + // Fix sign-in index + // TODO remove later + if (this.getSignInIndex() <= 0) { + this.getSignInRewards(false); + } + + // End return; } @@ -621,8 +631,12 @@ public class Player implements GameDatabaseObject { int curWeek = Utils.getWeeks(this.getLastEpochDay()); boolean hasWeekChanged = Nebula.getGameContext().getEpochWeeks() > curWeek; + // Check if month was changed + int curMonth = Utils.getMonths(this.getLastEpochDay()); + boolean hasMonthChanged = Nebula.getGameContext().getEpochMonths() > curMonth; + // Reset dailies - this.resetDailies(hasWeekChanged); + this.resetDailies(hasWeekChanged, hasMonthChanged); // Trigger quest/achievement login this.trigger(QuestCondition.LoginTotal, 1); @@ -633,12 +647,47 @@ public class Player implements GameDatabaseObject { this.getInventory().addItem(GameConstants.WEEKLY_ENTRY_ITEM_ID, 3 - entries); } + // Give sign-in rewards + this.getSignInRewards(hasMonthChanged); + // Update last epoch day this.lastEpochDay = Nebula.getGameContext().getEpochDays(); Nebula.getGameDatabase().update(this, this.getUid(), "lastEpochDay", this.lastEpochDay); } + + private void getSignInRewards(boolean resetMonthly) { + // Check monthly reset + if (resetMonthly) { + this.signInIndex = 0; + } + + // Get next sign-in index + int nextSignIn = this.signInIndex + 1; + int group = Utils.getDaysOfMonth(this.getLastEpochDay()); + + var data = GameData.getSignInDataTable().get((group << 16) + nextSignIn); + if (data == null) { + return; + } + + // Add rewards + var change = this.getInventory().addItem(data.getItemId(), data.getItemQty()); + + // Add package + this.addNextPackage( + NetMsgId.signin_reward_change_notify, + SigninRewardUpdate.newInstance() + .setIndex(nextSignIn) + .setSwitch(resetMonthly) + .setChange(change.toProto()) + ); + + // Update sign-in index + this.signInIndex = nextSignIn; + Nebula.getGameDatabase().update(this, this.getUid(), "signInIndex", this.signInIndex); + } - public void resetDailies(boolean resetWeekly) { + public void resetDailies(boolean resetWeekly, boolean resetMonthly) { // Reset daily quests this.getQuestManager().resetDailyQuests(); this.getBattlePassManager().getBattlePass().resetDailyQuests(resetWeekly); @@ -767,6 +816,7 @@ public class Player implements GameDatabaseObject { public PlayerInfo toProto() { PlayerInfo proto = PlayerInfo.newInstance() .setServerTs(Nebula.getCurrentTime()) + .setSigninIndex(this.getSignInIndex()) .setDailyShopRewardStatus(this.getQuestManager().hasDailyReward()) .setAchievements(new byte[64]); diff --git a/src/main/java/emu/nebula/util/Utils.java b/src/main/java/emu/nebula/util/Utils.java index 1acb285..4278b18 100644 --- a/src/main/java/emu/nebula/util/Utils.java +++ b/src/main/java/emu/nebula/util/Utils.java @@ -4,6 +4,9 @@ import java.io.File; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; +import java.time.LocalDate; +import java.time.YearMonth; +import java.time.temporal.ChronoUnit; import java.util.Base64; import java.util.List; import java.util.concurrent.ThreadLocalRandom; @@ -294,11 +297,28 @@ public class Utils { } /** - * Get amount weeks since this epoch day. Each week starts on monday. + * Get amount of weeks since this epoch day. Each week starts on monday. * @param epochDays * @return */ public static int getWeeks(long epochDays) { return (int) Math.floor((epochDays + 3) / 7D); } + + /** + * Get amount of months since this epoch day. + * @param epochDays + * @return + */ + public static int getMonths(long epochDays) { + var begin = LocalDate.ofEpochDay(0); + var date = LocalDate.ofEpochDay(epochDays); + return (int) ChronoUnit.MONTHS.between(begin, date); + } + + public static int getDaysOfMonth(long epochDays) { + var date = LocalDate.ofEpochDay(epochDays); + var month = YearMonth.of(date.getYear(), date.getMonthValue()); + return month.lengthOfMonth(); + } }