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,146 @@
using System;
using asap.core;
using cfg;
using GameCore;
using Newtonsoft.Json;
using UniRx;
using UnityEngine;
namespace game
{
public abstract class AEventDataManager<T>:IDisposable where T : class, IEventData, new()
{
protected AEventDataManager()
{
Disposables=new CompositeDisposable();
GContext.OnEvent<TargetEvent>().Subscribe(OnEvent_TargetEvent).AddTo(Disposables);
}
private Tables _tables;
[Inject] public PlayerDataStorage.IPlayerSaveStore SaveStore { get; set; }
bool disposed;
[Inject]
public Tables Tables
{
get=>_tables??=GContext.container.Resolve<Tables>();
set=>_tables=value;
}
protected CompositeDisposable Disposables { get; }
public virtual void Dispose()
{
disposed = true;
Disposables?.Dispose();
}
#region event
protected abstract int EventType { get; }
protected abstract int EventSubType { get; }
protected virtual void OnEvent_TargetEvent(TargetEvent targetEvent)
{
if (disposed || SaveStore == null || !SaveStore.IsCurrent) return;
if (targetEvent.type == EventType && targetEvent.subType == EventSubType) RefreshData(targetEvent.id);
}
#endregion
#region data
protected T _data;
protected internal T Data => _data ??= new T();
protected virtual void SaveData()
{
#if UNITY_EDITOR
Debug.Log($"jsd {typeof(T).Name} SaveData {JsonConvert.SerializeObject(Data)}");
#endif
if (disposed) throw new ObjectDisposedException(GetType().Name);
SaveStore.Write(typeof(T).Name, JsonConvert.SerializeObject(Data)).RequireAccepted();
}
protected virtual void SaveData(T data)
{
#if UNITY_EDITOR
Debug.Log($"jsd {typeof(T).Name} SaveData {JsonConvert.SerializeObject(data)}");
#endif
if (disposed) throw new ObjectDisposedException(GetType().Name);
SaveStore.Write(typeof(T).Name, JsonConvert.SerializeObject(data)).RequireAccepted();
}
protected virtual void RefreshData(int eventId)
{
//判断周开启时间
var fishingEvent = Tables.TbFishingEvent.GetOrDefault(eventId);
if (fishingEvent == null) return;
var cacheData = GetCacheData();
if (cacheData != null && cacheData.EventID == eventId)
{
Data.SetData(cacheData);
return;
}
InitData(eventId);
}
protected T GetCacheData()
{
if (disposed) throw new ObjectDisposedException(GetType().Name);
var read = SaveStore.Read(typeof(T).Name);
var dataStr = read.ValueOrThrow();
#if UNITY_EDITOR
Debug.Log($"jsd {typeof(T).Name} LoadData sData {dataStr}");
#endif
if (read.Found == false) return new T();
if (string.IsNullOrWhiteSpace(dataStr)) throw new JsonSerializationException("Stored event data cannot be empty.");
return JsonConvert.DeserializeObject<T>(dataStr) ?? throw new JsonSerializationException("Stored event data cannot be null.");
}
protected virtual void InitData(int eventId)
{
_data = new T();
Data.EventID = eventId;
Data.InflationRate=GContext.container.Resolve<PlayerData>().InflationRate;
}
#endregion
public static void Register<M>()where M:AEventDataManager<T>
{
var instance=GContext.container.Resolve<M>();
if (instance != null) return;
GContext.container.Register<M>().AsSingleton();
GContext.container.Resolve<M>();
}
#region Token
public virtual void AddToken(int count)
{
var currentCount = GetTokenCount();
if (Data == null) return;
var totalCount = currentCount + count;
GContext.container.Resolve<FishingEventData>().SaveTransitionData(Data.EventID, totalCount);
}
protected virtual void RemoveToken(int count)
{
if (Data == null) return;
var eventData = GContext.container.Resolve<FishingEventData>();
if (eventData == null) return;
var current = GetTokenCount();
var resCount = Mathf.Max(0, current - count);
eventData.SaveTransitionData(Data.EventID, resCount);
}
public virtual int GetTokenCount()
{
if (Data == null) return 0;
var eventData = GContext.container.Resolve<FishingEventData>();
return eventData?.GetTransitionDataCount(Data.EventID) ?? 0;
}
#endregion
}
}