dao::script

Script commands from C++: call them, intercept them, replace them or add new ones.

Declared in dao/Script.h, dao/ScriptCall.h, dao/ScriptHooks.h.

Classes

Class  
Array A script array that your plugin owns (dao/core/ScriptArray.h), such as one popped off the stack.
CallResult The outcome of a call by name.
CommandCall One call of a script command, as an intercept receives it.
Owned An engine structure that your plugin owns, such as one popped off the stack.
ScopedSelf Sets OBJECT_SELF for as long as it exists, and restores the previous one when destroyed.
Stack The script VM’s typed stack.

Type aliases

CommandInfo

using CommandInfo = core::ScriptCommandInfo

A script command’s number, name, signature and default arguments.

Handler

using Handler = int(__fastcall *)(void *commands, int edx, int id, int paramCount)

A command handler.

It pops its arguments off Stack, pushes its result, and returns kOk or one of the error codes above. The engine calls it as a __thiscall member of the commands object, so it is declared __fastcall: ecx arrives as commands, and edx is unused.

Parameter  
commands the script commands object (CClientExoApp::Commands)
id the command’s number
paramCount the number of arguments the script pushed

Intercept

using Intercept = std::function<int(CommandCall &)>

A plugin’s handler for a command.

It returns the command’s result, usually what Proceed returned.

OwnedCommand

using OwnedCommand = Owned<::dao::Command, EngineStructure::Command>

a command your plugin owns

OwnedEffect

using OwnedEffect = Owned<CGameEffect, EngineStructure::Effect>

an effect your plugin owns

OwnedEvent

using OwnedEvent = Owned<CScriptEvent, EngineStructure::Event>

an event your plugin owns

Value

using Value = core::ScriptValue

A script value of any type: int, float, string, object, vector or resource.

Functions

Call()

CallResult Call(const CommandInfo &command, std::span<const Value> args)

Calls a script command with the given arguments.

Arguments you leave off take the command’s defaults; a default of OBJECT_SELF is worked out at the time of the call. A returned array of ints, floats, strings or objects comes back as its elements.

Call it only on the game thread, like dao::script::Invoke. Frame callbacks run there.

Parameter  
command the command, from dao/generated/ScriptTable.h
args the arguments in the script’s order. They are checked against the command’s signature before the call.

Returns a result with ok false and error set if the arguments do not match, the command takes or returns a location, effect, event or command, or the call fails

Call()

CallResult Call(std::string_view name, std::initializer_list<Value> args)

Calls the script command with this name, taking the arguments as a braced list.

Call()

CallResult Call(std::string_view name, std::span<const Value> args)

Calls the script command with this name, as above.

Returns a result with ok false if there is no such command.

CallHandler()

int CallHandler(Handler handler, int id, int paramCount)

Calls a handler the way ExecuteCommand does.

A replacement handler uses it to call the one it replaced.

Returns what the handler returns

CommandsReady()

bool CommandsReady()

Returns whether the game has filled in its table of script commands.

CommandTable()

Handler * CommandTable()

Returns the command table: kSlots handlers, indexed by command number.

The engine fills the table early in start-up, after plugins load. To set your slots before the first script runs, detour addr::kCVirtualMachineCommands_InitializeCommands and call Set after the original returns.

Returns the table, or null before the engine has created it

Copy()

OwnedCommand Copy(const ::dao::Command &command)

Makes an owned copy of a command, so you can keep it after the call that passed it to you.

For example, an AddCommand intercept can hold a command back and queue it later.

Returns the copy, or an empty Owned if the engine made no copy

Copy()

OwnedEffect Copy(const CGameEffect &effect)

Makes an owned copy of an effect, so you can keep it after the call that passed it to you.

Returns the copy, or an empty Owned if the engine made no copy

Copy()

OwnedEvent Copy(const CScriptEvent &event)

Makes an owned copy of an event, so you can keep it after the call that passed it to you.

Returns the copy, or an empty Owned if the engine made no copy

CopyStructure()

void * CopyStructure(EngineStructure type, const void *structure)

Copies an engine structure by calling the engine, the same copy the VM makes on every push and pop.

Returns the copy, which you must free with DestroyStructure; null if the engine made no copy or the application does not exist yet

DestroyStructure()

void DestroyStructure(EngineStructure type, void *structure)

Destroys an engine structure by calling the engine, which frees it on the engine’s heap.

Does nothing if the structure is null or the application does not exist yet.

Get()

Handler Get(int id)

Returns the handler in a slot.

Returns the handler, or null if the slot is empty, the number is out of range, or the table does not exist yet

InterceptCommand()

bool InterceptCommand(int command, Intercept intercept)

Runs intercept each time a script calls the command.

Intercepts form a chain: each one’s Proceed runs the intercept installed before it, from this plugin or another, and the first one installed runs the game’s command. An intercept added before the game fills in its command table is installed when it does. Intercepts run on the game thread, in the middle of a script. If one throws, the rest of the chain answers instead.

Parameter  
command the command’s number, listed in dev/SCRIPT_COMMANDS.md

Returns false if no command has that number, or intercept is empty

Invoke()

auto Invoke(int command, const Args &...args)

Calls a script command by number, the same way a script does.

dao/generated/ScriptApi.h wraps every command in a function with its real signature, so you rarely need to call this directly.

auto team = dao::script::Invoke<std::int32_t>(807, creature); // GetTeamId(creature)

Call it on the game’s logic thread, where scripts run, because the VM is not thread-safe. It is fine to call while a script is running. If the call fails, the stack is cut back to the depth it had before the call.

Parameter  
command the command’s number
args the arguments, in the order a script would pass them

Returns for a command that returns an R, an optional holding the result, empty if the call failed; for R = void, true if the call succeeded

OnCommandsReady()

void OnCommandsReady(std::function<void()> ready)

Runs ready once the game has filled in its table of script commands, or right away if it already has.

The first call places a hook to find out when that happens.

ScriptThreadId()

std::uint32_t ScriptThreadId()

Returns the ID of the game thread: the thread that filled in the command table, which scripts and frame callbacks run on.

Call dao::script::Invoke on this thread.

Returns 0 until the table is filled in

Self()

ObjectId Self()

Returns the object a command sees as OBJECT_SELF, the object the running script belongs to.

Returns the object, or kInvalidObject if the application does not exist yet

Set()

Handler Set(int id, Handler handler)

Puts a handler in a slot, either to replace a command or to add a new one.

About 150 slots are empty, and a script can call a new command once script.ldf declares it with that number. The write is atomic, so the VM sees either the old handler or the new one, never half of one.

Returns the handler it replaced, or null if the slot was empty. Also returns null, and changes nothing, if the number is out of range or the table does not exist yet

StackDepth()

std::int32_t StackDepth()

Returns the number of values on the VM’s stack, or 0 if the VM does not exist yet.

TruncateStack()

void TruncateStack(std::int32_t depth)

Cuts the VM’s stack back to a depth, destroying the values above it as a pop would.

Does nothing if the depth is at or above the current one.

Variables

kInvalidCommand

constexpr int kInvalidCommand = -642

what ExecuteCommand returns for an empty slot

kOk

constexpr int kOk = 0

what a handler returns when it succeeds

kSlots

constexpr int kSlots = 0x35F

The number of slots in the command table.

ExecuteCommand rejects a command number of 0x35F or more.

kStackOverflow

constexpr int kStackOverflow = -638

what a handler returns when a push fails

kStackUnderflow

constexpr int kStackUnderflow = -639

what a handler returns when a pop fails