Implement Monthly and Weekly reset#645
Conversation
- Add shop restock reset (RestockCount=0) in daily reset for Day interval and weekly reset for Week interval (character-shop-data table) - Add meso market limits reset (MarketLimits JSON) in daily reset - Add ShopManager.DailyReset() and WeeklyReset() for in-memory state - Call Shop.DailyReset()/WeeklyReset() from GameSession reset methods - Remove resolved TODO comment about shop restock reset Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📝 WalkthroughWalkthroughAdds weekly and monthly reset flows across world, server, session, storage, shop, and service layers; refactors server-info creation for generic reset keys and exposes new admin command to trigger daily/weekly/monthly resets. Changes
Sequence DiagramsequenceDiagram
participant WS as WorldServer
participant DB as GameStorage (ServerInfo)
participant GS as GameServer
participant GSession as GameSession
participant Shop as ShopManager
participant Dungeon as DungeonManager
WS->>DB: GetLastWeeklyReset() / GetLastMonthlyReset()
DB-->>WS: LastModified or CreateServerInfo("Weekly"/"Monthly")
WS->>WS: Schedule next weekly/monthly reset
Note over WS: When scheduled reset triggers
WS->>DB: CreateServerInfo("WeeklyReset") / Update LastModified
DB-->>WS: Confirm
WS->>GS: WeeklyReset()
activate GS
GS->>GSession: WeeklyReset() for each active session
activate GSession
GSession->>GSession: Clear prestige rewards, refresh prestige, update dungeon limits
GSession->>Dungeon: ResetWeeklyClears()
GSession->>Shop: WeeklyReset()
Dungeon-->>GSession: Confirm & UI reload
Shop-->>GSession: Confirm
deactivate GSession
GS-->>WS: All sessions processed
deactivate GS
WS->>WS: Schedule next weekly reset
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Maple2.Database/Storage/Game/GameStorage.ServerInfo.cs`:
- Around line 49-57: The new ServerInfo created in the
ServerInfo.Find("WeeklyReset") null branch doesn't set LastModified, leaving it
at DateTime.MinValue; when creating the record (ServerInfo { Key = "WeeklyReset"
}) set serverInfo.LastModified to the current timestamp (e.g., DateTime.Now or
the same time semantics used by WeeklyReset/GetLastWeeklyReset) before calling
Context.ServerInfo.Add and Context.SaveChanges so subsequent calls to
GetLastWeeklyReset see a valid recent lastReset value; ensure the same property
is updated in the existing branch (serverInfo.LastModified = DateTime.Now;
Context.Update(serverInfo);) to keep behavior consistent.
In `@Maple2.Server.Game/Manager/ShopManager.cs`:
- Around line 635-646: ResetShopData currently resets RestockCount only in
accountShopData and characterShopData but leaves the Shop instances cached in
instancedShops stale, causing InstantRestock to use the old RestockCount on
activeShop; update ResetShopData to also find matching Shop objects in the
instancedShops cache (lookup by shopId/accountId/characterId as applicable) and
set their RestockCount to 0 so InstantRestock (which reads
activeShop.RestockCount) sees the cleared value; ensure you reference the same
Shop instances stored in instancedShops rather than creating new objects.
In `@Maple2.Server.Game/Session/GameSession.cs`:
- Around line 646-651: After resetting Player.Value.Account.MesoMarketListed and
MesoMarketPurchased in GameSession.DailyReset(), send a client-sync packet so
the UI reflects the change immediately; call Send(...) with an appropriate
meso-market packet (create one if missing) such as
MesoMarketPacket.UpdateCounters(Player.Value.Account.MesoMarketListed,
Player.Value.Account.MesoMarketPurchased) immediately after the assignments
(before/after Shop.DailyReset() as appropriate), and ensure the packet
implementation updates the client-side counters.
In `@Maple2.Server.World/WorldServer.cs`:
- Around line 192-194: The weekly reset is being anchored to the server start
time because nextFriday is computed with now.NextDayOfWeek(DayOfWeek.Friday)
which preserves the current time-of-day; change the calculation to anchor to
midnight like StartDailyReset (e.g., use .Date or construct new
DateTime(now.Year, now.Month, now.Day) on the NextDayOfWeek result) so
nextFriday refers to Friday at 00:00, then pass that corrected DateTime into
scheduler.Schedule for ScheduleWeeklyReset.
- Around line 185-186: StartWeeklyReset currently computes daysSinceFriday using
DayOfWeek.Friday which mismatches ShopManager.GetRestockTime's
Constant.ResetDay; update the logic in StartWeeklyReset to use Constant.ResetDay
instead of DayOfWeek.Friday (replace the daysSinceFriday and lastFridayMidnight
calculations and the other occurrence at the later reset computation to
reference Constant.ResetDay), e.g. compute daysSinceReset = ((int)now.DayOfWeek
- (int)Constant.ResetDay + 7) % 7 and use now.Date.AddDays(-daysSinceReset) for
the midnight reset time so weekly resets align with ShopManager.GetRestockTime.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
Maple2.Database/Storage/Game/GameStorage.ServerInfo.cs (2)
47-63:SaveChangescommits the reset timestamp before raw SQL updates — partial failure leaves a stale "reset done" marker
Context.SaveChanges()at line 57 commitsLastModified = DateTime.Nowto the database before any of the threeExecuteSqlRawcalls execute. If a SQL statement fails (e.g., network hiccup, schema mismatch), the server will believe the weekly reset completed and won't retry until the following week, leaving the game in a partially-reset state. Wrapping both the EF change and the raw statements in a single explicit transaction would ensure atomicity.♻️ Proposed fix — single transaction
public void WeeklyReset() { lock (Context) { + using var transaction = Context.Database.BeginTransaction(); ServerInfo? serverInfo = Context.ServerInfo.Find("WeeklyReset"); if (serverInfo == null) { serverInfo = new ServerInfo { Key = "WeeklyReset", LastModified = DateTime.Now }; Context.ServerInfo.Add(serverInfo); } else { serverInfo.LastModified = DateTime.Now; Context.Update(serverInfo); } Context.SaveChanges(); Context.Database.ExecuteSqlRaw("UPDATE `guild-member` SET `WeeklyContribution` = 0"); Context.Database.ExecuteSqlRaw("UPDATE `account` SET `PrestigeRewardsClaimed` = DEFAULT"); Context.Database.ExecuteSqlRaw("UPDATE `character-shop-data` SET `RestockCount` = 0 WHERE `Interval` = 2"); + transaction.Commit(); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Maple2.Database/Storage/Game/GameStorage.ServerInfo.cs` around lines 47 - 63, The WeeklyReset method currently calls Context.SaveChanges() before running the three Context.Database.ExecuteSqlRaw(...) statements which can leave the LastModified marker committed while the raw SQL updates fail; change WeeklyReset to create an explicit transaction via Context.Database.BeginTransaction()/BeginTransactionAsync, perform the ServerInfo creation/update (serverInfo.LastModified) and the three ExecuteSqlRaw calls inside that transaction, call Context.SaveChanges() (or SaveChangesAsync) while the transaction is open, then commit the transaction; ensure the transaction is rolled back on exceptions so serverInfo.LastModified is not persisted if any ExecuteSqlRaw fails.
42-44: Hardcoded JSON inMarketLimitsreset tightly couples SQL to the object's structure
SET MarketLimits = '{"MesoListed":0,"MesoPurchased":0}'replaces the entire JSON value. Any field added toMarketLimitslater will be silently wiped by this reset unless this SQL is also updated. MySQL'sJSON_SETwould reset only the targeted fields while preserving any others.♻️ Targeted reset using JSON_SET
-Context.Database.ExecuteSqlRaw("UPDATE `account` SET `MarketLimits` = '{\"MesoListed\":0,\"MesoPurchased\":0}'"); +Context.Database.ExecuteSqlRaw("UPDATE `account` SET `MarketLimits` = JSON_SET(`MarketLimits`, '$.MesoListed', 0, '$.MesoPurchased', 0)");🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Maple2.Database/Storage/Game/GameStorage.ServerInfo.cs` around lines 42 - 44, Replace the hardcoded JSON assignment in the two Context.Database.ExecuteSqlRaw calls in GameStorage.ServerInfo.cs so you don't overwrite other MarketLimits fields; instead use MySQL's JSON_SET to update only the MesoListed and MesoPurchased keys (e.g. JSON_SET(MarketLimits, '$.MesoListed', 0, '$.MesoPurchased', 0)), keeping the rest of the JSON intact; update the ExecuteSqlRaw invocation(s) that currently set MarketLimits = '{"MesoListed":0,"MesoPurchased":0}' to use JSON_SET and ensure the character-shop-data reset remains unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@Maple2.Database/Storage/Game/GameStorage.ServerInfo.cs`:
- Around line 49-57: Newly created ServerInfo for key "WeeklyReset" never has
LastModified set, causing GetLastWeeklyReset() to observe DateTime.MinValue;
when creating the new ServerInfo (the branch where serverInfo == null), set
serverInfo.LastModified = DateTime.Now (or a sensible UTC timestamp) before
calling Context.ServerInfo.Add(serverInfo) and Context.SaveChanges(), ensuring
the stored record has a valid LastModified value; keep the existing else branch
that updates LastModified for existing records.
---
Nitpick comments:
In `@Maple2.Database/Storage/Game/GameStorage.ServerInfo.cs`:
- Around line 47-63: The WeeklyReset method currently calls
Context.SaveChanges() before running the three
Context.Database.ExecuteSqlRaw(...) statements which can leave the LastModified
marker committed while the raw SQL updates fail; change WeeklyReset to create an
explicit transaction via
Context.Database.BeginTransaction()/BeginTransactionAsync, perform the
ServerInfo creation/update (serverInfo.LastModified) and the three ExecuteSqlRaw
calls inside that transaction, call Context.SaveChanges() (or SaveChangesAsync)
while the transaction is open, then commit the transaction; ensure the
transaction is rolled back on exceptions so serverInfo.LastModified is not
persisted if any ExecuteSqlRaw fails.
- Around line 42-44: Replace the hardcoded JSON assignment in the two
Context.Database.ExecuteSqlRaw calls in GameStorage.ServerInfo.cs so you don't
overwrite other MarketLimits fields; instead use MySQL's JSON_SET to update only
the MesoListed and MesoPurchased keys (e.g. JSON_SET(MarketLimits,
'$.MesoListed', 0, '$.MesoPurchased', 0)), keeping the rest of the JSON intact;
update the ExecuteSqlRaw invocation(s) that currently set MarketLimits =
'{"MesoListed":0,"MesoPurchased":0}' to use JSON_SET and ensure the
character-shop-data reset remains unchanged.
…ops, send MesoMarketQuota packet, use Constant.ResetDay and midnight anchor
Introduce a monthly reset flow and consolidate reset behavior across servers. Added GameResetRequest monthly proto, GameStorage monthly methods (GetLastMonthlyReset, MonthlyReset) and DB updates to reset market/purchase limits and dungeon clears. WorldServer now schedules and broadcasts monthly resets; ChannelService, GameServer and GameSession handle monthly reset actions. ShopManager/Shop/ShopItem and dungeon manager now reset item StockPurchased and clear daily/weekly dungeon clears in-memory and persistently. Also filter null plots in map loading and replace separate daily/weekly CLI commands with a unified reset command supporting daily/weekly/monthly.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Maple2.Database/Storage/Game/GameStorage.ServerInfo.cs (1)
33-38:⚠️ Potential issue | 🟡 Minor
DailyReset()uses null-forgivingFind(...)!unlikeWeeklyReset/MonthlyReset.
WeeklyReset()andMonthlyReset()gracefully handle the case whereServerInfo.Find(...)returnsnullby creating the record.DailyReset()uses the null-forgiving!operator, which will throw aNullReferenceExceptionif the "DailyReset" key doesn't exist. Consider applying the same null-safe pattern for consistency and robustness.🛡️ Proposed fix
public void DailyReset() { lock (Context) { - ServerInfo serverInfo = Context.ServerInfo.Find("DailyReset")!; - serverInfo.LastModified = DateTime.Now; - Context.Update(serverInfo); + ServerInfo? serverInfo = Context.ServerInfo.Find("DailyReset"); + if (serverInfo == null) { + serverInfo = new ServerInfo { Key = "DailyReset", LastModified = DateTime.Now }; + Context.ServerInfo.Add(serverInfo); + } else { + serverInfo.LastModified = DateTime.Now; + Context.Update(serverInfo); + } Context.SaveChanges();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Maple2.Database/Storage/Game/GameStorage.ServerInfo.cs` around lines 33 - 38, DailyReset() currently uses the null-forgiving operator on Context.ServerInfo.Find("DailyReset")! which will throw if the record is missing; change it to follow the null-safe pattern used by WeeklyReset()/MonthlyReset(): call Context.ServerInfo.Find("DailyReset"), test for null, and if null create a new ServerInfo with Key = "DailyReset" and set LastModified, then either Context.Add(newServerInfo) or update the existing serverInfo.LastModified when found, and finally call Context.SaveChanges(); preserve the lock(Context) and use the same methods (Context.Update/Context.Add and Context.SaveChanges) and the ServerInfo.LastModified field to implement this.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Maple2.Database/Storage/Game/GameStorage.ServerInfo.cs`:
- Around line 23-31: The CreateServerInfo method currently adds a new ServerInfo
without initializing LastModified, which (given
ServerInfo.Configure()/ModelSnapshot lacking value generation) will persist as
DateTime.MinValue; fix by either initializing the property before saving (set
model.LastModified = DateTime.UtcNow in CreateServerInfo) or update
ServerInfo.Configure() to enable DB-side/default generation (e.g.,
ValueGeneratedOnAdd() and HasDefaultValueSql("CURRENT_TIMESTAMP") for
LastModified) and update the migration/ModelSnapshot accordingly so
GetLastDailyReset/GetLastWeeklyReset/GetLastMonthlyReset do not receive
DateTime.MinValue.
---
Outside diff comments:
In `@Maple2.Database/Storage/Game/GameStorage.ServerInfo.cs`:
- Around line 33-38: DailyReset() currently uses the null-forgiving operator on
Context.ServerInfo.Find("DailyReset")! which will throw if the record is
missing; change it to follow the null-safe pattern used by
WeeklyReset()/MonthlyReset(): call Context.ServerInfo.Find("DailyReset"), test
for null, and if null create a new ServerInfo with Key = "DailyReset" and set
LastModified, then either Context.Add(newServerInfo) or update the existing
serverInfo.LastModified when found, and finally call Context.SaveChanges();
preserve the lock(Context) and use the same methods (Context.Update/Context.Add
and Context.SaveChanges) and the ServerInfo.LastModified field to implement
this.
Summary by CodeRabbit
New Features
Chores