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
Source branch: feature/ai-brain-router Source commit: c2a09e0460465201a58aaab56c71424e9a96405e
79 lines
3.3 KiB
TypeScript
79 lines
3.3 KiB
TypeScript
export type AleScalar = string|number|boolean|null;
|
|
export type AleOperatorRequest =
|
|
| {kind:'command';command:string}
|
|
| {kind:'global';method:string;args?:AleScalar[]}
|
|
| {kind:'player';player:string;method:string;args?:AleScalar[]}
|
|
| {kind:'playerbotAI';player:string;method:string;args?:AleScalar[]}
|
|
| {kind:'raw';source:string};
|
|
|
|
const IDENT=/^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
|
|
export function luaString(value:string){
|
|
return '"' + value
|
|
.replace(/\\/g,'\\\\')
|
|
.replace(/"/g,'\\"')
|
|
.replace(/\r/g,'\\r')
|
|
.replace(/\n/g,'\\n') + '"';
|
|
}
|
|
|
|
function luaValue(value:AleScalar):string{
|
|
if(value===null)return 'nil';
|
|
if(typeof value==='string')return luaString(value);
|
|
if(typeof value==='number'){
|
|
if(!Number.isFinite(value))throw new Error('ALE numeric arguments must be finite');
|
|
return String(value);
|
|
}
|
|
return value?'true':'false';
|
|
}
|
|
|
|
function method(name:string){
|
|
if(!IDENT.test(name))throw new Error('Invalid ALE method name');
|
|
return name;
|
|
}
|
|
|
|
function args(values:AleScalar[]|undefined){
|
|
return (values??[]).map(luaValue).join(',');
|
|
}
|
|
|
|
export function compileAleOperatorRequest(input:AleOperatorRequest,rawEnabled:boolean){
|
|
if(input.kind==='command'){
|
|
if(!input.command.trim()||input.command.length>500)throw new Error('Invalid server command');
|
|
return `RunCommand(${luaString(input.command.trim())}); return "command dispatched"`;
|
|
}
|
|
if(input.kind==='global'){
|
|
return `local fn=_G[${luaString(method(input.method))}]; if type(fn)~="function" then error("Unknown global ALE method") end; return fn(${args(input.args)})`;
|
|
}
|
|
if(input.kind==='player'){
|
|
return `local p=GetPlayerByName(${luaString(input.player)}); if not p then error("Player not found") end; return p:${method(input.method)}(${args(input.args)})`;
|
|
}
|
|
if(input.kind==='playerbotAI'){
|
|
return `local p=GetPlayerByName(${luaString(input.player)}); if not p then error("Player not found") end; local ai=p:GetBotAI(); if not ai then error("PlayerBotAI not found") end; return ai:${method(input.method)}(${args(input.args)})`;
|
|
}
|
|
if(!rawEnabled)throw new Error('Raw ALE execution is disabled. Set ALE_RAW_EXEC_ENABLED=true on the private server to enable the full operator surface.');
|
|
if(!input.source.trim()||input.source.length>12000)throw new Error('Invalid raw ALE source');
|
|
return input.source;
|
|
}
|
|
|
|
export function wrapAleCommand(id:string,source:string){
|
|
const qid=luaString(id);
|
|
return `local __command_id=${qid}
|
|
local function __stringify(v)
|
|
if v==nil then return "nil" end
|
|
local t=type(v)
|
|
if t=="string" or t=="number" or t=="boolean" then return tostring(v) end
|
|
return "<"..t.."> "..tostring(v)
|
|
end
|
|
local __ok,__result=pcall(function()
|
|
${source}
|
|
end)
|
|
AzerothAIComplete(__command_id,__ok,__stringify(__result))
|
|
`;
|
|
}
|
|
|
|
export const ALE_SURFACE = {
|
|
gameClasses:['ALEQuery','Achievement','Aura','BattleGround','ChatHandler','Corpse','Creature','GameObject','GemPropertiesEntry','Global','Group','Guild','Item','ItemTemplate','Loot','Map','Object','Pet','Player','Quest','Roll','Spell','SpellEntry','SpellInfo','Ticket','Transport','Unit','Vehicle','WorldObject','WorldPacket'],
|
|
playerbotClasses:['PlayerBot','PlayerBotAI','PlayerBotFactory','PlayerBotHolder','PlayerBotMgr','PlayerBotsMgr','RandomPlayerBotMgr'],
|
|
operatorModes:['server-command','global-method','player-method','playerbot-ai-method','raw-lua'],
|
|
modelAccess:'none'
|
|
} as const;
|