diff --git a/AscNet.Common/MsgPack/Types.cs b/AscNet.Common/MsgPack/Types.cs index 1aec1ab6..532b42e0 100644 --- a/AscNet.Common/MsgPack/Types.cs +++ b/AscNet.Common/MsgPack/Types.cs @@ -810,6 +810,7 @@ public class NotifyTaskDataTaskDataTaskLimitIdActiveInfo public List NewbieRecvProgress { get; set; } = new(); public Boolean NewbieHonorReward { get; set; } public Int32 NewbieUnlockPeriod { get; set; } + public List WeeklyTaskActivenessProgress { get; set; } = new(); } public NotifyTaskDataTaskData TaskData { get; set; } diff --git a/AscNet.GameServer/Handlers/AccountModule.cs b/AscNet.GameServer/Handlers/AccountModule.cs index efa81881..4e704bfe 100644 --- a/AscNet.GameServer/Handlers/AccountModule.cs +++ b/AscNet.GameServer/Handlers/AccountModule.cs @@ -819,6 +819,29 @@ private static NotifyLogin BuildNotifyLogin(Session session) return notifyLogin; } + // Persisted claims that the client cannot reconstruct from the task list are folded into the + // login FinishedTasks projection (e.g. weekly activeness counts the client's FinishedTasks map). + internal static NotifyTaskData BuildNotifyTaskData(Session session) + { + return new NotifyTaskData + { + TaskData = new() + { + NewbieHonorReward = session.player.MissionProgress.NewbieHonorReward, + NewbieUnlockPeriod = 7, + Course = session.stage.Course, + FinishedTasks = session.stage.FinishedTasks + .Concat(TaskModule.WeeklyTwoClaimedTaskIds(session)) + .Distinct() + .ToList(), + NewPlayerRewardRecord = session.player.MissionProgress.NewPlayerRewardRecords, + NewbieRecvProgress = session.player.MissionProgress.NewbieRewardRecords, + WeeklyTaskActivenessProgress = TaskModule.BuildWeeklyTwoProgress(session), + Tasks = TaskModule.BuildTaskData(session), + } + }; + } + private static Dictionary> BuildOwnedFashionColors(Character character) { character.FashionColors ??= []; @@ -1308,19 +1331,7 @@ static void DoLogin(Session session, bool updateLoginAccounting) PassportModule.PrepareLogin(session); BfrtModule.ReconcileTaskStages(session); - NotifyTaskData notifyTaskData = new() - { - TaskData = new() - { - NewbieHonorReward = session.player.MissionProgress.NewbieHonorReward, - NewbieUnlockPeriod = 7, - Course = session.stage.Course, - FinishedTasks = session.stage.FinishedTasks, - NewPlayerRewardRecord = session.player.MissionProgress.NewPlayerRewardRecords, - NewbieRecvProgress = session.player.MissionProgress.NewbieRewardRecords, - Tasks = TaskModule.BuildTaskData(session), - } - }; + NotifyTaskData notifyTaskData = BuildNotifyTaskData(session); NotifyGatherRewardList notifyGatherRewardList = new() { GatherRewards = session.player.GatherRewards diff --git a/AscNet.GameServer/Handlers/TaskModule.cs b/AscNet.GameServer/Handlers/TaskModule.cs index ad18984d..da4177e4 100644 --- a/AscNet.GameServer/Handlers/TaskModule.cs +++ b/AscNet.GameServer/Handlers/TaskModule.cs @@ -76,6 +76,14 @@ public class GetActivenessRewardResponse public List RewardGoodsList { get; set; } = new(); } + [MessagePackObject(true)] + public class GetWeeklyActivenessRewardResponse + { + public int Code { get; set; } + public List RewardGoodsList { get; set; } = new(); + public List WeeklyTaskActivenessProgress { get; set; } = new(); + } + [MessagePackObject(true)] public class FinishTaskRequest { @@ -139,6 +147,9 @@ internal class TaskModule }); private static readonly Lazy> GuildBossStageTypes = new(() => TableReaderV2.Parse().ToDictionary(stage => stage.StageId, stage => stage.StageType)); + // WeeklyTwo activeness counts finished tasks tagged Weekly in Task.tab (client XTaskManager.GetWeeklyTaskActiveness). + private static readonly Lazy> WeeklyTaggedTaskIds = new(() => + TableReaderV2.Parse().Where(task => task.Tag == 1).Select(task => task.Id).ToHashSet()); [RequestPacketHandler("DoClientTaskEventRequest")] public static void DoClientTaskEventRequestHandler(Session session, Packet.Request packet) @@ -408,6 +419,104 @@ public static void GetActivenessRewardRequestHandler(Session session, Packet.Req session.SendResponse(response, packet.Id); } + [RequestPacketHandler("GetWeeklyActivenessRewardRequest")] + public static void GetWeeklyActivenessRewardRequestHandler(Session session, Packet.Request packet) + { + session.SendResponse(ClaimWeeklyTwoRewards(session), packet.Id); + } + + internal static List BuildWeeklyTwoProgress(Session session) + { + EnsureMissionResets(session); + return WeeklyTwoClaimedMilestones(session); + } + + private static GetWeeklyActivenessRewardResponse ClaimWeeklyTwoRewards(Session session) + { + EnsureMissionResets(session); + CurrentTaskActivenessTable? rewards = TableReaderV2.Parse().FirstOrDefault(x => x.Type == 5); + if (rewards is null) + { + return new GetWeeklyActivenessRewardResponse { Code = 20026010 }; + } + + long week = session.player.MissionProgress.WeeklyResetWeek; + int activeness = WeeklyTwoActiveness(session); + List rewardIndexes = rewards.Activeness + .Select((milestone, index) => (milestone, index)) + .Where(entry => entry.milestone <= activeness && !WeeklyTwoClaimed(session, week, entry.milestone)) + .Select(entry => entry.index) + .ToList(); + if (rewardIndexes.Count == 0) + { + bool hasReachedMilestone = rewards.Activeness.Any(milestone => milestone <= activeness); + return new GetWeeklyActivenessRewardResponse { Code = hasReachedMilestone ? 20026012 : 20026010 }; + } + if (rewardIndexes.Any(index => index >= rewards.RewardId.Count)) + { + return new GetWeeklyActivenessRewardResponse { Code = 20026010 }; + } + + List grants = new(); + foreach (int index in rewardIndexes) + { + List goods = RewardHandler.GetRewardGoods(rewards.RewardId[index]); + if (goods.Count == 0) + { + return new GetWeeklyActivenessRewardResponse { Code = 20026010 }; + } + grants.Add(new RewardGrant(WeeklyTwoClaimKey(week, rewards.Activeness[index]), goods)); + } + + RewardApplicationResult application; + try + { + application = RewardHandler.ApplyRewardsOnceAndPersist(grants, session); + } + catch (Exception exception) + { + session.log.Error($"Failed to persist weekly activeness rewards: {exception}"); + return new GetWeeklyActivenessRewardResponse { Code = 20026003 }; + } + application.SendPushes(session); + return new GetWeeklyActivenessRewardResponse + { + Code = 0, + RewardGoodsList = application.RewardGoods, + WeeklyTaskActivenessProgress = WeeklyTwoClaimedMilestones(session) + }; + } + + private static int WeeklyTwoActiveness(Session session) => WeeklyTwoClaimedTaskIds(session).Count(); + + // The client's XTaskManager.GetWeeklyTaskActiveness counts finished tasks tagged Weekly; the login + // projection and the granted milestone must derive from this same set so they cannot diverge. + internal static IEnumerable WeeklyTwoClaimedTaskIds(Session session) => + session.player.MissionProgress.ClaimedTaskIds + .Concat(session.stage.FinishedTasks) + .Distinct() + .Where(WeeklyTaggedTaskIds.Value.Contains); + + private static void ResetWeeklyTagClaims(Session session) => + session.player.MissionProgress.ClaimedTaskIds.RemoveAll(WeeklyTaggedTaskIds.Value.Contains); + + private static string WeeklyTwoClaimKey(long week, int milestone) => $"weekly-two:{week}:{milestone}"; + + private static bool WeeklyTwoClaimed(Session session, long week, int milestone) => + session.inventory.AppliedRewardClaims.Contains(WeeklyTwoClaimKey(week, milestone), StringComparer.Ordinal) + || session.character.AppliedRewardClaims.Contains(WeeklyTwoClaimKey(week, milestone), StringComparer.Ordinal); + + private static List WeeklyTwoClaimedMilestones(Session session) + { + CurrentTaskActivenessTable? rewards = TableReaderV2.Parse().FirstOrDefault(x => x.Type == 5); + if (rewards is null) + { + return []; + } + long week = session.player.MissionProgress.WeeklyResetWeek; + return rewards.Activeness.Where(milestone => WeeklyTwoClaimed(session, week, milestone)).ToList(); + } + [RequestPacketHandler("GetNewbieRewardRequest")] public static void GetNewbieRewardRequestHandler(Session session, Packet.Request packet) { @@ -2337,6 +2446,10 @@ internal static void EnsureMissionResets(Session session) else if (session.player.MissionProgress.WeeklyResetWeek != week) { ResetMissionType(session, 3); + // Weekly-tagged tasks outside CurrentTask (GuildWeekly Type 23) are not covered by + // ResetMissionType; clear their claims too so the new week cannot inherit prior completions. + // Shared condition counters (e.g. 35010, also used by the Type 6 Thursday task) are preserved. + ResetWeeklyTagClaims(session); ResetPassportTaskType(session, 2); session.player.PlayerData.WeeklyActivenessRewardStatus = 0; Item? weeklyActiveness = session.inventory.Items.FirstOrDefault(item => item.Id == Inventory.WeeklyActiveness); diff --git a/AscNet.Test/Program.cs b/AscNet.Test/Program.cs index 30a06105..d49837da 100644 --- a/AscNet.Test/Program.cs +++ b/AscNet.Test/Program.cs @@ -474,6 +474,13 @@ static void Main(string[] args) ValidateLoginAccountCompatibility(); return; } + if (args.Contains("--weekly-activeness-compat-only")) + { + ValidateWeeklyActivenessRewardCompatibility(); + ValidateWeeklyActivenessLoginProjectionCompatibility(); + ValidateWeeklyActivenessRolloverCompatibility(); + return; + } if (args.Contains("--gate-login-compat-only")) { ValidateGateLoginCompatibility().GetAwaiter().GetResult(); @@ -4219,6 +4226,9 @@ private static void ValidateLoginAccountCompatibility() ValidateNewPlayerRewardCompatibility(); ValidateNewbieRewardCompatibility(); ValidateActivenessRewardCompatibility(); + ValidateWeeklyActivenessRewardCompatibility(); + ValidateWeeklyActivenessLoginProjectionCompatibility(); + ValidateWeeklyActivenessRolloverCompatibility(); ValidateAchievementRewardCompatibility(); ValidateLoginStartupPushOrder(); ValidateReconnectAckClientPushNoReplayStabilityCompatibility(); @@ -4877,6 +4887,255 @@ private static void ValidateActivenessRewardCompatibility() AssertEqual(0, duplicateResponse.RewardGoodsList.Count, "GetActivenessRewardResponse duplicate reward count"); } + private static void ValidateWeeklyActivenessRewardCompatibility() + { + using MongoCollectionOverride mongoOverride = MongoCollectionOverride.InstallForWeeklyActivenessCompatibility(); + const long playerId = 88_045; + AscNet.Common.Database.Player player = CreateDrawCompatibilityPlayer(playerId); + List taggedTaskIds = TableReaderV2.Parse() + .Where(task => task.Tag == 1) + .Select(task => task.Id) + .ToList(); + if (taggedTaskIds.Count < 10) + throw new InvalidDataException($"Weekly-two activeness test requires at least 10 Tag=1 tasks, found {taggedTaskIds.Count}."); + AscNet.Common.Database.Inventory inventory = CreateDrawCompatibilityInventory(playerId, []); + using LoopbackSessionHarness harness = new( + CreateDrawCompatibilityCharacter(playerId), + player, + inventory, + "weekly-activeness-reward-compat-test"); + harness.Session.stage = new AscNet.Common.Database.Stage + { + Uid = playerId, + Stages = new(), + Course = new(), + FinishedTasks = new() + }; + + RequestPacketHandlerDelegate handler = GetRegisteredRequestHandler("GetWeeklyActivenessRewardRequest"); + + player.MissionProgress.ClaimedTaskIds = taggedTaskIds.Take(4).ToList(); + const int belowPacketId = 13_210; + handler.Invoke(harness.Session, new Packet.Request + { + Id = belowPacketId, + Name = "GetWeeklyActivenessRewardRequest", + Content = [0xC0] + }); + GetWeeklyActivenessRewardResponse belowResponse = ReadResponsePayload( + harness, + belowPacketId, + nameof(GetWeeklyActivenessRewardResponse), + "GetWeeklyActivenessRewardRequest below-milestone response", + maxPacketsToRead: 16); + AssertEqual(20026010, belowResponse.Code, "GetWeeklyActivenessRewardResponse below-milestone Code"); + AssertEqual(0, belowResponse.RewardGoodsList.Count, "GetWeeklyActivenessRewardResponse below-milestone reward count"); + + long week = player.MissionProgress.WeeklyResetWeek; + player.MissionProgress.ClaimedTaskIds = taggedTaskIds.Take(5).ToList(); + const int firstPacketId = 13_211; + handler.Invoke(harness.Session, new Packet.Request + { + Id = firstPacketId, + Name = "GetWeeklyActivenessRewardRequest", + Content = [0xC0] + }); + GetWeeklyActivenessRewardResponse firstResponse = ReadResponsePayload( + harness, + firstPacketId, + nameof(GetWeeklyActivenessRewardResponse), + "GetWeeklyActivenessRewardRequest first-milestone response", + maxPacketsToRead: 16); + AssertEqual(0, firstResponse.Code, "GetWeeklyActivenessRewardResponse first-milestone Code"); + AssertEqual(1, firstResponse.RewardGoodsList.Count, "GetWeeklyActivenessRewardResponse first-milestone reward count"); + AssertIntegerList([5], firstResponse.WeeklyTaskActivenessProgress.Select(value => (long)value).ToArray(), + "GetWeeklyActivenessRewardResponse first-milestone progress"); + AssertEqual(true, inventory.AppliedRewardClaims.Contains($"weekly-two:{week}:5", StringComparer.Ordinal), + "GetWeeklyActivenessRewardRequest persisted first claim key"); + + player.MissionProgress.ClaimedTaskIds = taggedTaskIds.Take(10).ToList(); + const int secondPacketId = 13_212; + handler.Invoke(harness.Session, new Packet.Request + { + Id = secondPacketId, + Name = "GetWeeklyActivenessRewardRequest", + Content = [0xC0] + }); + GetWeeklyActivenessRewardResponse secondResponse = ReadResponsePayload( + harness, + secondPacketId, + nameof(GetWeeklyActivenessRewardResponse), + "GetWeeklyActivenessRewardRequest second-milestone response", + maxPacketsToRead: 16); + AssertEqual(0, secondResponse.Code, "GetWeeklyActivenessRewardResponse second-milestone Code"); + AssertEqual(1, secondResponse.RewardGoodsList.Count, "GetWeeklyActivenessRewardResponse second-milestone reward count"); + AssertIntegerList([5, 10], secondResponse.WeeklyTaskActivenessProgress.Select(value => (long)value).ToArray(), + "GetWeeklyActivenessRewardResponse second-milestone progress"); + + const int duplicatePacketId = 13_213; + handler.Invoke(harness.Session, new Packet.Request + { + Id = duplicatePacketId, + Name = "GetWeeklyActivenessRewardRequest", + Content = [0xC0] + }); + GetWeeklyActivenessRewardResponse duplicateResponse = ReadResponsePayload( + harness, + duplicatePacketId, + nameof(GetWeeklyActivenessRewardResponse), + "GetWeeklyActivenessRewardRequest duplicate response"); + AssertEqual(20026012, duplicateResponse.Code, "GetWeeklyActivenessRewardResponse duplicate Code"); + AssertEqual(0, duplicateResponse.RewardGoodsList.Count, "GetWeeklyActivenessRewardResponse duplicate reward count"); + } + + // Client XTaskManager.GetWeeklyTaskActiveness counts the login FinishedTasks map, so persisted + // weekly claims must survive a BSON relog before the client can issue the claim RPC. + private static void ValidateWeeklyActivenessLoginProjectionCompatibility() + { + using MongoCollectionOverride mongoOverride = MongoCollectionOverride.InstallForWeeklyActivenessCompatibility(); + const long playerId = 88_046; + AscNet.Common.Database.Player player = CreateDrawCompatibilityPlayer(playerId); + using LoopbackSessionHarness harness = new( + CreateDrawCompatibilityCharacter(playerId), + player, + CreateDrawCompatibilityInventory(playerId, []), + "weekly-activeness-login-projection-compat-test"); + harness.Session.stage = new AscNet.Common.Database.Stage + { + Uid = playerId, + Stages = new(), + Course = new(), + FinishedTasks = new() + }; + + HashSet taggedTaskIds = TableReaderV2.Parse() + .Where(task => task.Tag == 1) + .Select(task => task.Id) + .ToHashSet(); + int[] claimedTaskIds = [30039, 30040, 30041, 30042]; + int packetId = 13_220; + foreach (int taskId in claimedTaskIds) + { + CurrentTaskTable task = TableReaderV2.Parse().Single(row => row.Id == taskId); + player.MissionProgress.ConditionCounters[task.Condition] = task.Result; + InvokeRegisteredRequestHandler(nameof(FinishTaskRequest), harness.Session, packetId, + new FinishTaskRequest { TaskId = taskId }); + FinishTaskResponse claimResponse = ReadResponsePayload( + harness, packetId, nameof(FinishTaskResponse), + $"Weekly activeness FinishTaskRequest {taskId} response", maxPacketsToRead: 32); + AssertEqual(0, claimResponse.Code, $"Weekly activeness FinishTaskRequest {taskId} Code"); + AssertEqual(true, player.MissionProgress.ClaimedTaskIds.Contains(taskId), + $"Weekly activeness FinishTaskRequest {taskId} claim persisted"); + packetId++; + } + + AscNet.Common.Database.Player reloaded = + MongoDB.Bson.Serialization.BsonSerializer.Deserialize(player.ToBson()); + using LoopbackSessionHarness relogHarness = new( + CreateDrawCompatibilityCharacter(playerId), + reloaded, + CreateDrawCompatibilityInventory(playerId, []), + "weekly-activeness-login-projection-relog-test"); + relogHarness.Session.stage = new AscNet.Common.Database.Stage + { + Uid = playerId, + Stages = new(), + Course = new(), + FinishedTasks = new() + }; + + Type accountModule = RequiredAscNetGameServerType("AscNet.GameServer.Handlers.AccountModule"); + NotifyTaskData relogTaskData = InvokePrivateStatic( + accountModule, "BuildNotifyTaskData", relogHarness.Session); + List finishedTasks = relogTaskData.TaskData.FinishedTasks; + foreach (int taskId in claimedTaskIds) + { + AssertEqual(true, finishedTasks.Contains(taskId), + $"Weekly activeness login projection contains claimed task {taskId}"); + } + AssertEqual( + reloaded.MissionProgress.ClaimedTaskIds.Count(id => taggedTaskIds.Contains(id)), + finishedTasks.Count(id => taggedTaskIds.Contains(id)), + "Weekly activeness login projection count matches server activeness"); + + CurrentTaskTable fifthTask = TableReaderV2.Parse().Single(row => row.Id == 30043); + reloaded.MissionProgress.ConditionCounters[fifthTask.Condition] = fifthTask.Result; + const int fifthPacketId = 13_230; + InvokeRegisteredRequestHandler(nameof(FinishTaskRequest), relogHarness.Session, fifthPacketId, + new FinishTaskRequest { TaskId = 30043 }); + FinishTaskResponse fifthClaim = ReadResponsePayload( + relogHarness, fifthPacketId, nameof(FinishTaskResponse), + "Weekly activeness relog further claim response", maxPacketsToRead: 32); + AssertEqual(0, fifthClaim.Code, "Weekly activeness relog further claim Code"); + + const int activenessPacketId = 13_231; + InvokeRegisteredRequestHandler("GetWeeklyActivenessRewardRequest", relogHarness.Session, activenessPacketId, null); + GetWeeklyActivenessRewardResponse activenessResponse = ReadResponsePayload( + relogHarness, activenessPacketId, nameof(GetWeeklyActivenessRewardResponse), + "Weekly activeness relog reward response", maxPacketsToRead: 16); + AssertEqual(0, activenessResponse.Code, "Weekly activeness relog reward Code"); + AssertIntegerList([5], activenessResponse.WeeklyTaskActivenessProgress.Select(value => (long)value).ToArray(), + "Weekly activeness relog reward progress"); + } + + // Prior-week GuildWeekly (Type 23) claims are not in CurrentTask, so the weekly reset must clear + // them explicitly without disturbing unrelated lifetime tasks that share condition counters. + private static void ValidateWeeklyActivenessRolloverCompatibility() + { + using MongoCollectionOverride mongoOverride = MongoCollectionOverride.InstallForWeeklyActivenessCompatibility(); + const long playerId = 88_047; + AscNet.Common.Database.Player player = CreateDrawCompatibilityPlayer(playerId); + using LoopbackSessionHarness harness = new( + CreateDrawCompatibilityCharacter(playerId), + player, + CreateDrawCompatibilityInventory(playerId, []), + "weekly-activeness-rollover-compat-test"); + harness.Session.stage = new AscNet.Common.Database.Stage + { + Uid = playerId, + Stages = new(), + Course = new(), + FinishedTasks = new() + }; + + Type taskModule = RequiredAscNetGameServerType("AscNet.GameServer.Handlers.TaskModule"); + long now = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + player.MissionProgress.DailyResetDay = InvokePrivateStatic(taskModule, "CurrentDailyResetPeriod", now); + player.MissionProgress.WeeklyResetWeek = InvokePrivateStatic(taskModule, "CurrentWeeklyResetPeriod", now) - 1; + + player.MissionProgress.ClaimedTaskIds = [30039, 3001728, 35010]; + const int rolloverPacketId = 13_240; + InvokeRegisteredRequestHandler("GetWeeklyActivenessRewardRequest", harness.Session, rolloverPacketId, null); + GetWeeklyActivenessRewardResponse rolloverResponse = ReadResponsePayload( + harness, rolloverPacketId, nameof(GetWeeklyActivenessRewardResponse), + "Weekly activeness rollover response", maxPacketsToRead: 8); + AssertEqual(20026010, rolloverResponse.Code, "Weekly activeness rollover below-milestone Code"); + AssertEqual(false, player.MissionProgress.ClaimedTaskIds.Contains(30039), + "Weekly activeness rollover clears CurrentTask weekly claim"); + AssertEqual(false, player.MissionProgress.ClaimedTaskIds.Contains(3001728), + "Weekly activeness rollover clears prior-week GuildWeekly claim"); + AssertEqual(true, player.MissionProgress.ClaimedTaskIds.Contains(35010), + "Weekly activeness rollover preserves unrelated lifetime task claim"); + + player.MissionProgress.ClaimedTaskIds = [30039, 30040, 30041, 30042]; + const int belowPacketId = 13_241; + InvokeRegisteredRequestHandler("GetWeeklyActivenessRewardRequest", harness.Session, belowPacketId, null); + GetWeeklyActivenessRewardResponse belowResponse = ReadResponsePayload( + harness, belowPacketId, nameof(GetWeeklyActivenessRewardResponse), + "Weekly activeness rollover four-task response", maxPacketsToRead: 8); + AssertEqual(20026010, belowResponse.Code, "Weekly activeness rollover four tasks below milestone"); + + player.MissionProgress.ClaimedTaskIds = [30039, 30040, 30041, 30042, 30043]; + const int thresholdPacketId = 13_242; + InvokeRegisteredRequestHandler("GetWeeklyActivenessRewardRequest", harness.Session, thresholdPacketId, null); + GetWeeklyActivenessRewardResponse thresholdResponse = ReadResponsePayload( + harness, thresholdPacketId, nameof(GetWeeklyActivenessRewardResponse), + "Weekly activeness rollover threshold response", maxPacketsToRead: 16); + AssertEqual(0, thresholdResponse.Code, "Weekly activeness rollover threshold Code"); + AssertIntegerList([5], thresholdResponse.WeeklyTaskActivenessProgress.Select(value => (long)value).ToArray(), + "Weekly activeness rollover threshold progress"); + } + private static void ValidateAchievementRewardCompatibility() { using MongoCollectionOverride mongoOverride = MongoCollectionOverride.InstallForShopCompatibility(); @@ -17564,6 +17823,24 @@ public static MongoCollectionOverride InstallForShopCompatibility() ]); } + // Guild membership and guild-war task lookups read Guild.collection during FinishTaskRequest and + // login task projection; return an empty result set so the weekly regressions stay DB-isolated. + public static MongoCollectionOverride InstallForWeeklyActivenessCompatibility() + { + IMongoCollection guildCollection = + CreateRecordingMongoCollection( + out RecordingMongoCollectionProxy guildRecorder); + guildRecorder.FindResults = []; + return new MongoCollectionOverride( + [ + (RequiredCollectionField(typeof(AscNet.Common.Database.Inventory)), CreateNoOpMongoCollection()), + (RequiredCollectionField(typeof(AscNet.Common.Database.Character)), CreateNoOpMongoCollection()), + (RequiredCollectionField(typeof(AscNet.Common.Database.Player)), CreateNoOpMongoCollection()), + (RequiredCollectionField(typeof(AscNet.Common.Database.Stage)), CreateNoOpMongoCollection()), + (RequiredCollectionField(typeof(AscNet.Common.Database.Guild)), guildCollection) + ]); + } + public static MongoCollectionOverride InstallForBossCompatibility( out RecordingMongoCollectionProxy playerCollection, out RecordingMongoCollectionProxy stageCollection)