feat(upserver): 保存M2客户端KV迁移至本人统计写检查点
保存独立KV用户服务、固定会话存档与原业务接线,以及C3d资料缓存和C3e本人统计写。 保留原PF/AGG路线、业务公式和时序;榜单读取、好友资料、公告、匹配及首场景闸仍待完成。 验证:ServiceHost两条件各33组646断言;Unity CLI Loop编译0错误269警告。 包含相关方案、评审裁决、测试及状态文档。未操作服务器、正式账号或打包。
This commit is contained in:
@@ -131,7 +131,7 @@ namespace game.PlayerDataStorage
|
||||
}
|
||||
internal static string Read(string key)
|
||||
{
|
||||
var result=PlayFabMgr.ReadKV(Capture(),key);
|
||||
var result=KVMgr.ReadKV(Capture(),key);
|
||||
if(!result.Success) throw Stop("Read: "+result.Error);
|
||||
return result.Found==true ? result.Value : null;
|
||||
}
|
||||
@@ -142,7 +142,7 @@ namespace game.PlayerDataStorage
|
||||
CheckRewardWrite(key,value);
|
||||
var valueHash=value==null?null:PlayerKVLoopPermit.Hash(Encoding.UTF8.GetBytes(value));
|
||||
Audit("write-attempt",new{key,remote,valueHash});
|
||||
var result=PlayFabMgr.WriteKV(owner,key,value,remote);
|
||||
var result=KVMgr.WriteKV(owner,key,value,remote);
|
||||
if(!result.Accepted) throw Stop("Write: "+result.StateResult.Error+" uncertain="+result.StateResult.LocalCommitMayHaveSucceeded);
|
||||
if(collecting!=null)
|
||||
{
|
||||
@@ -220,7 +220,7 @@ namespace game.PlayerDataStorage
|
||||
last.Values.Any(r=>!r.Remote || r.Edit.Ticket==null)) throw Stop("Fishing final writes require original remote tickets.");
|
||||
using(var timeout=new CancellationTokenSource(TimeSpan.FromSeconds(30)))
|
||||
{
|
||||
var flush=await PlayFabMgr.FlushKVAsync(batch.Owner,timeout.Token);
|
||||
var flush=await KVMgr.FlushKVAsync(batch.Owner,timeout.Token);
|
||||
if(!FishingCurrent(action) || flush.Unknown>0 || flush.Blocked>0 || flush.TransportError!=PlayerKVError.None)
|
||||
throw Stop("Fishing flush: "+flush.TransportError);
|
||||
foreach(var record in batch.Records)
|
||||
@@ -231,7 +231,7 @@ namespace game.PlayerDataStorage
|
||||
if(ticket.OwnerInstanceId!=batch.Owner.OwnerInstanceId || ticket.Epoch!=batch.Owner.Epoch ||
|
||||
ticket.Key!=record.Key || final.OwnerInstanceId!=ticket.OwnerInstanceId || final.Epoch!=ticket.Epoch)
|
||||
throw Stop("Fishing ticket identity mismatch.");
|
||||
var result=await PlayFabMgr.WaitForKVTicketAsync(batch.Owner,ticket,timeout.Token);
|
||||
var result=await KVMgr.WaitForKVTicketAsync(batch.Owner,ticket,timeout.Token);
|
||||
if(!FishingCurrent(action)) throw Stop("Fishing action changed while waiting.");
|
||||
bool explained=result.Status==PlayerKVTicketStatus.Superseded && !ReferenceEquals(ticket,final) && final.Sequence>ticket.Sequence;
|
||||
if(result.Status!=PlayerKVTicketStatus.Confirmed && !explained) throw Stop("Fishing ticket: "+result.Status);
|
||||
@@ -254,12 +254,12 @@ namespace game.PlayerDataStorage
|
||||
{
|
||||
if(primary?.Ticket==null || !batch.Edits.Contains(primary) || !ReferenceEquals(Capture(),batch.Owner))
|
||||
throw Stop("Missing original equipment ticket.");
|
||||
var flush=await PlayFabMgr.FlushKVAsync(batch.Owner);
|
||||
var flush=await KVMgr.FlushKVAsync(batch.Owner);
|
||||
if(flush.Unknown>0 || flush.Blocked>0 || flush.TransportError!=PlayerKVError.None) throw Stop("Equipment flush: "+flush.TransportError);
|
||||
foreach(var edit in batch.Edits)
|
||||
{
|
||||
if(edit.Ticket==null) continue; // localOnly is audited, never claimed as remotely confirmed.
|
||||
var result=await PlayFabMgr.WaitForKVTicketAsync(batch.Owner,edit.Ticket);
|
||||
var result=await KVMgr.WaitForKVTicketAsync(batch.Owner,edit.Ticket);
|
||||
if(result.Status!=PlayerKVTicketStatus.Confirmed) throw Stop("Equipment ticket: "+result.Status);
|
||||
}
|
||||
if(!ReferenceEquals(Capture(),batch.Owner)) throw Stop("Equipment session changed.");
|
||||
@@ -270,7 +270,7 @@ namespace game.PlayerDataStorage
|
||||
if(!Ready) return;
|
||||
try
|
||||
{
|
||||
var result=await PlayFabMgr.FlushKVAsync(Capture());
|
||||
var result=await KVMgr.FlushKVAsync(Capture());
|
||||
if(result.Unknown>0 || result.Blocked>0 || result.TransportError!=PlayerKVError.None) throw Stop("Periodic flush: "+result.TransportError);
|
||||
}
|
||||
catch(Exception e) { Fault(e.Message); }
|
||||
@@ -342,14 +342,14 @@ namespace game.PlayerDataStorage
|
||||
Check(batch.Edits.Count==2 && ReferenceEquals(primary,batch.Edits[1]),"all original edits collected");
|
||||
Check(fixture.Sets==0,"write acceptance is local only");
|
||||
await ConfirmEquipAsync(batch,primary);
|
||||
foreach(var edit in batch.Edits) Check((await PlayFabMgr.WaitForKVTicketAsync(batch.Owner,edit.Ticket)).Status==PlayerKVTicketStatus.Confirmed,"each original ticket confirmed");
|
||||
foreach(var edit in batch.Edits) Check((await KVMgr.WaitForKVTicketAsync(batch.Owner,edit.Ticket)).Status==PlayerKVTicketStatus.Confirmed,"each original ticket confirmed");
|
||||
Check(fixture.Remote["k"]=="nested" && fixture.Remote["NewPlayerDataDic"]=="whole-save" && Ready && fixture.Faults==0,"complete batch committed");
|
||||
|
||||
await Start(); batch=BeginEquip();
|
||||
var first=Write("k","first",true); Write("k","second",true);
|
||||
primary=Write("NewPlayerDataDic","whole-save",true); batch.Dispose();
|
||||
await Reject(ConfirmEquipAsync(batch,primary),"Superseded");
|
||||
Check((await PlayFabMgr.WaitForKVTicketAsync(batch.Owner,first.Ticket)).Status==PlayerKVTicketStatus.Superseded,"original nested ticket never replaced");
|
||||
Check((await KVMgr.WaitForKVTicketAsync(batch.Owner,first.Ticket)).Status==PlayerKVTicketStatus.Superseded,"original nested ticket never replaced");
|
||||
|
||||
await Start(); batch=BeginEquip();
|
||||
Write("k","nested",true); primary=Write("NewPlayerDataDic","watermark",true); batch.Dispose();
|
||||
@@ -362,8 +362,8 @@ namespace game.PlayerDataStorage
|
||||
Check(batch.Edits.Count==2,"later write excluded from synchronous batch");
|
||||
fixture.ReleaseSet.TrySetResult(true); await confirmation; pendingConfirmation=null;
|
||||
Check(!fixture.Remote.ContainsKey("late"),"flush watermark excludes later write");
|
||||
await PlayFabMgr.FlushKVAsync(batch.Owner);
|
||||
Check((await PlayFabMgr.WaitForKVTicketAsync(batch.Owner,late.Ticket)).Status==PlayerKVTicketStatus.Confirmed,"later write needs its own flush");
|
||||
await KVMgr.FlushKVAsync(batch.Owner);
|
||||
Check((await KVMgr.WaitForKVTicketAsync(batch.Owner,late.Ticket)).Status==PlayerKVTicketStatus.Confirmed,"later write needs its own flush");
|
||||
|
||||
await Start(); batch=BeginEquip(); primary=Write("NewPlayerDataDic","unauthorized",true); batch.Dispose();
|
||||
fixture.Unauthorized=true;
|
||||
@@ -374,7 +374,7 @@ namespace game.PlayerDataStorage
|
||||
fixture.LoseResponse=true;
|
||||
await Reject(ConfirmEquipAsync(batch,primary),"Equipment flush");
|
||||
Check(fixture.Remote["NewPlayerDataDic"]=="lost-response" && fixture.Sets==1,"unknown remote commit is not replayed");
|
||||
Check((await PlayFabMgr.WaitForKVTicketAsync(batch.Owner,primary.Ticket)).Status==PlayerKVTicketStatus.Unknown,"unknown original ticket preserved");
|
||||
Check((await KVMgr.WaitForKVTicketAsync(batch.Owner,primary.Ticket)).Status==PlayerKVTicketStatus.Unknown,"unknown original ticket preserved");
|
||||
|
||||
await Start(); batch=BeginEquip(); primary=Write("NewPlayerDataDic","closed",true); batch.Dispose();
|
||||
await fixture.Close();
|
||||
|
||||
Reference in New Issue
Block a user