diff --git a/build.zig b/build.zig index 891c199a..2d0c0ed4 100644 --- a/build.zig +++ b/build.zig @@ -125,7 +125,9 @@ pub fn build(b: *std.Build) void { for (examples) |file| if (std.mem.endsWith(u8, file, ".luau")) scripts.append(b.allocator, file) catch @panic("out of memory"); example_scripts.addOption([]const []const u8, "scripts", scripts.items); scripting.addOptions("example_scripts", example_scripts); - const source_parser = sourceParser(b, translate_c, lib, target, optimize); + // The parser is exported next to the library, for projects that build missions from their + // source as sltool does, such as the editor. + const source_parser = sourceParser(b, translate_c, lib, target, optimize, "source_parser"); // The definitions files, which its tests parse as luau-lsp reads them, and the guide to writing // missions, whose missions its tests build and play. source_parser.addAnonymousImport("openreliant.d.luau", .{ .root_source_file = b.path("docs/guide/openreliant.d.luau") }); @@ -183,7 +185,7 @@ pub fn build(b: *std.Build) void { .target = b.graph.host, .imports = &.{ .{ .name = "openreliant", .module = host_lib }, - .{ .name = "source_parser", .module = sourceParser(b, translate_c, host_lib, b.graph.host, .debug) }, + .{ .name = "source_parser", .module = sourceParser(b, translate_c, host_lib, b.graph.host, .debug, null) }, }, }), }); @@ -359,10 +361,10 @@ fn addWindowsResources(b: *std.Build, program: *std.Build.Step.Compile, build_re /// The module that reads mission source with Luau's own parser (`src/source_parser.zig`), for /// `target`, on `lib`, the library built for it. It links the parser through a C++ shim, since the -/// parser has no C API. -fn sourceParser(b: *std.Build, translate_c: *std.Build.Dependency, lib: *std.Build.Module, target: std.Build.ResolvedTarget, optimize: std.lang.Optimize) *std.Build.Module { +/// parser has no C API. With a `name`, other packages can import the module by that name. +fn sourceParser(b: *std.Build, translate_c: *std.Build.Dependency, lib: *std.Build.Module, target: std.Build.ResolvedTarget, optimize: std.lang.Optimize, name: ?[]const u8) *std.Build.Module { const luau_ast_library = b.dependency("luau", .{ .target = target, .optimize = .fast }).artifact("luau-ast"); - const module = b.createModule(.{ + const options: std.Build.Module.CreateOptions = .{ .root_source_file = b.path("src/source_parser.zig"), .target = target, .optimize = optimize, @@ -371,7 +373,8 @@ fn sourceParser(b: *std.Build, translate_c: *std.Build.Dependency, lib: *std.Bui .{ .name = "openreliant", .module = lib }, .{ .name = "luau_syntax", .module = translateHeader(translate_c, b.path("src/source_parser/luau.h"), luau_ast_library, target, optimize) }, }, - }); + }; + const module = if (name) |exported| b.addModule(exported, options) else b.createModule(options); module.linkLibrary(luau_ast_library); module.addIncludePath(luau_ast_library.getEmittedIncludeTree()); module.addCSourceFile(.{ .file = b.path("src/source_parser/luau.cpp"), .flags = &.{ "-std=c++17", "-fno-sanitize=undefined" } }); diff --git a/build.zig.zon b/build.zig.zon index 8338c491..32cb5613 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -38,11 +38,16 @@ .hash = "rc-0.0.0-rMftL71EAAAxhe4swpM5rrfcmC9k_j7ApwJJPRssBiDL", }, }, + // The package holds what the build needs, so that other projects can use the library: the + // modules embed the guide's definitions and reference pages, and the scripting module's tests + // embed the example mods. .paths = .{ "build.zig", "build.zig.zon", "src", "deps", "assets", + "docs/guide", + "examples/mods", }, } diff --git a/docs/README.md b/docs/README.md index c6da5fc7..bb33b577 100644 --- a/docs/README.md +++ b/docs/README.md @@ -70,7 +70,7 @@ The [user guide](guide/README.md) covers installing, configuring and playing Ope | [`engine/ion-cannon.md`](engine/ion-cannon.md) | The ion cannon: how the Dark Reign, the Boridin and the rogue base pick a ship, lock on, charge and fire. | | [`engine/director.md`](engine/director.md) | The director's camera: the script's shots, the mission's curves they fly along, and the ships they hold. | | [`engine/maneuvers.md`](engine/maneuvers.md) | Combat maneuvers: the scripts Fight runs, their language, and how it chooses them. | -| [`port/platform.md`](port/platform.md) | The platform: the `openreliant` executable on SDL3, how to build and run it on each system, the installer, and joysticks and gamepads. | +| [`port/platform.md`](port/platform.md) | The platform: the `openreliant` executable on SDL3, how to build and run it on each system, the installer, joysticks and gamepads, and using the library in another project. | | [`port/renderer.md`](port/renderer.md) | The renderer: Surrender's pipeline and Direct3D driver as ported, improvements and what is not yet ported. | | [`port/scripting.md`](port/scripting.md) | Scripting: mod scripts in Luau, the Luau state, the sandbox and limits, how scripts read and change the engine's values, and the records load scripts can change. | | [`port/sound.md`](port/sound.md) | Sound: the stand-in for the Miles Sound System, and its output through SDL3. | diff --git a/docs/engine/editor-link.md b/docs/engine/editor-link.md index 796f1987..944f3414 100644 --- a/docs/engine/editor-link.md +++ b/docs/engine/editor-link.md @@ -350,14 +350,14 @@ needs that OpenReliant doesn't carry yet: ([#1057](https://github.com/OpenReliant/openreliant/issues/1057)); - the live scene: the objects' places, orientations and orders, which the original's editor kept from its own copy of the mission ([#1059](https://github.com/OpenReliant/openreliant/issues/1059)); -- the mission format as a library, so that it reads and writes missions as OpenReliant does - ([#1060](https://github.com/OpenReliant/openreliant/issues/1060)); - the other games' script dialects, which `Hello`'s `vm` names ([#1017](https://github.com/OpenReliant/openreliant/issues/1017)). ### Writing a client -A client works the way `openreliant debug` does: +A client in Zig can read and write missions exactly as OpenReliant does by importing its library +([Using the library in another project](../port/platform.md#using-the-library-in-another-project)). +It works the way `openreliant debug` does: 1. Connect to 127.0.0.1 at the port, and read `Hello`. 2. On `Mission file`, which comes right after `Mission started`, send tag `0x09` with the script's diff --git a/docs/port/platform.md b/docs/port/platform.md index a8db8e0a..0c286ab2 100644 --- a/docs/port/platform.md +++ b/docs/port/platform.md @@ -185,6 +185,21 @@ On Windows, both executables carry OpenReliant's icon ([The icon](#the-icon)), a release-please opens its pull request with the workflow's own token, which needs "Allow GitHub Actions to create and approve pull requests" turned on in the repository's Actions settings. GitHub does not run workflows for pull requests opened with that token, so the release pull request never gets its checks, and an admin merges it past the branch protection. +## Using the library in another project + +Another Zig project, such as [OpenReliant Editor](https://github.com/OpenReliant/openreliant-editor), can add OpenReliant to its `build.zig.zon` with `zig fetch --save` and import two of its modules: + +- `openreliant`, the library: the readers and writers of the game's files, the engine and its tables; +- `source_parser`, which parses mission source and builds it into a mission file, as `sltool dte build` does ([Mission source](../formats/mission-source.md)). + +```zig +const openreliant = b.dependency("openreliant", .{ .target = target, .optimize = optimize }); +module.addImport("openreliant", openreliant.module("openreliant")); +module.addImport("source_parser", openreliant.module("source_parser")); +``` + +Besides the source, the package holds the guide's definitions and reference pages, which the modules embed, and the example mods, which the scripting module's tests embed. The project fetches every library the game builds on too, not only the ones these modules use ([#1204](https://github.com/OpenReliant/openreliant/issues/1204)). + ## The icon OpenReliant's icon is the letters OR in Newtown on the website's dark sky ([`assets/icon`](../../assets/icon/README.md)). On Windows, the executables carry it, and SDL gives the window the executable's icon. On macOS and Linux, the game gives its window the icon as PNGs at 256 and 512 pixels, which SDL decodes (`Window.setIcon`), and macOS shows it in the Dock. A Wayland compositor without the protocol for a window's icon shows its own. `make icon` makes the icon's PNG, ICO and ICNS files from its SVG.