Files
AzerothAI/scripts/simulate-live-realm.mjs
T
Richard Clark 5f0fc86cf9
Core tests / core (push) Successful in 9m57s
Full verification / application (push) Failing after 5m16s
Full verification / local-brain (push) Failing after 6m15s
Realm simulation / simulated-live-world (push) Failing after 5m32s
WoW integration lab / real-worldserver (push) Canceled after 17m23s
Import verified AzerothAI snapshot from GitHub
Source branch: feature/ai-brain-router
Source commit: c2a09e0460465201a58aaab56c71424e9a96405e
2026-09-22 13:12:54 -04:00

193 lines
9.6 KiB
JavaScript

const base=(process.env.AZEROTHAI_URL||'http://127.0.0.1:8787').replace(/\/$/,'');
const token=process.env.ALE_BRIDGE_TOKEN||'simulation-bridge-token';
const autonomyToken=process.env.AUTONOMY_TOKEN||'simulation-autonomy-token';
const sleep=ms=>new Promise(r=>setTimeout(r,ms));
const assert=(condition,message)=>{if(!condition)throw new Error(message);};
async function json(path,options={}){
const r=await fetch(base+path,options);
const text=await r.text();
let body;
try{body=text?JSON.parse(text):null;}catch{body={raw:text};}
if(!r.ok)throw new Error(`${options.method||'GET'} ${path} -> ${r.status}: ${text.slice(0,1000)}`);
return body;
}
async function ale(kind,payload={}){
return json('/api/bridge/ale/event',{
method:'POST',
headers:{'Content-Type':'application/json',Authorization:`Bearer ${token}`},
body:JSON.stringify({kind,payload})
});
}
async function state(){return json('/api/state');}
console.log('Booting simulated live-world event sequence...');
const initial=await state();
assert(initial.bots.some(b=>b.id==='hero-zaku'),'Seeded Zaku is missing');
assert(!initial.population?.lastPlan,'Population plan should be empty before authoritative human movement');
await ale('bridge.ready',{pollMs:750,protectedCharacter:'Zaku'});
await ale('server.startup',{players:1});
await ale('player.login',{
name:'Zaku',characterGuid:9001,accountId:501,level:14,mapId:0,zoneId:85,areaId:159,isBot:true,zoneName:'Brill'
});
await ale('player.zone',{
name:'Zaku',characterGuid:9001,accountId:501,level:14,mapId:0,zoneId:130,areaId:130,isBot:true,zoneName:'Silverpine Forest'
});
let afterBotMove=await state();
assert(!afterBotMove.population?.lastPlan,'AI resident movement incorrectly triggered the Population Director');
await ale('player.combat.enter',{
name:'Zaku',characterGuid:9001,accountId:501,level:14,mapId:0,zoneId:130,areaId:130,isBot:true,zoneName:'Silverpine Forest',enemy:'Moonrage Glutton'
});
await ale('player.close-call',{
name:'Zaku',characterGuid:9001,accountId:501,level:14,mapId:0,zoneId:130,areaId:130,isBot:true,zoneName:'Silverpine Forest',healthBefore:23,incomingDamage:61
});
await ale('player.combat.leave',{
name:'Zaku',characterGuid:9001,accountId:501,level:14,mapId:0,zoneId:130,areaId:130,isBot:true,zoneName:'Silverpine Forest'
});
await ale('player.quest.accept',{
name:'Zaku',characterGuid:9001,accountId:501,level:14,mapId:0,zoneId:130,areaId:130,isBot:true,zoneName:'Silverpine Forest',quest:'The Dead Fields'
});
await ale('player.snapshot',{
name:'Zaku',characterGuid:9001,accountId:501,level:14,mapId:0,zoneId:130,areaId:130,isBot:true,zoneName:'Silverpine Forest'
});
await ale('player.login',{
name:'Durnak',characterGuid:9002,accountId:502,level:22,mapId:0,zoneId:40,areaId:40,isBot:true,zoneName:'Westfall'
});
await ale('player.zone',{
name:'Durnak',characterGuid:9002,accountId:502,level:22,mapId:0,zoneId:40,areaId:40,isBot:true,zoneName:'Westfall'
});
await ale('player.combat.enter',{
name:'Durnak',characterGuid:9002,accountId:502,level:22,mapId:0,zoneId:40,areaId:40,isBot:true,zoneName:'Westfall',enemy:'Defias Pillager'
});
await ale('player.killed-by-creature',{
name:'Durnak',characterGuid:9002,accountId:502,level:22,mapId:0,zoneId:40,areaId:40,isBot:true,zoneName:'Westfall',killer:'Defias Pillager'
});
await ale('player.resurrect',{
name:'Durnak',characterGuid:9002,accountId:502,level:22,mapId:0,zoneId:40,areaId:40,isBot:true,zoneName:'Westfall'
});
await ale('player.killed-by-creature',{
name:'Zaku',characterGuid:9001,accountId:501,level:14,mapId:0,zoneId:130,areaId:130,isBot:true,zoneName:'Silverpine Forest',killer:'Moonrage Glutton'
});
await ale('player.login',{
name:'Player',level:12,mapId:0,zoneId:10,areaId:10,isBot:false,zoneName:'Duskwood'
});
await ale('player.zone',{
name:'Player',level:12,mapId:0,zoneId:10,areaId:10,isBot:false,zoneName:'Duskwood'
});
await sleep(250);
const autonomy=await json('/api/autonomy/tick',{
method:'POST',
headers:{'Content-Type':'application/json',Authorization:`Bearer ${autonomyToken}`},
body:'{}'
});
assert(autonomy.ran===true,'Autonomy heartbeat did not select a live managed Playerbot');
assert(autonomy.name==='Zaku',`Expected Zaku to receive autonomous intent, got ${autonomy.name}`);
const polled=await fetch(base+'/api/bridge/ale/poll',{
headers:{Authorization:`Bearer ${token}`}
});
assert(polled.status===200,`ALE bridge did not receive queued autonomous command: HTTP ${polled.status}`);
const commandId=polled.headers.get('x-azerothai-command');
const lua=await polled.text();
assert(commandId,'Autonomous ALE command had no command id');
assert(lua.includes('GetPlayerByName("Zaku")'),'Autonomous ALE command did not target Zaku');
assert(lua.includes(':GetBotAI()'),'Autonomous ALE command did not route through PlayerBotAI');
await json('/api/bridge/ale/result',{
method:'POST',
headers:{'Content-Type':'application/json',Authorization:`Bearer ${token}`},
body:JSON.stringify({id:commandId,ok:true,result:'simulated PlayerBotAI intent accepted'})
});
const finalState=await state();
const monitor=await json('/api/world-monitor');
const zaku=finalState.bots.find(b=>b.id==='hero-zaku');
const durnak=finalState.bots.find(b=>b.id==='bot-2');
assert(zaku,'Zaku disappeared');
assert(durnak,'Durnak disappeared');
assert(zaku.status!=='dead','Zaku entered the death state');
assert(zaku.zone==='Silverpine Forest',`Zaku authoritative zone did not sync: ${zaku.zone}`);
assert(zaku.level===14,`Zaku authoritative level did not sync: ${zaku.level}`);
assert(zaku.autonomy?.managed===true,'Zaku was not adopted as a managed live resident');
assert(zaku.autonomy?.characterGuid===9001,`Zaku character GUID binding was not persisted: ${zaku.autonomy?.characterGuid}`);
assert(zaku.autonomy?.accountId===501,`Zaku account binding was not persisted: ${zaku.autonomy?.accountId}`);
assert(zaku.decisions>=1,'Autonomous heartbeat did not persist a Zaku decision');
assert(zaku.memories.some(m=>m.source==='observation'&&/barely survived/i.test(m.text)),'Zaku did not retain a close-call memory');
assert(zaku.memories.some(m=>/accepted The Dead Fields/i.test(m.text)),'Zaku did not retain an authoritative quest memory');
assert(durnak.status==='dead',`Durnak should be permanently dead, got ${durnak.status}`);
assert(durnak.death?.verified===true,'Durnak death was not verified');
assert(durnak.death?.source==='azerothcore',`Durnak death source should be azerothcore, got ${durnak.death?.source}`);
assert(durnak.death?.killer==='Defias Pillager','Durnak killer was not preserved');
assert(zaku.memories.some(m=>/Durnak died permanently/i.test(m.text)),'Zaku did not receive memorial memory for a known realm death');
assert(finalState.population?.lastPlan?.trigger?.zone==='Duskwood','Human movement did not drive the Population Director');
assert(finalState.population?.lastPlan?.trigger?.playerName==='Player','Human player name was not preserved in population trigger');
assert(monitor.ale?.bridgeReady===true,'World Monitor did not see bridge.ready');
assert(monitor.ale?.activeRecently===true,'World Monitor did not mark recent ALE activity live');
assert((monitor.playerbots?.observedBots??0)>=2,'World Monitor did not observe both live Playerbot identities');
assert(monitor.events.some(e=>e.kind==='player.close-call'&&e.name==='Zaku'),'World Monitor missed Zaku close-call ALE event');
assert(monitor.events.some(e=>e.kind==='player.killed-by-creature'&&e.name==='Durnak'),'World Monitor missed authoritative death ALE event');
assert(finalState.events.some(e=>e.kind==='error'&&/Plot armor breach/.test(e.message)&&e.botId==='hero-zaku'),'Zaku kill report did not produce a plot-armor breach alert');
assert(finalState.events.some(e=>e.kind==='error'&&/permanently dead resident Durnak/.test(e.message)),'Hardcore resurrection attempt was not rejected in state');
const report={
result:'PASS',
zaku:{
status:zaku.status,
level:zaku.level,
zone:zaku.zone,
closeCallRemembered:zaku.memories.some(m=>/barely survived/i.test(m.text)),
rememberedDurnakDeath:zaku.memories.some(m=>/Durnak died permanently/i.test(m.text)),
plotArmorBreachAlerted:finalState.events.some(e=>e.kind==='error'&&/Plot armor breach/.test(e.message)&&e.botId==='hero-zaku'),
characterGuid:zaku.autonomy?.characterGuid,
autonomousDecisions:zaku.decisions,
lastDecision:zaku.lastDecision?.action
},
durnak:{
status:durnak.status,
verifiedDeath:durnak.death?.verified,
deathSource:durnak.death?.source,
killer:durnak.death?.killer,
resurrectionRejected:finalState.events.some(e=>e.kind==='error'&&/permanently dead resident Durnak/.test(e.message))
},
population:{
trigger:finalState.population?.lastPlan?.trigger?.kind,
zone:finalState.population?.lastPlan?.trigger?.zone,
playerName:finalState.population?.lastPlan?.trigger?.playerName,
selected:finalState.population?.lastPlan?.selectedBotIds?.length??0
},
worldMonitor:{
bridgeReady:monitor.ale?.bridgeReady,
activeRecently:monitor.ale?.activeRecently,
observedBotEvents:monitor.playerbots?.observedBots,
visibleAleEvents:monitor.events.length
},
checks:[
'AI movement does not trigger human-centered population activation',
'authoritative bot zone and level sync back into AzerothAI',
'Zaku close call becomes a diegetic memory',
'ordinary Hardcore death is permanent and verified',
'dead resident resurrection is rejected by AzerothAI state',
'Zaku cannot enter the death state even when a bad authoritative kill event arrives',
'human movement drives the Population Director',
'World Monitor receives live-style ALE telemetry',
'live resident identity is bound to authoritative character GUID/account',
'autonomy heartbeat queues a real PlayerBotAI command through ALE'
]
};
console.log('\n=== AZEROTHAI LIVE-WORLD SIMULATION ===');
console.log(JSON.stringify(report,null,2));