feat(upserver): 保存M2客户端KV迁移至本人统计写检查点
保存独立KV用户服务、固定会话存档与原业务接线,以及C3d资料缓存和C3e本人统计写。 保留原PF/AGG路线、业务公式和时序;榜单读取、好友资料、公告、匹配及首场景闸仍待完成。 验证:ServiceHost两条件各33组646断言;Unity CLI Loop编译0错误269警告。 包含相关方案、评审裁决、测试及状态文档。未操作服务器、正式账号或打包。
This commit is contained in:
93
CodexOutput/upserver/B3BatchMigration/M2/C2d-按核证基线核减.py.txt
Normal file
93
CodexOutput/upserver/B3BatchMigration/M2/C2d-按核证基线核减.py.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
import hashlib, json, subprocess
|
||||
from pathlib import Path
|
||||
root=Path('Client/Assets/Scripts')
|
||||
m=Path('CodexOutput/upserver/B3BatchMigration/M2')
|
||||
baseline={x['path']:x for x in json.loads((m/'M2-合批正式受审基线.json').read_text(encoding='utf-8-sig'))['items']}
|
||||
def original(suffix, require_match=True):
|
||||
path=root/suffix
|
||||
raw=subprocess.run(['git','show','HEAD:'+path.as_posix()],capture_output=True,check=True).stdout
|
||||
s=raw.decode('utf-8-sig').replace('\r\n','\n')
|
||||
variants=[s.encode('utf-8'),s.encode('utf-8-sig'),s.replace('\n','\r\n').encode('utf-8'),s.replace('\n','\r\n').encode('utf-8-sig')]
|
||||
if require_match:
|
||||
assert any(hashlib.sha256(b).hexdigest().upper()==baseline[path.as_posix()]['sha256'] for b in variants),suffix
|
||||
return s
|
||||
def write(suffix,s):
|
||||
path=root/suffix; raw=path.read_bytes()
|
||||
text=s.replace('\r\n','\n')
|
||||
if b'\r\n' in raw: text=text.replace('\n','\r\n')
|
||||
path.write_bytes(text.encode('utf-8-sig' if raw.startswith(b'\xef\xbb\xbf') else 'utf-8'))
|
||||
def calls(s,old,new,tail):
|
||||
pos=0
|
||||
while True:
|
||||
start=s.find(old+'(',pos)
|
||||
if start<0:return s
|
||||
if s[s.rfind('\n',0,start)+1:start].lstrip().startswith('//'):
|
||||
pos=start+len(old);continue
|
||||
i=start+len(old);depth=0;quote=None;esc=False
|
||||
while i<len(s):
|
||||
c=s[i]
|
||||
if quote:
|
||||
if esc:esc=False
|
||||
elif c=='\\':esc=True
|
||||
elif c==quote:quote=None
|
||||
elif c in ('"',"'"):quote=c
|
||||
elif c=='(':depth+=1
|
||||
elif c==')':
|
||||
depth-=1
|
||||
if depth==0:break
|
||||
i+=1
|
||||
assert depth==0
|
||||
text=new+s[start+len(old):i+1]+tail
|
||||
s=s[:start]+text+s[i+1:];pos=start+len(text)
|
||||
def storage(s,name):
|
||||
s=calls(s,'PlayFabMgr.Instance.GetLocalData',name+'.Read','.ValueOrThrow()')
|
||||
return calls(s,'PlayFabMgr.Instance.UpdateUserDataValue',name+'.Write','.RequireAccepted()')
|
||||
|
||||
# All ten source bodies below were independently matched against the frozen formal baseline.
|
||||
for f in ['Services/ChatService.cs','Services/InGameMailHandler.cs','DataCenter/DataCenter.PlayerItemData.cs','DataCenter/PlayerItemData_Inflation.cs','UI/Setting/SurveyPanel.cs','UI/Friends/EventInviteFriends.cs']:
|
||||
write(f,original(f))
|
||||
s=original('Services/SurveyService.cs')
|
||||
s=s.replace(' IUserService userService;',' readonly game.PlayerDataStorage.IPlayerSaveStore saveStore;\n IUserService userService;',1)
|
||||
s=s.replace('public SurveyService(IUserService userService, cfg.Tables tables)','public SurveyService(IUserService userService, cfg.Tables tables, game.PlayerDataStorage.IPlayerSaveStore saveStore)',1)
|
||||
s=s.replace(' this.userService = userService;',' this.userService = userService;\n this.saveStore = saveStore;',1)
|
||||
s=s.replace('Convert.ToUInt64(userService.UserId, 16)','Convert.ToUInt64(userService.UserId, saveStore is game.PlayerDataStorage.KVPlayerSaveStore ? 10 : 16)',1)
|
||||
write('Services/SurveyService.cs',storage(s,'saveStore'))
|
||||
s=original('Services/FriendService.cs')
|
||||
s=s.replace(' public class FriendService\n {',' public class FriendService\n {\n [Inject] public PlayerDataStorage.IPlayerSaveStore SaveStore { get; set; }',1)
|
||||
write('Services/FriendService.cs',storage(s,'SaveStore'))
|
||||
s=original('DataCenter/DataCenter.PlayerShopData.cs')
|
||||
binding=''' game.PlayerDataStorage.IPlayerSaveStore boundSaveStore;
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore SaveStore
|
||||
{
|
||||
get => boundSaveStore;
|
||||
set { CouponData.BindSaveStore(value); boundSaveStore = value; }
|
||||
}
|
||||
'''
|
||||
s=s.replace(' public partial class PlayerShopData : IDisposable\n {',' public partial class PlayerShopData : IDisposable\n {\n'+binding,1)
|
||||
write('DataCenter/DataCenter.PlayerShopData.cs',storage(s,'SaveStore'))
|
||||
s=original('UI/Turntable/FishingTurntablePanel.cs')
|
||||
s=s.replace(' Tables _tables;',' game.PlayerDataStorage.IPlayerSaveStore saveStore;\n Tables _tables;',1)
|
||||
s=s.replace(' void Awake()\n {',' void Awake()\n {\n saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();',1)
|
||||
s=calls(s,'await PlayFabMgr.Instance.SyncDataImmidiate','(await saveStore.WriteAndConfirmAsync',').Confirmed')
|
||||
write('UI/Turntable/FishingTurntablePanel.cs',s)
|
||||
|
||||
# Preserve all pre-existing uncommitted KV-loop/prompt work: replace only the inspected business methods.
|
||||
f='GampPlay/FishingB/FishingBehaviorB.cs';s=(root/f).read_text(encoding='utf-8-sig');old=original(f,False)
|
||||
start=old.index(' private void DoConsumeBait()');end=old.index(' void LoadFish()',start)
|
||||
body=calls(old[start:end],'await PlayFabMgr.Instance.SyncDataImmidiate','(await saveStore.WriteAndConfirmAsync',').Confirmed')
|
||||
start=s.index(' private async void DoConsumeBait()');end=s.index(' void LoadFish()',start)
|
||||
s=s[:start]+body+s[end:]
|
||||
for line in [' game.IUserService loginUser;\n',' bool consumptionPending;\n',' bool SaveSessionCurrent => this != null && isActiveAndEnabled && saveStore != null && saveStore.IsCurrent && !loginUser.LoginLifetime.IsCancellationRequested;\n',' loginUser = GContext.container.Resolve<game.IUserService>();\n']:
|
||||
assert line in s,line;s=s.replace(line,'',1)
|
||||
write(f,s)
|
||||
f='Services/ClubService.cs';s=(root/f).read_text(encoding='utf-8-sig');old=original(f,False)
|
||||
start=old.index(' private int? signingSession;');end=old.index(' public void SendSystemMessage2',start)
|
||||
body=storage(old[start:end],'SaveStore')
|
||||
start=s.index(' private int? signingSession;');end=s.index(' public void SendSystemMessage2',start)
|
||||
write(f,s[:start]+body+s[end:])
|
||||
# This present wrapper was introduced only by C2c; retain the surrounding original deferred reward queue.
|
||||
f='Services/DeferredRewardStashService.cs';s=(root/f).read_text(encoding='utf-8-sig')
|
||||
s=s.replace('game.PlayerDataStorage.PlayerSaveAction.Present(() => GContext.Publish(new ShowData()));','GContext.Publish(new ShowData());')
|
||||
s=s.replace('PlayerSaveAction.Present(() => GContext.Publish(new ShowData()));','GContext.Publish(new ShowData());')
|
||||
write(f,s)
|
||||
print('Verified baseline + minimum storage/identity substitutions applied; historical partial-file work retained.')
|
||||
Reference in New Issue
Block a user