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,247 @@
using asap.core;
using GameCore;
using cfg;
using UnityEngine;
using System.Collections.Generic;
using System;
using game;
using System.Linq;
public class PiggyBankPackData
{
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore playerSaveStore { get; set; }
private readonly Tables _tables = GContext.container.Resolve<Tables>();
private readonly TbEventPackManager _epm
= GContext.container.Resolve<Tables>().TbEventPackManager;
private struct Data
{
public int currentEventID;
public int purchaseCount;
public int VIPLvlWhenEventActivate;
public bool doNeedTrigger;
public bool isTriggered;
public int savingProgress;
public int redirectID;
public float inflation;
public Data(int eventID = 0, int v = 0, bool b = false, int rid = 0, float inflation = 0)
{
currentEventID = eventID;
purchaseCount = 0;
VIPLvlWhenEventActivate = v;
doNeedTrigger = b;
savingProgress = 0;
isTriggered = false;
redirectID = rid;
this.inflation = inflation;
}
}
private Data _data;
private bool _kvLoadPresent;
internal bool KVLoadPresent => _kvLoadPresent;
internal static game.PlayerDataStorage.PlayerKVPiggyBankState DecodeForKVLoad(
IReadOnlyDictionary<string, string> raw, game.PlayerDataStorage.PlayerKVSchemaSnapshot snapshot,
game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
game.PlayerDataStorage.PlayerKVSaveSchema.DecodePiggyBank(raw, snapshot, errors);
internal void ResetForKVLoad()
{
_kvLoadPresent = false;
_data = default(Data);
}
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVPiggyBankState state)
{
if (state == null) throw new ArgumentNullException(nameof(state));
if (!state.IsPresent) { ResetForKVLoad(); return; }
var replacement = new Data
{
currentEventID = state.currentEventID,
purchaseCount = state.purchaseCount,
VIPLvlWhenEventActivate = state.VIPLvlWhenEventActivate,
savingProgress = state.savingProgress,
redirectID = state.redirectID,
doNeedTrigger = state.doNeedTrigger,
isTriggered = state.isTriggered,
inflation = state.inflation,
};
_data = replacement;
_kvLoadPresent = true;
}
internal game.PlayerDataStorage.PlayerKVPiggyBankState SnapshotForKVValidation() =>
new game.PlayerDataStorage.PlayerKVPiggyBankState(_kvLoadPresent, _data.currentEventID, _data.purchaseCount, _data.VIPLvlWhenEventActivate, _data.savingProgress, _data.redirectID, _data.doNeedTrigger, _data.isTriggered, _data.inflation);
public bool IsWithinEventTime
{
get
{
var fishingEvent = _tables.TbFishingEvent.GetOrDefault(_data.currentEventID);
if (!(fishingEvent?.TimeDefinition is LimitedTime time)) return false;
var et = DateTime.Parse(time.EndTime);
var st = DateTime.Parse(time.StartTime);
return ZZTimeHelper.UtcNow() >= st && ZZTimeHelper.UtcNow() < et;
}
}
public bool IsPackActivated { get => _data.isTriggered && _tables.TbFishingEvent.DataMap.Keys.Contains(_data.currentEventID) && IsWithinEventTime && _data.purchaseCount < _epm[RedirectID].MaxCount; }
public bool DoNeedUpdate { get => _data.doNeedTrigger; set => _data.doNeedTrigger = value; }
public int CurrentEventID { get => _data.currentEventID; }
public int PurchaseCount { get => _data.purchaseCount; }
public int VIPLvlWhenEventActivated { get => _data.VIPLvlWhenEventActivate; }
public int Progress { get => _data.savingProgress; }
public float Inflation { get => _data.inflation; }
public bool IsFull { get => _data.savingProgress >= Target; }
public int ActivateItemId
{
get => _tables.TbSpecialPack[_epm[RedirectID]
.VIPPackList[_data.VIPLvlWhenEventActivate][0]]
.ActivateItemId;
}
public string ActiveItemImg
{
get => _tables.TbSpecialPack[_epm[RedirectID]
.VIPPackList[_data.VIPLvlWhenEventActivate][0]].ActiveItemImg;
}
public int EventItemId
{
get
{
SpecialPack specialPack = GetSpecialPack();
if (specialPack != null)
{
return specialPack.EventItemId;
}
return 0;
}
}
SpecialPack GetSpecialPack()
{
var TbEventPackManager = _tables.TbEventPackManager.GetOrDefault(_data.redirectID);
if (TbEventPackManager == null)
{
return null;
}
return _tables.TbSpecialPack.GetOrDefault(TbEventPackManager.VIPPackList[_data.VIPLvlWhenEventActivate][0]);
}
public int Target
{
get
{
int count = _tables.TbSpecialPack[_epm[RedirectID]
.VIPPackList[_data.VIPLvlWhenEventActivate][0]]
.EventItemRequire[0];
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
count = playerItemData.IntInflation(count, _data.inflation);
return count;
}
}
public int CanBuyCount
{
get
{
int count = _tables.TbSpecialPack[_epm[RedirectID]
.VIPPackList[_data.VIPLvlWhenEventActivate][0]]
.CanBuyCount;
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
count = playerItemData.IntInflation(count, _data.inflation);
return count;
}
}
//public int RedirectID { get => _tables.TbFishingEvent[_data.currentEventID].RedirectID; }
public int RedirectID { get => _data.redirectID; }
public int RewardDropID
{
get => _tables.TbSpecialPack[_epm[RedirectID]
.VIPPackList[_data.VIPLvlWhenEventActivate][0]]
.DropID[0];
}
public List<int> FishingScoreList
{
get => _tables.TbSpecialPack[_epm[RedirectID].VIPPackList[_data.VIPLvlWhenEventActivate][0]]
.EventItemGet;
}
public TimeSpan RemainingTime
{
get
{
return DateTime.Parse((_tables.TbFishingEvent[_data.currentEventID].TimeDefinition as LimitedTime).EndTime)
- ZZTimeHelper.UtcNow();
}
}
public int MaxCount { get => _epm[RedirectID].MaxCount; }
public void LoadData(string s)
{
_data = Newtonsoft.Json.JsonConvert.DeserializeObject<Data>(s);
//_data.redirectID = 0;
if (_data.redirectID == 0)
{
_data.redirectID = 4055001;
}
}
public void UpdateData(cfg.FishingEvent e)
{
if (_epm[_tables.TbFishingEvent[e.ID].RedirectID].PackType != 2)
{
Debug.LogError($"Fishing event ID {e.ID} is not a piggyBank event.");
return;
}
if (_data.currentEventID != e.ID)
_data = new Data(e.ID, GContext.container.Resolve<PlayerData>().PriceLv, true, e.RedirectID, GContext.container.Resolve<PlayerData>().InflationRate);
if (_data.redirectID != e.RedirectID)
{
_data.redirectID = e.RedirectID;
Debug.Log("working!!!!!!!!!!");
}
SaveData();
}
public void SaveData()
{
playerSaveStore.Write("PiggyBankPackData",
Newtonsoft.Json.JsonConvert.SerializeObject(_data)).RequireAccepted();
}
public bool AddProgress(int fishTier, int magnification)
{
if (!IsPackActivated || fishTier < 1 || fishTier > 5)
{
//Debug.LogError($"Fish Tier {fishTier} not defined in TbSpedialPack");
return false;
}
var TbSpecialPack = _tables.TbSpecialPack[_epm[RedirectID].VIPPackList[_data.VIPLvlWhenEventActivate][0]];
int progress = TbSpecialPack.EventItemGet[fishTier - 1] * magnification;
if (progress > 0 && _data.savingProgress < Target)
{
int curProgress = _data.savingProgress;
_data.savingProgress += progress;
GContext.Publish(new TargetAddData(TbSpecialPack.EventItemId, curProgress, progress));
if (_data.savingProgress >= Target)
{
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIForce(_data.currentEventID, UITypes.GiftPiggyBankPopupPanel);
_data.savingProgress = Target;
SaveData();
return true;
}
SaveData();
}
return false;
}
public void AddPurchase()
{
_data.purchaseCount++;
SaveData();
}
public void TriggerPack()
{
DoNeedUpdate = false;
_data.isTriggered = true;
int EventItemCountFirst = _tables.TbSpecialPack[_epm[RedirectID]
.VIPPackList[_data.VIPLvlWhenEventActivate][0]].EventItemCountFirst;
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
EventItemCountFirst = playerItemData.IntInflation(EventItemCountFirst, _data.inflation);
_data.savingProgress += EventItemCountFirst;
SaveData();
}
}
public class PiggyBankProgressEvent
{
public int type;//0: 初始化 1:增加动画
public int addProgress;
}