feat(upserver): 保存M2客户端KV迁移至本人统计写检查点

保存独立KV用户服务、固定会话存档与原业务接线,以及C3d资料缓存和C3e本人统计写。
保留原PF/AGG路线、业务公式和时序;榜单读取、好友资料、公告、匹配及首场景闸仍待完成。

验证:ServiceHost两条件各33组646断言;Unity CLI Loop编译0错误269警告。
包含相关方案、评审裁决、测试及状态文档。未操作服务器、正式账号或打包。
This commit is contained in:
Liubing\LB
2026-09-30 19:19:05 +08:00
parent 1c0cdf7a99
commit 6813da149a
511 changed files with 151858 additions and 2376 deletions

View File

@@ -0,0 +1,239 @@
using asap.core;
using cfg;
using GameCore;
using System;
using System.Collections.Generic;
using UnityEngine;
public class WelcomeBackData
{
public string lastLoginTime;
public string openTime;
public string rewardTime;
public int received;
public int eventCount;
public int offlineDays;
}
public class EventWelcomeBackManager
{
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
string DataKey_WelcomeBack = "WelcomeBack";
Tables tables;
public TbEventWelcomeBackConfig eventWelcomeBackConfig;
public WelcomeBackData welcomeBackData;
public EventWelcomeBackManager()
{
tables = GContext.container.Resolve<Tables>();
eventWelcomeBackConfig = tables.TbEventWelcomeBackConfig;
}
public WelcomeBackData LoadData()
{
try
{
string data = saveStore.Read(DataKey_WelcomeBack).ValueOrThrow();
if (data != null)
{
welcomeBackData = Newtonsoft.Json.JsonConvert.DeserializeObject<WelcomeBackData>(data) ?? throw new Newtonsoft.Json.JsonSerializationException("Null welcome-back save.");
}
else
{
welcomeBackData = new WelcomeBackData();
}
}
catch (Exception e)
{
Debug.LogError($"LoadData failed: {e}");
throw;
}
return welcomeBackData;
}
public int GetState(int index, EventWelcomeBackTimeReward timeReward)
{
if (index < welcomeBackData.received)
{
return 2;
}
// 仅用于本方法内计算,不修改 welcomeBackData,避免 GetState 产生副作用
string baseTimeStr = welcomeBackData.received < 7 ? welcomeBackData.openTime : welcomeBackData.rewardTime;
if (string.IsNullOrEmpty(baseTimeStr))
return 0;
System.DateTime curTime = ZZTimeHelper.UtcNow();
System.DateTime baseTime = System.DateTime.Parse(baseTimeStr);
return baseTime.AddMinutes(timeReward.Time) <= curTime ? 1 : 0;
}
public int CheckRedPoint()
{
var dataList = tables.TbEventWelcomeBackTimeReward.DataList;
int red = 0;
int count = dataList.Count;
if (welcomeBackData.received < 7)
{
count = Math.Min(7, dataList.Count);
}
for (int i = welcomeBackData.received; i < count; i++)
{
int state = GetState(i, dataList[i]);
if (state == 1)
{
red++;
}
else
{
break;
}
}
return red;
}
public List<int> ClaimReward()
{
var dataList = tables.TbEventWelcomeBackTimeReward.DataList;
List<int> dropID = new List<int>();
int state = 0;
int count = dataList.Count;
if (welcomeBackData.received < 7)
{
count = Math.Min(7, dataList.Count);
}
int curIndex = welcomeBackData.received;
for (int i = welcomeBackData.received; i < count; i++)
{
state = GetState(i, dataList[i]);
if (state == 1)
{
dropID.Add(dataList[i].DropID);
welcomeBackData.received = i + 1;
}
else
{
break;
}
}
if (dropID.Count > 0)
{
if (curIndex < 7 && welcomeBackData.received >= 7)
{
welcomeBackData.rewardTime = ZZTimeHelper.UtcNow().ToString();
}
GContext.container.Resolve<PlayerItemData>().AddItemByDropListLureInflation(null, dropID, null, false);
SaveData();
return dropID;
}
return null;
}
public System.DateTime GetCurrentRoundEndTime()
{
System.DateTime baseTime;
int windowSeconds;
if (welcomeBackData.received < 7)
{
baseTime = System.DateTime.Parse(welcomeBackData.openTime);
windowSeconds = eventWelcomeBackConfig.WelcomeBackTimeRewardWindow[0];
}
else
{
baseTime = System.DateTime.Parse(welcomeBackData.rewardTime);
windowSeconds = eventWelcomeBackConfig.WelcomeBackTimeRewardWindow[1];
}
return baseTime.AddSeconds(windowSeconds);
}
public EventWelcomeBackTimeReward GetCurRefreshTime()
{
if (welcomeBackData.received < tables.TbEventWelcomeBackTimeReward.DataList.Count)
{
return tables.TbEventWelcomeBackTimeReward.DataList[welcomeBackData.received];
}
return null;
}
public bool CheckWelcomeBack()
{
if (GContext.container.Resolve<PlayerData>().lv < eventWelcomeBackConfig.WelcomeBackConditionLevel)
{
return false;
}
try
{
LoadData();
if (string.IsNullOrEmpty(welcomeBackData.lastLoginTime))
{
SetLastLoginTime();
return false;
}
else
{
System.DateTime lastTime = System.DateTime.Parse(welcomeBackData.lastLoginTime);
System.DateTime curTime = ZZTimeHelper.UtcNow();
if (!string.IsNullOrEmpty(welcomeBackData.openTime))
{
System.DateTime openTime = System.DateTime.Parse(welcomeBackData.openTime);
var offlineTime = curTime - openTime;
if (offlineTime.TotalSeconds >= eventWelcomeBackConfig.WelcomeBackCD)
{
welcomeBackData.openTime = string.Empty;// 超过回归活动CD时间,重置数据
SaveData();
// 不 return,继续往下判断能否开启新一轮(依据 lastLogin 离线时间)
}
else if (curTime >= GetCurrentRoundEndTime()
|| welcomeBackData.received >= tables.TbEventWelcomeBackTimeReward.DataList.Count)
{
return false;
}
else
{
return true;
}
}
if (string.IsNullOrEmpty(welcomeBackData.openTime))
{
var offlineTime = curTime - lastTime;
if (offlineTime.TotalSeconds >= eventWelcomeBackConfig.WelcomeBackConditionTime)
{
welcomeBackData.openTime = curTime.ToString();
welcomeBackData.rewardTime = curTime.ToString();
welcomeBackData.received = 0;
welcomeBackData.eventCount++;
welcomeBackData.offlineDays = (int)offlineTime.TotalDays;
SetLastLoginTime();
GContext.container.Resolve<IFaceUIService>().AddFaceCustomUIData(new UIType("ComebackGiftPopupPanel"));
GContext.container.Resolve<IFaceUIService>().AddFaceCustomUIData(new UIType("ComebackChainsPopupPanel"));
return true;
}
}
}
}
catch (System.Exception e)
{
Debug.LogError(e);
SetLastLoginTime();
return false;
}
return false;
}
void SetLastLoginTime()
{
if (welcomeBackData == null)
{
welcomeBackData = new WelcomeBackData();
}
welcomeBackData.lastLoginTime = ZZTimeHelper.UtcNow().ToString();
SaveData();
}
void SaveData()
{
saveStore.Write(DataKey_WelcomeBack, Newtonsoft.Json.JsonConvert.SerializeObject(welcomeBackData)).RequireAccepted();
}
}
public struct ClaimComeBack
{
public int state;
}