"""Prepare a bounded, inactive Client audit from the actual outgoing ID catalog.""" import argparse import hashlib import json from pathlib import Path parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--catalog', type=Path, required=True) parser.add_argument('--output', type=Path, required=True) args = parser.parse_args() raw = args.catalog.read_bytes() ids = sorted(set(json.loads(raw)['subMissionIds'])) if not ids or any(not isinstance(i, int) or not 0 < i < 2**32 for i in ids): raise SystemExit('Invalid outgoing mission catalog') ranges = [] for i in ids: if ranges and ranges[-1][1] + 1 == i: ranges[-1][1] = i else: ranges.append([i, i]) source = '''-- Temporary: read-only lookup of outgoing sub-missions, at most four per frame. do if not _G.__gacMissionAudit1 then local s={range=1,done=false,checked=0,valid=0,missing={},frame=-1} _G.__gacMissionAudit1=s local ranges=__RANGES__ local log=function(v)CS.UnityEngine.Debug.Log('[GAC_MISSION_AUDIT] '..tostring(v))end log('BEGIN expected=__COUNT__ catalog_sha256=__HASH__') s.tick=function() if s.done then return end local frame=CS.UnityEngine.Time.frameCount if s.frame==frame then return end s.frame=frame local good,err=pcall(function() local manager=CS.RPG.Client.GlobalVars.s_GamePhaseManager local flags=CS.System.Enum.ToObject(typeof(CS.System.Reflection.BindingFlags),60) local phase=manager and typeof(CS.RPG.Client.GamePhaseManager):GetField('_CurrentPhase',flags):GetValue(manager) if phase and typeof(CS.RPG.Client.BattleGamePhase):IsInstanceOfType(phase)then return end local m=CS.RPG.Client.GlobalVars.s_ModuleManager.MissionModule local start=CS.UnityEngine.Time.realtimeSinceStartup for n=1,4 do local r=ranges[s.range] if not r then s.done=true log('END checked='..s.checked..' valid='..s.valid..' missing='..table.concat(s.missing,',')) return end local id=s.next or r[1] local cfg=m:GetSubMissionInfoConfig(id) if cfg then s.valid=s.valid+1 else s.missing[#s.missing+1]=tostring(id)end s.checked=s.checked+1 if id==r[2]then s.range=s.range+1;s.next=nil else s.next=id+1 end if s.checked%1000==0 then log('PROGRESS checked='..s.checked..' valid='..s.valid)end if CS.UnityEngine.Time.realtimeSinceStartup-start>0.002 then break end end end) if not good then s.done=true;log('ERROR checked='..s.checked..' '..tostring(err))end end local ok,e=pcall(function()CS.UnityEngine.Application.onBeforeRender('+',s.tick)end) if not ok then s.done=true;log('ERROR '..tostring(e))end end end ''' source = source.replace('__RANGES__', '{' + ','.join('{%d,%d}' % tuple(r) for r in ranges) + '}') source = source.replace('__COUNT__', str(len(ids))).replace('__HASH__', hashlib.sha256(raw).hexdigest()) args.output.write_text(source, encoding='utf-8') print(f'Prepared {len(ids)} IDs in {len(ranges)} ranges; {len(source)} bytes')