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

@@ -37,14 +37,68 @@ public class LocalNotificationService : ILocalNotificationService
#endif
bool isInit = false;
bool initing = false;
DefaultSettingService promptSettings;
Task initialization;
Task opening;
internal bool HasKVPromptOwner => promptSettings != null;
public LocalNotificationService()
{
_notificationList = GContext.container.Resolve<cfg.Tables>().TbNotification.DataList;
}
public async System.Threading.Tasks.Task Init()
internal LocalNotificationService(cfg.Tables tables)
{
if (isInit) return;
_notificationList = (tables ?? throw new ArgumentNullException(nameof(tables))).TbNotification.DataList;
}
internal LocalNotificationService(DefaultSettingService settings, cfg.Tables tables)
{
if (settings == null || !settings.HasKVPromptOwner)
throw new ArgumentException("Fixed KV settings required.", nameof(settings));
promptSettings = settings;
_notificationList = (tables ?? throw new ArgumentNullException(nameof(tables))).TbNotification.DataList;
RequirePromptOwner();
}
internal void RequirePromptOwner(ISettingService expected = null)
{
if (expected != null && (promptSettings != null || expected is DefaultSettingService setting && setting.HasKVPromptOwner)
&& !ReferenceEquals(expected, promptSettings))
throw DefaultSettingService.PromptFailure("ShowNotice: notification/settings owner mismatch.");
promptSettings?.RequirePromptOwner();
}
internal void PreflightFormalPromptSettings(DefaultSettingService settings)
{
if (settings == null || !isInit || initing ||
(initialization != null && !initialization.IsCompleted) ||
(opening != null && !opening.IsCompleted) ||
(promptSettings != null && !ReferenceEquals(promptSettings, settings)))
throw DefaultSettingService.PromptFailure("ShowNotice: notification handoff is not idle.");
}
internal void BindFormalPromptSettings(DefaultSettingService settings)
{
PreflightFormalPromptSettings(settings);
if (!settings.HasKVPromptOwner)
throw DefaultSettingService.PromptFailure("ShowNotice: notification owner not handed off.");
settings.RequirePromptOwner();
if (promptSettings == null) promptSettings = settings;
RequirePromptOwner(settings);
}
internal bool HasFormalPromptSettings(DefaultSettingService settings) =>
ReferenceEquals(promptSettings, settings);
public Task Init()
{
RequirePromptOwner();
if (isInit) return Task.CompletedTask;
if (initialization != null && !initialization.IsCompleted) return initialization;
return initialization = InitCoreAsync();
}
async Task InitCoreAsync()
{
RequirePromptOwner();
#if UNITY_EDITOR
await System.Threading.Tasks.Task.Yield();
#else
@@ -59,9 +113,11 @@ public class LocalNotificationService : ILocalNotificationService
Debug.Log("[LocalNotificationService::Init] Channel Inited");
await NotificationCenter.RequestPermission();
RequirePromptOwner();
ScheduleDefaultNotification();
GContext.OnEvent<EventRankData>().Subscribe(ScheduleRankNotification);
#endif
RequirePromptOwner();
isInit = true;
}
@@ -86,14 +142,36 @@ public class LocalNotificationService : ILocalNotificationService
public async void OpenSettings()
{
if (initing) return;
try { await OpenSettingsAsync(); }
catch (Exception ex) { game.DefaultSettingService.LogPromptFailure(ex); }
}
internal Task OpenSettingsAsync()
{
if (promptSettings == null && GContext.container.Resolve<ISettingService>() is DefaultSettingService pending)
pending.RequirePromptOwner();
RequirePromptOwner();
if (initing && opening == null)
throw DefaultSettingService.PromptFailure("ShowNotice: reentrant permission request.");
if (opening != null && !opening.IsCompleted) return opening;
return opening = OpenSettingsCoreAsync();
}
async Task OpenSettingsCoreAsync()
{
ISettingService settings = promptSettings ?? GContext.container.Resolve<ISettingService>();
RequirePromptOwner(settings);
(settings as DefaultSettingService)?.RequirePromptOwner();
initing = true;
if (!isInit)
await Init();
else
NotificationCenter.OpenNotificationSettings();
GContext.container.Resolve<ISettingService>().ShowNotice = true;
initing = false;
try
{
if (!isInit) await Init();
else NotificationCenter.OpenNotificationSettings();
RequirePromptOwner(settings);
(settings as DefaultSettingService)?.RequirePromptOwner();
settings.ShowNotice = true;
}
finally { initing = false; }
}
public void ScheduleDailyNotification(int notificationID, DateTime fireTime, string title, string text)
@@ -150,6 +228,8 @@ public class LocalNotificationService : ILocalNotificationService
private void ScheduleRankNotification(EventRankData evt)
{
try { RequirePromptOwner(); }
catch (InvalidOperationException ex) { Debug.LogError(ex.Message); return; }
if (rankNotifyScheduled || !IsNoticeOpen || !isInit) return;
Debug.Log("[LocalNotificationService::SendNotification] RankEvent");
@@ -170,4 +250,4 @@ public class LocalNotificationService : ILocalNotificationService
rankNotifyScheduled = true;
Debug.Log("[LocalNotificationService::SendRankEventNotification] ");
}
}
}