Skip to content

Repository files navigation

XRF X-Ray 16 SDK

XRF X-Ray 16 SDK

npm version sdk book Node.js CI

xray16 provides TypeScript declarations, test helpers, and TypeScriptToLua plugins for the Lua APIs exposed by OpenXRay (X-Ray 16). Use it to compile XRF game scripts from TypeScript, including with xrf-engine.

X-Ray 16 engine API documents game globals, luabind classes, UI and GOAP classes, and script objects.

Quick start

Install the SDK and its required TypeScriptToLua peer dependency:

npm install xray16 typescript-to-lua

typescript-to-lua is the SDK's only required peer dependency and requires a compatible TypeScript version. Install jest, ts-jest, and typescript when using xray16/testing; they are marked as optional peers so other consumers do not need Jest. The fengari and ini dependencies used by xray16/mocks install automatically.

Merge the following into your tsconfig.json to load the declarations. Include only the ambient typedef packages your project uses.

{
  "compilerOptions": {
    "types": [
      "@typescript-to-lua/language-extensions",
      "xray16",
      "xray16/typedefs/extensions",
      "xray16/typedefs/luajit",
    ],
  },
}

This config loads types; game builds also need the relevant TypeScriptToLua plugins.

For an unreleased build, install xray16@experimental from npm, or use the rolling nightly GitHub release:

npm install https://github.com/xray-forge/xrf-xray16-sdk/releases/download/nightly/xray16-nightly.tgz

Entry points

