Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AscNet.Common/MsgPack/Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,7 @@ public class NotifyTaskDataTaskDataTaskLimitIdActiveInfo
public List<Int32> NewbieRecvProgress { get; set; } = new();
public Boolean NewbieHonorReward { get; set; }
public Int32 NewbieUnlockPeriod { get; set; }
public List<Int32> WeeklyTaskActivenessProgress { get; set; } = new();
}

public NotifyTaskDataTaskData TaskData { get; set; }
Expand Down
37 changes: 24 additions & 13 deletions AscNet.GameServer/Handlers/AccountModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<int, List<int>> BuildOwnedFashionColors(Character character)
{
character.FashionColors ??= [];
Expand Down Expand Up @@ -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
Expand Down
113 changes: 113 additions & 0 deletions AscNet.GameServer/Handlers/TaskModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ public class GetActivenessRewardResponse
public List<RewardGoods> RewardGoodsList { get; set; } = new();
}

[MessagePackObject(true)]
public class GetWeeklyActivenessRewardResponse
{
public int Code { get; set; }
public List<RewardGoods> RewardGoodsList { get; set; } = new();
public List<int> WeeklyTaskActivenessProgress { get; set; } = new();
}

[MessagePackObject(true)]
public class FinishTaskRequest
{
Expand Down Expand Up @@ -139,6 +147,9 @@ internal class TaskModule
});
private static readonly Lazy<IReadOnlyDictionary<int, int>> GuildBossStageTypes = new(() =>
TableReaderV2.Parse<GuildBossStageCatalogTable>().ToDictionary(stage => stage.StageId, stage => stage.StageType));
// WeeklyTwo activeness counts finished tasks tagged Weekly in Task.tab (client XTaskManager.GetWeeklyTaskActiveness).
private static readonly Lazy<IReadOnlySet<int>> WeeklyTaggedTaskIds = new(() =>
TableReaderV2.Parse<TaskTable>().Where(task => task.Tag == 1).Select(task => task.Id).ToHashSet());

[RequestPacketHandler("DoClientTaskEventRequest")]
public static void DoClientTaskEventRequestHandler(Session session, Packet.Request packet)
Expand Down Expand Up @@ -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<int> BuildWeeklyTwoProgress(Session session)
{
EnsureMissionResets(session);
return WeeklyTwoClaimedMilestones(session);
}

private static GetWeeklyActivenessRewardResponse ClaimWeeklyTwoRewards(Session session)
{
EnsureMissionResets(session);
CurrentTaskActivenessTable? rewards = TableReaderV2.Parse<CurrentTaskActivenessTable>().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<int> 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<RewardGrant> grants = new();
foreach (int index in rewardIndexes)
{
List<RewardGoodsTable> 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<int> 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<int> WeeklyTwoClaimedMilestones(Session session)
{
CurrentTaskActivenessTable? rewards = TableReaderV2.Parse<CurrentTaskActivenessTable>().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)
{
Expand Down Expand Up @@ -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);
Expand Down
Loading