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,103 @@
using System.Threading.Tasks;
using asap.core;
using Game;
using GameCore;
using UnityEngine;
namespace game
{
public class QuitAct : AGameAct
{
[Inject]
public IConfig config { get; set; }
[Inject]
public IRTService rtService { get; set; }
[Inject]
public IUserService userService { get; set; }
[Inject]
public SoundMgr soundMgr { get; set; }
[Inject]
public IAdsService adsService { get; set; }
#if AGG
[Inject]
public HeartBeat heartBeat {get; set;}
#endif
protected override void OnDestroy() { }
public string[] DestoryRoot;
public override async Task<bool> StartAsync()
{
#if UNITY_EDITOR && !SERVICE_HOST
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) _=game.PlayerDataStorage.PlayerKVGameplayLoop.CloseAsync();
#endif
if(userService is PlayerDataStorage.IKVUserLifecycle closingKVUser) _=closingKVUser.CloseKVForValidationAsync();
// Invalidate identity before any await can allow old login/HTTP callbacks to resume.
if (QuitStage.Logout)
{
QuitStage.Logout = false;
try { userService.Logout(); }
catch (System.Exception ex) { Debug.LogException(ex); }
}
LoadingScreen.Hide();
GameObject.Destroy(UIManager.Instance.gameObject);
config.Rest();
try { await rtService.ReleaseAsync(); }
catch (System.Exception ex) { Debug.LogException(ex); }
// Observation is bounded; the process registry still owns any unfinished close.
#if UNITY_EDITOR && !SERVICE_HOST
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) await PlayerDataStorage.PlayerKVShutdownRegistry.Process.ObserveAsync(5000);
else
#endif
if(userService is PlayerDataStorage.IKVUserLifecycle kvUser) await kvUser.ObserveKVQuitAsync();
adsService.Release();
GameEventMgr.Instance.Clear();
#if AGG
heartBeat?.Dispose();
#endif
GameCore.Timer.CancelAllRegisteredTimers();
GameCore.Timer.Destroy();
soundMgr.Release();
RedPointManager.Instance.Init();
return await base.StartAsync();
}
protected override void OnStarted()
{
RootCtx.DestoryRootCtx();
for (int i = 0; i < DestoryRoot.Length; i++)
{
GameObject go = GameObject.Find(DestoryRoot[i]);
if (go != null)
{
GameObject.DestroyImmediate(go);
}
}
base.OnStarted();
#if UNITY_EDITOR && !SERVICE_HOST
if (game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled)
{
var status = PlayerDataStorage.PlayerKVShutdownRegistry.Process.Status;
Debug.Log("KV loop ended; restart Unity before another run. Shutdown: " + status);
if (status == PlayerDataStorage.PlayerKVShutdownStatus.Ready)
UnityEditor.EditorApplication.isPlaying = false;
return; // Keep the process barrier; never return this run to PreBoot.
}
#endif
Invoke("Unload", 2f);
}
}
}