Import Use it for
xray16 Engine globals, luabind classes, UI and GOAP classes, and script objects.
xray16/alias Readable aliases for engine declaration names and virtual engine enums.
xray16/macros Compile-time helpers with a Node/Jest fallback.
xray16/lib Shared aliases, constants, and small runtime helpers.
xray16/testing Jest configuration and setup helpers.
xray16/mocks Lua-like runtime helpers for Node-based tests.
xray16/typedefs/* Opt-in ambient declarations for X-Ray and bundled Lua libraries.
xray16/plugins/* TypeScriptToLua build plugins.

Engine types and aliases

Import from xray16 to use the engine's Lua binding names. This example checks whether a stalker or monster can see the object under the player's crosshair:

import { type game_object, level } from "xray16";

export function isTargetVisible(npc: game_object): boolean {
  const target = level.get_target_obj();

  return target !== null && npc.see(target);
}

Use xray16/alias for TypeScript-style names such as GameObject and Vector.

import type { GameObject, ServerObject, Vector } from "xray16/alias";

export interface SpawnPoint {
  object: ServerObject;
  position: Vector;
  owner: GameObject;
}

Type aliases are erased at build time. Virtual enums are compile-time declarations folded by the inline plugin in game builds; Jest and Node can import their runtime objects from xray16/alias.

Macros and shared helpers

Use macros for operations that should be replaced during Lua compilation but still run under Jest or Node.

import { $filename, $fromObject, $isNil } from "xray16/macros";

export function readConfig(value: Record<string, string> | null | undefined): LuaTable<string, string> {
  if ($isNil(value)) {
    return $fromObject<string, string>({ source: $filename });
  }

  return $fromObject(value);
}

The macros plugin removes the import and replaces helper usage in game builds. The shipped runtime module supports the same imports under Jest and Node, but $filename and $dirname return placeholder strings there. Table conversions such as $fromObject require the Lua-like globals installed by the test setup.

xray16/lib provides shared aliases, constants, and utility helpers:

import { MAX_U16, clamp, type TSection } from "xray16/lib";

export function normalizeSection(section: TSection, value: number): string {
  return `${section}:${clamp(value, 0, MAX_U16)}`;
}

Type aliases are erased at build time. The inline plugin can fold @inline constants and helpers such as clamp. Runtime helpers such as round and range need a Lua module in game builds. Follow the libcompile setup to map xray16/lib to its TypeScript source and emit an xray_bundle Lua module.

Test X-Ray code under Node

createJestConfig() returns a ts-jest configuration that maps bare xray16 imports to the SDK runtime stand-in.

// jest.config.cjs
const { createJestConfig } = require("xray16/testing");

module.exports = createJestConfig({
  roots: ["<rootDir>/src"],
  moduleNameMapper: { "^@/(.*)": "<rootDir>/src/$1" },
});

It installs Lua-like globals and the xray16 module mock before each test file, then registers Jest matchers such as toBeNil, toEqualLuaTables, and toEqualLuaArrays. Add xray16/typedefs/jest to compilerOptions.types to type-check those matchers.

Consumer moduleNameMapper entries override SDK entries with the same key. Consumer setupFiles and setupFilesAfterEnv entries run after the SDK entries in their respective setup phases; other top-level options replace their defaults.

For a custom setup, call setupLuaGlobals() from xray16/testing. Import setupXrayRuntime() only from a Jest setup file or test setup module, not from the Jest config file: its module eagerly loads the Jest-dependent mock runtime. The helper calls jest.mock to register engine exports.

import { setupLuaGlobals } from "xray16/testing";
import { setupXrayRuntime } from "xray16/testing/setup-xray-runtime";

setupLuaGlobals();
setupXrayRuntime({
  editor: jest.fn(() => true),
});

Jest mock helpers are available from xray16/testing/utils:

import { replaceFunctionMock, resetFunctionMock } from "xray16/testing/utils";

Ambient typedefs

Ambient typedefs describe X-Ray globals and bundled Lua libraries. They are not modules to import; add them to compilerOptions.types or reference them with /// <reference types="..." />.

Typedef Provides
xray16/typedefs/extensions OpenXRay table and string extensions, including table.random, table.size, and string.trim variants.
xray16/typedefs/luajit LuaJIT globals and methods missing from the default TSTL typings.
xray16/typedefs/lfs LuaFileSystem (lfs).
xray16/typedefs/marshal marshal serialization helpers.
xray16/typedefs/jest Types for the custom Jest matchers.

TypeScriptToLua plugins

Plugins are opt-in. Merge the transformations your game build needs into tstl.luaPlugins, preserving the following order:

{
  "tstl": {
    "luaPlugins": [
      { "name": "xray16/plugins/luabind" },
      { "name": "xray16/plugins/strip" },
      { "name": "xray16/plugins/macros" },
      { "name": "xray16/plugins/optimize" },
      { "name": "xray16/plugins/inline" },
      { "name": "xray16/plugins/libcompile" },
      { "name": "xray16/plugins/tracy" },
    ],
  },
}
Plugin Purpose
luabind Emits class("Name") declarations for @LuabindClass() classes.
strip Removes engine declaration imports and optionally LuaLogger declarations and calls.
macros Folds filename, dirname, nil-check, cast, and build-header helpers.
optimize Rewrites returned ternaries into direct Lua if / else returns.
inline Inlines tagged constants, functions, and $inline / $noInline hints.
libcompile Emits xray16/lib source as a flat xray_bundle module.
tracy Injects Tracy profiler zones when enabled.

Logger removal and Tracy instrumentation are disabled by default. When their config fields are unset, strip.luaLogger is enabled by XR_NO_LUA_LOGS=true or --no-lua-logs, and tracy.enabled is enabled by XR_INJECT_TRACY_ZONES=true or --inject-tracy-zones. An explicit config value takes precedence.

Classes that extend C++ objects need luabind registration rather than default TypeScriptToLua prototype output:

import { type cse_alife_object, LuabindClass, object_binder } from "xray16";

@LuabindClass()
export class ActorBinder extends object_binder {
  public override net_spawn(object: cse_alife_object): boolean {
    return super.net_spawn(object);
  }
}

See the luabind plugin guide for constructor and inheritance rules.

API documentation and development

The SDK documentation website contains guides, plugin pages, and generated API references for the package entry points. Declarations describe the TypeScript-visible API; C++ engine bindings define runtime behavior. Check the engine source when declaration syntax is ambiguous.

To refresh local binding dumps, run OpenXRay with -dump_bindings, open the generated ScriptBindings_*.txt files in the user data directory ($app_data_root$), and compare them with this package's declarations.

To work on the SDK, use Node.js and run npm ci from the repository root. Then run the checks and builds you need:

npm run typecheck
npm run lint
npm run test
npm run build
npm run docs

npm run build stages the publishable package in target/pkg/xray16. npm run docs builds the VitePress website, including the TypeDoc API reference, into target/docs; npm run docs:dev serves it locally.

Website content lives in docs/, build and tooling configuration in cli/, and the published manifest is src/package.json.

Releases

Packages

Used by

Contributors

Languages