diff --git a/Common/Data/Excel/BattlePassTimeExcel.cs b/Common/Data/Excel/BattlePassTimeExcel.cs new file mode 100644 index 0000000..1089ef5 --- /dev/null +++ b/Common/Data/Excel/BattlePassTimeExcel.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; + +namespace MikuSB.Data.Excel; + +[ResourceEntity("battlepass/timelist.json")] +public class BattlePassTimeExcel : ExcelResource +{ + [JsonProperty("ID")] public uint Id { get; set; } + [JsonProperty("StartTime")] public string StartTime { get; set; } = ""; + [JsonProperty("EndTime")] public string EndTime { get; set; } = ""; + [JsonProperty("BuyStartTime")] public string BuyStartTime { get; set; } = ""; + [JsonProperty("BuyEndTime")] public string BuyEndTime { get; set; } = ""; + [JsonProperty("Condition")] public string Condition { get; set; } = ""; + [JsonProperty("ExpStep")] public uint ExpStep { get; set; } + [JsonProperty("MaxExPerWeek")] public uint MaxExPerWeek { get; set; } + + public override uint GetId() => Id; + + public override void Loaded() + { + GameData.BattlePassTimeData[Id] = this; + } +} diff --git a/Common/Data/GameData.cs b/Common/Data/GameData.cs index 52f6c93..64b5a43 100644 --- a/Common/Data/GameData.cs +++ b/Common/Data/GameData.cs @@ -60,6 +60,7 @@ public static class GameData public static Dictionary VirCaptureTowerData { get; private set; } = []; public static Dictionary DreamCardActivityData { get; private set; } = []; public static Dictionary DlcActivityData { get; private set; } = []; + public static Dictionary BattlePassTimeData { get; private set; } = []; } public static class GameResourceTemplateId diff --git a/GameServer/Server/CallGS/Handlers/BattlePass/BattlePassLogic_ClientRefresh.cs b/GameServer/Server/CallGS/Handlers/BattlePass/BattlePassLogic_ClientRefresh.cs new file mode 100644 index 0000000..46d6f1f --- /dev/null +++ b/GameServer/Server/CallGS/Handlers/BattlePass/BattlePassLogic_ClientRefresh.cs @@ -0,0 +1,113 @@ +using MikuSB.Data; +using MikuSB.Data.Excel; +using MikuSB.Database.Player; +using MikuSB.GameServer.Game.Player; +using MikuSB.Proto; +using System.Globalization; +using System.Text.Json.Nodes; + +namespace MikuSB.GameServer.Server.CallGS.Handlers.BattlePass; + +[CallGSApi("BattlePassLogic_ClientRefresh")] +public class BattlePassLogic_ClientRefresh : ICallGSHandler +{ + private const uint GroupId = 25; + private const uint CurIdSid = 1; + + public async Task Handle(Connection connection, string param, ushort seqNo) + { + var now = DateTime.Now; + var battlePass = ResolveCurrent(GameData.BattlePassTimeData.Values, now); + var player = connection.Player!; + var sync = new NtfSyncPlayer(); + + if (battlePass == null) + { + SetAttr(player, CurIdSid, 0, sync); + await CallGSRouter.SendScript(connection, "BattlePassLogic_ClientRefresh", "{}", sync); + return; + } + + SetAttr(player, CurIdSid, battlePass.Id, sync); + + var response = new JsonObject + { + ["nId"] = battlePass.Id, + ["nStartTime"] = ToUnixSeconds(ParseConfigTime(battlePass.StartTime)), + ["nEndTime"] = ToUnixSeconds(ParseConfigTime(battlePass.EndTime)) + }; + + await CallGSRouter.SendScript(connection, "BattlePassLogic_ClientRefresh", response.ToJsonString(), sync); + } + + private static BattlePassTimeExcel? ResolveCurrent(IEnumerable configs, DateTime now) + { + var parsed = configs + .Select(x => new + { + Config = x, + Start = ParseConfigTime(x.StartTime), + End = ParseConfigTime(x.EndTime) + }) + .Where(x => x.Start.HasValue && x.End.HasValue) + .OrderBy(x => x.Start) + .ToList(); + + var current = parsed.FirstOrDefault(x => x.Start <= now && now < x.End); + if (current != null) + return current.Config; + + var latestStarted = parsed.LastOrDefault(x => x.Start <= now && x.End > x.Start); + return latestStarted?.Config; + } + + private static DateTime? ParseConfigTime(string? raw) + { + if (string.IsNullOrWhiteSpace(raw)) + return null; + + var normalized = raw.Trim().Trim('[', ']'); + if (normalized.Length != 12) + return null; + + return DateTime.TryParseExact( + normalized, + "yyyyMMddHHmm", + CultureInfo.InvariantCulture, + DateTimeStyles.None, + out var value) + ? value + : null; + } + + private static long ToUnixSeconds(DateTime? value) + { + return value.HasValue ? new DateTimeOffset(value.Value).ToUnixTimeSeconds() : 0L; + } + + private static void SetAttr(PlayerInstance player, uint sid, uint value, NtfSyncPlayer sync) + { + var attr = GetOrCreateAttr(player, sid); + if (attr.Val != value) + { + attr.Val = value; + sync.Custom[player.ToPackedAttrKey(GroupId, sid)] = value; + sync.Custom[player.ToShiftedAttrKey(GroupId, sid)] = value; + } + } + + private static PlayerAttr GetOrCreateAttr(PlayerInstance player, uint sid) + { + var attr = player.Data.Attrs.FirstOrDefault(x => x.Gid == GroupId && x.Sid == sid); + if (attr != null) + return attr; + + attr = new PlayerAttr + { + Gid = GroupId, + Sid = sid + }; + player.Data.Attrs.Add(attr); + return attr; + } +} diff --git a/GameServer/Server/CallGS/Handlers/Activity/DLCLogic_CheckOpenAct.cs b/GameServer/Server/CallGS/Handlers/DLC/DLCLogic_CheckOpenAct.cs similarity index 98% rename from GameServer/Server/CallGS/Handlers/Activity/DLCLogic_CheckOpenAct.cs rename to GameServer/Server/CallGS/Handlers/DLC/DLCLogic_CheckOpenAct.cs index 0e59d46..b5b7958 100644 --- a/GameServer/Server/CallGS/Handlers/Activity/DLCLogic_CheckOpenAct.cs +++ b/GameServer/Server/CallGS/Handlers/DLC/DLCLogic_CheckOpenAct.cs @@ -6,7 +6,7 @@ using MikuSB.Proto; using System.Globalization; using System.Text.Json.Nodes; -namespace MikuSB.GameServer.Server.CallGS.Handlers.Activity; +namespace MikuSB.GameServer.Server.CallGS.Handlers.DLC; [CallGSApi("DLCLogic_CheckOpenAct")] public class DLCLogic_CheckOpenAct : ICallGSHandler