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