feat(upserver): 接入认证与KV存档并完成限定钓鱼闭环
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace game.PlayerDataStorage
|
||||
{
|
||||
internal interface IPlayerKVLoadModule
|
||||
{
|
||||
int Id { get; }
|
||||
void Reset();
|
||||
void Replace(PlayerKVDecodedSave decoded);
|
||||
}
|
||||
internal sealed class PlayerKVLoadModule : IPlayerKVLoadModule
|
||||
{
|
||||
readonly Action reset;
|
||||
readonly Action<PlayerKVDecodedSave> replace;
|
||||
public int Id { get; }
|
||||
internal PlayerKVLoadModule(int id,Action reset,Action<PlayerKVDecodedSave> replace)
|
||||
{ Id=id; this.reset=reset ?? throw new ArgumentNullException(nameof(reset)); this.replace=replace ?? throw new ArgumentNullException(nameof(replace)); }
|
||||
public void Reset() => reset();
|
||||
public void Replace(PlayerKVDecodedSave decoded) => replace(decoded);
|
||||
}
|
||||
internal enum PlayerKVApplyError { None, Input, Stale, Apply, SideEffect, Poisoned }
|
||||
internal sealed class PlayerKVGameLoadTransaction
|
||||
{
|
||||
readonly IPlayerKVLoadModule[] modules;
|
||||
readonly int mainThread;
|
||||
readonly Action discard;
|
||||
bool applying;
|
||||
internal bool IsApplying => applying;
|
||||
internal bool Poisoned { get; private set; }
|
||||
internal int AppliedModules { get; private set; }
|
||||
internal PlayerKVGameLoadTransaction(IEnumerable<IPlayerKVLoadModule> modules,int mainThread,Action discard)
|
||||
{
|
||||
this.modules=modules?.ToArray() ?? throw new ArgumentNullException(nameof(modules));
|
||||
if(this.modules.Length!=31 || !this.modules.Select(m=>m?.Id ?? 0).SequenceEqual(Enumerable.Range(1,31)))
|
||||
throw new ArgumentException("Exactly 31 ordered modules required.");
|
||||
this.mainThread=mainThread; this.discard=discard ?? throw new ArgumentNullException(nameof(discard));
|
||||
}
|
||||
// Synchronous main-thread region: no callback may yield between Reset and publication.
|
||||
internal PlayerKVApplyError Apply(PlayerKVDecodedSave decoded,Func<bool> current,Func<bool> clean,Action notify)
|
||||
{
|
||||
if(Poisoned) return PlayerKVApplyError.Poisoned;
|
||||
if(applying || Thread.CurrentThread.ManagedThreadId!=mainThread || decoded==null || current==null || clean==null || notify==null)
|
||||
return PlayerKVApplyError.Input;
|
||||
applying=true; AppliedModules=0;
|
||||
PlayerKVApplyError error=PlayerKVApplyError.Apply;
|
||||
try
|
||||
{
|
||||
if(!current()) error=PlayerKVApplyError.Stale;
|
||||
else if(!clean()) error=PlayerKVApplyError.SideEffect;
|
||||
else if(!ResetAll()) error=PlayerKVApplyError.Poisoned;
|
||||
else
|
||||
{
|
||||
foreach(var module in modules)
|
||||
{
|
||||
if(!current()) { error=PlayerKVApplyError.Stale; break; }
|
||||
if(!clean()) { error=PlayerKVApplyError.SideEffect; break; }
|
||||
module.Replace(decoded); AppliedModules++;
|
||||
}
|
||||
if(AppliedModules==31)
|
||||
{
|
||||
// The last module can revoke the epoch or trip a guard; never publish in that gap.
|
||||
if(!current()) error=PlayerKVApplyError.Stale;
|
||||
else if(!clean()) error=PlayerKVApplyError.SideEffect;
|
||||
else
|
||||
{
|
||||
notify();
|
||||
error=!current() ? PlayerKVApplyError.Stale : !clean() ? PlayerKVApplyError.SideEffect : PlayerKVApplyError.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { error=PlayerKVApplyError.Apply; }
|
||||
finally { applying=false; }
|
||||
if(error!=PlayerKVApplyError.None)
|
||||
{
|
||||
if(!ResetAll()) error=PlayerKVApplyError.Poisoned;
|
||||
try { discard(); } catch { Poisoned=true; error=PlayerKVApplyError.Poisoned; }
|
||||
AppliedModules=0;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
internal bool Clear()
|
||||
{
|
||||
if(applying || Thread.CurrentThread.ManagedThreadId!=mainThread) throw new InvalidOperationException("Main thread required.");
|
||||
bool cleared=ResetAll();
|
||||
try { discard(); } catch { Poisoned=true; cleared=false; }
|
||||
AppliedModules=0; return cleared;
|
||||
}
|
||||
bool ResetAll()
|
||||
{
|
||||
bool ok=true;
|
||||
foreach(var module in modules) try { module.Reset(); } catch { ok=false; }
|
||||
if(!ok) Poisoned=true;
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user