Add native gameplay outcomes, recovery verification, and realm diagnostics
Core tests / core (push) Successful in 40s

This commit is contained in:
Codex
2026-09-22 21:18:32 -04:00
parent 9d586f4c44
commit 290926f33f
18 changed files with 264 additions and 21 deletions
+11 -4
View File
@@ -81,11 +81,18 @@ except Exception as e:maintenance.append('Recovery check needs attention: '+type
try:
recovery_rows=[]
try:
for line in sql('SELECT id,guid,name,UNIX_TIMESTAMP(at),detail FROM acore_auth.azerothai_recovery_log ORDER BY id DESC LIMIT 100').splitlines():
fields=line.split('\t',4)
recovery_rows.append(dict(id=int(fields[0]),guid=int(fields[1]),name=fields[2],at=int(fields[3]),detail=fields[4]))
for line in sql("SELECT l.id,l.guid,l.name,UNIX_TIMESTAMP(l.at),l.detail,COALESCE(r.status,IF(l.at>NOW()-INTERVAL 3 MINUTE,'pending','unverified')),COALESCE(UNIX_TIMESTAMP(r.checked_at),0),COALESCE(r.detail,'No verification recorded') FROM acore_auth.azerothai_recovery_log l LEFT JOIN acore_auth.azerothai_recovery_results r ON r.attempt_id=l.id ORDER BY l.id DESC LIMIT 100").splitlines():
fields=line.split('\t')
recovery_rows.append(dict(id=int(fields[0]),guid=int(fields[1]),name=fields[2],at=int(fields[3]),detail=fields[4],status=fields[5],checkedAt=int(fields[6]),result=fields[7]))
except Exception as e:maintenance.append('Recovery history unavailable: '+type(e).__name__)
request=urllib.request.Request(base+'/api/realm-insights',data=json.dumps({'at':time.time(),'recoveries':recovery_rows}).encode(),headers={'Content-Type':'application/json','Authorization':'Bearer '+cfg['AUTONOMY_TOKEN']})
spec=importlib.util.spec_from_file_location('realm_evidence',repo/'scripts/realm-evidence.py')
evidence=importlib.util.module_from_spec(spec);spec.loader.exec_module(evidence)
outcomes=None;perf=None
try:outcomes=evidence.collect(sql,current)
except Exception as e:maintenance.append('Gameplay counters unavailable: '+type(e).__name__)
try:perf=evidence.performance()
except Exception as e:maintenance.append('Profiler unavailable: '+type(e).__name__)
request=urllib.request.Request(base+'/api/realm-insights',data=json.dumps({'at':time.time(),'recoveries':recovery_rows,'outcomes':outcomes,'performance':perf}).encode(),headers={'Content-Type':'application/json','Authorization':'Bearer '+cfg['AUTONOMY_TOKEN']})
with urllib.request.urlopen(request,timeout=20) as response:assert response.status==200
maintenance.append('Realm report sampled')
except Exception as e:maintenance.append('Realm report needs attention: '+type(e).__name__)
+41
View File
@@ -0,0 +1,41 @@
"""Read native counters and profiler output; return bounded public measurements."""
import json,subprocess,time,math
COUNTERS=('quests','abandoned','lootEvents','lootItems','partyKills','rewarded')
def normalize_performance(raw):
def number(v):
try:
n=float(v);return n if math.isfinite(n) and n>=0 else 0
except (TypeError,ValueError):return 0
rows=[]
for metric in raw.get('metrics',[]):
for row in metric.get('rows',[]):
if number(row.get('count'))<=0:continue
rows.append({'category':str(metric.get('type',''))[:40],'name':str(row.get('name',''))[:160],'calls':number(row.get('count')),'totalMs':number(row.get('totalTime'))/1000,'averageMs':number(row.get('avgTime'))/1000,'maxMs':number(row.get('maxTime'))/1000,'perTickMs':number(row.get('timePerTick'))/1000})
# Nested timers overlap. Do not sum their percentages or call this CPU usage.
rows.sort(key=lambda r:r['totalMs'],reverse=True)
ticks=number(raw.get('fullTickCount'))
return {'at':number(raw.get('generatedAt')),'enabled':raw.get('enabled') is True,'ticks':ticks,'averageTickMs':number(raw.get('fullTickTotalTime'))/max(1,ticks)/1000,'rows':rows[:20]}
def collect(sql,state):
bots=[b for b in state.get('bots',[]) if b.get('autonomy',{}).get('managed') and type(b.get('autonomy',{}).get('characterGuid')) is int]
allowed={b['autonomy']['characterGuid']:b for b in bots}
rows={}
for line in sql('SELECT guid,quests,abandoned,loot_events,loot_items,party_kills FROM acore_auth.azerothai_outcomes').splitlines():
fields=list(map(int,line.split('\t')))
if fields[0] in allowed:rows[fields[0]]=dict(zip(COUNTERS,fields[1:]+[0]))
rewarded={}
if allowed:
guids=','.join(map(str,allowed))
for line in sql('SELECT guid,COUNT(*) FROM acore_characters.character_queststatus_rewarded WHERE guid IN ('+guids+') GROUP BY guid').splitlines():
guid,count=map(int,line.split('\t'));rewarded[guid]=count
result=[]
for guid,b in allowed.items():
counters=rows.get(guid,dict.fromkeys(COUNTERS,0));counters['rewarded']=rewarded.get(guid,0)
result.append({'guid':guid,'name':b['name'],**counters})
return result
def performance():
# Both paths are fixed; no user-controlled shell input and no credentials.
for path in ['/azerothcore/env/dist/logs/pmon_tick.json','/azerothcore/pmon_tick.json']:
p=subprocess.run(['docker','exec','ac-worldserver','cat',path],capture_output=True,text=True,timeout=10)
if p.returncode==0:return normalize_performance(json.loads(p.stdout))
return None
+2 -2
View File
@@ -1,6 +1,6 @@
import {spawnSync} from 'node:child_process';
import {writeFileSync,mkdirSync} from 'node:fs';
const result=spawnSync(process.execPath,['node_modules/typescript/bin/tsc','core/realm-insights.ts','core/service.ts','core/seed.ts','core/ale.ts','core/conversation.ts','core/autonomy.ts','--types','@cloudflare/workers-types,node','--outDir','.test-build','--module','commonjs','--target','es2022','--moduleResolution','node','--esModuleInterop','--skipLibCheck','--strict'],{stdio:'inherit'});
const result=spawnSync(process.execPath,['node_modules/typescript/bin/tsc','core/realm-outcomes.ts','core/realm-insights.ts','core/service.ts','core/seed.ts','core/ale.ts','core/conversation.ts','core/autonomy.ts','--types','@cloudflare/workers-types,node','--outDir','.test-build','--module','commonjs','--target','es2022','--moduleResolution','node','--esModuleInterop','--skipLibCheck','--strict'],{stdio:'inherit'});
if(result.status)process.exit(result.status);
mkdirSync('.test-build',{recursive:true});writeFileSync('.test-build/package.json','{"type":"commonjs"}');
process.exit(spawnSync(process.execPath,['--test','tests/realm-insights.test.cjs','tests/core.test.cjs','tests/hardcore.test.cjs','tests/living-world.test.cjs','tests/ale.test.cjs','tests/brains.test.cjs','tests/conversation.test.cjs','tests/autonomy.test.cjs','tests/community.test.cjs'],{stdio:'inherit'}).status??1);
process.exit(spawnSync(process.execPath,['--test','tests/realm-outcomes.test.cjs','tests/realm-insights.test.cjs','tests/core.test.cjs','tests/hardcore.test.cjs','tests/living-world.test.cjs','tests/ale.test.cjs','tests/brains.test.cjs','tests/conversation.test.cjs','tests/autonomy.test.cjs','tests/community.test.cjs'],{stdio:'inherit'}).status??1);