chore(m2): 保存功能验收及后续清理前检查点
This commit is contained in:
@@ -7,12 +7,9 @@ using game.Account;
|
||||
|
||||
namespace game.PlayerDataStorage
|
||||
{
|
||||
// The lease-only capability remains source-compatible with the independent 4B1 regression host.
|
||||
// This separate capability supplies every dependency required by a complete controlled load.
|
||||
// Prebuild distinct transports with one cookie jar, then transfer each owned handler once.
|
||||
internal sealed class PlayerKVGameLoadCapability
|
||||
{
|
||||
internal PlayerKVIdentitySnapshot Identity { get; }
|
||||
internal PlayerKVSchemaSnapshot Snapshot { get; }
|
||||
internal Uri Api { get; }
|
||||
internal string CacheRoot { get; }
|
||||
internal int LoginObserveMs { get; }
|
||||
@@ -20,47 +17,59 @@ namespace game.PlayerDataStorage
|
||||
internal bool RequireExistingArchive { get; }
|
||||
internal Func<int,CancellationToken,Task> Delay { get; }
|
||||
internal PlayerKVControlledLoadCapability LeaseCapability { get; private set; }
|
||||
readonly PlayerKVControlledLoadCapability auth,kv,lease;
|
||||
private readonly Func<CookieContainer, HttpMessageHandler> auth;
|
||||
private readonly Func<CookieContainer, HttpMessageHandler> kv;
|
||||
private readonly Func<CookieContainer, HttpMessageHandler> lease;
|
||||
private readonly bool prepareOriginalComparison;
|
||||
readonly object gate=new object();
|
||||
CookieContainer cookies;
|
||||
HttpMessageHandler pendingKV,pendingLease;
|
||||
Exception cleanupFailure;
|
||||
readonly System.Collections.Generic.List<HttpMessageHandler> failedHandlers=new System.Collections.Generic.List<HttpMessageHandler>();
|
||||
bool claimed;
|
||||
private PlayerKVGameLoadCapability(PlayerKVIdentitySnapshot identity,PlayerKVSchemaSnapshot snapshot,Uri api,string cacheRoot,
|
||||
int loginMs,int quitMs,Func<int,CancellationToken,Task> delay,
|
||||
PlayerKVControlledLoadCapability auth,PlayerKVControlledLoadCapability kv,PlayerKVControlledLoadCapability lease,
|
||||
bool requireExistingArchive=false)
|
||||
private PlayerKVGameLoadCapability(Uri api, string cacheRoot,
|
||||
int loginMs, int quitMs, Func<int, CancellationToken, Task> delay,
|
||||
Func<CookieContainer, HttpMessageHandler> auth, Func<CookieContainer, HttpMessageHandler> kv,
|
||||
Func<CookieContainer, HttpMessageHandler> lease, bool requireExistingArchive = false, bool prepareOriginalComparison = false)
|
||||
{
|
||||
Identity=identity ?? throw new ArgumentNullException(nameof(identity)); Snapshot=snapshot ?? throw new ArgumentNullException(nameof(snapshot));
|
||||
if(api==null || !api.IsAbsoluteUri || (api.Scheme!="http" && api.Scheme!="https") || !string.IsNullOrEmpty(api.UserInfo) ||
|
||||
// Preserve the former wrappers' null-check order, diagnostic and configuration precedence.
|
||||
this.auth = auth ?? throw new ArgumentNullException("leaseHandler");
|
||||
this.kv = kv ?? throw new ArgumentNullException("leaseHandler");
|
||||
this.lease = lease ?? throw new ArgumentNullException("leaseHandler");
|
||||
if (api == null || !api.IsAbsoluteUri || (api.Scheme != "http" && api.Scheme != "https") || !string.IsNullOrEmpty(api.UserInfo) ||
|
||||
!string.IsNullOrEmpty(api.Query) || !string.IsNullOrEmpty(api.Fragment) || string.IsNullOrEmpty(cacheRoot) ||
|
||||
loginMs<1000 || loginMs>15000 || quitMs<1000 || quitMs>15000) throw new ArgumentException("Controlled load configuration invalid.");
|
||||
Api=new Uri(api.AbsoluteUri.TrimEnd('/')+"/"); CacheRoot=cacheRoot;
|
||||
LoginObserveMs=loginMs; QuitObserveMs=quitMs; Delay=delay ?? Task.Delay; this.auth=auth; this.kv=kv; this.lease=lease;
|
||||
RequireExistingArchive=requireExistingArchive;
|
||||
loginMs < 1000 || loginMs > 15000 || quitMs < 1000 || quitMs > 15000)
|
||||
{
|
||||
throw new ArgumentException("Controlled load configuration invalid.");
|
||||
}
|
||||
Api = new Uri(api.AbsoluteUri.TrimEnd('/') + "/");
|
||||
CacheRoot = cacheRoot;
|
||||
LoginObserveMs = loginMs;
|
||||
QuitObserveMs = quitMs;
|
||||
Delay = delay ?? Task.Delay;
|
||||
RequireExistingArchive = requireExistingArchive;
|
||||
this.prepareOriginalComparison = prepareOriginalComparison;
|
||||
}
|
||||
internal static PlayerKVGameLoadCapability CreateForGame(PlayerKVIdentitySnapshot identity,PlayerKVSchemaSnapshot snapshot,
|
||||
Uri api,string cacheRoot,Func<CookieContainer,HttpMessageHandler> auth,Func<CookieContainer,HttpMessageHandler> kv,
|
||||
Func<CookieContainer,HttpMessageHandler> lease)
|
||||
internal static PlayerKVGameLoadCapability CreateForGame(Uri api, string cacheRoot,
|
||||
Func<CookieContainer, HttpMessageHandler> auth, Func<CookieContainer, HttpMessageHandler> kv,
|
||||
Func<CookieContainer, HttpMessageHandler> lease, bool prepareOriginalComparison = false)
|
||||
{
|
||||
if(api==null || api.AbsoluteUri!="http://s101.local:18080/")
|
||||
throw new ArgumentException("Formal KV endpoint mismatch.",nameof(api));
|
||||
var capability=new PlayerKVGameLoadCapability(identity,snapshot,api,cacheRoot,10000,5000,null,
|
||||
PlayerKVControlledLoadCapability.CreateForGame(auth),PlayerKVControlledLoadCapability.CreateForGame(kv),
|
||||
PlayerKVControlledLoadCapability.CreateForGame(lease),true);
|
||||
capability.LeaseCapability=PlayerKVControlledLoadCapability.CreateForGame(capability.TakeLease);
|
||||
if (api == null || api.AbsoluteUri != "http://s101.local:18080/")
|
||||
{
|
||||
throw new ArgumentException("Formal KV endpoint mismatch.", nameof(api));
|
||||
}
|
||||
var capability = new PlayerKVGameLoadCapability(api, cacheRoot, 10000, 5000, null, auth, kv, lease, true, prepareOriginalComparison);
|
||||
capability.LeaseCapability = PlayerKVControlledLoadCapability.CreateForGame(capability.TakeLease);
|
||||
return capability;
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
internal static PlayerKVGameLoadCapability CreateForValidation(PlayerKVIdentitySnapshot identity,PlayerKVSchemaSnapshot snapshot,
|
||||
Uri api,string cacheRoot,Func<CookieContainer,HttpMessageHandler> auth,Func<CookieContainer,HttpMessageHandler> kv,
|
||||
Func<CookieContainer,HttpMessageHandler> lease,int loginMs=10000,int quitMs=5000,Func<int,CancellationToken,Task> delay=null)
|
||||
internal static PlayerKVGameLoadCapability CreateForValidation(Uri api, string cacheRoot,
|
||||
Func<CookieContainer, HttpMessageHandler> auth, Func<CookieContainer, HttpMessageHandler> kv,
|
||||
Func<CookieContainer, HttpMessageHandler> lease, int loginMs = 10000, int quitMs = 5000,
|
||||
Func<int, CancellationToken, Task> delay = null)
|
||||
{
|
||||
var capability=new PlayerKVGameLoadCapability(identity,snapshot,api,cacheRoot,loginMs,quitMs,delay,
|
||||
PlayerKVControlledLoadCapability.CreateForValidation(auth),PlayerKVControlledLoadCapability.CreateForValidation(kv),
|
||||
PlayerKVControlledLoadCapability.CreateForValidation(lease));
|
||||
capability.LeaseCapability=PlayerKVControlledLoadCapability.CreateForValidation(capability.TakeLease);
|
||||
var capability = new PlayerKVGameLoadCapability(api, cacheRoot, loginMs, quitMs, delay, auth, kv, lease);
|
||||
capability.LeaseCapability = PlayerKVControlledLoadCapability.CreateForValidation(capability.TakeLease);
|
||||
return capability;
|
||||
}
|
||||
#endif
|
||||
@@ -68,26 +77,48 @@ namespace game.PlayerDataStorage
|
||||
{
|
||||
lock(gate) { if(claimed) throw new InvalidOperationException("Controlled capability already used."); claimed=true; }
|
||||
return new PlayerKVLoginContext(id,CacheRoot,epoch,current,Api,
|
||||
()=>new S101AccountSession(Api,CreateAuthentication),()=>new PlayerKVGameSessionBridge(TakeKV));
|
||||
()=>new S101AccountSession(Api,CreateAuthentication),()=>new PlayerKVGameSessionBridge(TakeKV, prepareOriginalComparison));
|
||||
}
|
||||
HttpMessageHandler CreateAuthentication(CookieContainer jar)
|
||||
private HttpMessageHandler CreateAuthentication(CookieContainer jar)
|
||||
{
|
||||
lock(gate)
|
||||
lock (gate)
|
||||
{
|
||||
if(cookies!=null) throw new InvalidOperationException("Repeated authentication factory.");
|
||||
cookies=jar; HttpMessageHandler authentication=null;
|
||||
if (cookies != null)
|
||||
{
|
||||
throw new InvalidOperationException("Repeated authentication factory.");
|
||||
}
|
||||
cookies = jar;
|
||||
HttpMessageHandler authentication = null;
|
||||
try
|
||||
{
|
||||
// Validate all three returned handler graphs before the first authentication send.
|
||||
authentication=auth.CreateHandler(jar); pendingKV=kv.CreateHandler(jar); pendingLease=lease.CreateHandler(jar);
|
||||
if(ShareHandlerGraph(authentication,pendingKV,pendingLease))
|
||||
authentication = PlayerKVControlledLoadCapability.CreateHandler(auth, jar, LeaseCapability.IsFormal);
|
||||
pendingKV = PlayerKVControlledLoadCapability.CreateHandler(kv, jar, LeaseCapability.IsFormal);
|
||||
pendingLease = PlayerKVControlledLoadCapability.CreateHandler(lease, jar, LeaseCapability.IsFormal);
|
||||
if (ShareHandlerGraph(authentication, pendingKV, pendingLease))
|
||||
{
|
||||
throw new InvalidOperationException("Distinct transport ownership required.");
|
||||
}
|
||||
return authentication;
|
||||
}
|
||||
catch
|
||||
{
|
||||
try { authentication?.Dispose(); } catch(Exception ex) { cleanupFailure=ex; failedHandlers.Add(authentication); }
|
||||
try { CloseUnused(); } catch { }
|
||||
try
|
||||
{
|
||||
authentication?.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
cleanupFailure = ex;
|
||||
failedHandlers.Add(authentication);
|
||||
}
|
||||
try
|
||||
{
|
||||
CloseUnused();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user