Appearance
aowlspt/abi
Source: aowl/src/aowlspt/abi.nim — 305 lines.
The raw ABI, transliterated from abi/aowlspt_abi.h, for nimony/aowl.
Deliberately mechanical: the same structs, in the same order, with the same calling convention, and nothing else. Ergonomics live in aowlspt.nim, so a new field in the header has exactly one place to be mirrored.
Two nimony specifics that are load-bearing here:
- Null pointers are written as
cast[T](0). nimony'sptr Tis non-nil by default and rejects a barenilliteral, which is a feature — it means every null in this file is one somebody chose. {.bycopy.}objects match C layout.AowlSliceis 16 bytes on x86-64 (8-byte pointer, 4-byte length, 4 bytes tail padding);{.packed.}would break the match rather than tighten it.sizeofis asserted against the C header bytests/abi_layout.nim.
Types
Status
nim
Status* = int32aowl/src/aowlspt/abi.nim:57
LogLevel
nim
LogLevel* = enum
llTrace = 0'i32
llDebug = 1
llInfo = 2
llSuccess = 3
llWarn = 4
llError = 5aowl/src/aowlspt/abi.nim:91
Encoding
nim
Encoding* = enum
encJson = 0'i32
encCbor = 1
encRaw = 2
encNif = 3aowl/src/aowlspt/abi.nim:99
Side
nim
Side* = enum
sideServer = 1'i32
sideClient = 2
sideSim = 3aowl/src/aowlspt/abi.nim:105
PatchKind
nim
PatchKind* = enum
pkPrefix = 0'i32
pkPostfix = 1
pkFinalizer = 2aowl/src/aowlspt/abi.nim:110
RouteKind
nim
RouteKind* = enum
rkStatic = 0'i32
rkDynamic = 1aowl/src/aowlspt/abi.nim:115
ModFlag
nim
ModFlag* = enum
mfHotReloadable ## bit 0 — implements stateSave/stateLoad
mfThreadSafe ## bit 1 — callbacks are safe off the main threadaowl/src/aowlspt/abi.nim:119
ModFlags
nim
ModFlags* = set[ModFlag]aowl/src/aowlspt/abi.nim:123
Handle
nim
Handle* = uint64aowl/src/aowlspt/abi.nim:125
Bytes
nim
Bytes* = ptr UncheckedArray[uint8]aowl/src/aowlspt/abi.nim:127
AowlSlice
nim
AowlSlice* {.bycopy.} = object
data*: Bytes
len*: int32Borrowed bytes, valid only for the duration of the call that produced it.
aowl/src/aowlspt/abi.nim:129
AowlBuffer
nim
AowlBuffer* {.bycopy.} = object
data*: Bytes
len*: int32Owned bytes, allocated with the host allocator and freed by the receiver.
aowl/src/aowlspt/abi.nim:134
HostInfo
nim
HostInfo* {.bycopy.} = object
size*: int32
abiVersion*: uint32
abiRevision*: uint32
side*: int32
hostName*: AowlSlice
hostVersion*: AowlSlice
sptVersion*: AowlSlice
gameVersion*: AowlSlice
encodings*: uint32
modDir*: AowlSlice
dataDir*: AowlSliceaowl/src/aowlspt/abi.nim:139
CallbackFn
nim
CallbackFn* = proc (user: pointer; payload: AowlSlice;
outBuf: ptr AowlBuffer): Status {.cdecl.}aowl/src/aowlspt/abi.nim:154
RouteFn
nim
RouteFn* = proc (user: pointer; url, body, session: AowlSlice;
outBuf: ptr AowlBuffer): Status {.cdecl.}aowl/src/aowlspt/abi.nim:156
PatchFn
nim
PatchFn* = proc (user: pointer; target, args: AowlSlice;
outBuf: ptr AowlBuffer): Status {.cdecl.}aowl/src/aowlspt/abi.nim:158
PatchFramePtr
nim
PatchFramePtr* = pointerA borrowed const AowlPatchFrame*. Opaque here: the accessors live in aowlspt.nim, over the static readers in abi/aowlspt_frame.h, so that this file stays a transliteration of the struct layout and nothing else.
aowl/src/aowlspt/abi.nim:161
TypedPatchFn
nim
TypedPatchFn* = proc (user: pointer; frame: PatchFramePtr): Status {.cdecl.}Revision 4's patch handler: the same two return statuses, and a borrowed view of the saved registers instead of a JSON payload.
aowl/src/aowlspt/abi.nim:167
ArgKind
nim
ArgKind* = enum
akNone = 0'i32
akInt = 1
akFloat = 2
akDouble = 3
akObject = 4
akValue = 5
akBigValue = 6
akVoid = 7
akStack = 8
akUnknown = 9What one slot of a typed frame is, decided once at registration. Mirrors AowlArgKind; the numbers are wire values.
aowl/src/aowlspt/abi.nim:171
HostApi
nim
HostApi* {.bycopy.} = object
size*: int32
ctx*: pointer
info*: ptr HostInfo
alloc*: proc (ctx: pointer; bytes: int32): pointer {.cdecl.}
free*: proc (ctx: pointer; p: pointer) {.cdecl.}
log*: proc (ctx: pointer; level: int32; message: AowlSlice) {.cdecl.}
lastError*: proc (ctx: pointer; outSlice: ptr AowlSlice) {.cdecl.}
configGet*: proc (ctx: pointer; key: AowlSlice; outBuf: ptr AowlBuffer): Status {.cdecl.}
configSet*: proc (ctx: pointer; key, valueJson: AowlSlice): Status {.cdecl.}
dbGet*: proc (ctx: pointer; path: AowlSlice; outBuf: ptr AowlBuffer): Status {.cdecl.}
dbPatch*: proc (ctx: pointer; path, patchJson: AowlSlice): Status {.cdecl.}
routeRegister*: proc (ctx: pointer; url: AowlSlice; kind: int32;
handler: RouteFn; user: pointer): Status {.cdecl.}
eventSubscribe*: proc (ctx: pointer; name: AowlSlice;
handler: CallbackFn; user: pointer): Status {.cdecl.}
eventEmit*: proc (ctx: pointer; name, payload: AowlSlice): Status {.cdecl.}
call*: proc (ctx: pointer; target, args: AowlSlice; outBuf: ptr AowlBuffer): Status {.cdecl.}
resolve*: proc (ctx: pointer; typeName: AowlSlice; outHandle: ptr Handle): Status {.cdecl.}
handleRelease*: proc (ctx: pointer; handle: Handle) {.cdecl.}
patch*: proc (ctx: pointer; target: AowlSlice; kind: int32;
handler: PatchFn; user: pointer): Status {.cdecl.}
schedule*: proc (ctx: pointer; delayMs: int32;
cb: CallbackFn; user: pointer): Status {.cdecl.}
invokeMain*: proc (ctx: pointer; cb: CallbackFn; user: pointer): Status {.cdecl.}
nowMs*: proc (ctx: pointer): int64 {.cdecl.}
# -- revision 2 ---------------------------------------------------------
# Appended, never inserted: a mod built against revision 1 checks `size`
# and simply does not see these.
storeGet*: proc (ctx: pointer; key: AowlSlice; outBuf: ptr AowlBuffer): Status {.cdecl.}
storeSet*: proc (ctx: pointer; key, value: AowlSlice): Status {.cdecl.}
storeList*: proc (ctx: pointer; prefix: AowlSlice; outBuf: ptr AowlBuffer): Status {.cdecl.}
# -- revision 3 ---------------------------------------------------------
# The address behind a handle, for a mod that wants to reach an object on
# its own fast path rather than through `call`. Present only on a host with
# a managed heap, which is why `size` may still say revision 2 on a host
# built from this same header -- see `livePointersReady`.
handlePointer*: proc (ctx: pointer; handle: Handle;
outAddress: ptr uint64): Status {.cdecl.}
handlePin*: proc (ctx: pointer; handle: Handle;
outPinned: ptr Handle): Status {.cdecl.}
# -- revision 4 ---------------------------------------------------------
# The same detour, with the arguments left in the registers the thunk saved
# rather than described as JSON. See `patchTyped` in `aowlspt.nim` for what
# that is worth and `abi/aowlspt_frame.h` for how the frame is read.
patchTyped*: proc (ctx: pointer; target: AowlSlice; kind: int32;
handler: TypedPatchFn; user: pointer): Status {.cdecl.}
# -- revision 5 ---------------------------------------------------------
# Hand a notification to whatever the host has that reaches this player --
# a websocket, on the backend. `ErrNotFound` means "no connection for that
# session", which is a normal answer and the mod's cue to fall back rather
# than a failure.
notifyPush*: proc (ctx: pointer; session, payload: AowlSlice): Status {.cdecl.}
# -- revision 6 ---------------------------------------------------------
# Run `cb` on Unity's thread DURING rendering, where immediate-mode
# `UnityEngine.GL` drawing rasterizes -- for native ESP and GL HUDs, which
# `invokeMain` (the update phase) is on the right thread but the wrong point
# in the frame to draw from. `ErrUnsupported` means no render-phase drain is
# bound on this host/build/scene; a mod gates on
# `call("aowlspt.host::render_thread")` -> `bound:true` and draws only then.
invokeRender*: proc (ctx: pointer; cb: CallbackFn; user: pointer): Status {.cdecl.}aowl/src/aowlspt/abi.nim:185
ModInfo
nim
ModInfo* {.bycopy.} = object
size*: int32
abiVersion*: uint32
abiRevision*: uint32
guid*: AowlSlice
name*: AowlSlice
author*: AowlSlice
version*: AowlSlice
sptRange*: AowlSlice
sides*: uint32
flags*: uint32aowl/src/aowlspt/abi.nim:262
ModApi
nim
ModApi* {.bycopy.} = object
size*: int32
self*: pointer
onLoad*: proc (self: pointer): Status {.cdecl.}
onUpdate*: proc (self: pointer; elapsedMs: int64): Status {.cdecl.}
onUnload*: proc (self: pointer): Status {.cdecl.}
stateSave*: proc (self: pointer; outBuf: ptr AowlBuffer): Status {.cdecl.}
stateLoad*: proc (self: pointer; state: AowlSlice): Status {.cdecl.}aowl/src/aowlspt/abi.nim:274
Constants
AbiVersion
nim
AbiVersion* = 1'u32aowl/src/aowlspt/abi.nim:18
AbiRevision
nim
AbiRevision* = 6'u32aowl/src/aowlspt/abi.nim:19
HostApiSizeRev1
nim
HostApiSizeRev1* = 168'i32aowl/src/aowlspt/abi.nim:21
HostApiSizeRev2
nim
HostApiSizeRev2* = 192'i32aowl/src/aowlspt/abi.nim:22
HostApiSizeRev3
nim
HostApiSizeRev3* = 208'i32aowl/src/aowlspt/abi.nim:23
HostApiSizeRev4
nim
HostApiSizeRev4* = 216'i32aowl/src/aowlspt/abi.nim:24
HostApiSizeRev5
nim
HostApiSizeRev5* = 224'i32aowl/src/aowlspt/abi.nim:25
HostApiSizeRev6
nim
HostApiSizeRev6* = 232'i32How large HostApi was at each revision, as literals.
A capability test has to be against the boundary the capability appeared at, not against sizeof(HostApi). The obvious form -- "the host's size is at least the size I know about" -- is right for exactly one revision: revision 3 grows sizeof to 208, so a mod asking only for the revision-2 store would start refusing a revision-2 host that has it.
Literals rather than arithmetic over the struct because the whole point is that they must not move when the struct does. tests/abi_layout.nim and tests/abi_layout.c pin all four to the real offsets, from the two sides.
Revision 3 is handlePointer/handlePin, revision 4 is patchTyped and revision 5 is notifyPush. They are separate boundaries rather than one because a host may have any of them without the others: the addresses need a managed heap, the typed patch needs a working detour engine, the push needs a listening socket, and size says how much was filled rather than which header the host saw.
Revision 5 is where the watermark in that sentence stopped being free. Only the client host can fill 3 and 4; only the backend can fill 5; one integer cannot say "the fifth and not the third". A host in that position fills the entries under its watermark with the refusal it already owes -- abi/aowlspt_notify.h does exactly that for the backend -- so a size test keeps meaning "there is a function here that will answer", which is all it ever established. It cannot mean more than that here in any case: nimony will not cast a proc field to a pointer to compare it against null.
aowl/src/aowlspt/abi.nim:26
Ok
nim
Ok* = 0'i32aowl/src/aowlspt/abi.nim:60
ErrGeneric
nim
ErrGeneric* = -1'i32aowl/src/aowlspt/abi.nim:61
ErrAbi
nim
ErrAbi* = -2'i32aowl/src/aowlspt/abi.nim:62
ErrNotFound
nim
ErrNotFound* = -3'i32aowl/src/aowlspt/abi.nim:63
ErrBadArg
nim
ErrBadArg* = -4'i32aowl/src/aowlspt/abi.nim:64
ErrDecode
nim
ErrDecode* = -5'i32aowl/src/aowlspt/abi.nim:65
ErrUnsupported
nim
ErrUnsupported* = -6'i32aowl/src/aowlspt/abi.nim:66
ErrWrongThread
nim
ErrWrongThread* = -7'i32aowl/src/aowlspt/abi.nim:67
ErrDisposed
nim
ErrDisposed* = -8'i32aowl/src/aowlspt/abi.nim:68
ErrModFault
nim
ErrModFault* = -9'i32aowl/src/aowlspt/abi.nim:69
ErrConfigParse
nim
ErrConfigParse* = -10'i32config.json exists and is not readable JSON, so no key in it can be answered. Distinct from ErrNotFound, which is the ordinary "no such setting, use your default".
They were the same status, and that is how a BOM on the mod manager's config made it read activeLists as empty, resolve nothing, and write a selection naming only itself -- one mod out of ten loaded on the next start, with no error anywhere. A mod that falls back to defaults on any failure, which is most of them, cannot tell "absent" from "the file is broken" without this.
lastError() after one of these names the file and the fault -- the offset and what was found there -- not the key. The key is not what is missing.
Returned by a prefix patch to suppress the original method.
aowl/src/aowlspt/abi.nim:71
PatchSkip
nim
PatchSkip* = 1'i32aowl/src/aowlspt/abi.nim:88
SymAbiVersion
nim
SymAbiVersion* = "aowlspt_abi_version"aowl/src/aowlspt/abi.nim:284
SymDescribe
nim
SymDescribe* = "aowlspt_describe"aowl/src/aowlspt/abi.nim:285
SymInit
nim
SymInit* = "aowlspt_init"aowl/src/aowlspt/abi.nim:286
Routines
nilBytes
nim
func nilBytes*(): Bytes {.inline.}aowl/src/aowlspt/abi.nim:288
isNilPtr
nim
func isNilPtr*(p: pointer): bool {.inline.}aowl/src/aowlspt/abi.nim:289
isNilBytes
nim
func isNilBytes*(p: Bytes): bool {.inline.}aowl/src/aowlspt/abi.nim:290
emptySlice
nim
func emptySlice*(): AowlSlice {.inline.}aowl/src/aowlspt/abi.nim:292
emptyBuffer
nim
func emptyBuffer*(): AowlBuffer {.inline.}aowl/src/aowlspt/abi.nim:295
sideBit
nim
func sideBit*(s: Side): uint32 {.inline.}The sides bitmask is 1 << AowlSide, matching the header.
aowl/src/aowlspt/abi.nim:298
flagBits
nim
func flagBits*(flags: ModFlags): uint32aowl/src/aowlspt/abi.nim:301

