Add Zig bindings for the Pumpkin plugin API

The bindings under src/wit are generated from the pumpkin-plugin-wit
submodule by tools/gen.zig. The canonical ABI is handled at comptime in
src/abi.zig, so plugins are plain wasm32-freestanding modules and only
need wasm-tools to become a component.

On top of that there is a small hand written layer: plugin registration,
typed event handlers, declarative commands (cmd), text components from
struct literals (msg) and std.log support.
This commit is contained in:
MegalithOfficial 2026-09-19 17:46:24 +02:00
commit 28c3e929ca
No known key found for this signature in database
70 changed files with 37318 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.zig-cache/
zig-out/
zig-pkg/

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "wit"]
path = wit
url = https://github.com/Pumpkin-MC/pumpkin-plugin-wit

176
LICENSE-APACHE Normal file
View File

@ -0,0 +1,176 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or exemplary damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

21
LICENSE-MIT Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024-2026 Pumpkin Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

77
README.md Normal file
View File

@ -0,0 +1,77 @@
# Pumpkin API for Zig
This package provides Zig bindings for building Pumpkin plugins using WebAssembly (Wasm) components.
You will need [Zig](https://ziglang.org/download/) 0.16.0 and [`wasm-tools`](https://github.com/bytecodealliance/wasm-tools) in your `PATH`.
## Quick Start
1. Setup Your Project
Add the package to your project:
```sh
zig fetch --save git+https://github.com/Pumpkin-MC/pumpkin-api-zig
```
Create a `build.zig` for your plugin:
```zig
const std = @import("std");
const pumpkin_api = @import("pumpkin_api_zig");
pub fn build(b: *std.Build) void {
_ = pumpkin_api.addPlugin(b, b.dependency("pumpkin_api_zig", .{}), .{
.name = "pumpkin-plugin-example",
.root_source_file = b.path("src/main.zig"),
});
}
```
2. Write Your Plugin
Create your source file (e.g. `src/main.zig`):
```zig
const std = @import("std");
const pumpkin = @import("pumpkin");
pub const std_options: std.Options = .{ .logFn = pumpkin.logFn };
const MyPlugin = struct {
pub const metadata: pumpkin.Metadata = .{
.name = "pumpkin-plugin-example",
.version = "0.1.0",
.authors = &.{"You"},
.description = "An example plugin in Zig.",
};
pub const events = .{
.player_join_event = onJoin,
};
pub fn onLoad(_: pumpkin.Context) !void {
std.log.info("Hello from Zig!", .{});
}
fn onJoin(_: pumpkin.Server, ev: *pumpkin.EventData(.player_join_event)) void {
std.log.info("{s} joined", .{ev.player.getName()});
}
};
comptime {
pumpkin.register(MyPlugin);
}
```
Everything the API returns, handles included, is only valid until the current callback returns. Call `keep()` on a handle to hold on to it, then release it with `deinit()` when you're done.
3. Build Your Plugin
Compile your plugin into a WebAssembly component:
```sh
zig build
```
This will produce `zig-out/pumpkin-plugin-example.wasm` ready to be loaded into Pumpkin.

122
build.zig Normal file
View File

@ -0,0 +1,122 @@
const std = @import("std");
pub const PluginOptions = struct {
name: []const u8,
root_source_file: std.Build.LazyPath,
optimize: std.builtin.OptimizeMode = .ReleaseSmall,
};
/// Builds a plugin and installs `<name>.wasm`, ready to be dropped into the
/// server's `plugins` folder. Needs `wasm-tools` in PATH.
///
/// `api` is this package as the caller sees it:
/// const api = b.dependency("pumpkin_api_zig", .{});
pub fn addPlugin(b: *std.Build, api: *std.Build.Dependency, options: PluginOptions) *std.Build.Step.InstallFile {
return addPluginFrom(b, api.builder, options);
}
fn addPluginFrom(b: *std.Build, api: *std.Build, options: PluginOptions) *std.Build.Step.InstallFile {
const target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
const pumpkin = api.createModule(.{
.root_source_file = api.path("src/pumpkin.zig"),
.target = target,
.optimize = options.optimize,
});
const core = b.addExecutable(.{
.name = options.name,
.root_module = b.createModule(.{
.root_source_file = options.root_source_file,
.target = target,
.optimize = options.optimize,
.imports = &.{.{ .name = "pumpkin", .module = pumpkin }},
}),
});
core.entry = .disabled;
core.rdynamic = true;
// Zig has no component model target, so the core module gets its WIT
// attached and is wrapped into a component afterwards.
const embed = b.addSystemCommand(&.{ "wasm-tools", "component", "embed", "--world", "plugin" });
embed.addFileArg(api.path("src/plugin.wit"));
embed.addFileArg(core.getEmittedBin());
embed.addArg("-o");
const embedded = embed.addOutputFileArg("embedded.wasm");
const new = b.addSystemCommand(&.{ "wasm-tools", "component", "new" });
new.addFileArg(embedded);
new.addArg("-o");
const component = new.addOutputFileArg(b.fmt("{s}.wasm", .{options.name}));
const install = b.addInstallFile(component, b.fmt("{s}.wasm", .{options.name}));
b.getInstallStep().dependOn(&install.step);
return install;
}
pub fn build(b: *std.Build) void {
_ = addPluginFrom(b, b, .{
.name = "pumpkin-plugin-example",
.root_source_file = b.path("example/src/main.zig"),
});
const test_step = b.step("test", "Run unit tests");
for ([_][]const u8{ "src/abi.zig", "tools/gen.zig" }) |path| {
const tests = b.addTest(.{
.root_module = b.createModule(.{
.root_source_file = b.path(path),
.target = b.graph.host,
}),
});
test_step.dependOn(&b.addRunArtifact(tests).step);
}
const check = b.addObject(.{
.name = "check",
.root_module = b.createModule(.{
.root_source_file = b.path("src/check.zig"),
.target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
.optimize = .ReleaseSmall,
}),
});
b.step("check", "Compile every generated binding").dependOn(&check.step);
// zig build gen: src/wit/ and src/plugin.wit from the wit submodule.
// Directory inputs aren't hashed, so these always rerun.
const dump = b.addSystemCommand(&.{ "wasm-tools", "component", "wit", "--json" });
dump.addDirectoryArg(b.path("wit/v0.1"));
dump.has_side_effects = true;
const json = dump.captureStdOut(.{});
const merge = b.addSystemCommand(&.{ "wasm-tools", "component", "wit", "-o", "src/plugin.wit" });
merge.addDirectoryArg(b.path("wit/v0.1"));
merge.setCwd(b.path("."));
merge.has_side_effects = true;
const generator = b.addExecutable(.{
.name = "gen",
.root_module = b.createModule(.{
.root_source_file = b.path("tools/gen.zig"),
.target = b.graph.host,
}),
});
const generate = b.addRunArtifact(generator);
generate.addFileArg(json);
generate.addArg("src");
generate.setCwd(b.path("."));
generate.has_side_effects = true;
const fmt = b.addFmt(.{ .paths = &.{ "src/wit", "src/wit.zig" } });
fmt.step.dependOn(&generate.step);
fmt.step.dependOn(&merge.step);
b.step("gen", "Regenerate src/wit from wit/v0.1").dependOn(&fmt.step);
// zig build fixtures: rewrites what the generator test compares against.
// demo.json comes from `wasm-tools component wit tools/testdata/wit --json`.
const fixtures = b.addRunArtifact(generator);
fixtures.addFileArg(b.path("tools/testdata/demo.json"));
fixtures.addArg("tools/testdata/expected");
fixtures.setCwd(b.path("."));
fixtures.has_side_effects = true;
b.step("fixtures", "Update the generator test snapshots").dependOn(&fixtures.step);
}

13
build.zig.zon Normal file
View File

@ -0,0 +1,13 @@
.{
.name = .pumpkin_api_zig,
.version = "0.1.0",
.fingerprint = 0x8b5633e6f3f6f585,
.minimum_zig_version = "0.16.0",
.paths = .{
"build.zig",
"build.zig.zon",
"src",
"LICENSE-MIT",
"LICENSE-APACHE",
},
}

70
example/src/main.zig Normal file
View File

@ -0,0 +1,70 @@
const std = @import("std");
const pumpkin = @import("pumpkin");
const cmd = pumpkin.cmd;
const msg = pumpkin.msg;
pub const std_options: std.Options = .{ .logFn = pumpkin.logFn };
const log = std.log.scoped(.example);
const Example = struct {
pub const metadata: pumpkin.Metadata = .{
.name = "pumpkin-plugin-example",
.version = "0.1.0",
.authors = &.{"You"},
.description = "An example plugin in Zig.",
};
pub const events = .{
.player_join_event = onJoin,
.player_chat_event = .{ onChat, pumpkin.EventOptions{ .priority = .high } },
};
// /greet and /greet <targets>
pub const commands: []const cmd.Spec = &.{.{
.names = &.{ "greet", "hi" },
.description = "Says hello.",
.permission = "pumpkin-plugin-example:command.greet",
.run = greetSender,
.then = &.{
.argument("targets", .players, .{ .run = greetTargets }),
},
}};
pub fn onLoad(_: pumpkin.Context) !void {
log.info("Hello from Zig!", .{});
}
fn onJoin(_: pumpkin.Server, ev: *pumpkin.EventData(.player_join_event)) void {
log.info("{s} joined", .{ev.player.getName()});
ev.join_message = msg.build(&.{
.{ .text = "+ ", .color = .green, .bold = true },
.{ .text = ev.player.getName(), .color = .yellow },
});
}
fn onChat(_: pumpkin.Server, ev: *pumpkin.EventData(.player_chat_event)) void {
if (std.mem.indexOf(u8, ev.message, "spoiler") != null) ev.cancelled = true;
}
fn greetSender(sender: cmd.Sender, _: pumpkin.Server, _: cmd.Args) cmd.Result {
sender.sendMessage(msg.build(&.{
.{ .text = "Hello from Zig! ", .color = .gold },
.{ .text = "[again]", .color = .gray, .click = .{ .run_command = "/greet" }, .hover = "Runs /greet" },
}));
return cmd.ok(1);
}
fn greetTargets(sender: cmd.Sender, _: pumpkin.Server, args: cmd.Args) cmd.Result {
const targets = cmd.get(args, "targets", .players) orelse return cmd.fail("nobody to greet");
for (targets) |target| {
target.sendSystemMessage(msg.fmt(.aqua, "{s} says hello", .{sender.getName()}), false);
}
sender.sendMessage(msg.fmt(null, "Greeted {d} player(s).", .{targets.len}));
return cmd.ok(@intCast(targets.len));
}
};
comptime {
pumpkin.register(Example);
}

687
src/abi.zig Normal file
View File

@ -0,0 +1,687 @@
//! Component model canonical ABI, driven by Zig types at comptime.
//!
//! WIT types map to Zig like this:
//! string, list<T> []const u8, []const T
//! record, tuple struct, tuple
//! variant, result union(enum)
//! option<T> ?T
//! enum enum(uN)
//! flags packed struct(uN) of bools
//! own<R> non-exhaustive enum(u32)
//! borrow<R> Borrowed(R), parameters only
const std = @import("std");
const builtin = @import("builtin");
pub fn Result(comptime T: type, comptime E: type) type {
return union(enum) { ok: T, err: E };
}
const is_wasm = builtin.cpu.arch == .wasm32;
const backing = if (is_wasm) std.heap.wasm_allocator else std.heap.page_allocator;
var call_arena: std.heap.ArenaAllocator = .init(backing);
/// Everything the host hands us lives here and is gone once the current export returns.
pub fn arena() std.mem.Allocator {
return call_arena.allocator();
}
const DropFn = *const fn (i32) callconv(.c) void;
const Tracked = struct { handle: u32, drop: DropFn };
/// Owned handles received during the current export, newest last.
var tracked: std.ArrayList(Tracked) = .empty;
fn track(comptime T: type, handle: T) void {
tracked.append(backing, .{ .handle = @intFromEnum(handle), .drop = T.resource_drop }) catch @panic("OOM");
}
fn untrack(comptime T: type, handle: T) void {
var i = tracked.items.len;
while (i > 0) {
i -= 1;
const item = tracked.items[i];
if (item.handle == @intFromEnum(handle) and item.drop == T.resource_drop) {
_ = tracked.orderedRemove(i);
return;
}
}
}
/// Takes a handle out of the per-call cleanup so it can be stored across calls.
/// From then on it has to be released with `deinit`.
pub fn keep(comptime T: type, handle: T) void {
untrack(T, handle);
}
pub fn drop(comptime T: type, handle: T) void {
untrack(T, handle);
T.resource_drop(@bitCast(@intFromEnum(handle)));
}
/// A handle that is only lent to the callee. Plain handles are given away when lowered.
pub fn Borrowed(comptime T: type) type {
return struct {
handle: T,
pub const wit_borrowed = {};
};
}
pub fn borrow(handle: anytype) Borrowed(@TypeOf(handle)) {
return .{ .handle = handle };
}
fn isBorrowed(comptime T: type) bool {
return @typeInfo(T) == .@"struct" and @hasDecl(T, "wit_borrowed");
}
pub const Stats = struct {
/// Size of the linear memory. Wasm memory never shrinks, so this is the
/// number to watch for leaks.
memory_bytes: usize,
/// What the per-call arena holds on to between calls.
arena_bytes: usize,
/// Handles that will be released when the current call ends.
tracked_handles: usize,
};
pub fn stats() Stats {
return .{
.memory_bytes = if (is_wasm) @wasmMemorySize(0) * std.wasm.page_size else 0,
.arena_bytes = call_arena.queryCapacity(),
.tracked_handles = tracked.items.len,
};
}
/// Drops the handles nobody kept. This calls into the host, which is not
/// allowed from a post-return function, so it can't be left to `endCall`.
fn releaseHandles() void {
while (tracked.pop()) |item| item.drop(@bitCast(item.handle));
}
/// Resets the arena. With an indirect result the host still reads from it
/// after the export returns, those exports call this from `cabi_post_*`.
pub fn endCall() void {
releaseHandles();
_ = call_arena.reset(.retain_capacity);
}
fn cabiRealloc(old: ?[*]u8, old_len: usize, alignment: usize, new_len: usize) callconv(.c) ?[*]u8 {
const a: std.mem.Alignment = .fromByteUnits(alignment);
const new = arena().rawAlloc(new_len, a, @returnAddress()) orelse @panic("OOM");
if (old) |p| @memcpy(new[0..old_len], p[0..old_len]);
return new;
}
comptime {
if (is_wasm) @export(&cabiRealloc, .{ .name = "cabi_realloc" });
}
const Core = enum { i32, i64, f32, f64 };
fn CoreType(comptime c: Core) type {
return switch (c) {
.i32 => i32,
.i64 => i64,
.f32 => f32,
.f64 => f64,
};
}
const max_flat_params = 16;
// The event variant alone has a few hundred cases.
const branch_quota = 10_000_000;
fn isHandle(comptime T: type) bool {
return @typeInfo(T) == .@"enum" and !@typeInfo(T).@"enum".is_exhaustive;
}
fn Discriminant(comptime cases: usize) type {
if (cases <= 1 << 8) return u8;
if (cases <= 1 << 16) return u16;
return u32;
}
fn caseCount(comptime T: type) comptime_int {
return switch (@typeInfo(T)) {
.optional => 2,
.@"union" => |u| u.fields.len,
.@"enum" => |e| e.fields.len,
else => unreachable,
};
}
/// Whether a []const T already has the canonical layout in memory.
fn isPlain(comptime T: type) bool {
return switch (@typeInfo(T)) {
.int => |i| i.bits == 8 or i.bits == 16 or i.bits == 32 or i.bits == 64,
.float => true,
else => false,
};
}
pub fn alignOf(comptime T: type) comptime_int {
@setEvalBranchQuota(branch_quota);
return switch (@typeInfo(T)) {
.void => 1,
.bool => 1,
.int => |i| if (i.bits == 21) 4 else @divExact(i.bits, 8),
.float => |f| @divExact(f.bits, 8),
.pointer => 4,
.@"enum" => if (isHandle(T)) 4 else @sizeOf(Discriminant(caseCount(T))),
.optional => |o| @max(1, alignOf(o.child)),
.@"union" => |u| blk: {
var a = @sizeOf(Discriminant(u.fields.len));
for (u.fields) |f| a = @max(a, alignOf(f.type));
break :blk a;
},
.@"struct" => |s| blk: {
if (s.layout == .@"packed") break :blk @sizeOf(s.backing_integer.?);
if (isBorrowed(T)) break :blk 4;
var a = 1;
for (s.fields) |f| a = @max(a, alignOf(f.type));
break :blk a;
},
else => @compileError("no canonical ABI for " ++ @typeName(T)),
};
}
fn alignUp(comptime n: comptime_int, comptime a: comptime_int) comptime_int {
return @divFloor(n + a - 1, a) * a;
}
pub fn sizeOf(comptime T: type) comptime_int {
@setEvalBranchQuota(branch_quota);
return switch (@typeInfo(T)) {
.void => 0,
.bool => 1,
.int => |i| if (i.bits == 21) 4 else @divExact(i.bits, 8),
.float => |f| @divExact(f.bits, 8),
.pointer => 8,
.@"enum" => if (isHandle(T)) 4 else @sizeOf(Discriminant(caseCount(T))),
.optional => |o| alignUp(payloadOffset(T) + sizeOf(o.child), alignOf(T)),
.@"union" => |u| blk: {
var max = 0;
for (u.fields) |f| max = @max(max, sizeOf(f.type));
break :blk alignUp(payloadOffset(T) + max, alignOf(T));
},
.@"struct" => |s| blk: {
if (s.layout == .@"packed") break :blk @sizeOf(s.backing_integer.?);
if (isBorrowed(T)) break :blk 4;
var n = 0;
for (s.fields) |f| n = alignUp(n, alignOf(f.type)) + sizeOf(f.type);
break :blk alignUp(n, alignOf(T));
},
else => @compileError("no canonical ABI for " ++ @typeName(T)),
};
}
fn payloadOffset(comptime T: type) comptime_int {
@setEvalBranchQuota(branch_quota);
const disc = @sizeOf(Discriminant(caseCount(T)));
const payload_align = switch (@typeInfo(T)) {
.optional => |o| alignOf(o.child),
.@"union" => alignOf(T),
else => unreachable,
};
return alignUp(disc, payload_align);
}
fn fieldOffset(comptime T: type, comptime index: usize) comptime_int {
@setEvalBranchQuota(branch_quota);
var n = 0;
for (@typeInfo(T).@"struct".fields, 0..) |f, i| {
n = alignUp(n, alignOf(f.type));
if (i == index) return n;
n += sizeOf(f.type);
}
unreachable;
}
fn join(a: Core, b: Core) Core {
if (a == b) return a;
if ((a == .i32 and b == .f32) or (a == .f32 and b == .i32)) return .i32;
return .i64;
}
fn flattenCases(comptime payloads: []const type) []const Core {
var out: []const Core = &.{};
for (payloads) |P| {
const flat = flatten(P);
var merged: []const Core = &.{};
for (0..@max(out.len, flat.len)) |i| {
const c = if (i >= out.len) flat[i] else if (i >= flat.len) out[i] else join(out[i], flat[i]);
merged = merged ++ [1]Core{c};
}
out = merged;
}
return [1]Core{.i32} ++ out;
}
pub fn flatten(comptime T: type) []const Core {
return comptime blk: {
@setEvalBranchQuota(branch_quota);
break :blk flattenType(T);
};
}
fn flattenType(comptime T: type) []const Core {
return switch (@typeInfo(T)) {
.void => &.{},
.bool, .@"enum" => &.{.i32},
.int => |i| if (i.bits == 64) &.{.i64} else &.{.i32},
.float => |f| if (f.bits == 64) &.{.f64} else &.{.f32},
.pointer => &.{ .i32, .i32 },
.optional => |o| flattenCases(&.{ void, o.child }),
.@"union" => |u| blk: {
var payloads: []const type = &.{};
for (u.fields) |f| payloads = payloads ++ [1]type{f.type};
break :blk flattenCases(payloads);
},
.@"struct" => |s| blk: {
if (s.layout == .@"packed" or isBorrowed(T)) break :blk &.{.i32};
var out: []const Core = &.{};
for (s.fields) |f| out = out ++ flatten(f.type);
break :blk out;
},
else => @compileError("no canonical ABI for " ++ @typeName(T)),
};
}
// Flat values travel as raw bits in u64 slots, which makes the variant
// payload joins (f32 in an i32 slot, i32 in an i64 slot, ...) free.
fn lowerSlice(comptime T: type, items: []const T, slots: []u64) void {
slots[1] = items.len;
if (items.len == 0) {
slots[0] = 0;
return;
}
if (comptime isPlain(T)) {
slots[0] = @intFromPtr(items.ptr);
return;
}
const a: std.mem.Alignment = comptime .fromByteUnits(alignOf(T));
const buf = arena().alignedAlloc(u8, a, items.len * sizeOf(T)) catch @panic("OOM");
for (items, 0..) |item, i| store(T, item, buf.ptr + i * sizeOf(T));
slots[0] = @intFromPtr(buf.ptr);
}
fn liftSlice(comptime T: type, addr: u64, len: u64) []const T {
if (len == 0) return &.{};
const n: usize = @intCast(len);
if (comptime isPlain(T)) {
const ptr: [*]const T = @ptrFromInt(@as(usize, @intCast(addr)));
return ptr[0..n];
}
const base: [*]const u8 = @ptrFromInt(@as(usize, @intCast(addr)));
const out = arena().alloc(T, n) catch @panic("OOM");
for (out, 0..) |*item, i| item.* = load(T, base + i * sizeOf(T));
return out;
}
pub fn lowerFlat(comptime T: type, value: T, slots: []u64) void {
switch (@typeInfo(T)) {
.void => {},
.bool => slots[0] = @intFromBool(value),
.int => |i| {
if (i.bits == 64) {
slots[0] = @bitCast(value);
} else if (i.signedness == .signed) {
slots[0] = @as(u32, @bitCast(@as(i32, value)));
} else {
slots[0] = value;
}
},
.float => |f| slots[0] = if (f.bits == 64) @bitCast(value) else @as(u32, @bitCast(value)),
.pointer => |p| lowerSlice(p.child, value, slots),
.@"enum" => {
if (comptime isHandle(T)) untrack(T, value);
slots[0] = @intFromEnum(value);
},
.optional => |o| {
@memset(slots[0..comptime flatten(T).len], 0);
if (value) |v| {
slots[0] = 1;
lowerFlat(o.child, v, slots[1..]);
}
},
.@"union" => |u| {
@memset(slots[0..comptime flatten(T).len], 0);
switch (value) {
inline else => |v, tag| {
slots[0] = @intFromEnum(tag);
lowerFlat(u.fields[@intFromEnum(tag)].type, v, slots[1..]);
},
}
},
.@"struct" => |s| {
if (s.layout == .@"packed") {
slots[0] = @as(s.backing_integer.?, @bitCast(value));
return;
}
if (comptime isBorrowed(T)) {
slots[0] = @intFromEnum(value.handle);
return;
}
var at: usize = 0;
inline for (s.fields) |f| {
lowerFlat(f.type, @field(value, f.name), slots[at..]);
at += comptime flatten(f.type).len;
}
},
else => comptime unreachable,
}
}
pub fn liftFlat(comptime T: type, slots: []const u64) T {
switch (@typeInfo(T)) {
.void => return {},
.bool => return slots[0] != 0,
.int => |i| {
if (i.bits == 64) return @bitCast(slots[0]);
const word: u32 = @truncate(slots[0]);
if (i.signedness == .signed) return @truncate(@as(i32, @bitCast(word)));
return @truncate(word);
},
.float => |f| {
if (f.bits == 64) return @bitCast(slots[0]);
return @bitCast(@as(u32, @truncate(slots[0])));
},
.pointer => |p| return liftSlice(p.child, slots[0], slots[1]),
.@"enum" => {
const value: T = @enumFromInt(slots[0]);
if (comptime isHandle(T)) track(T, value);
return value;
},
.optional => |o| return if (slots[0] == 0) null else liftFlat(o.child, slots[1..]),
.@"union" => |u| {
inline for (u.fields, 0..) |f, i| {
if (slots[0] == i) return @unionInit(T, f.name, liftFlat(f.type, slots[1..]));
}
unreachable;
},
.@"struct" => |s| {
if (s.layout == .@"packed") return @bitCast(@as(s.backing_integer.?, @truncate(slots[0])));
if (comptime isBorrowed(T)) @compileError("borrowed handles can only be passed to imports");
var out: T = undefined;
var at: usize = 0;
inline for (s.fields) |f| {
@field(out, f.name) = liftFlat(f.type, slots[at..]);
at += comptime flatten(f.type).len;
}
return out;
},
else => comptime unreachable,
}
}
fn put(comptime Int: type, ptr: [*]u8, value: Int) void {
std.mem.writeInt(Int, ptr[0..@sizeOf(Int)], value, .little);
}
fn get(comptime Int: type, ptr: [*]const u8) Int {
return std.mem.readInt(Int, ptr[0..@sizeOf(Int)], .little);
}
pub fn store(comptime T: type, value: T, ptr: [*]u8) void {
switch (@typeInfo(T)) {
.void => {},
.bool => ptr[0] = @intFromBool(value),
.int => |i| if (i.bits == 21) put(u32, ptr, value) else put(T, ptr, value),
.float => |f| put(std.meta.Int(.unsigned, f.bits), ptr, @bitCast(value)),
.pointer => |p| {
var slots: [2]u64 = undefined;
lowerSlice(p.child, value, &slots);
put(u32, ptr, @intCast(slots[0]));
put(u32, ptr + 4, @intCast(slots[1]));
},
.@"enum" => {
if (comptime isHandle(T)) {
untrack(T, value);
return put(u32, ptr, @intFromEnum(value));
}
put(Discriminant(caseCount(T)), ptr, @intFromEnum(value));
},
.optional => |o| {
ptr[0] = @intFromBool(value != null);
if (value) |v| store(o.child, v, ptr + payloadOffset(T));
},
.@"union" => |u| switch (value) {
inline else => |v, tag| {
put(Discriminant(u.fields.len), ptr, @intFromEnum(tag));
store(u.fields[@intFromEnum(tag)].type, v, ptr + payloadOffset(T));
},
},
.@"struct" => |s| {
if (s.layout == .@"packed") return put(s.backing_integer.?, ptr, @bitCast(value));
if (comptime isBorrowed(T)) return put(u32, ptr, @intFromEnum(value.handle));
inline for (s.fields, 0..) |f, i| {
store(f.type, @field(value, f.name), ptr + fieldOffset(T, i));
}
},
else => comptime unreachable,
}
}
pub fn load(comptime T: type, ptr: [*]const u8) T {
switch (@typeInfo(T)) {
.void => return {},
.bool => return ptr[0] != 0,
.int => |i| return if (i.bits == 21) @truncate(get(u32, ptr)) else get(T, ptr),
.float => |f| return @bitCast(get(std.meta.Int(.unsigned, f.bits), ptr)),
.pointer => |p| return liftSlice(p.child, get(u32, ptr), get(u32, ptr + 4)),
.@"enum" => {
if (comptime isHandle(T)) {
const value: T = @enumFromInt(get(u32, ptr));
track(T, value);
return value;
}
return @enumFromInt(get(Discriminant(caseCount(T)), ptr));
},
.optional => |o| return if (ptr[0] == 0) null else load(o.child, ptr + payloadOffset(T)),
.@"union" => |u| {
const tag = get(Discriminant(u.fields.len), ptr);
inline for (u.fields, 0..) |f, i| {
if (tag == i) return @unionInit(T, f.name, load(f.type, ptr + payloadOffset(T)));
}
unreachable;
},
.@"struct" => |s| {
if (s.layout == .@"packed") return @bitCast(get(s.backing_integer.?, ptr));
var out: T = undefined;
inline for (s.fields, 0..) |f, i| {
@field(out, f.name) = load(f.type, ptr + fieldOffset(T, i));
}
return out;
},
else => comptime unreachable,
}
}
fn toCore(comptime C: type, slot: u64) C {
return switch (C) {
i32 => @bitCast(@as(u32, @truncate(slot))),
i64 => @bitCast(slot),
f32 => @bitCast(@as(u32, @truncate(slot))),
f64 => @bitCast(slot),
else => comptime unreachable,
};
}
fn fromCore(value: anytype) u64 {
return switch (@TypeOf(value)) {
i32, f32 => @as(u32, @bitCast(value)),
i64, f64 => @bitCast(value),
else => comptime unreachable,
};
}
fn scratch(comptime T: type) [*]u8 {
const a: std.mem.Alignment = comptime .fromByteUnits(alignOf(T));
const buf = arena().alignedAlloc(u8, a, @max(sizeOf(T), 1)) catch @panic("OOM");
return buf.ptr;
}
/// The generator spells out raw signatures on its own, make sure it agrees with us.
fn checkSignature(comptime Func: type, comptime Args: type, comptime Ret: type) void {
const info = @typeInfo(Func).@"fn";
const args = flatten(Args);
const ret = flatten(Ret);
var expected: []const Core = if (args.len > max_flat_params) &.{.i32} else args;
if (ret.len > 1) expected = expected ++ [1]Core{.i32};
const expected_ret = if (ret.len == 1) CoreType(ret[0]) else void;
var ok = info.params.len == expected.len and info.return_type.? == expected_ret;
if (ok) for (info.params, expected) |p, e| {
if (p.type.? != CoreType(e)) ok = false;
};
if (!ok) @compileError("raw signature " ++ @typeName(Func) ++ " does not match " ++ @typeName(Args) ++ " -> " ++ @typeName(Ret));
}
/// Calls an imported function. `func` is the raw extern with the flattened signature.
pub fn call(func: anytype, comptime Ret: type, args: anytype) Ret {
const Args = @TypeOf(args);
const Raw = std.meta.ArgsTuple(@TypeOf(func));
const n = comptime flatten(Args).len;
const ret_flat = comptime flatten(Ret).len;
var slots: [@max(n, 1)]u64 = undefined;
var raw: Raw = undefined;
const used = comptime if (n > max_flat_params) 1 else n;
comptime checkSignature(@TypeOf(func), Args, Ret);
if (n > max_flat_params) {
const buf = scratch(Args);
store(Args, args, buf);
raw[0] = @bitCast(@as(u32, @intFromPtr(buf)));
} else {
lowerFlat(Args, args, &slots);
inline for (0..n) |i| raw[i] = toCore(@TypeOf(raw[i]), slots[i]);
}
if (ret_flat > 1) {
const out = scratch(Ret);
raw[used] = @bitCast(@as(u32, @intFromPtr(out)));
@call(.auto, func, raw);
return load(Ret, out);
}
if (ret_flat == 0) {
@call(.auto, func, raw);
return liftFlat(Ret, &.{});
}
const slot = fromCore(@call(.auto, func, raw));
return liftFlat(Ret, &.{slot});
}
/// Turns the raw parameters of an export back into `Args`.
pub fn liftArgs(comptime Args: type, raw: anytype) Args {
const n = comptime flatten(Args).len;
if (n > max_flat_params) {
const ptr: [*]const u8 = @ptrFromInt(@as(u32, @bitCast(raw[0])));
return load(Args, ptr);
}
var slots: [@max(n, 1)]u64 = undefined;
inline for (0..n) |i| slots[i] = fromCore(raw[i]);
return liftFlat(Args, &slots);
}
pub fn RawReturn(comptime Ret: type) type {
const flat = flatten(Ret);
if (flat.len == 0) return void;
if (flat.len == 1) return CoreType(flat[0]);
return i32;
}
/// Lowers the return value of an export, the last thing an export does.
/// Handles that are part of `value` go back to the host, the rest is released.
pub fn lowerReturn(comptime Ret: type, value: Ret) RawReturn(Ret) {
defer releaseHandles();
const flat = comptime flatten(Ret);
if (flat.len == 0) return;
if (flat.len == 1) {
var slots: [1]u64 = undefined;
lowerFlat(Ret, value, &slots);
return toCore(CoreType(flat[0]), slots[0]);
}
const out = scratch(Ret);
store(Ret, value, out);
return @bitCast(@as(u32, @intFromPtr(out)));
}
test "layout" {
const Pos = struct { f64, f64, f64 };
try std.testing.expectEqual(24, sizeOf(Pos));
try std.testing.expectEqual(8, alignOf(Pos));
const Rec = struct { a: u8, b: u32, c: []const u8, d: ?u16 };
try std.testing.expectEqual(4, alignOf(Rec));
try std.testing.expectEqual(20, sizeOf(Rec));
const Var = union(enum) { a, b: u64, c: []const u8 };
try std.testing.expectEqual(8, alignOf(Var));
try std.testing.expectEqual(16, sizeOf(Var));
try std.testing.expectEqual(2, sizeOf(?u8));
try std.testing.expectEqual(16, sizeOf(?f64));
}
test "flatten" {
try std.testing.expectEqualSlices(Core, &.{ .i32, .i32 }, flatten([]const u8));
try std.testing.expectEqualSlices(Core, &.{ .i32, .f64 }, flatten(?f64));
try std.testing.expectEqualSlices(Core, &.{ .i32, .i32 }, flatten(Result(f32, u32)));
try std.testing.expectEqualSlices(Core, &.{ .i32, .i64, .i32 }, flatten(union(enum) { a: f64, b: []const u8, c }));
}
test "flat round trip" {
const Rec = struct { a: i8, b: ?f32, c: Result(void, u64), d: packed struct(u8) { x: bool, y: bool, _: u6 = 0 } };
const value: Rec = .{ .a = -3, .b = 1.5, .c = .{ .err = 1 << 40 }, .d = .{ .x = false, .y = true } };
var slots: [flatten(Rec).len]u64 = undefined;
lowerFlat(Rec, value, &slots);
try std.testing.expectEqual(@as(u64, 0xffff_fffd), slots[0]);
try std.testing.expectEqualDeep(value, liftFlat(Rec, &slots));
}
test "memory round trip" {
const Rec = struct { a: u16, b: ?i64, c: union(enum) { x: f32, y: bool }, d: enum(u8) { p, q } };
const value: Rec = .{ .a = 7, .b = -9, .c = .{ .y = true }, .d = .q };
var buf: [sizeOf(Rec)]u8 align(8) = undefined;
store(Rec, value, &buf);
try std.testing.expectEqualDeep(value, load(Rec, &buf));
}
test "handle tracking" {
const Res = enum(u32) {
_,
var dropped: [8]u32 = undefined;
var count: usize = 0;
pub const resource_drop = &record;
fn record(handle: i32) callconv(.c) void {
dropped[count] = @intCast(handle);
count += 1;
}
};
const Pair = struct { a: Res, b: ?Res };
var slots: [flatten(Pair).len]u64 = .{ 1, 1, 2 };
const pair = liftFlat(Pair, &slots);
const third = liftFlat(Res, &.{3});
const fourth = liftFlat(Res, &.{4});
try std.testing.expectEqual(4, tracked.items.len);
// Given away, lent, kept, dropped by hand. Only `a` is left for endCall.
lowerFlat(Res, pair.b.?, &slots);
lowerFlat(Borrowed(Res), borrow(pair.a), &slots);
keep(Res, third);
drop(Res, fourth);
try std.testing.expectEqualSlices(u32, &.{4}, Res.dropped[0..Res.count]);
lowerReturn(void, {});
try std.testing.expectEqualSlices(u32, &.{ 4, 1 }, Res.dropped[0..Res.count]);
try std.testing.expectEqual(0, tracked.items.len);
}

31
src/check.zig Normal file
View File

@ -0,0 +1,31 @@
//! Not part of the package. Zig only analyzes what gets used, so a plugin build
//! never looks at most of src/wit. This takes the address of every generated
//! function to get all of them compiled, see `zig build check`.
const std = @import("std");
const wit = @import("wit.zig");
fn collect(comptime T: type) []const *const anyopaque {
var out: []const *const anyopaque = &.{};
for (std.meta.declarations(T)) |decl| {
const Decl = @TypeOf(@field(T, decl.name));
if (Decl == type) {
const Inner = @field(T, decl.name);
switch (@typeInfo(Inner)) {
.@"struct", .@"enum", .@"union" => out = out ++ collect(Inner),
else => {},
}
} else if (@typeInfo(Decl) == .@"fn" and !@typeInfo(Decl).@"fn".is_generic) {
out = out ++ [1]*const anyopaque{@ptrCast(&@field(T, decl.name))};
}
}
return out;
}
const all = blk: {
@setEvalBranchQuota(10_000_000);
const list = collect(wit);
break :blk list[0..list.len].*;
};
export const table: [all.len]*const anyopaque = all;

146
src/cmd.zig Normal file
View File

@ -0,0 +1,146 @@
//! Declarative commands on top of the generated `command` interface.
//!
//! try pumpkin.cmd.register(ctx, .{
//! .names = &.{ "greet", "hi" },
//! .description = "Greets players.",
//! .permission = "my-plugin:command.greet",
//! .run = greetSelf,
//! .then = &.{
//! .argument("targets", .players, .{ .run = greetTargets }),
//! },
//! });
const std = @import("std");
const abi = @import("abi.zig");
const wit = @import("wit.zig");
const command = wit.command;
const Context = wit.context.Context;
const Server = wit.server.Server;
const TextComponent = wit.text.TextComponent;
pub const Sender = command.CommandSender;
pub const Args = command.ConsumedArgs;
pub const Arg = command.Arg;
pub const ArgumentType = command.ArgumentType;
pub const Result = abi.Result(i32, command.CommandError);
pub const Handler = fn (Sender, Server, Args) Result;
pub const Suggester = fn (Sender, Server, command.SuggestionRequest) command.CommandSuggestions;
pub const Node = struct {
kind: union(enum) {
literal: []const u8,
argument: struct { name: []const u8, type: ArgumentType },
},
options: Options,
pub const Options = struct {
run: ?*const Handler = null,
/// Only meaningful on arguments.
suggest: ?*const Suggester = null,
then: []const Node = &.{},
};
pub fn literal(name: []const u8, options: Options) Node {
return .{ .kind = .{ .literal = name }, .options = options };
}
pub fn argument(name: []const u8, arg_type: ArgumentType, options: Options) Node {
return .{ .kind = .{ .argument = .{ .name = name, .type = arg_type } }, .options = options };
}
fn build(node: Node) command.CommandNode {
const built: command.CommandNode = switch (node.kind) {
.literal => |name| .literal(name),
.argument => |arg| .argument(arg.name, arg.type),
};
if (node.options.run) |handler| built.executeWithHandlerId(@intFromPtr(handler));
if (node.options.suggest) |suggester| built.suggestWithHandlerId(@intFromPtr(suggester));
for (node.options.then) |child| built.then(child.build());
return built;
}
};
pub const Spec = struct {
/// The command's name followed by its aliases.
names: []const []const u8,
description: []const u8 = "",
/// Has to start with "<plugin name>:". A node the server has never heard
/// of denies everyone, so `register` registers it as well.
permission: []const u8,
default: wit.permission.PermissionDefault = .allow,
run: ?*const Handler = null,
then: []const Node = &.{},
};
pub fn register(ctx: Context, spec: Spec) error{PermissionRejected}!void {
switch (ctx.registerPermission(.{
.node = spec.permission,
.description = spec.description,
.default = spec.default,
.children = &.{},
})) {
.ok => {},
.err => |message| {
std.log.err("permission {s}: {s}", .{ spec.permission, message });
return error.PermissionRejected;
},
}
const root: command.Command = .init(spec.names, spec.description);
if (spec.run) |handler| root.executeWithHandlerId(@intFromPtr(handler));
for (spec.then) |child| root.then(child.build());
ctx.registerCommand(root, spec.permission);
}
pub fn ok(count: i32) Result {
return .{ .ok = count };
}
pub fn fail(message: []const u8) Result {
return .{ .err = .{ .command_failed = .text(message) } };
}
/// The value of argument `name` if it was parsed as `tag`.
pub fn get(args: Args, name: []const u8, comptime tag: std.meta.Tag(Arg)) ?@FieldType(Arg, @tagName(tag)) {
const value = args.getValue(name);
return if (value == tag) @field(value, @tagName(tag)) else null;
}
/// Any numeric argument as an integer, null when missing or out of bounds.
pub fn int(args: Args, name: []const u8) ?i64 {
return switch ((get(args, name, .num) orelse return null)) {
.ok => |number| switch (number) {
.int32 => |v| v,
.int64 => |v| v,
.float32 => |v| @intFromFloat(v),
.float64 => |v| @intFromFloat(v),
},
.err => null,
};
}
pub fn float(args: Args, name: []const u8) ?f64 {
return switch ((get(args, name, .num) orelse return null)) {
.ok => |number| switch (number) {
.int32 => |v| @floatFromInt(v),
.int64 => |v| @floatFromInt(v),
.float32 => |v| v,
.float64 => |v| v,
},
.err => null,
};
}
/// Suggests `values` for the word under the cursor.
pub fn suggestions(request: command.SuggestionRequest, values: []const []const u8) command.CommandSuggestions {
const out = abi.arena().alloc(command.CommandSuggestion, values.len) catch @panic("OOM");
var n: usize = 0;
for (values) |value| {
if (!std.mem.startsWith(u8, value, request.remaining)) continue;
out[n] = .{ .value = value, .tooltip = null };
n += 1;
}
return .{ .start = request.start, .length = @intCast(request.remaining.len), .values = out[0..n] };
}

83
src/msg.zig Normal file
View File

@ -0,0 +1,83 @@
//! Text components from struct literals instead of a call per style.
//!
//! sender.sendMessage(pumpkin.msg.build(&.{
//! .{ .text = "[Shop] ", .color = .gold, .bold = true },
//! .{ .text = "open", .click = .{ .run_command = "/shop" }, .hover = "Opens the shop" },
//! }));
const std = @import("std");
const abi = @import("abi.zig");
const wit = @import("wit.zig");
const TextComponent = wit.text.TextComponent;
pub const Color = wit.common.NamedColor;
pub const Rgb = wit.common.RgbColor;
pub const Click = union(enum) {
open_url: []const u8,
run_command: []const u8,
suggest_command: []const u8,
copy: []const u8,
change_page: u32,
};
pub const Span = struct {
text: []const u8 = "",
/// A translation key, used instead of `text`.
translate: ?[]const u8 = null,
color: ?Color = null,
/// Wins over `color`.
rgb: ?Rgb = null,
bold: ?bool = null,
italic: ?bool = null,
underlined: ?bool = null,
strikethrough: ?bool = null,
obfuscated: ?bool = null,
click: ?Click = null,
/// Plain text shown on hover.
hover: ?[]const u8 = null,
/// Inserted into the chat box on shift click.
insertion: ?[]const u8 = null,
font: ?[]const u8 = null,
};
pub fn span(s: Span) TextComponent {
const out: TextComponent = if (s.translate) |key| .translate(key, &.{}) else .text(s.text);
if (s.rgb) |rgb| out.colorRgb(rgb) else if (s.color) |color| out.colorNamed(color);
if (s.bold) |v| out.bold(v);
if (s.italic) |v| out.italic(v);
if (s.underlined) |v| out.underlined(v);
if (s.strikethrough) |v| out.strikethrough(v);
if (s.obfuscated) |v| out.obfuscated(v);
if (s.insertion) |v| out.insertion(v);
if (s.font) |v| out.font(v);
if (s.hover) |v| out.hoverShowText(.text(v));
if (s.click) |click| switch (click) {
.open_url => |v| out.clickOpenUrl(v),
.run_command => |v| out.clickRunCommand(v),
.suggest_command => |v| out.clickSuggestCommand(v),
.copy => |v| out.clickCopyToClipboard(v),
.change_page => |v| out.clickChangePage(v),
};
return out;
}
/// One component made of `spans`, in order.
pub fn build(spans: []const Span) TextComponent {
if (spans.len == 1) return span(spans[0]);
const root: TextComponent = .text("");
for (spans) |s| root.addChild(span(s));
return root;
}
pub fn plain(text: []const u8) TextComponent {
return .text(text);
}
/// A formatted single color message, `color` may be null.
pub fn fmt(color: ?Color, comptime format: []const u8, args: anytype) TextComponent {
const text = std.fmt.allocPrint(abi.arena(), format, args) catch @panic("OOM");
return span(.{ .text = text, .color = color });
}

14266
src/plugin.wit Normal file

File diff suppressed because it is too large Load Diff

307
src/pumpkin.zig Normal file
View File

@ -0,0 +1,307 @@
//! Zig bindings for Pumpkin's plugin API.
//!
//! The namespaces below mirror the WIT interfaces one to one and are generated.
//! What's written by hand here is the glue a plugin needs to get going:
//! `register`, typed event and task handlers, `cmd` for commands, `msg` for
//! text components and `std.log` support.
//!
//! Whatever the API hands out is only good until the current callback returns:
//! strings, slices and records live in a per-call arena and handles are
//! released along with it. Call `keep` on a handle to hold on to it, from then
//! on it is yours to `deinit`. Functions that take a handle over are marked
//! "Consumes" in their docs.
const std = @import("std");
pub const abi = @import("abi.zig");
pub const cmd = @import("cmd.zig");
pub const msg = @import("msg.zig");
const wit = @import("wit.zig");
pub const advancement = wit.advancement;
pub const attributes = wit.attributes;
pub const bedrock_packets = wit.bedrock_packets;
pub const biomes = wit.biomes;
pub const block_entity = wit.block_entity;
pub const boss_bar = wit.boss_bar;
pub const command = wit.command;
pub const common = wit.common;
pub const context = wit.context;
pub const damage_types = wit.damage_types;
pub const data_components = wit.data_components;
pub const datapack = wit.datapack;
pub const display = wit.display;
pub const enchantments = wit.enchantments;
pub const entity = wit.entity;
pub const entity_statuses = wit.entity_statuses;
pub const entity_types = wit.entity_types;
pub const event = wit.event;
pub const forms = wit.forms;
pub const game_events = wit.game_events;
pub const game_rules = wit.game_rules;
pub const gui = wit.gui;
pub const i18n = wit.i18n;
pub const inventory = wit.inventory;
pub const ipc = wit.ipc;
pub const item_stack = wit.item_stack;
pub const java_dialogs = wit.java_dialogs;
pub const java_packets = wit.java_packets;
pub const logging = wit.logging;
pub const particles = wit.particles;
pub const permission = wit.permission;
pub const player = wit.player;
pub const potions = wit.potions;
pub const recipe = wit.recipe;
pub const scheduler = wit.scheduler;
pub const scoreboard = wit.scoreboard;
pub const screens = wit.screens;
pub const server = wit.server;
pub const sounds = wit.sounds;
pub const statistics = wit.statistics;
pub const status_effect = wit.status_effect;
pub const text = wit.text;
pub const uuid = wit.uuid;
pub const world = wit.world;
pub const Context = context.Context;
pub const Server = server.Server;
pub const Player = player.Player;
pub const Entity = world.Entity;
pub const World = world.World;
pub const TextComponent = text.TextComponent;
pub const Event = event.Event;
pub const EventType = event.EventType;
pub const EventPriority = event.EventPriority;
pub const CommandSender = command.CommandSender;
pub const ConsumedArgs = command.ConsumedArgs;
pub const CommandError = command.CommandError;
pub const CommandResult = abi.Result(i32, CommandError);
pub const Metadata = struct {
name: []const u8,
version: []const u8,
authors: []const []const u8 = &.{},
description: []const u8 = "",
dependencies: []const []const u8 = &.{},
permissions: []const []const u8 = &.{},
};
/// Plug this into `std_options.logFn` to route `std.log` to the server console.
pub fn logFn(
comptime level: std.log.Level,
comptime scope: @EnumLiteral(),
comptime format: []const u8,
args: anytype,
) void {
const prefix = if (scope == .default) "" else "(" ++ @tagName(scope) ++ ") ";
const message = std.fmt.allocPrint(abi.arena(), prefix ++ format, args) catch return;
logging.log(switch (level) {
.err => .@"error",
.warn => .warn,
.info => .info,
.debug => .debug,
}, message);
}
pub const EventOptions = struct {
priority: EventPriority = .normal,
/// Whether the server waits for the handler before carrying on.
/// Changes made to the event are only seen when this is set.
blocking: bool = true,
};
pub fn EventData(comptime event_type: EventType) type {
return @FieldType(Event, @tagName(event_type));
}
/// Registers `handler` for one kind of event. It gets the payload of exactly
/// that event and may edit it in place, e.g. set `cancelled`.
pub fn on(
ctx: Context,
comptime event_type: EventType,
comptime handler: fn (Server, *EventData(event_type)) void,
options: EventOptions,
) void {
const glue = struct {
fn dispatch(srv: Server, ev: *Event) void {
handler(srv, &@field(ev, @tagName(event_type)));
}
};
ctx.registerEvent(handlerId(&glue.dispatch), event_type, options.priority, options.blocking);
}
pub const EventHandler = fn (Server, *Event) void;
pub const CommandHandler = fn (CommandSender, Server, ConsumedArgs) CommandResult;
pub const SuggestionHandler = fn (CommandSender, Server, command.SuggestionRequest) command.CommandSuggestions;
pub const TaskHandler = fn (Server) void;
pub const GenerateHandler = fn (world.GenerationPhase, world.ChunkBuffer) void;
pub const AiGoal = struct {
canStart: *const fn (Server, Entity) bool,
shouldContinue: *const fn (Server, Entity) bool,
start: *const fn (Server, Entity) void = noop,
tick: *const fn (Server, Entity) void = noop,
stop: *const fn (Server, Entity) void = noop,
fn noop(_: Server, _: Entity) void {}
};
/// The server hands ids back untouched, so an id is simply the address of the
/// handler. Works for every `*-with-handler-id` style function in the API,
/// pass a pointer to a function of the matching `*Handler` type or to a
/// static `AiGoal`.
pub fn handlerId(handler: anytype) u32 {
return @intFromPtr(handler);
}
pub fn runLater(comptime handler: TaskHandler, delay_ticks: u64) u32 {
return scheduler.scheduleDelayedTask(handlerId(&handler), delay_ticks);
}
pub fn runEvery(comptime handler: TaskHandler, delay_ticks: u64, period_ticks: u64) u32 {
return scheduler.scheduleRepeatingTask(handlerId(&handler), delay_ticks, period_ticks);
}
/// Registers what `Plugin.events` lists, each entry being `.event_name = handler`
/// or `.event_name = .{ handler, EventOptions{...} }`.
fn registerEvents(comptime Plugin: type, ctx: Context) void {
const table = Plugin.events;
inline for (@typeInfo(@TypeOf(table)).@"struct".fields) |field| {
const event_type = comptime std.meta.stringToEnum(EventType, field.name) orelse
@compileError("no event called '" ++ field.name ++ "'");
const entry = @field(table, field.name);
if (@typeInfo(@TypeOf(entry)) == .@"fn") {
on(ctx, event_type, entry, .{});
} else {
on(ctx, event_type, entry[0], entry[1]);
}
}
}
/// Exports the plugin world for `Plugin`. Call it from a `comptime` block.
///
/// `Plugin` needs a `pub const metadata: Metadata` and can have any of
/// pub const events = .{ .player_join_event = onJoin, ... }
/// pub const commands: []const cmd.Spec = &.{ ... }
/// pub fn onLoad(ctx: Context) !void
/// pub fn onUnload(ctx: Context) !void
/// pub fn onIpcMessage(sender: ipc.PluginId, message: ipc.IpcMessage) abi.Result(ipc.IpcMessage, []const u8)
///
/// Events and commands are registered before `onLoad` runs.
pub fn register(comptime Plugin: type) void {
const glue = struct {
fn initPlugin() void {}
fn getMetadata() wit.metadata.PluginMetadata {
const meta: Metadata = Plugin.metadata;
return .{
.name = meta.name,
.version = meta.version,
.authors = meta.authors,
.description = meta.description,
.dependencies = meta.dependencies,
.permissions = meta.permissions,
};
}
fn onLoad(ctx: Context) abi.Result(void, []const u8) {
if (@hasDecl(Plugin, "events")) registerEvents(Plugin, ctx);
if (@hasDecl(Plugin, "commands")) {
const specs: []const cmd.Spec = Plugin.commands;
for (specs) |spec| cmd.register(ctx, spec) catch |err| return .{ .err = @errorName(err) };
}
return lifecycle("onLoad", ctx);
}
fn onUnload(ctx: Context) abi.Result(void, []const u8) {
return lifecycle("onUnload", ctx);
}
fn lifecycle(comptime name: []const u8, ctx: Context) abi.Result(void, []const u8) {
if (!@hasDecl(Plugin, name)) return .ok;
@field(Plugin, name)(ctx) catch |err| return .{ .err = @errorName(err) };
return .ok;
}
fn handleEvent(id: u32, srv: Server, ev: Event) Event {
var out = ev;
handlerFromId(EventHandler, id)(srv, &out);
return out;
}
fn handleCommand(id: u32, sender: CommandSender, srv: Server, args: ConsumedArgs) CommandResult {
return handlerFromId(CommandHandler, id)(sender, srv, args);
}
fn handleCommandSuggestion(
id: u32,
sender: CommandSender,
srv: Server,
request: command.SuggestionRequest,
) command.CommandSuggestions {
return handlerFromId(SuggestionHandler, id)(sender, srv, request);
}
fn handleTask(id: u32, srv: Server) void {
handlerFromId(TaskHandler, id)(srv);
}
fn handleIpcMessage(sender: ipc.PluginId, message: ipc.IpcMessage) abi.Result(ipc.IpcMessage, []const u8) {
if (@hasDecl(Plugin, "onIpcMessage")) return Plugin.onIpcMessage(sender, message);
return .{ .err = "this plugin does not take messages" };
}
fn aiCanStart(id: u32, srv: Server, mob: Entity) bool {
return goalFromId(id).canStart(srv, mob);
}
fn aiShouldContinue(id: u32, srv: Server, mob: Entity) bool {
return goalFromId(id).shouldContinue(srv, mob);
}
fn aiStart(id: u32, srv: Server, mob: Entity) void {
goalFromId(id).start(srv, mob);
}
fn aiTick(id: u32, srv: Server, mob: Entity) void {
goalFromId(id).tick(srv, mob);
}
fn aiStop(id: u32, srv: Server, mob: Entity) void {
goalFromId(id).stop(srv, mob);
}
fn handleGeneratePhase(id: u32, phase: world.GenerationPhase, chunk: world.ChunkBuffer) void {
handlerFromId(GenerateHandler, id)(phase, chunk);
}
};
const exports = wit.exports;
exports.initPlugin(glue.initPlugin);
exports.getMetadata(glue.getMetadata);
exports.onLoad(glue.onLoad);
exports.onUnload(glue.onUnload);
exports.handleEvent(glue.handleEvent);
exports.handleCommand(glue.handleCommand);
exports.handleCommandSuggestion(glue.handleCommandSuggestion);
exports.handleTask(glue.handleTask);
exports.handleIpcMessage(glue.handleIpcMessage);
exports.handleAiGoalCanStart(glue.aiCanStart);
exports.handleAiGoalShouldContinue(glue.aiShouldContinue);
exports.handleAiGoalStart(glue.aiStart);
exports.handleAiGoalTick(glue.aiTick);
exports.handleAiGoalStop(glue.aiStop);
exports.handleGeneratePhase(glue.handleGeneratePhase);
}
fn handlerFromId(comptime Fn: type, id: u32) *const Fn {
return @ptrFromInt(id);
}
fn goalFromId(id: u32) *const AiGoal {
return @ptrFromInt(id);
}
test {
_ = abi;
}

48
src/wit.zig Normal file
View File

@ -0,0 +1,48 @@
// Generated by tools/gen.zig, do not edit.
pub const attributes = @import("wit/attributes.zig");
pub const biomes = @import("wit/biomes.zig");
pub const common = @import("wit/common.zig");
pub const damage_types = @import("wit/damage_types.zig");
pub const data_components = @import("wit/data_components.zig");
pub const datapack = @import("wit/datapack.zig");
pub const entity_statuses = @import("wit/entity_statuses.zig");
pub const entity_types = @import("wit/entity_types.zig");
pub const game_events = @import("wit/game_events.zig");
pub const game_rules = @import("wit/game_rules.zig");
pub const i18n = @import("wit/i18n.zig");
pub const ipc = @import("wit/ipc.zig");
pub const logging = @import("wit/logging.zig");
pub const metadata = @import("wit/metadata.zig");
pub const particles = @import("wit/particles.zig");
pub const permission = @import("wit/permission.zig");
pub const potions = @import("wit/potions.zig");
pub const scheduler = @import("wit/scheduler.zig");
pub const screens = @import("wit/screens.zig");
pub const sounds = @import("wit/sounds.zig");
pub const statistics = @import("wit/statistics.zig");
pub const status_effect = @import("wit/status_effect.zig");
pub const text = @import("wit/text.zig");
pub const advancement = @import("wit/advancement.zig");
pub const enchantments = @import("wit/enchantments.zig");
pub const forms = @import("wit/forms.zig");
pub const item_stack = @import("wit/item_stack.zig");
pub const inventory = @import("wit/inventory.zig");
pub const block_entity = @import("wit/block_entity.zig");
pub const gui = @import("wit/gui.zig");
pub const java_dialogs = @import("wit/java_dialogs.zig");
pub const recipe = @import("wit/recipe.zig");
pub const scoreboard = @import("wit/scoreboard.zig");
pub const uuid = @import("wit/uuid.zig");
pub const bedrock_packets = @import("wit/bedrock_packets.zig");
pub const java_packets = @import("wit/java_packets.zig");
pub const world = @import("wit/world.zig");
pub const display = @import("wit/display.zig");
pub const entity = @import("wit/entity.zig");
pub const player = @import("wit/player.zig");
pub const boss_bar = @import("wit/boss_bar.zig");
pub const event = @import("wit/event.zig");
pub const server = @import("wit/server.zig");
pub const command = @import("wit/command.zig");
pub const context = @import("wit/context.zig");
pub const exports = @import("wit/exports.zig");

41
src/wit/advancement.zig Normal file
View File

@ -0,0 +1,41 @@
// Generated by tools/gen.zig, do not edit.
const text = @import("text.zig");
pub const TextComponent = text.TextComponent;
/// Frame type for an advancement.
pub const FrameType = enum(u8) {
task,
challenge,
goal,
};
/// Display information for an advancement.
pub const AdvancementDisplay = struct {
title: TextComponent,
description: TextComponent,
frame: FrameType,
show_toast: bool,
hidden: bool,
announce_to_chat: bool,
background: ?[]const u8,
x: f32,
y: f32,
};
/// Detailed information about an advancement.
pub const AdvancementInfo = struct {
id: []const u8,
parent_id: ?[]const u8,
criteria: []const []const u8,
display: ?AdvancementDisplay,
};
/// Represents progress of an advancement for a player.
pub const AdvancementProgress = struct {
advancement_id: []const u8,
done: bool,
awarded_criteria: []const []const u8,
remaining_criteria: []const []const u8,
};

56
src/wit/attributes.zig Normal file
View File

@ -0,0 +1,56 @@
// Generated by tools/gen.zig, do not edit.
pub const Attribute = enum(u8) {
air_drag_modifier,
armor,
armor_toughness,
attack_damage,
attack_knockback,
attack_speed,
below_name_distance,
block_break_speed,
block_interaction_range,
bounciness,
burning_time,
camera_distance,
explosion_knockback_resistance,
entity_interaction_range,
fall_damage_multiplier,
flying_speed,
follow_range,
friction_modifier,
gravity,
jump_strength,
knockback_resistance,
luck,
max_absorption,
max_health,
mining_efficiency,
movement_efficiency,
movement_speed,
name_tag_distance,
oxygen_bonus,
safe_fall_distance,
scale,
sneaking_speed,
spawn_reinforcements,
step_height,
submerged_mining_speed,
sweeping_damage_ratio,
tempt_range,
water_movement_efficiency,
waypoint_transmit_range,
waypoint_receive_range,
};
pub const ModifierOperation = enum(u8) {
add,
multiply_base,
multiply_total,
};
pub const AttributeModifier = struct {
id: []const u8,
amount: f64,
operation: ModifierOperation,
};

1901
src/wit/bedrock_packets.zig Normal file

File diff suppressed because it is too large Load Diff

70
src/wit/biomes.zig Normal file
View File

@ -0,0 +1,70 @@
// Generated by tools/gen.zig, do not edit.
pub const Biome = enum(u8) {
badlands,
bamboo_jungle,
basalt_deltas,
beach,
birch_forest,
cherry_grove,
cold_ocean,
crimson_forest,
dark_forest,
deep_cold_ocean,
deep_dark,
deep_frozen_ocean,
deep_lukewarm_ocean,
deep_ocean,
desert,
dripstone_caves,
end_barrens,
end_highlands,
end_midlands,
eroded_badlands,
flower_forest,
forest,
frozen_ocean,
frozen_peaks,
frozen_river,
grove,
ice_spikes,
jagged_peaks,
jungle,
lukewarm_ocean,
lush_caves,
mangrove_swamp,
meadow,
mushroom_fields,
nether_wastes,
ocean,
old_growth_birch_forest,
old_growth_pine_taiga,
old_growth_spruce_taiga,
pale_garden,
plains,
river,
savanna,
savanna_plateau,
small_end_islands,
snowy_beach,
snowy_plains,
snowy_slopes,
snowy_taiga,
soul_sand_valley,
sparse_jungle,
stony_peaks,
stony_shore,
sulfur_caves,
sunflower_plains,
swamp,
taiga,
the_end,
the_void,
warm_ocean,
warped_forest,
windswept_forest,
windswept_gravelly_hills,
windswept_hills,
windswept_savanna,
wooded_badlands,
};

1865
src/wit/block_entity.zig Normal file

File diff suppressed because it is too large Load Diff

157
src/wit/boss_bar.zig Normal file
View File

@ -0,0 +1,157 @@
// Generated by tools/gen.zig, do not edit.
const abi = @import("../abi.zig");
const text = @import("text.zig");
const player = @import("player.zig");
pub const TextComponent = text.TextComponent;
pub const Player = player.Player;
/// Represents the color of a boss bar.
pub const BossBarColor = enum(u8) {
pink,
blue,
red,
green,
yellow,
purple,
white,
};
/// Represents the notches/divisions of a boss bar.
pub const BossBarDivision = enum(u8) {
no_division,
notches_6,
notches_10,
notches_12,
notches_20,
};
/// Metadata flags that can be toggled on a boss bar.
pub const BossBarMetadata = struct {
/// Darkens the sky for players who see the boss bar.
darken_sky: bool,
/// If true, the bar is considered a "dragon bar" (used for Ender Dragon).
dragon_bar: bool,
/// If true, fog is created around the players who see the boss bar.
create_fog: bool,
};
/// Represents a boss bar that can be shown to players.
pub const BossBar = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]boss-bar";
/// Creates a new boss bar.
///
/// Consumes `title`.
pub fn init(title: TextComponent, color: BossBarColor, division: BossBarDivision) BossBar {
return abi.call(raw.@"[constructor]boss-bar", BossBar, .{ title, color, division });
}
/// Returns the title of this boss bar.
pub fn getTitle(self: BossBar) TextComponent {
return abi.call(raw.@"[method]boss-bar.get-title", TextComponent, .{abi.borrow(self)});
}
/// Sets the title of this boss bar.
///
/// Consumes `title`.
pub fn setTitle(self: BossBar, title: TextComponent) void {
return abi.call(raw.@"[method]boss-bar.set-title", void, .{ abi.borrow(self), title });
}
/// Returns the health (0.0 to 1.0) of this boss bar.
pub fn getHealth(self: BossBar) f32 {
return abi.call(raw.@"[method]boss-bar.get-health", f32, .{abi.borrow(self)});
}
/// Sets the health (0.0 to 1.0) of this boss bar.
pub fn setHealth(self: BossBar, health: f32) void {
return abi.call(raw.@"[method]boss-bar.set-health", void, .{ abi.borrow(self), health });
}
/// Returns the color of this boss bar.
pub fn getColor(self: BossBar) BossBarColor {
return abi.call(raw.@"[method]boss-bar.get-color", BossBarColor, .{abi.borrow(self)});
}
/// Sets the color of this boss bar.
pub fn setColor(self: BossBar, color: BossBarColor) void {
return abi.call(raw.@"[method]boss-bar.set-color", void, .{ abi.borrow(self), color });
}
/// Returns the division of this boss bar.
pub fn getDivision(self: BossBar) BossBarDivision {
return abi.call(raw.@"[method]boss-bar.get-division", BossBarDivision, .{abi.borrow(self)});
}
/// Sets the division of this boss bar.
pub fn setDivision(self: BossBar, division: BossBarDivision) void {
return abi.call(raw.@"[method]boss-bar.set-division", void, .{ abi.borrow(self), division });
}
/// Returns the currently active metadata on this boss bar.
pub fn getMetadata(self: BossBar) BossBarMetadata {
return abi.call(raw.@"[method]boss-bar.get-metadata", BossBarMetadata, .{abi.borrow(self)});
}
/// Updates the metadata on this boss bar.
pub fn setMetadata(self: BossBar, metadata: BossBarMetadata) void {
return abi.call(raw.@"[method]boss-bar.set-metadata", void, .{ abi.borrow(self), metadata });
}
/// Adds a player to this boss bar, making it visible to them.
///
/// Consumes `player_`.
pub fn addPlayer(self: BossBar, player_: Player) void {
return abi.call(raw.@"[method]boss-bar.add-player", void, .{ abi.borrow(self), player_ });
}
/// Removes a player from this boss bar, hiding it from them.
///
/// Consumes `player_`.
pub fn removePlayer(self: BossBar, player_: Player) void {
return abi.call(raw.@"[method]boss-bar.remove-player", void, .{ abi.borrow(self), player_ });
}
/// Returns a list of all players currently seeing this boss bar.
pub fn getPlayers(self: BossBar) []const Player {
return abi.call(raw.@"[method]boss-bar.get-players", []const Player, .{abi.borrow(self)});
}
/// Removes this boss bar from all players and cleans it up.
pub fn removeAll(self: BossBar) void {
return abi.call(raw.@"[method]boss-bar.remove-all", void, .{abi.borrow(self)});
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: BossBar) void {
abi.keep(BossBar, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: BossBar) void {
abi.drop(BossBar, self);
}
};
const raw = struct {
extern "pumpkin:plugin/boss-bar@0.1.0" fn @"[constructor]boss-bar"(i32, i32, i32) i32;
extern "pumpkin:plugin/boss-bar@0.1.0" fn @"[method]boss-bar.get-title"(i32) i32;
extern "pumpkin:plugin/boss-bar@0.1.0" fn @"[method]boss-bar.set-title"(i32, i32) void;
extern "pumpkin:plugin/boss-bar@0.1.0" fn @"[method]boss-bar.get-health"(i32) f32;
extern "pumpkin:plugin/boss-bar@0.1.0" fn @"[method]boss-bar.set-health"(i32, f32) void;
extern "pumpkin:plugin/boss-bar@0.1.0" fn @"[method]boss-bar.get-color"(i32) i32;
extern "pumpkin:plugin/boss-bar@0.1.0" fn @"[method]boss-bar.set-color"(i32, i32) void;
extern "pumpkin:plugin/boss-bar@0.1.0" fn @"[method]boss-bar.get-division"(i32) i32;
extern "pumpkin:plugin/boss-bar@0.1.0" fn @"[method]boss-bar.set-division"(i32, i32) void;
extern "pumpkin:plugin/boss-bar@0.1.0" fn @"[method]boss-bar.get-metadata"(i32, i32) void;
extern "pumpkin:plugin/boss-bar@0.1.0" fn @"[method]boss-bar.set-metadata"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/boss-bar@0.1.0" fn @"[method]boss-bar.add-player"(i32, i32) void;
extern "pumpkin:plugin/boss-bar@0.1.0" fn @"[method]boss-bar.remove-player"(i32, i32) void;
extern "pumpkin:plugin/boss-bar@0.1.0" fn @"[method]boss-bar.get-players"(i32, i32) void;
extern "pumpkin:plugin/boss-bar@0.1.0" fn @"[method]boss-bar.remove-all"(i32) void;
extern "pumpkin:plugin/boss-bar@0.1.0" fn @"[resource-drop]boss-bar"(i32) void;
};

434
src/wit/command.zig Normal file
View File

@ -0,0 +1,434 @@
// Generated by tools/gen.zig, do not edit.
const abi = @import("../abi.zig");
const world = @import("world.zig");
const player = @import("player.zig");
const block_entity = @import("block_entity.zig");
const common = @import("common.zig");
const item_stack = @import("item_stack.zig");
const server = @import("server.zig");
const text = @import("text.zig");
pub const World = world.World;
pub const Entity = world.Entity;
pub const Player = player.Player;
pub const CommandBlockEntity = block_entity.CommandBlockEntity;
pub const Position = common.Position;
pub const BlockPos = common.BlockPos;
pub const Locale = common.Locale;
pub const GameMode = common.GameMode;
pub const ItemStack = item_stack.ItemStack;
pub const Server = server.Server;
pub const Difficulty = server.Difficulty;
pub const TextComponent = text.TextComponent;
pub const CommandError = union(enum) {
invalid_consumption: ?[]const u8,
invalid_requirement,
permission_denied,
command_failed: TextComponent,
};
pub const CommandNode = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]command-node";
pub fn literal(name: []const u8) CommandNode {
return abi.call(raw.@"[static]command-node.literal", CommandNode, .{name});
}
pub fn argument(name: []const u8, @"type": ArgumentType) CommandNode {
return abi.call(raw.@"[static]command-node.argument", CommandNode, .{ name, @"type" });
}
/// Consumes `node`.
pub fn then(self: CommandNode, node: CommandNode) void {
return abi.call(raw.@"[method]command-node.then", void, .{ abi.borrow(self), node });
}
pub fn executeWithHandlerId(self: CommandNode, handler_id: u32) void {
return abi.call(raw.@"[method]command-node.execute-with-handler-id", void, .{ abi.borrow(self), handler_id });
}
pub fn suggestWithHandlerId(self: CommandNode, handler_id: u32) void {
return abi.call(raw.@"[method]command-node.suggest-with-handler-id", void, .{ abi.borrow(self), handler_id });
}
pub fn requireWithHandlerId(self: CommandNode, handler_id: u32) void {
return abi.call(raw.@"[method]command-node.require-with-handler-id", void, .{ abi.borrow(self), handler_id });
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: CommandNode) void {
abi.keep(CommandNode, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: CommandNode) void {
abi.drop(CommandNode, self);
}
};
pub const SuggestionRequest = struct {
input: []const u8,
cursor: u32,
start: u32,
remaining: []const u8,
};
pub const CommandSuggestion = struct {
value: []const u8,
tooltip: ?TextComponent,
};
pub const CommandSuggestions = struct {
start: u32,
length: u32,
values: []const CommandSuggestion,
};
pub const EntityArgument = struct {
single: bool,
players_only: bool,
};
pub const CommandSenderType = union(enum) {
rcon,
console,
player: Player,
command_block: struct { CommandBlockEntity, World },
dummy,
};
pub const PermissionLevel = enum(u8) {
zero,
one,
two,
three,
four,
};
pub const CommandSender = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]command-sender";
pub fn getCommandSenderType(self: CommandSender) CommandSenderType {
return abi.call(raw.@"[method]command-sender.get-command-sender-type", CommandSenderType, .{abi.borrow(self)});
}
pub fn getName(self: CommandSender) []const u8 {
return abi.call(raw.@"[method]command-sender.get-name", []const u8, .{abi.borrow(self)});
}
/// Consumes `text_`.
pub fn sendMessage(self: CommandSender, text_: TextComponent) void {
return abi.call(raw.@"[method]command-sender.send-message", void, .{ abi.borrow(self), text_ });
}
/// Consumes `text_`.
pub fn sendSystemMessage(self: CommandSender, text_: TextComponent) void {
return abi.call(raw.@"[method]command-sender.send-system-message", void, .{ abi.borrow(self), text_ });
}
/// Consumes `text_`.
pub fn sendError(self: CommandSender, text_: TextComponent) void {
return abi.call(raw.@"[method]command-sender.send-error", void, .{ abi.borrow(self), text_ });
}
pub fn setSuccessCount(self: CommandSender, count: i32) void {
return abi.call(raw.@"[method]command-sender.set-success-count", void, .{ abi.borrow(self), count });
}
pub fn isPlayer(self: CommandSender) bool {
return abi.call(raw.@"[method]command-sender.is-player", bool, .{abi.borrow(self)});
}
pub fn isConsole(self: CommandSender) bool {
return abi.call(raw.@"[method]command-sender.is-console", bool, .{abi.borrow(self)});
}
pub fn asPlayer(self: CommandSender) ?Player {
return abi.call(raw.@"[method]command-sender.as-player", ?Player, .{abi.borrow(self)});
}
pub fn permissionLevel(self: CommandSender) PermissionLevel {
return abi.call(raw.@"[method]command-sender.permission-level", PermissionLevel, .{abi.borrow(self)});
}
pub fn hasPermissionLevel(self: CommandSender, level: PermissionLevel) bool {
return abi.call(raw.@"[method]command-sender.has-permission-level", bool, .{ abi.borrow(self), level });
}
pub fn hasPermission(self: CommandSender, server_: Server, node: []const u8) bool {
return abi.call(raw.@"[method]command-sender.has-permission", bool, .{ abi.borrow(self), abi.borrow(server_), node });
}
pub fn position(self: CommandSender) ?Position {
return abi.call(raw.@"[method]command-sender.position", ?Position, .{abi.borrow(self)});
}
pub fn world(self: CommandSender) ?World {
return abi.call(raw.@"[method]command-sender.world", ?World, .{abi.borrow(self)});
}
pub fn getLocale(self: CommandSender) Locale {
return abi.call(raw.@"[method]command-sender.get-locale", Locale, .{abi.borrow(self)});
}
pub fn shouldReceiveFeedback(self: CommandSender) bool {
return abi.call(raw.@"[method]command-sender.should-receive-feedback", bool, .{abi.borrow(self)});
}
pub fn shouldBroadcastConsoleToOps(self: CommandSender) bool {
return abi.call(raw.@"[method]command-sender.should-broadcast-console-to-ops", bool, .{abi.borrow(self)});
}
pub fn shouldTrackOutput(self: CommandSender) bool {
return abi.call(raw.@"[method]command-sender.should-track-output", bool, .{abi.borrow(self)});
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: CommandSender) void {
abi.keep(CommandSender, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: CommandSender) void {
abi.drop(CommandSender, self);
}
};
pub const StringType = enum(u8) {
single_word,
quotable,
greedy,
};
pub const ArgumentType = union(enum) {
bool,
float: struct { ?f32, ?f32 },
double: struct { ?f64, ?f64 },
integer: struct { ?i32, ?i32 },
long: struct { ?i64, ?i64 },
string: StringType,
entities,
entity,
players,
game_profile,
block_pos,
column_pos,
position3d,
position2d,
block_state,
block_predicate,
item,
item_predicate,
color,
component,
style,
message,
nbt_compound_tag,
nbt_tag,
nbt_path,
objective,
objective_criteria,
operation,
particle,
angle,
rotation,
scoreboard_slot,
score_holder: bool,
swizzle,
team,
item_slot,
resource_location,
mob_effect,
function,
entity_anchor,
int_range,
float_range,
dimension,
gamemode,
difficulty,
time: ?i32,
resource: []const u8,
resource_or_tag: []const u8,
resource_or_tag_key: []const u8,
resource_key: []const u8,
template_mirror,
template_rotation,
uuid,
};
pub const SuggestionType = enum(u8) {
ask_server,
all_recipes,
available_sounds,
available_biomes,
summonable_entities,
};
pub const BossbarColor = enum(u8) {
pink,
blue,
red,
green,
yellow,
purple,
white,
};
pub const BossbarStyle = enum(u8) {
no_division,
notches6,
notches10,
notches12,
notches20,
};
pub const SoundCategory = enum(u8) {
master,
music,
records,
weather,
blocks,
hostile,
neutral,
players,
ambient,
voice,
};
pub const EntityAnchor = enum(u8) {
eyes,
feet,
};
pub const Number = union(enum) {
float64: f64,
float32: f32,
int32: i32,
int64: i64,
};
pub const NotInBounds = union(enum) {
lower_bound: struct { Number, Number },
upper_bound: struct { Number, Number },
};
pub const Arg = union(enum) {
simple: []const u8,
msg: []const u8,
bool: bool,
item: []const u8,
item_predicate: []const u8,
resource_location: []const u8,
block: []const u8,
block_predicate: []const u8,
time: i32,
num: abi.Result(Number, NotInBounds),
block_pos: BlockPos,
pos3d: struct { f64, f64, f64 },
pos2d: struct { f64, f64 },
rotation: struct { f32, bool, f32, bool },
gamemode: GameMode,
difficulty: Difficulty,
players: []const Player,
particle: []const u8,
text_component: TextComponent,
bossbar_color: BossbarColor,
bossbar_style: BossbarStyle,
sound_category: SoundCategory,
damage_type: []const u8,
effect: []const u8,
enchantment: []const u8,
advancement: []const u8,
entity_anchor: EntityAnchor,
};
pub const ConsumedArgs = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]consumed-args";
pub fn getValue(self: ConsumedArgs, key: []const u8) Arg {
return abi.call(raw.@"[method]consumed-args.get-value", Arg, .{ abi.borrow(self), key });
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: ConsumedArgs) void {
abi.keep(ConsumedArgs, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: ConsumedArgs) void {
abi.drop(ConsumedArgs, self);
}
};
pub const Command = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]command";
/// First name is primary, rest are aliases
pub fn init(names: []const []const u8, description: []const u8) Command {
return abi.call(raw.@"[constructor]command", Command, .{ names, description });
}
/// Consumes `node`.
pub fn then(self: Command, node: CommandNode) void {
return abi.call(raw.@"[method]command.then", void, .{ abi.borrow(self), node });
}
pub fn executeWithHandlerId(self: Command, handler_id: u32) void {
return abi.call(raw.@"[method]command.execute-with-handler-id", void, .{ abi.borrow(self), handler_id });
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: Command) void {
abi.keep(Command, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: Command) void {
abi.drop(Command, self);
}
};
const raw = struct {
extern "pumpkin:plugin/command@0.1.0" fn @"[static]command-node.literal"(i32, i32) i32;
extern "pumpkin:plugin/command@0.1.0" fn @"[static]command-node.argument"(i32, i32, i32, i32, i64, i32, i64) i32;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-node.then"(i32, i32) void;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-node.execute-with-handler-id"(i32, i32) void;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-node.suggest-with-handler-id"(i32, i32) void;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-node.require-with-handler-id"(i32, i32) void;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-sender.get-command-sender-type"(i32, i32) void;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-sender.get-name"(i32, i32) void;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-sender.send-message"(i32, i32) void;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-sender.send-system-message"(i32, i32) void;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-sender.send-error"(i32, i32) void;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-sender.set-success-count"(i32, i32) void;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-sender.is-player"(i32) i32;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-sender.is-console"(i32) i32;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-sender.as-player"(i32, i32) void;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-sender.permission-level"(i32) i32;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-sender.has-permission-level"(i32, i32) i32;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-sender.has-permission"(i32, i32, i32, i32) i32;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-sender.position"(i32, i32) void;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-sender.world"(i32, i32) void;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-sender.get-locale"(i32) i32;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-sender.should-receive-feedback"(i32) i32;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-sender.should-broadcast-console-to-ops"(i32) i32;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command-sender.should-track-output"(i32) i32;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]consumed-args.get-value"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/command@0.1.0" fn @"[constructor]command"(i32, i32, i32, i32) i32;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command.then"(i32, i32) void;
extern "pumpkin:plugin/command@0.1.0" fn @"[method]command.execute-with-handler-id"(i32, i32) void;
extern "pumpkin:plugin/command@0.1.0" fn @"[resource-drop]command-node"(i32) void;
extern "pumpkin:plugin/command@0.1.0" fn @"[resource-drop]command-sender"(i32) void;
extern "pumpkin:plugin/command@0.1.0" fn @"[resource-drop]consumed-args"(i32) void;
extern "pumpkin:plugin/command@0.1.0" fn @"[resource-drop]command"(i32) void;
};

260
src/wit/common.zig Normal file
View File

@ -0,0 +1,260 @@
// Generated by tools/gen.zig, do not edit.
/// Serialized text component as a postcard byte array.
/// @deprecated Use the text-component resource from the text interface instead.
pub const RawTextComponent = []const u8;
/// Represents one entry in an NBT compound tag.
pub const NbtEntry = struct {
key: []const u8,
value: u32,
};
/// Represents a Minecraft NBT tag.
pub const NbtTag = union(enum) {
byte: i8,
short: i16,
int: i32,
long: i64,
float: f32,
double: f64,
byte_array: []const i8,
string_tag: []const u8,
list_tag: []const u32,
compound: []const NbtEntry,
int_array: []const i32,
long_array: []const i64,
};
/// Represents a Minecraft NBT tag tree.
pub const NbtTree = struct {
root: u32,
tags: []const NbtTag,
};
/// A position in the world grid (integers).
pub const BlockPos = struct {
x: i32,
y: i32,
z: i32,
};
/// A precise coordinate in the world (floats).
pub const Position = struct { f64, f64, f64 };
/// Represents the hands of a player.
pub const Hand = enum(u8) {
left,
right,
};
/// Represents the game modes available in Minecraft.
pub const GameMode = enum(u8) {
survival,
creative,
adventure,
spectator,
};
/// The 16 base Minecraft colors.
pub const NamedColor = enum(u8) {
black,
dark_blue,
dark_green,
dark_aqua,
dark_red,
dark_purple,
gold,
gray,
dark_gray,
blue,
green,
aqua,
red,
light_purple,
yellow,
white,
};
/// An RGB color represented by its red, green, and blue components.
pub const RgbColor = struct {
r: u8,
g: u8,
b: u8,
};
/// An ARGB color with alpha (transparency) channel.
pub const ArgbColor = struct {
a: u8,
r: u8,
g: u8,
b: u8,
};
/// Represents the type of click interaction in a GUI.
pub const ClickType = enum(u8) {
left,
right,
shift_left,
shift_right,
middle,
drop,
control_drop,
double_click,
number_key,
unknown,
};
/// Represents the various poses an entity can be in.
pub const EntityPose = enum(u8) {
standing,
fall_flying,
sleeping,
swimming,
spin_attack,
crouching,
long_jumping,
dying,
croaking,
using_tongue,
sitting,
roaring,
sniffing,
emerging,
digging,
sliding,
shooting,
inhaling,
};
/// All Minecraft-supported locales.
pub const Locale = enum(u8) {
af_za,
ar_sa,
ast_es,
az_az,
ba_ru,
bar,
be_by,
bg_bg,
br_fr,
brb,
bs_ba,
ca_es,
cs_cz,
cy_gb,
da_dk,
de_at,
de_ch,
de_de,
el_gr,
en_au,
en_ca,
en_gb,
en_nz,
en_pt,
en_ud,
en_us,
enp,
enws,
eo_uy,
es_ar,
es_cl,
es_ec,
es_es,
es_mx,
es_uy,
es_ve,
esan,
et_ee,
eu_es,
fa_ir,
fi_fi,
fil_ph,
fo_fo,
fr_ca,
fr_fr,
fra_de,
fur_it,
fy_nl,
ga_ie,
gd_gb,
gl_es,
haw_us,
he_il,
hi_in,
hr_hr,
hu_hu,
hy_am,
id_id,
ig_ng,
io_en,
is_is,
isv,
it_it,
ja_jp,
jbo_en,
ka_ge,
kk_kz,
kn_in,
ko_kr,
ksh,
kw_gb,
la_la,
lb_lu,
li_li,
lmo,
lo_la,
lol_us,
lt_lt,
lv_lv,
lzh,
mk_mk,
mn_mn,
ms_my,
mt_mt,
nah,
nds_de,
nl_be,
nl_nl,
nn_no,
no_no,
oc_fr,
ovd,
pl_pl,
pt_br,
pt_pt,
qya_aa,
ro_ro,
rpr,
ru_ru,
ry_ua,
sah_sah,
se_no,
sk_sk,
sl_si,
so_so,
sq_al,
sr_cs,
sr_sp,
sv_se,
sxu,
szl,
ta_in,
th_th,
tl_ph,
tlh_aa,
tok,
tr_tr,
tt_ru,
uk_ua,
val_es,
vec_it,
vi_vn,
yi_de,
yo_ng,
zh_cn,
zh_hk,
zh_tw,
zlm_arab,
};

104
src/wit/context.zig Normal file
View File

@ -0,0 +1,104 @@
// Generated by tools/gen.zig, do not edit.
const abi = @import("../abi.zig");
const server = @import("server.zig");
const event = @import("event.zig");
const command = @import("command.zig");
const permission = @import("permission.zig");
pub const Server = server.Server;
pub const EventType = event.EventType;
pub const EventPriority = event.EventPriority;
pub const Command = command.Command;
pub const Permission = permission.Permission;
/// Verified marketplace metadata for signed plugins.
pub const MarketplaceMetadata = struct {
/// The marketplace base URL where this plugin is registered.
marketplace_url: []const u8,
/// The unique plugin ID on the marketplace.
plugin_id: i64,
/// The canonical plugin name.
plugin_name: []const u8,
/// The semver version string of the plugin.
version: []const u8,
/// Developer ID.
dev_id: i64,
/// Developer display name or username.
dev_name: []const u8,
/// Whether this is a paid marketplace plugin.
is_paid: bool,
/// The buyer / licensee user ID (0 for free/open-source).
user_id: i64,
/// Unique license key issued to the buyer, if paid.
license_key: ?[]const u8,
/// ISO-8601 timestamp of when this binary/license was issued.
issued_at: []const u8,
};
/// A contextual handle for plugin-related operations.
pub const Context = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]context";
/// Registers a handler for a specific event.
///
/// * `handler-id`: Unique ID for the event handler.
/// * `event-type`: The type of event to listen for.
/// * `event-priority`: When this handler should be called relative to others.
/// * `blocking`: Whether the event should wait for this handler to finish.
pub fn registerEvent(self: Context, handler_id: u32, event_type: EventType, event_priority: EventPriority, blocking: bool) void {
return abi.call(raw.@"[method]context.register-event", void, .{ abi.borrow(self), handler_id, event_type, event_priority, blocking });
}
/// Registers a new command.
///
/// * `command`: The command definition.
/// * `permission`: The required permission node to execute this command.
///
/// Consumes `command_`.
pub fn registerCommand(self: Context, command_: Command, permission_: []const u8) void {
return abi.call(raw.@"[method]context.register-command", void, .{ abi.borrow(self), command_, permission_ });
}
/// Registers a permission node with the server.
pub fn registerPermission(self: Context, permission_: Permission) abi.Result(void, []const u8) {
return abi.call(raw.@"[method]context.register-permission", abi.Result(void, []const u8), .{ abi.borrow(self), permission_ });
}
/// Returns the path to the plugin's private data folder.
pub fn getDataFolder(self: Context) []const u8 {
return abi.call(raw.@"[method]context.get-data-folder", []const u8, .{abi.borrow(self)});
}
/// Returns the global server instance.
pub fn getServer(self: Context) Server {
return abi.call(raw.@"[method]context.get-server", Server, .{abi.borrow(self)});
}
/// Returns verified marketplace metadata if this plugin binary is signed, or none if unsigned.
pub fn getMarketplaceMetadata(self: Context) ?MarketplaceMetadata {
return abi.call(raw.@"[method]context.get-marketplace-metadata", ?MarketplaceMetadata, .{abi.borrow(self)});
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: Context) void {
abi.keep(Context, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: Context) void {
abi.drop(Context, self);
}
};
const raw = struct {
extern "pumpkin:plugin/context@0.1.0" fn @"[method]context.register-event"(i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/context@0.1.0" fn @"[method]context.register-command"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/context@0.1.0" fn @"[method]context.register-permission"(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/context@0.1.0" fn @"[method]context.get-data-folder"(i32, i32) void;
extern "pumpkin:plugin/context@0.1.0" fn @"[method]context.get-server"(i32) i32;
extern "pumpkin:plugin/context@0.1.0" fn @"[method]context.get-marketplace-metadata"(i32, i32) void;
extern "pumpkin:plugin/context@0.1.0" fn @"[resource-drop]context"(i32) void;
};

55
src/wit/damage_types.zig Normal file
View File

@ -0,0 +1,55 @@
// Generated by tools/gen.zig, do not edit.
pub const DamageType = enum(u8) {
arrow,
bad_respawn_point,
cactus,
campfire,
cramming,
dragon_breath,
drown,
dry_out,
ender_pearl,
explosion,
fall,
falling_anvil,
falling_block,
falling_stalactite,
fireball,
fireworks,
fly_into_wall,
freeze,
generic,
generic_kill,
hot_floor,
in_fire,
in_wall,
indirect_magic,
lava,
lightning_bolt,
mace_smash,
magic,
mob_attack,
mob_attack_no_aggro,
mob_projectile,
on_fire,
out_of_world,
outside_border,
player_attack,
player_explosion,
sonic_boom,
spear,
spit,
stalagmite,
starve,
sting,
sulfur_cube_hot,
sweet_berry_bush,
thorns,
thrown,
trident,
unattributed_fireball,
wind_charge,
wither,
wither_skull,
};

115
src/wit/data_components.zig Normal file
View File

@ -0,0 +1,115 @@
// Generated by tools/gen.zig, do not edit.
pub const DataComponent = enum(u8) {
custom_data,
max_stack_size,
max_damage,
damage,
unbreakable,
use_effects,
custom_name,
minimum_attack_charge,
damage_type,
item_name,
item_model,
lore,
rarity,
enchantments,
can_place_on,
can_break,
attribute_modifiers,
custom_model_data,
tooltip_display,
repair_cost,
creative_slot_lock,
enchantment_glint_override,
intangible_projectile,
food,
consumable,
use_remainder,
use_cooldown,
damage_resistant,
tool,
weapon,
attack_range,
enchantable,
equippable,
repairable,
glider,
tooltip_style,
death_protection,
blocks_attacks,
piercing_weapon,
kinetic_weapon,
swing_animation,
additional_trade_cost,
stored_enchantments,
dye,
dyed_color,
map_color,
map_id,
map_decorations,
map_post_processing,
charged_projectiles,
bundle_contents,
potion_contents,
potion_duration_scale,
suspicious_stew_effects,
writable_book_content,
written_book_content,
trim,
debug_stick_state,
entity_data,
bucket_entity_data,
block_entity_data,
instrument,
provides_trim_material,
ominous_bottle_amplifier,
jukebox_playable,
provides_banner_patterns,
recipes,
lodestone_tracker,
firework_explosion,
fireworks,
profile,
note_block_sound,
banner_patterns,
base_color,
pot_decorations,
container,
block_state,
bees,
sulfur_cube_content,
lock,
container_loot,
break_sound,
villager_variant,
wolf_variant,
wolf_sound_variant,
wolf_collar,
fox_variant,
salmon_size,
parrot_variant,
tropical_fish_pattern,
tropical_fish_base_color,
tropical_fish_pattern_color,
mooshroom_variant,
rabbit_variant,
pig_variant,
pig_sound_variant,
cow_variant,
cow_sound_variant,
chicken_variant,
chicken_sound_variant,
zombie_nautilus_variant,
frog_variant,
horse_variant,
painting_variant,
llama_variant,
axolotl_variant,
cat_variant,
cat_sound_variant,
cat_collar,
sheep_color,
shulker_color,
};

99
src/wit/datapack.zig Normal file
View File

@ -0,0 +1,99 @@
// Generated by tools/gen.zig, do not edit.
const abi = @import("../abi.zig");
/// Information about a discovered or loaded datapack.
pub const DatapackInfo = struct {
id: []const u8,
name: []const u8,
description: []const u8,
pack_format: u32,
is_enabled: bool,
recipe_count: u32,
function_count: u32,
};
/// Position options for enabling a datapack relative to other packs.
pub const EnablePosition = union(enum) {
first,
last,
before: []const u8,
after: []const u8,
};
/// Global manager for inspecting and controlling server datapacks.
pub const DatapackManager = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]datapack-manager";
/// Returns all known datapacks (both enabled and available/disabled).
pub fn listAllPacks(self: DatapackManager) []const DatapackInfo {
return abi.call(raw.@"[method]datapack-manager.list-all-packs", []const DatapackInfo, .{abi.borrow(self)});
}
/// Returns only currently enabled datapacks.
pub fn listEnabledPacks(self: DatapackManager) []const DatapackInfo {
return abi.call(raw.@"[method]datapack-manager.list-enabled-packs", []const DatapackInfo, .{abi.borrow(self)});
}
/// Returns available (disabled) datapacks.
pub fn listAvailablePacks(self: DatapackManager) []const DatapackInfo {
return abi.call(raw.@"[method]datapack-manager.list-available-packs", []const DatapackInfo, .{abi.borrow(self)});
}
/// Retrieves details about a specific datapack by name or ID.
pub fn getPack(self: DatapackManager, name: []const u8) ?DatapackInfo {
return abi.call(raw.@"[method]datapack-manager.get-pack", ?DatapackInfo, .{ abi.borrow(self), name });
}
/// Checks if a datapack is currently enabled.
pub fn isEnabled(self: DatapackManager, name: []const u8) bool {
return abi.call(raw.@"[method]datapack-manager.is-enabled", bool, .{ abi.borrow(self), name });
}
/// Enables a datapack with a specific priority position.
pub fn enablePack(self: DatapackManager, name: []const u8, position: EnablePosition) abi.Result(void, []const u8) {
return abi.call(raw.@"[method]datapack-manager.enable-pack", abi.Result(void, []const u8), .{ abi.borrow(self), name, position });
}
/// Disables an enabled datapack.
pub fn disablePack(self: DatapackManager, name: []const u8) abi.Result(void, []const u8) {
return abi.call(raw.@"[method]datapack-manager.disable-pack", abi.Result(void, []const u8), .{ abi.borrow(self), name });
}
/// Reloads all enabled datapacks, recipes, and function tags,
/// synchronizes recipes with online players, and triggers `#minecraft:load`.
pub fn reload(self: DatapackManager) abi.Result(void, []const u8) {
return abi.call(raw.@"[method]datapack-manager.reload", abi.Result(void, []const u8), .{abi.borrow(self)});
}
/// Executes a datapack function or function tag (e.g. "namespace:fn" or "#namespace:tag").
/// Returns the number of commands executed.
pub fn executeFunction(self: DatapackManager, name: []const u8) abi.Result(u32, []const u8) {
return abi.call(raw.@"[method]datapack-manager.execute-function", abi.Result(u32, []const u8), .{ abi.borrow(self), name });
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: DatapackManager) void {
abi.keep(DatapackManager, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: DatapackManager) void {
abi.drop(DatapackManager, self);
}
};
const raw = struct {
extern "pumpkin:plugin/datapack@0.1.0" fn @"[method]datapack-manager.list-all-packs"(i32, i32) void;
extern "pumpkin:plugin/datapack@0.1.0" fn @"[method]datapack-manager.list-enabled-packs"(i32, i32) void;
extern "pumpkin:plugin/datapack@0.1.0" fn @"[method]datapack-manager.list-available-packs"(i32, i32) void;
extern "pumpkin:plugin/datapack@0.1.0" fn @"[method]datapack-manager.get-pack"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/datapack@0.1.0" fn @"[method]datapack-manager.is-enabled"(i32, i32, i32) i32;
extern "pumpkin:plugin/datapack@0.1.0" fn @"[method]datapack-manager.enable-pack"(i32, i32, i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/datapack@0.1.0" fn @"[method]datapack-manager.disable-pack"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/datapack@0.1.0" fn @"[method]datapack-manager.reload"(i32, i32) void;
extern "pumpkin:plugin/datapack@0.1.0" fn @"[method]datapack-manager.execute-function"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/datapack@0.1.0" fn @"[resource-drop]datapack-manager"(i32) void;
};

492
src/wit/display.zig Normal file
View File

@ -0,0 +1,492 @@
// Generated by tools/gen.zig, do not edit.
const abi = @import("../abi.zig");
const world = @import("world.zig");
const text = @import("text.zig");
const item_stack = @import("item_stack.zig");
const uuid = @import("uuid.zig");
pub const Entity = world.Entity;
pub const TextComponent = text.TextComponent;
pub const ItemStack = item_stack.ItemStack;
pub const Uuid = uuid.Uuid;
/// Billboard rendering constraints for display entities.
pub const BillboardMode = enum(u8) {
fixed,
vertical,
horizontal,
center,
};
/// Item display transformation modes for item display entities.
pub const ItemDisplayMode = enum(u8) {
none,
thirdperson_lefthand,
thirdperson_righthand,
firstperson_lefthand,
firstperson_righthand,
head,
gui,
ground,
fixed,
};
/// Text alignment options for text display entities.
pub const TextAlignment = enum(u8) {
center,
left,
right,
};
/// 3D single-precision vector.
pub const Vector3f = struct {
x: f32,
y: f32,
z: f32,
};
/// Single-precision quaternion representing a 3D rotation.
pub const Quaternionf = struct {
x: f32,
y: f32,
z: f32,
w: f32,
};
/// Transformation (translation, scale, left and right rotations) for display entities.
pub const DisplayTransformation = struct {
translation: Vector3f,
scale: Vector3f,
left_rotation: Quaternionf,
right_rotation: Quaternionf,
};
/// Common base resource for all display entities.
pub const DisplayEntity = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]display-entity";
pub fn fromEntity(entity: Entity) ?DisplayEntity {
return abi.call(raw.@"[static]display-entity.from-entity", ?DisplayEntity, .{abi.borrow(entity)});
}
pub fn getEntity(self: DisplayEntity) Entity {
return abi.call(raw.@"[method]display-entity.get-entity", Entity, .{abi.borrow(self)});
}
pub fn getTransformation(self: DisplayEntity) DisplayTransformation {
return abi.call(raw.@"[method]display-entity.get-transformation", DisplayTransformation, .{abi.borrow(self)});
}
pub fn setTransformation(self: DisplayEntity, transformation: DisplayTransformation) void {
return abi.call(raw.@"[method]display-entity.set-transformation", void, .{ abi.borrow(self), transformation });
}
pub fn getInterpolationDuration(self: DisplayEntity) i32 {
return abi.call(raw.@"[method]display-entity.get-interpolation-duration", i32, .{abi.borrow(self)});
}
pub fn setInterpolationDuration(self: DisplayEntity, duration: i32) void {
return abi.call(raw.@"[method]display-entity.set-interpolation-duration", void, .{ abi.borrow(self), duration });
}
pub fn getInterpolationStart(self: DisplayEntity) i32 {
return abi.call(raw.@"[method]display-entity.get-interpolation-start", i32, .{abi.borrow(self)});
}
pub fn setInterpolationStart(self: DisplayEntity, delta_ticks: i32) void {
return abi.call(raw.@"[method]display-entity.set-interpolation-start", void, .{ abi.borrow(self), delta_ticks });
}
pub fn getTeleportDuration(self: DisplayEntity) i32 {
return abi.call(raw.@"[method]display-entity.get-teleport-duration", i32, .{abi.borrow(self)});
}
pub fn setTeleportDuration(self: DisplayEntity, duration: i32) void {
return abi.call(raw.@"[method]display-entity.set-teleport-duration", void, .{ abi.borrow(self), duration });
}
pub fn getBillboard(self: DisplayEntity) BillboardMode {
return abi.call(raw.@"[method]display-entity.get-billboard", BillboardMode, .{abi.borrow(self)});
}
pub fn setBillboard(self: DisplayEntity, mode: BillboardMode) void {
return abi.call(raw.@"[method]display-entity.set-billboard", void, .{ abi.borrow(self), mode });
}
pub fn getViewRange(self: DisplayEntity) f32 {
return abi.call(raw.@"[method]display-entity.get-view-range", f32, .{abi.borrow(self)});
}
pub fn setViewRange(self: DisplayEntity, range: f32) void {
return abi.call(raw.@"[method]display-entity.set-view-range", void, .{ abi.borrow(self), range });
}
pub fn getShadowRadius(self: DisplayEntity) f32 {
return abi.call(raw.@"[method]display-entity.get-shadow-radius", f32, .{abi.borrow(self)});
}
pub fn setShadowRadius(self: DisplayEntity, radius: f32) void {
return abi.call(raw.@"[method]display-entity.set-shadow-radius", void, .{ abi.borrow(self), radius });
}
pub fn getShadowStrength(self: DisplayEntity) f32 {
return abi.call(raw.@"[method]display-entity.get-shadow-strength", f32, .{abi.borrow(self)});
}
pub fn setShadowStrength(self: DisplayEntity, strength: f32) void {
return abi.call(raw.@"[method]display-entity.set-shadow-strength", void, .{ abi.borrow(self), strength });
}
pub fn getDisplayWidth(self: DisplayEntity) f32 {
return abi.call(raw.@"[method]display-entity.get-display-width", f32, .{abi.borrow(self)});
}
pub fn setDisplayWidth(self: DisplayEntity, width: f32) void {
return abi.call(raw.@"[method]display-entity.set-display-width", void, .{ abi.borrow(self), width });
}
pub fn getDisplayHeight(self: DisplayEntity) f32 {
return abi.call(raw.@"[method]display-entity.get-display-height", f32, .{abi.borrow(self)});
}
pub fn setDisplayHeight(self: DisplayEntity, height: f32) void {
return abi.call(raw.@"[method]display-entity.set-display-height", void, .{ abi.borrow(self), height });
}
pub fn getGlowColorOverride(self: DisplayEntity) i32 {
return abi.call(raw.@"[method]display-entity.get-glow-color-override", i32, .{abi.borrow(self)});
}
pub fn setGlowColorOverride(self: DisplayEntity, color: i32) void {
return abi.call(raw.@"[method]display-entity.set-glow-color-override", void, .{ abi.borrow(self), color });
}
pub fn getBrightness(self: DisplayEntity) i32 {
return abi.call(raw.@"[method]display-entity.get-brightness", i32, .{abi.borrow(self)});
}
pub fn setBrightness(self: DisplayEntity, brightness: i32) void {
return abi.call(raw.@"[method]display-entity.set-brightness", void, .{ abi.borrow(self), brightness });
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: DisplayEntity) void {
abi.keep(DisplayEntity, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: DisplayEntity) void {
abi.drop(DisplayEntity, self);
}
};
/// Resource specifically for Block Display entities.
pub const BlockDisplayEntity = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]block-display-entity";
pub fn fromEntity(entity: Entity) ?BlockDisplayEntity {
return abi.call(raw.@"[static]block-display-entity.from-entity", ?BlockDisplayEntity, .{abi.borrow(entity)});
}
pub fn getDisplay(self: BlockDisplayEntity) DisplayEntity {
return abi.call(raw.@"[method]block-display-entity.get-display", DisplayEntity, .{abi.borrow(self)});
}
pub fn getEntity(self: BlockDisplayEntity) Entity {
return abi.call(raw.@"[method]block-display-entity.get-entity", Entity, .{abi.borrow(self)});
}
pub fn getBlockStateId(self: BlockDisplayEntity) u16 {
return abi.call(raw.@"[method]block-display-entity.get-block-state-id", u16, .{abi.borrow(self)});
}
pub fn setBlockStateId(self: BlockDisplayEntity, state_id: u16) void {
return abi.call(raw.@"[method]block-display-entity.set-block-state-id", void, .{ abi.borrow(self), state_id });
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: BlockDisplayEntity) void {
abi.keep(BlockDisplayEntity, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: BlockDisplayEntity) void {
abi.drop(BlockDisplayEntity, self);
}
};
/// Resource specifically for Item Display entities.
pub const ItemDisplayEntity = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]item-display-entity";
pub fn fromEntity(entity: Entity) ?ItemDisplayEntity {
return abi.call(raw.@"[static]item-display-entity.from-entity", ?ItemDisplayEntity, .{abi.borrow(entity)});
}
pub fn getDisplay(self: ItemDisplayEntity) DisplayEntity {
return abi.call(raw.@"[method]item-display-entity.get-display", DisplayEntity, .{abi.borrow(self)});
}
pub fn getEntity(self: ItemDisplayEntity) Entity {
return abi.call(raw.@"[method]item-display-entity.get-entity", Entity, .{abi.borrow(self)});
}
pub fn getItem(self: ItemDisplayEntity) ?ItemStack {
return abi.call(raw.@"[method]item-display-entity.get-item", ?ItemStack, .{abi.borrow(self)});
}
pub fn setItem(self: ItemDisplayEntity, item: ?ItemStack) void {
return abi.call(raw.@"[method]item-display-entity.set-item", void, .{ abi.borrow(self), item });
}
pub fn getItemDisplayMode(self: ItemDisplayEntity) ItemDisplayMode {
return abi.call(raw.@"[method]item-display-entity.get-item-display-mode", ItemDisplayMode, .{abi.borrow(self)});
}
pub fn setItemDisplayMode(self: ItemDisplayEntity, mode: ItemDisplayMode) void {
return abi.call(raw.@"[method]item-display-entity.set-item-display-mode", void, .{ abi.borrow(self), mode });
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: ItemDisplayEntity) void {
abi.keep(ItemDisplayEntity, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: ItemDisplayEntity) void {
abi.drop(ItemDisplayEntity, self);
}
};
/// Resource specifically for Text Display entities.
pub const TextDisplayEntity = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]text-display-entity";
pub fn fromEntity(entity: Entity) ?TextDisplayEntity {
return abi.call(raw.@"[static]text-display-entity.from-entity", ?TextDisplayEntity, .{abi.borrow(entity)});
}
pub fn getDisplay(self: TextDisplayEntity) DisplayEntity {
return abi.call(raw.@"[method]text-display-entity.get-display", DisplayEntity, .{abi.borrow(self)});
}
pub fn getEntity(self: TextDisplayEntity) Entity {
return abi.call(raw.@"[method]text-display-entity.get-entity", Entity, .{abi.borrow(self)});
}
pub fn getText(self: TextDisplayEntity) TextComponent {
return abi.call(raw.@"[method]text-display-entity.get-text", TextComponent, .{abi.borrow(self)});
}
/// Consumes `text_`.
pub fn setText(self: TextDisplayEntity, text_: TextComponent) void {
return abi.call(raw.@"[method]text-display-entity.set-text", void, .{ abi.borrow(self), text_ });
}
pub fn getLineWidth(self: TextDisplayEntity) i32 {
return abi.call(raw.@"[method]text-display-entity.get-line-width", i32, .{abi.borrow(self)});
}
pub fn setLineWidth(self: TextDisplayEntity, width: i32) void {
return abi.call(raw.@"[method]text-display-entity.set-line-width", void, .{ abi.borrow(self), width });
}
pub fn getBackground(self: TextDisplayEntity) i32 {
return abi.call(raw.@"[method]text-display-entity.get-background", i32, .{abi.borrow(self)});
}
pub fn setBackground(self: TextDisplayEntity, color: i32) void {
return abi.call(raw.@"[method]text-display-entity.set-background", void, .{ abi.borrow(self), color });
}
pub fn getTextOpacity(self: TextDisplayEntity) i8 {
return abi.call(raw.@"[method]text-display-entity.get-text-opacity", i8, .{abi.borrow(self)});
}
pub fn setTextOpacity(self: TextDisplayEntity, opacity: i8) void {
return abi.call(raw.@"[method]text-display-entity.set-text-opacity", void, .{ abi.borrow(self), opacity });
}
pub fn getShadow(self: TextDisplayEntity) bool {
return abi.call(raw.@"[method]text-display-entity.get-shadow", bool, .{abi.borrow(self)});
}
pub fn setShadow(self: TextDisplayEntity, shadow: bool) void {
return abi.call(raw.@"[method]text-display-entity.set-shadow", void, .{ abi.borrow(self), shadow });
}
pub fn getSeeThrough(self: TextDisplayEntity) bool {
return abi.call(raw.@"[method]text-display-entity.get-see-through", bool, .{abi.borrow(self)});
}
pub fn setSeeThrough(self: TextDisplayEntity, see_through: bool) void {
return abi.call(raw.@"[method]text-display-entity.set-see-through", void, .{ abi.borrow(self), see_through });
}
pub fn getDefaultBackground(self: TextDisplayEntity) bool {
return abi.call(raw.@"[method]text-display-entity.get-default-background", bool, .{abi.borrow(self)});
}
pub fn setDefaultBackground(self: TextDisplayEntity, default_background: bool) void {
return abi.call(raw.@"[method]text-display-entity.set-default-background", void, .{ abi.borrow(self), default_background });
}
pub fn getAlignment(self: TextDisplayEntity) TextAlignment {
return abi.call(raw.@"[method]text-display-entity.get-alignment", TextAlignment, .{abi.borrow(self)});
}
pub fn setAlignment(self: TextDisplayEntity, alignment: TextAlignment) void {
return abi.call(raw.@"[method]text-display-entity.set-alignment", void, .{ abi.borrow(self), alignment });
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: TextDisplayEntity) void {
abi.keep(TextDisplayEntity, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: TextDisplayEntity) void {
abi.drop(TextDisplayEntity, self);
}
};
/// Resource specifically for Interaction entities.
pub const InteractionEntity = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]interaction-entity";
pub fn fromEntity(entity: Entity) ?InteractionEntity {
return abi.call(raw.@"[static]interaction-entity.from-entity", ?InteractionEntity, .{abi.borrow(entity)});
}
pub fn getEntity(self: InteractionEntity) Entity {
return abi.call(raw.@"[method]interaction-entity.get-entity", Entity, .{abi.borrow(self)});
}
pub fn getWidth(self: InteractionEntity) f32 {
return abi.call(raw.@"[method]interaction-entity.get-width", f32, .{abi.borrow(self)});
}
pub fn setWidth(self: InteractionEntity, width: f32) void {
return abi.call(raw.@"[method]interaction-entity.set-width", void, .{ abi.borrow(self), width });
}
pub fn getHeight(self: InteractionEntity) f32 {
return abi.call(raw.@"[method]interaction-entity.get-height", f32, .{abi.borrow(self)});
}
pub fn setHeight(self: InteractionEntity, height: f32) void {
return abi.call(raw.@"[method]interaction-entity.set-height", void, .{ abi.borrow(self), height });
}
pub fn getResponse(self: InteractionEntity) bool {
return abi.call(raw.@"[method]interaction-entity.get-response", bool, .{abi.borrow(self)});
}
pub fn setResponse(self: InteractionEntity, response: bool) void {
return abi.call(raw.@"[method]interaction-entity.set-response", void, .{ abi.borrow(self), response });
}
pub fn getLastAttacker(self: InteractionEntity) ?Uuid {
return abi.call(raw.@"[method]interaction-entity.get-last-attacker", ?Uuid, .{abi.borrow(self)});
}
pub fn getLastInteraction(self: InteractionEntity) ?Uuid {
return abi.call(raw.@"[method]interaction-entity.get-last-interaction", ?Uuid, .{abi.borrow(self)});
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: InteractionEntity) void {
abi.keep(InteractionEntity, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: InteractionEntity) void {
abi.drop(InteractionEntity, self);
}
};
const raw = struct {
extern "pumpkin:plugin/display@0.1.0" fn @"[static]display-entity.from-entity"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.get-entity"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.get-transformation"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.set-transformation"(i32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.get-interpolation-duration"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.set-interpolation-duration"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.get-interpolation-start"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.set-interpolation-start"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.get-teleport-duration"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.set-teleport-duration"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.get-billboard"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.set-billboard"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.get-view-range"(i32) f32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.set-view-range"(i32, f32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.get-shadow-radius"(i32) f32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.set-shadow-radius"(i32, f32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.get-shadow-strength"(i32) f32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.set-shadow-strength"(i32, f32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.get-display-width"(i32) f32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.set-display-width"(i32, f32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.get-display-height"(i32) f32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.set-display-height"(i32, f32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.get-glow-color-override"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.set-glow-color-override"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.get-brightness"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]display-entity.set-brightness"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[static]block-display-entity.from-entity"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]block-display-entity.get-display"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]block-display-entity.get-entity"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]block-display-entity.get-block-state-id"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]block-display-entity.set-block-state-id"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[static]item-display-entity.from-entity"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]item-display-entity.get-display"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]item-display-entity.get-entity"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]item-display-entity.get-item"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]item-display-entity.set-item"(i32, i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]item-display-entity.get-item-display-mode"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]item-display-entity.set-item-display-mode"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[static]text-display-entity.from-entity"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]text-display-entity.get-display"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]text-display-entity.get-entity"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]text-display-entity.get-text"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]text-display-entity.set-text"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]text-display-entity.get-line-width"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]text-display-entity.set-line-width"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]text-display-entity.get-background"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]text-display-entity.set-background"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]text-display-entity.get-text-opacity"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]text-display-entity.set-text-opacity"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]text-display-entity.get-shadow"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]text-display-entity.set-shadow"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]text-display-entity.get-see-through"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]text-display-entity.set-see-through"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]text-display-entity.get-default-background"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]text-display-entity.set-default-background"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]text-display-entity.get-alignment"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]text-display-entity.set-alignment"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[static]interaction-entity.from-entity"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]interaction-entity.get-entity"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]interaction-entity.get-width"(i32) f32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]interaction-entity.set-width"(i32, f32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]interaction-entity.get-height"(i32) f32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]interaction-entity.set-height"(i32, f32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]interaction-entity.get-response"(i32) i32;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]interaction-entity.set-response"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]interaction-entity.get-last-attacker"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[method]interaction-entity.get-last-interaction"(i32, i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[resource-drop]display-entity"(i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[resource-drop]block-display-entity"(i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[resource-drop]item-display-entity"(i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[resource-drop]text-display-entity"(i32) void;
extern "pumpkin:plugin/display@0.1.0" fn @"[resource-drop]interaction-entity"(i32) void;
};

133
src/wit/enchantments.zig Normal file
View File

@ -0,0 +1,133 @@
// Generated by tools/gen.zig, do not edit.
const abi = @import("../abi.zig");
const text = @import("text.zig");
pub const TextComponent = text.TextComponent;
/// Equipment slot where an enchantment is active.
pub const AttributeModifierSlot = enum(u8) {
any,
main_hand,
off_hand,
hand,
feet,
legs,
chest,
head,
armor,
body,
saddle,
};
/// Vanilla enchantments enum.
pub const Enchantment = enum(u8) {
aqua_affinity,
bane_of_arthropods,
binding_curse,
blast_protection,
breach,
channeling,
density,
depth_strider,
efficiency,
feather_falling,
fire_aspect,
fire_protection,
flame,
fortune,
frost_walker,
impaling,
infinity,
knockback,
looting,
loyalty,
luck_of_the_sea,
lunge,
lure,
mending,
multishot,
piercing,
power,
projectile_protection,
protection,
punch,
quick_charge,
respiration,
riptide,
sharpness,
silk_touch,
smite,
soul_speed,
sweeping_edge,
swift_sneak,
thorns,
unbreaking,
vanishing_curse,
wind_burst,
};
/// Represents a custom enchantment definition.
pub const CustomEnchantment = struct {
/// Unique identifier for the enchantment (e.g. "my_plugin:lifesteal").
id: []const u8,
/// Description or display name of the enchantment.
description: TextComponent,
/// Maximum level of the enchantment (e.g. 1..=10).
max_level: u32,
/// Base anvil repair/combination cost multiplier.
anvil_cost: u32,
/// Tag or item pattern for supported items (e.g. "#minecraft:enchantable/weapon").
supported_items: []const u8,
/// Weight / rarity of the enchantment (higher = more common, default 5).
weight: u32,
/// Equipment slots where this enchantment is active.
slots: []const AttributeModifierSlot,
/// List of exclusive/conflicting enchantment IDs.
exclusive_set: []const []const u8,
};
/// Global manager for registering and querying custom enchantments.
pub const EnchantmentManager = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]enchantment-manager";
/// Registers a new custom enchantment with the server.
pub fn registerEnchantment(self: EnchantmentManager, enchantment: CustomEnchantment) abi.Result(void, []const u8) {
return abi.call(raw.@"[method]enchantment-manager.register-enchantment", abi.Result(void, []const u8), .{ abi.borrow(self), enchantment });
}
/// Gets an enchantment definition by its ID.
pub fn getEnchantment(self: EnchantmentManager, id: []const u8) ?CustomEnchantment {
return abi.call(raw.@"[method]enchantment-manager.get-enchantment", ?CustomEnchantment, .{ abi.borrow(self), id });
}
/// Checks if an enchantment ID is registered.
pub fn hasEnchantment(self: EnchantmentManager, id: []const u8) bool {
return abi.call(raw.@"[method]enchantment-manager.has-enchantment", bool, .{ abi.borrow(self), id });
}
/// Returns all registered custom enchantment IDs.
pub fn getAllEnchantmentIds(self: EnchantmentManager) []const []const u8 {
return abi.call(raw.@"[method]enchantment-manager.get-all-enchantment-ids", []const []const u8, .{abi.borrow(self)});
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: EnchantmentManager) void {
abi.keep(EnchantmentManager, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: EnchantmentManager) void {
abi.drop(EnchantmentManager, self);
}
};
const raw = struct {
extern "pumpkin:plugin/enchantments@0.1.0" fn @"[method]enchantment-manager.register-enchantment"(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/enchantments@0.1.0" fn @"[method]enchantment-manager.get-enchantment"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/enchantments@0.1.0" fn @"[method]enchantment-manager.has-enchantment"(i32, i32, i32) i32;
extern "pumpkin:plugin/enchantments@0.1.0" fn @"[method]enchantment-manager.get-all-enchantment-ids"(i32, i32) void;
extern "pumpkin:plugin/enchantments@0.1.0" fn @"[resource-drop]enchantment-manager"(i32) void;
};

27
src/wit/entity.zig Normal file
View File

@ -0,0 +1,27 @@
// Generated by tools/gen.zig, do not edit.
const world = @import("world.zig");
pub const Entity = world.Entity;
pub const LivingEntity = world.LivingEntity;
pub const Mob = world.Mob;
pub const PathNodeType = world.PathNodeType;
pub const DyeColor = world.DyeColor;
pub const VillagerProfession = world.VillagerProfession;
pub const MobData = world.MobData;
pub const SheepData = world.SheepData;
pub const WolfData = world.WolfData;
pub const CatData = world.CatData;
pub const VillagerData = world.VillagerData;
pub const CreeperData = world.CreeperData;
pub const SlimeData = world.SlimeData;
pub const EndermanData = world.EndermanData;
pub const IronGolemData = world.IronGolemData;
pub const FoxData = world.FoxData;
pub const AgeableData = world.AgeableData;
pub const ZombieData = world.ZombieData;
pub const ShulkerData = world.ShulkerData;
pub const RaycastResult = world.RaycastResult;
pub const RayTraceBlockResult = world.RayTraceBlockResult;
pub const RayTraceEntityResult = world.RayTraceEntityResult;
pub const BlockDirection = world.BlockDirection;

View File

@ -0,0 +1,66 @@
// Generated by tools/gen.zig, do not edit.
pub const EntityStatus = enum(u8) {
armadillo_peek,
armorstand_wobble,
body_break,
cancel_shake_wetness,
chest_break,
death,
dolphin_looking_for_treasure,
drown_particles,
eat_grass,
end_ram,
feet_break,
fireworks_explode,
fishing_rod_reel_in,
fox_eat,
full_debug_info,
guardian_attack_sound,
head_break,
honey_jump,
honey_slide,
in_love_hearts,
jump,
kinetic_hit,
legs_break,
love_hearts,
mainhand_break,
offer_flower,
offhand_break,
permission_level_admins,
permission_level_all,
permission_level_gamemasters,
permission_level_moderators,
permission_level_owners,
poof,
protected_from_death,
ravager_roared,
ravager_stunned,
reduced_debug_info,
saddle_break,
shake,
shake_wetness,
silverfish_merge_anim,
sniffer_digging_sound,
sonic_charge,
squid_anim_synch,
start_attacking,
start_ram,
stop_attacking,
stop_offer_flower,
swap_hands,
taming_failed,
taming_succeeded,
teleport,
tendrils_shiver,
tnt_prime,
trusting_failed,
trusting_succeeded,
use_item_complete,
villager_angry,
villager_happy,
villager_sweat,
witch_hat_magic,
zombie_converting,
};

162
src/wit/entity_types.zig Normal file
View File

@ -0,0 +1,162 @@
// Generated by tools/gen.zig, do not edit.
pub const EntityType = enum(u8) {
acacia_boat,
acacia_chest_boat,
allay,
area_effect_cloud,
armadillo,
armor_stand,
arrow,
axolotl,
bamboo_chest_raft,
bamboo_raft,
bat,
bee,
birch_boat,
birch_chest_boat,
blaze,
block_display,
bogged,
breeze,
breeze_wind_charge,
camel,
camel_husk,
cat,
cave_spider,
cherry_boat,
cherry_chest_boat,
chest_minecart,
chicken,
cod,
command_block_minecart,
copper_golem,
cow,
creaking,
creeper,
dark_oak_boat,
dark_oak_chest_boat,
dolphin,
donkey,
dragon_fireball,
drowned,
egg,
elder_guardian,
end_crystal,
ender_dragon,
ender_pearl,
enderman,
endermite,
evoker,
evoker_fangs,
experience_bottle,
experience_orb,
eye_of_ender,
falling_block,
fireball,
firework_rocket,
fishing_bobber,
fox,
frog,
furnace_minecart,
ghast,
giant,
glow_item_frame,
glow_squid,
goat,
guardian,
happy_ghast,
hoglin,
hopper_minecart,
horse,
husk,
illusioner,
interaction,
iron_golem,
item,
item_display,
item_frame,
jungle_boat,
jungle_chest_boat,
leash_knot,
lightning_bolt,
lingering_potion,
llama,
llama_spit,
magma_cube,
mangrove_boat,
mangrove_chest_boat,
mannequin,
marker,
minecart,
mooshroom,
mule,
nautilus,
oak_boat,
oak_chest_boat,
ocelot,
ominous_item_spawner,
painting,
pale_oak_boat,
pale_oak_chest_boat,
panda,
parched,
parrot,
phantom,
pig,
piglin,
piglin_brute,
pillager,
player,
polar_bear,
pufferfish,
rabbit,
ravager,
salmon,
sheep,
shulker,
shulker_bullet,
silverfish,
skeleton,
skeleton_horse,
slime,
small_fireball,
sniffer,
snow_golem,
snowball,
spawner_minecart,
spectral_arrow,
spider,
splash_potion,
spruce_boat,
spruce_chest_boat,
squid,
stray,
strider,
sulfur_cube,
tadpole,
text_display,
tnt,
tnt_minecart,
trader_llama,
trident,
tropical_fish,
turtle,
vex,
villager,
vindicator,
wandering_trader,
warden,
wind_charge,
witch,
wither,
wither_skeleton,
wither_skull,
wolf,
zoglin,
zombie,
zombie_horse,
zombie_nautilus,
zombie_villager,
zombified_piglin,
};

2454
src/wit/event.zig Normal file

File diff suppressed because it is too large Load Diff

217
src/wit/exports.zig Normal file
View File

@ -0,0 +1,217 @@
// Generated by tools/gen.zig, do not edit.
const std = @import("std");
const abi = @import("../abi.zig");
const context = @import("context.zig");
const server = @import("server.zig");
const event = @import("event.zig");
const command = @import("command.zig");
const ipc = @import("ipc.zig");
const world = @import("world.zig");
const metadata = @import("metadata.zig");
/// --- Plugin-provided exports ---
/// Called by the host once to initialize the plugin.
/// Internal use only; plugins should use the `register_plugin!` macro.
pub fn initPlugin(comptime impl: fn () void) void {
const glue = struct {
fn call() callconv(.c) void {
const args = abi.liftArgs(std.meta.ArgsTuple(@TypeOf(impl)), .{});
defer abi.endCall();
return abi.lowerReturn(void, @call(.auto, impl, args));
}
};
@export(&glue.call, .{ .name = "init-plugin" });
}
/// Called when the plugin is being loaded.
/// Use this to register commands, events, and perform initial setup.
pub fn onLoad(comptime impl: fn (context.Context) abi.Result(void, []const u8)) void {
const glue = struct {
fn call(p0: i32) callconv(.c) i32 {
const args = abi.liftArgs(std.meta.ArgsTuple(@TypeOf(impl)), .{p0});
return abi.lowerReturn(abi.Result(void, []const u8), @call(.auto, impl, args));
}
fn post(_: i32) callconv(.c) void {
abi.endCall();
}
};
@export(&glue.call, .{ .name = "on-load" });
@export(&glue.post, .{ .name = "cabi_post_on-load" });
}
/// Called when the plugin is being unloaded.
/// Use this to save data and clean up resources.
pub fn onUnload(comptime impl: fn (context.Context) abi.Result(void, []const u8)) void {
const glue = struct {
fn call(p0: i32) callconv(.c) i32 {
const args = abi.liftArgs(std.meta.ArgsTuple(@TypeOf(impl)), .{p0});
return abi.lowerReturn(abi.Result(void, []const u8), @call(.auto, impl, args));
}
fn post(_: i32) callconv(.c) void {
abi.endCall();
}
};
@export(&glue.call, .{ .name = "on-unload" });
@export(&glue.post, .{ .name = "cabi_post_on-unload" });
}
/// Dispatches an event to the plugin for handling.
pub fn handleEvent(comptime impl: fn (u32, server.Server, event.Event) event.Event) void {
const glue = struct {
fn call(p0: i32) callconv(.c) i32 {
const args = abi.liftArgs(std.meta.ArgsTuple(@TypeOf(impl)), .{p0});
return abi.lowerReturn(event.Event, @call(.auto, impl, args));
}
fn post(_: i32) callconv(.c) void {
abi.endCall();
}
};
@export(&glue.call, .{ .name = "handle-event" });
@export(&glue.post, .{ .name = "cabi_post_handle-event" });
}
/// Dispatches a command execution to the plugin.
pub fn handleCommand(comptime impl: fn (u32, command.CommandSender, server.Server, command.ConsumedArgs) abi.Result(i32, command.CommandError)) void {
const glue = struct {
fn call(p0: i32, p1: i32, p2: i32, p3: i32) callconv(.c) i32 {
const args = abi.liftArgs(std.meta.ArgsTuple(@TypeOf(impl)), .{ p0, p1, p2, p3 });
return abi.lowerReturn(abi.Result(i32, command.CommandError), @call(.auto, impl, args));
}
fn post(_: i32) callconv(.c) void {
abi.endCall();
}
};
@export(&glue.call, .{ .name = "handle-command" });
@export(&glue.post, .{ .name = "cabi_post_handle-command" });
}
/// Dispatches a command suggestion request to the plugin.
pub fn handleCommandSuggestion(comptime impl: fn (u32, command.CommandSender, server.Server, command.SuggestionRequest) command.CommandSuggestions) void {
const glue = struct {
fn call(p0: i32, p1: i32, p2: i32, p3: i32, p4: i32, p5: i32, p6: i32, p7: i32, p8: i32) callconv(.c) i32 {
const args = abi.liftArgs(std.meta.ArgsTuple(@TypeOf(impl)), .{ p0, p1, p2, p3, p4, p5, p6, p7, p8 });
return abi.lowerReturn(command.CommandSuggestions, @call(.auto, impl, args));
}
fn post(_: i32) callconv(.c) void {
abi.endCall();
}
};
@export(&glue.call, .{ .name = "handle-command-suggestion" });
@export(&glue.post, .{ .name = "cabi_post_handle-command-suggestion" });
}
/// Dispatches a scheduled task execution to the plugin.
pub fn handleTask(comptime impl: fn (u32, server.Server) void) void {
const glue = struct {
fn call(p0: i32, p1: i32) callconv(.c) void {
const args = abi.liftArgs(std.meta.ArgsTuple(@TypeOf(impl)), .{ p0, p1 });
defer abi.endCall();
return abi.lowerReturn(void, @call(.auto, impl, args));
}
};
@export(&glue.call, .{ .name = "handle-task" });
}
/// Processes an incoming message from another plugin if possible, and returns (a possibly empty) response.
pub fn handleIpcMessage(comptime impl: fn (ipc.PluginId, ipc.IpcMessage) abi.Result(ipc.IpcMessage, []const u8)) void {
const glue = struct {
fn call(p0: i32, p1: i32, p2: i32, p3: i32) callconv(.c) i32 {
const args = abi.liftArgs(std.meta.ArgsTuple(@TypeOf(impl)), .{ p0, p1, p2, p3 });
return abi.lowerReturn(abi.Result(ipc.IpcMessage, []const u8), @call(.auto, impl, args));
}
fn post(_: i32) callconv(.c) void {
abi.endCall();
}
};
@export(&glue.call, .{ .name = "handle-ipc-message" });
@export(&glue.post, .{ .name = "cabi_post_handle-ipc-message" });
}
pub fn handleAiGoalCanStart(comptime impl: fn (u32, server.Server, world.Entity) bool) void {
const glue = struct {
fn call(p0: i32, p1: i32, p2: i32) callconv(.c) i32 {
const args = abi.liftArgs(std.meta.ArgsTuple(@TypeOf(impl)), .{ p0, p1, p2 });
defer abi.endCall();
return abi.lowerReturn(bool, @call(.auto, impl, args));
}
};
@export(&glue.call, .{ .name = "handle-ai-goal-can-start" });
}
pub fn handleAiGoalShouldContinue(comptime impl: fn (u32, server.Server, world.Entity) bool) void {
const glue = struct {
fn call(p0: i32, p1: i32, p2: i32) callconv(.c) i32 {
const args = abi.liftArgs(std.meta.ArgsTuple(@TypeOf(impl)), .{ p0, p1, p2 });
defer abi.endCall();
return abi.lowerReturn(bool, @call(.auto, impl, args));
}
};
@export(&glue.call, .{ .name = "handle-ai-goal-should-continue" });
}
pub fn handleAiGoalStart(comptime impl: fn (u32, server.Server, world.Entity) void) void {
const glue = struct {
fn call(p0: i32, p1: i32, p2: i32) callconv(.c) void {
const args = abi.liftArgs(std.meta.ArgsTuple(@TypeOf(impl)), .{ p0, p1, p2 });
defer abi.endCall();
return abi.lowerReturn(void, @call(.auto, impl, args));
}
};
@export(&glue.call, .{ .name = "handle-ai-goal-start" });
}
pub fn handleAiGoalTick(comptime impl: fn (u32, server.Server, world.Entity) void) void {
const glue = struct {
fn call(p0: i32, p1: i32, p2: i32) callconv(.c) void {
const args = abi.liftArgs(std.meta.ArgsTuple(@TypeOf(impl)), .{ p0, p1, p2 });
defer abi.endCall();
return abi.lowerReturn(void, @call(.auto, impl, args));
}
};
@export(&glue.call, .{ .name = "handle-ai-goal-tick" });
}
pub fn handleAiGoalStop(comptime impl: fn (u32, server.Server, world.Entity) void) void {
const glue = struct {
fn call(p0: i32, p1: i32, p2: i32) callconv(.c) void {
const args = abi.liftArgs(std.meta.ArgsTuple(@TypeOf(impl)), .{ p0, p1, p2 });
defer abi.endCall();
return abi.lowerReturn(void, @call(.auto, impl, args));
}
};
@export(&glue.call, .{ .name = "handle-ai-goal-stop" });
}
/// Dispatches a chunk generation phase to the plugin.
pub fn handleGeneratePhase(comptime impl: fn (u32, world.GenerationPhase, world.ChunkBuffer) void) void {
const glue = struct {
fn call(p0: i32, p1: i32, p2: i32) callconv(.c) void {
const args = abi.liftArgs(std.meta.ArgsTuple(@TypeOf(impl)), .{ p0, p1, p2 });
defer abi.endCall();
return abi.lowerReturn(void, @call(.auto, impl, args));
}
};
@export(&glue.call, .{ .name = "handle-generate-phase" });
}
pub fn getMetadata(comptime impl: fn () metadata.PluginMetadata) void {
const glue = struct {
fn call() callconv(.c) i32 {
const args = abi.liftArgs(std.meta.ArgsTuple(@TypeOf(impl)), .{});
return abi.lowerReturn(metadata.PluginMetadata, @call(.auto, impl, args));
}
fn post(_: i32) callconv(.c) void {
abi.endCall();
}
};
@export(&glue.call, .{ .name = "pumpkin:plugin/metadata@0.1.0#get-metadata" });
@export(&glue.post, .{ .name = "cabi_post_pumpkin:plugin/metadata@0.1.0#get-metadata" });
}

56
src/wit/forms.zig Normal file
View File

@ -0,0 +1,56 @@
// Generated by tools/gen.zig, do not edit.
const text = @import("text.zig");
pub const TextComponent = text.TextComponent;
pub const ImageType = enum(u8) {
url,
path,
};
pub const FormImage = struct {
type: ImageType,
data: []const u8,
};
pub const SimpleFormButton = struct {
text: TextComponent,
image: ?FormImage,
};
pub const SimpleForm = struct {
title: TextComponent,
content: TextComponent,
buttons: []const SimpleFormButton,
};
pub const ModalForm = struct {
title: TextComponent,
content: TextComponent,
button1: TextComponent,
button2: TextComponent,
};
pub const CustomFormElement = union(enum) {
label: TextComponent,
toggle: struct { TextComponent, bool },
slider: struct { TextComponent, f32, f32, f32, f32 },
/// text, min, max, step, default
step_slider: struct { TextComponent, []const []const u8, u32 },
/// text, steps, default
dropdown: struct { TextComponent, []const []const u8, u32 },
/// text, options, default
input: struct { TextComponent, []const u8, []const u8 },
};
pub const CustomForm = struct {
title: TextComponent,
elements: []const CustomFormElement,
};
pub const Form = union(enum) {
simple: SimpleForm,
modal: ModalForm,
custom: CustomForm,
};

65
src/wit/game_events.zig Normal file
View File

@ -0,0 +1,65 @@
// Generated by tools/gen.zig, do not edit.
pub const GameEvent = enum(u8) {
block_activate,
block_attach,
block_change,
block_close,
block_deactivate,
block_destroy,
block_detach,
block_open,
block_place,
bounce,
container_close,
container_open,
drink,
eat,
elytra_glide,
entity_damage,
entity_die,
entity_dismount,
entity_interact,
entity_mount,
entity_place,
entity_action,
equip,
explode,
flap,
fluid_pickup,
fluid_place,
hit_ground,
instrument_play,
item_interact_finish,
item_interact_start,
jukebox_play,
jukebox_stop_play,
lightning_strike,
note_block_play,
prime_fuse,
projectile_land,
projectile_shoot,
sculk_sensor_tendrils_clicking,
shear,
shriek,
splash,
step,
swim,
teleport,
unequip,
resonate_1,
resonate_2,
resonate_3,
resonate_4,
resonate_5,
resonate_6,
resonate_7,
resonate_8,
resonate_9,
resonate_10,
resonate_11,
resonate_12,
resonate_13,
resonate_14,
resonate_15,
};

68
src/wit/game_rules.zig Normal file
View File

@ -0,0 +1,68 @@
// Generated by tools/gen.zig, do not edit.
pub const GameRule = enum(u8) {
advance_time,
advance_weather,
allow_entering_nether_using_portals,
block_drops,
block_explosion_drop_decay,
command_block_output,
command_blocks_work,
drowning_damage,
elytra_movement_check,
ender_pearls_vanish_on_death,
entity_drops,
fall_damage,
fire_damage,
fire_spread_radius_around_player,
forgive_dead_players,
freeze_damage,
global_sound_events,
immediate_respawn,
keep_inventory,
lava_source_conversion,
limited_crafting,
locator_bar,
log_admin_commands,
max_block_modifications,
max_command_forks,
max_command_sequence_length,
max_entity_cramming,
max_minecart_speed,
max_snow_accumulation_height,
mob_drops,
mob_explosion_drop_decay,
mob_griefing,
natural_health_regeneration,
player_movement_check,
players_nether_portal_creative_delay,
players_nether_portal_default_delay,
players_sleeping_percentage,
projectiles_can_break_blocks,
pvp,
raids,
random_tick_speed,
reduced_debug_info,
respawn_radius,
send_command_feedback,
show_advancement_messages,
show_death_messages,
spawn_mobs,
spawn_monsters,
spawn_patrols,
spawn_phantoms,
spawn_wandering_traders,
spawn_wardens,
spawner_blocks_work,
spectators_generate_chunks,
spread_vines,
tnt_explodes,
tnt_explosion_drop_decay,
universal_anger,
water_source_conversion,
};
pub const GameRuleValue = union(enum) {
int: i32,
bool: bool,
};

105
src/wit/gui.zig Normal file
View File

@ -0,0 +1,105 @@
// Generated by tools/gen.zig, do not edit.
const abi = @import("../abi.zig");
const text = @import("text.zig");
const common = @import("common.zig");
const item_stack = @import("item_stack.zig");
const screens = @import("screens.zig");
const inventory = @import("inventory.zig");
pub const TextComponent = text.TextComponent;
pub const ClickType = common.ClickType;
pub const ItemStack = item_stack.ItemStack;
pub const Screen = screens.Screen;
pub const Inventory = inventory.Inventory;
pub const Gui = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]gui";
/// Consumes `title`.
pub fn init(@"type": Screen, title: TextComponent) Gui {
return abi.call(raw.@"[constructor]gui", Gui, .{ @"type", title });
}
/// Returns the underlying inventory handle for this GUI.
pub fn getInventory(self: Gui) Inventory {
return abi.call(raw.@"[method]gui.get-inventory", Inventory, .{abi.borrow(self)});
}
/// Consumes `item`.
pub fn setItem(self: Gui, slot: u32, item: ItemStack) void {
return abi.call(raw.@"[method]gui.set-item", void, .{ abi.borrow(self), slot, item });
}
pub fn getItem(self: Gui, slot: u32) ?ItemStack {
return abi.call(raw.@"[method]gui.get-item", ?ItemStack, .{ abi.borrow(self), slot });
}
/// Returns the type of this GUI.
pub fn getType(self: Gui) Screen {
return abi.call(raw.@"[method]gui.get-type", Screen, .{abi.borrow(self)});
}
/// Returns the title of this GUI.
pub fn getTitle(self: Gui) TextComponent {
return abi.call(raw.@"[method]gui.get-title", TextComponent, .{abi.borrow(self)});
}
/// Returns the number of slots in this GUI.
pub fn getSize(self: Gui) u32 {
return abi.call(raw.@"[method]gui.get-size", u32, .{abi.borrow(self)});
}
/// Clears all items from the GUI.
pub fn clearItems(self: Gui) void {
return abi.call(raw.@"[method]gui.clear-items", void, .{abi.borrow(self)});
}
/// Sets whether players can grab items out of the inventory.
pub fn setAllowGrabItems(self: Gui, allow: bool) void {
return abi.call(raw.@"[method]gui.set-allow-grab-items", void, .{ abi.borrow(self), allow });
}
/// Returns whether players can grab items out of the inventory.
pub fn getAllowGrabItems(self: Gui) bool {
return abi.call(raw.@"[method]gui.get-allow-grab-items", bool, .{abi.borrow(self)});
}
/// Sets whether players can put items into the inventory from their own.
pub fn setAllowPutItems(self: Gui, allow: bool) void {
return abi.call(raw.@"[method]gui.set-allow-put-items", void, .{ abi.borrow(self), allow });
}
/// Returns whether players can put items into the inventory from their own.
pub fn getAllowPutItems(self: Gui) bool {
return abi.call(raw.@"[method]gui.get-allow-put-items", bool, .{abi.borrow(self)});
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: Gui) void {
abi.keep(Gui, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: Gui) void {
abi.drop(Gui, self);
}
};
const raw = struct {
extern "pumpkin:plugin/gui@0.1.0" fn @"[constructor]gui"(i32, i32) i32;
extern "pumpkin:plugin/gui@0.1.0" fn @"[method]gui.get-inventory"(i32) i32;
extern "pumpkin:plugin/gui@0.1.0" fn @"[method]gui.set-item"(i32, i32, i32) void;
extern "pumpkin:plugin/gui@0.1.0" fn @"[method]gui.get-item"(i32, i32, i32) void;
extern "pumpkin:plugin/gui@0.1.0" fn @"[method]gui.get-type"(i32) i32;
extern "pumpkin:plugin/gui@0.1.0" fn @"[method]gui.get-title"(i32) i32;
extern "pumpkin:plugin/gui@0.1.0" fn @"[method]gui.get-size"(i32) i32;
extern "pumpkin:plugin/gui@0.1.0" fn @"[method]gui.clear-items"(i32) void;
extern "pumpkin:plugin/gui@0.1.0" fn @"[method]gui.set-allow-grab-items"(i32, i32) void;
extern "pumpkin:plugin/gui@0.1.0" fn @"[method]gui.get-allow-grab-items"(i32) i32;
extern "pumpkin:plugin/gui@0.1.0" fn @"[method]gui.set-allow-put-items"(i32, i32) void;
extern "pumpkin:plugin/gui@0.1.0" fn @"[method]gui.get-allow-put-items"(i32) i32;
extern "pumpkin:plugin/gui@0.1.0" fn @"[resource-drop]gui"(i32) void;
};

33
src/wit/i18n.zig Normal file
View File

@ -0,0 +1,33 @@
// Generated by tools/gen.zig, do not edit.
const abi = @import("../abi.zig");
const common = @import("common.zig");
pub const Locale = common.Locale;
/// Retrieves a translated string from the host.
///
/// # Arguments
/// * `key`: The namespaced translation key (e.g. `pumpkin:my_key`).
/// * `locale`: The target locale.
///
/// # Returns
/// The localized string, falling back to English or the key itself.
pub fn translate(key: []const u8, locale: Locale) []const u8 {
return abi.call(raw.translate, []const u8, .{ key, locale });
}
/// Loads custom translations from a JSON string.
///
/// # Arguments
/// * `namespace`: The namespace to associate these keys with.
/// * `json`: A JSON string containing a flat map of translations.
/// * `locale`: The target locale for these translations.
pub fn loadTranslations(namespace: []const u8, json: []const u8, locale: Locale) void {
return abi.call(raw.@"load-translations", void, .{ namespace, json, locale });
}
const raw = struct {
extern "pumpkin:plugin/i18n@0.1.0" fn translate(i32, i32, i32, i32) void;
extern "pumpkin:plugin/i18n@0.1.0" fn @"load-translations"(i32, i32, i32, i32, i32) void;
};

206
src/wit/inventory.zig Normal file
View File

@ -0,0 +1,206 @@
// Generated by tools/gen.zig, do not edit.
const abi = @import("../abi.zig");
const item_stack = @import("item_stack.zig");
const common = @import("common.zig");
pub const ItemStack = item_stack.ItemStack;
pub const Hand = common.Hand;
/// Common inventory resource used across GUIs, player inventories, and containers.
pub const Inventory = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]inventory";
/// Returns the number of slots in this inventory.
pub fn getSize(self: Inventory) u32 {
return abi.call(raw.@"[method]inventory.get-size", u32, .{abi.borrow(self)});
}
/// Returns true if all slots in this inventory are empty.
pub fn isEmpty(self: Inventory) bool {
return abi.call(raw.@"[method]inventory.is-empty", bool, .{abi.borrow(self)});
}
/// Returns the item in the specified slot index, or none if empty.
pub fn getItem(self: Inventory, slot: u32) ?ItemStack {
return abi.call(raw.@"[method]inventory.get-item", ?ItemStack, .{ abi.borrow(self), slot });
}
/// Sets the item in the specified slot index.
pub fn setItem(self: Inventory, slot: u32, item: ?ItemStack) void {
return abi.call(raw.@"[method]inventory.set-item", void, .{ abi.borrow(self), slot, item });
}
/// Removes and returns the item in the specified slot index.
pub fn removeItem(self: Inventory, slot: u32) ?ItemStack {
return abi.call(raw.@"[method]inventory.remove-item", ?ItemStack, .{ abi.borrow(self), slot });
}
/// Clears all items from this inventory.
pub fn clear(self: Inventory) void {
return abi.call(raw.@"[method]inventory.clear", void, .{abi.borrow(self)});
}
/// Returns all items in this inventory as a list of slots.
pub fn getAllItems(self: Inventory) []const ?ItemStack {
return abi.call(raw.@"[method]inventory.get-all-items", []const ?ItemStack, .{abi.borrow(self)});
}
/// Sets all items in this inventory from a list.
pub fn setAllItems(self: Inventory, items: []const ?ItemStack) void {
return abi.call(raw.@"[method]inventory.set-all-items", void, .{ abi.borrow(self), items });
}
/// Counts the total number of items with the given ID in this inventory.
pub fn countItem(self: Inventory, item_id: []const u8) u32 {
return abi.call(raw.@"[method]inventory.count-item", u32, .{ abi.borrow(self), item_id });
}
/// Checks if the inventory contains at least one item with the given ID.
pub fn containsItem(self: Inventory, item_id: []const u8) bool {
return abi.call(raw.@"[method]inventory.contains-item", bool, .{ abi.borrow(self), item_id });
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: Inventory) void {
abi.keep(Inventory, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: Inventory) void {
abi.drop(Inventory, self);
}
};
/// Specialized player inventory handle providing access to main inventory, armor, offhand, and hotbar.
pub const PlayerInventory = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]player-inventory";
/// Returns the generic 36-slot inventory handle (hotbar + main storage).
pub fn asInventory(self: PlayerInventory) Inventory {
return abi.call(raw.@"[method]player-inventory.as-inventory", Inventory, .{abi.borrow(self)});
}
/// Returns the item currently held in the specified hand.
pub fn getItemInHand(self: PlayerInventory, hand: Hand) ?ItemStack {
return abi.call(raw.@"[method]player-inventory.get-item-in-hand", ?ItemStack, .{ abi.borrow(self), hand });
}
/// Sets the item in the specified hand.
pub fn setItemInHand(self: PlayerInventory, hand: Hand, item: ?ItemStack) void {
return abi.call(raw.@"[method]player-inventory.set-item-in-hand", void, .{ abi.borrow(self), hand, item });
}
/// Returns the currently selected hotbar slot index (0-8).
pub fn getSelectedSlot(self: PlayerInventory) u8 {
return abi.call(raw.@"[method]player-inventory.get-selected-slot", u8, .{abi.borrow(self)});
}
/// Sets the currently selected hotbar slot index (0-8).
pub fn setSelectedSlot(self: PlayerInventory, slot: u8) void {
return abi.call(raw.@"[method]player-inventory.set-selected-slot", void, .{ abi.borrow(self), slot });
}
/// Armor slot accessors
pub fn getHelmet(self: PlayerInventory) ?ItemStack {
return abi.call(raw.@"[method]player-inventory.get-helmet", ?ItemStack, .{abi.borrow(self)});
}
pub fn setHelmet(self: PlayerInventory, item: ?ItemStack) void {
return abi.call(raw.@"[method]player-inventory.set-helmet", void, .{ abi.borrow(self), item });
}
pub fn getChestplate(self: PlayerInventory) ?ItemStack {
return abi.call(raw.@"[method]player-inventory.get-chestplate", ?ItemStack, .{abi.borrow(self)});
}
pub fn setChestplate(self: PlayerInventory, item: ?ItemStack) void {
return abi.call(raw.@"[method]player-inventory.set-chestplate", void, .{ abi.borrow(self), item });
}
pub fn getLeggings(self: PlayerInventory) ?ItemStack {
return abi.call(raw.@"[method]player-inventory.get-leggings", ?ItemStack, .{abi.borrow(self)});
}
pub fn setLeggings(self: PlayerInventory, item: ?ItemStack) void {
return abi.call(raw.@"[method]player-inventory.set-leggings", void, .{ abi.borrow(self), item });
}
pub fn getBoots(self: PlayerInventory) ?ItemStack {
return abi.call(raw.@"[method]player-inventory.get-boots", ?ItemStack, .{abi.borrow(self)});
}
pub fn setBoots(self: PlayerInventory, item: ?ItemStack) void {
return abi.call(raw.@"[method]player-inventory.set-boots", void, .{ abi.borrow(self), item });
}
pub fn getOffHand(self: PlayerInventory) ?ItemStack {
return abi.call(raw.@"[method]player-inventory.get-off-hand", ?ItemStack, .{abi.borrow(self)});
}
pub fn setOffHand(self: PlayerInventory, item: ?ItemStack) void {
return abi.call(raw.@"[method]player-inventory.set-off-hand", void, .{ abi.borrow(self), item });
}
/// Clears all armor slots.
pub fn clearArmor(self: PlayerInventory) void {
return abi.call(raw.@"[method]player-inventory.clear-armor", void, .{abi.borrow(self)});
}
/// Clears the 36 main inventory slots.
pub fn clearMain(self: PlayerInventory) void {
return abi.call(raw.@"[method]player-inventory.clear-main", void, .{abi.borrow(self)});
}
/// Clears all slots (main inventory, armor, offhand).
pub fn clearAll(self: PlayerInventory) void {
return abi.call(raw.@"[method]player-inventory.clear-all", void, .{abi.borrow(self)});
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: PlayerInventory) void {
abi.keep(PlayerInventory, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: PlayerInventory) void {
abi.drop(PlayerInventory, self);
}
};
const raw = struct {
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]inventory.get-size"(i32) i32;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]inventory.is-empty"(i32) i32;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]inventory.get-item"(i32, i32, i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]inventory.set-item"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]inventory.remove-item"(i32, i32, i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]inventory.clear"(i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]inventory.get-all-items"(i32, i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]inventory.set-all-items"(i32, i32, i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]inventory.count-item"(i32, i32, i32) i32;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]inventory.contains-item"(i32, i32, i32) i32;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]player-inventory.as-inventory"(i32) i32;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]player-inventory.get-item-in-hand"(i32, i32, i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]player-inventory.set-item-in-hand"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]player-inventory.get-selected-slot"(i32) i32;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]player-inventory.set-selected-slot"(i32, i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]player-inventory.get-helmet"(i32, i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]player-inventory.set-helmet"(i32, i32, i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]player-inventory.get-chestplate"(i32, i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]player-inventory.set-chestplate"(i32, i32, i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]player-inventory.get-leggings"(i32, i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]player-inventory.set-leggings"(i32, i32, i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]player-inventory.get-boots"(i32, i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]player-inventory.set-boots"(i32, i32, i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]player-inventory.get-off-hand"(i32, i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]player-inventory.set-off-hand"(i32, i32, i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]player-inventory.clear-armor"(i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]player-inventory.clear-main"(i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[method]player-inventory.clear-all"(i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[resource-drop]inventory"(i32) void;
extern "pumpkin:plugin/inventory@0.1.0" fn @"[resource-drop]player-inventory"(i32) void;
};

16
src/wit/ipc.zig Normal file
View File

@ -0,0 +1,16 @@
// Generated by tools/gen.zig, do not edit.
const abi = @import("../abi.zig");
pub const PluginId = []const u8;
pub const IpcMessage = []const u8;
/// Sends a message to the specified plugin, synchronously receiving a response (or an error).
pub fn sendIpcMessage(recipient: PluginId, message: IpcMessage) abi.Result(abi.Result(IpcMessage, []const u8), void) {
return abi.call(raw.@"send-ipc-message", abi.Result(abi.Result(IpcMessage, []const u8), void), .{ recipient, message });
}
const raw = struct {
extern "pumpkin:plugin/ipc@0.1.0" fn @"send-ipc-message"(i32, i32, i32, i32, i32) void;
};

239
src/wit/item_stack.zig Normal file
View File

@ -0,0 +1,239 @@
// Generated by tools/gen.zig, do not edit.
const abi = @import("../abi.zig");
const attributes = @import("attributes.zig");
const data_components = @import("data_components.zig");
const enchantments = @import("enchantments.zig");
const text = @import("text.zig");
const common = @import("common.zig");
pub const Attribute = attributes.Attribute;
pub const AttributeModifier = attributes.AttributeModifier;
pub const DataComponent = data_components.DataComponent;
pub const AttributeModifierSlot = enchantments.AttributeModifierSlot;
pub const Enchantment = enchantments.Enchantment;
pub const TextComponent = text.TextComponent;
pub const NbtEntry = common.NbtEntry;
pub const NbtTree = common.NbtTree;
pub const NbtTag = common.NbtTag;
/// Represents an enchantment and its level.
pub const EnchantmentValue = struct {
enchantment: Enchantment,
level: u32,
};
/// Represents a custom enchantment and its level on an item stack.
pub const CustomEnchantmentValue = struct {
enchantment_id: []const u8,
level: u32,
};
/// Represents an attribute modifier applied to an item stack for a specific slot.
pub const ItemAttributeModifier = struct {
attribute: Attribute,
modifier: AttributeModifier,
slot: AttributeModifierSlot,
};
/// Represents a data component and its serialized value.
pub const DataComponentValue = struct {
component: DataComponent,
/// The serialized value of the component.
value: []const u8,
};
pub const ItemStack = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]item-stack";
pub fn init(registry_key: []const u8, count: u8) ItemStack {
return abi.call(raw.@"[constructor]item-stack", ItemStack, .{ registry_key, count });
}
/// Returns the unique registry key of the item (e.g., "minecraft:diamond").
pub fn getRegistryKey(self: ItemStack) []const u8 {
return abi.call(raw.@"[method]item-stack.get-registry-key", []const u8, .{abi.borrow(self)});
}
/// Returns the number of items in the stack.
pub fn getCount(self: ItemStack) u8 {
return abi.call(raw.@"[method]item-stack.get-count", u8, .{abi.borrow(self)});
}
/// Sets the number of items in the stack.
pub fn setCount(self: ItemStack, count: u8) void {
return abi.call(raw.@"[method]item-stack.set-count", void, .{ abi.borrow(self), count });
}
/// Returns the maximum number of items that can be in this stack.
pub fn getMaxCount(self: ItemStack) u8 {
return abi.call(raw.@"[method]item-stack.get-max-count", u8, .{abi.borrow(self)});
}
/// Returns all enchantments on this item stack.
pub fn getEnchantments(self: ItemStack) []const EnchantmentValue {
return abi.call(raw.@"[method]item-stack.get-enchantments", []const EnchantmentValue, .{abi.borrow(self)});
}
/// Adds an enchantment to this item stack.
pub fn addEnchantment(self: ItemStack, enchantment: Enchantment, level: u32) void {
return abi.call(raw.@"[method]item-stack.add-enchantment", void, .{ abi.borrow(self), enchantment, level });
}
/// Removes an enchantment from this item stack.
pub fn removeEnchantment(self: ItemStack, enchantment: Enchantment) void {
return abi.call(raw.@"[method]item-stack.remove-enchantment", void, .{ abi.borrow(self), enchantment });
}
/// Returns all custom enchantments on this item stack.
pub fn getCustomEnchantments(self: ItemStack) []const CustomEnchantmentValue {
return abi.call(raw.@"[method]item-stack.get-custom-enchantments", []const CustomEnchantmentValue, .{abi.borrow(self)});
}
/// Adds a custom enchantment to this item stack.
pub fn addCustomEnchantment(self: ItemStack, enchantment_id: []const u8, level: u32) void {
return abi.call(raw.@"[method]item-stack.add-custom-enchantment", void, .{ abi.borrow(self), enchantment_id, level });
}
/// Removes a custom enchantment from this item stack.
pub fn removeCustomEnchantment(self: ItemStack, enchantment_id: []const u8) void {
return abi.call(raw.@"[method]item-stack.remove-custom-enchantment", void, .{ abi.borrow(self), enchantment_id });
}
/// Returns the level of a custom enchantment on this item stack, if present.
pub fn getCustomEnchantmentLevel(self: ItemStack, enchantment_id: []const u8) ?u32 {
return abi.call(raw.@"[method]item-stack.get-custom-enchantment-level", ?u32, .{ abi.borrow(self), enchantment_id });
}
/// Returns whether this item stack has a specific custom enchantment.
pub fn hasCustomEnchantment(self: ItemStack, enchantment_id: []const u8) bool {
return abi.call(raw.@"[method]item-stack.has-custom-enchantment", bool, .{ abi.borrow(self), enchantment_id });
}
/// Returns all attribute modifiers on this item stack.
pub fn getAttributeModifiers(self: ItemStack) []const ItemAttributeModifier {
return abi.call(raw.@"[method]item-stack.get-attribute-modifiers", []const ItemAttributeModifier, .{abi.borrow(self)});
}
/// Adds an attribute modifier to this item stack.
pub fn addAttributeModifier(self: ItemStack, modifier: ItemAttributeModifier) void {
return abi.call(raw.@"[method]item-stack.add-attribute-modifier", void, .{ abi.borrow(self), modifier });
}
/// Removes all attribute modifiers for a specific attribute from this item stack.
pub fn removeAttributeModifiers(self: ItemStack, attribute: Attribute) void {
return abi.call(raw.@"[method]item-stack.remove-attribute-modifiers", void, .{ abi.borrow(self), attribute });
}
/// Clears all custom attribute modifiers from this item stack.
pub fn clearAttributeModifiers(self: ItemStack) void {
return abi.call(raw.@"[method]item-stack.clear-attribute-modifiers", void, .{abi.borrow(self)});
}
/// Returns the lore of this item stack.
pub fn getLore(self: ItemStack) []const TextComponent {
return abi.call(raw.@"[method]item-stack.get-lore", []const TextComponent, .{abi.borrow(self)});
}
/// Sets the lore of this item stack.
pub fn setLore(self: ItemStack, lore: []const TextComponent) void {
return abi.call(raw.@"[method]item-stack.set-lore", void, .{ abi.borrow(self), lore });
}
/// Adds a line to the lore of this item stack.
///
/// Consumes `line`.
pub fn addLore(self: ItemStack, line: TextComponent) void {
return abi.call(raw.@"[method]item-stack.add-lore", void, .{ abi.borrow(self), line });
}
/// Returns the custom name of this item stack, if set.
pub fn getCustomName(self: ItemStack) ?TextComponent {
return abi.call(raw.@"[method]item-stack.get-custom-name", ?TextComponent, .{abi.borrow(self)});
}
/// Sets the custom name of this item stack.
pub fn setCustomName(self: ItemStack, name: ?TextComponent) void {
return abi.call(raw.@"[method]item-stack.set-custom-name", void, .{ abi.borrow(self), name });
}
/// Sets a namespaced custom data value on this item stack.
pub fn setCustomData(self: ItemStack, namespace: []const u8, key: []const u8, value: NbtTree) void {
return abi.call(raw.@"[method]item-stack.set-custom-data", void, .{ abi.borrow(self), namespace, key, value });
}
/// Returns a namespaced custom data value from this item stack, if set.
pub fn getCustomData(self: ItemStack, namespace: []const u8, key: []const u8) ?NbtTree {
return abi.call(raw.@"[method]item-stack.get-custom-data", ?NbtTree, .{ abi.borrow(self), namespace, key });
}
/// Removes a namespaced custom data value from this item stack.
pub fn removeCustomData(self: ItemStack, namespace: []const u8, key: []const u8) void {
return abi.call(raw.@"[method]item-stack.remove-custom-data", void, .{ abi.borrow(self), namespace, key });
}
/// Returns whether this item stack has a namespaced custom data value.
pub fn hasCustomData(self: ItemStack, namespace: []const u8, key: []const u8) bool {
return abi.call(raw.@"[method]item-stack.has-custom-data", bool, .{ abi.borrow(self), namespace, key });
}
/// Returns all data components on this item stack.
pub fn getComponents(self: ItemStack) []const DataComponentValue {
return abi.call(raw.@"[method]item-stack.get-components", []const DataComponentValue, .{abi.borrow(self)});
}
/// Sets a data component on this item stack.
pub fn setComponent(self: ItemStack, component: DataComponent, value: []const u8) void {
return abi.call(raw.@"[method]item-stack.set-component", void, .{ abi.borrow(self), component, value });
}
/// Removes a data component from this item stack.
pub fn removeComponent(self: ItemStack, component: DataComponent) void {
return abi.call(raw.@"[method]item-stack.remove-component", void, .{ abi.borrow(self), component });
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: ItemStack) void {
abi.keep(ItemStack, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: ItemStack) void {
abi.drop(ItemStack, self);
}
};
const raw = struct {
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[constructor]item-stack"(i32, i32, i32) i32;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.get-registry-key"(i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.get-count"(i32) i32;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.set-count"(i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.get-max-count"(i32) i32;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.get-enchantments"(i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.add-enchantment"(i32, i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.remove-enchantment"(i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.get-custom-enchantments"(i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.add-custom-enchantment"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.remove-custom-enchantment"(i32, i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.get-custom-enchantment-level"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.has-custom-enchantment"(i32, i32, i32) i32;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.get-attribute-modifiers"(i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.add-attribute-modifier"(i32, i32, i32, i32, f64, i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.remove-attribute-modifiers"(i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.clear-attribute-modifiers"(i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.get-lore"(i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.set-lore"(i32, i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.add-lore"(i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.get-custom-name"(i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.set-custom-name"(i32, i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.set-custom-data"(i32, i32, i32, i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.get-custom-data"(i32, i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.remove-custom-data"(i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.has-custom-data"(i32, i32, i32, i32, i32) i32;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.get-components"(i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.set-component"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[method]item-stack.remove-component"(i32, i32) void;
extern "pumpkin:plugin/item-stack@0.1.0" fn @"[resource-drop]item-stack"(i32) void;
};

155
src/wit/java_dialogs.zig Normal file
View File

@ -0,0 +1,155 @@
// Generated by tools/gen.zig, do not edit.
const text = @import("text.zig");
const item_stack = @import("item_stack.zig");
pub const TextComponent = text.TextComponent;
pub const ItemStack = item_stack.ItemStack;
/// Represents the layout and button configuration of a dialog.
pub const DialogType = enum(u8) {
/// A simple dialog with a single "OK" button.
notice,
/// A dialog with "Yes" and "No" buttons.
confirmation,
/// A scrollable list of custom buttons.
multi_action,
/// A list of buttons that open other registered dialogs.
dialog_list,
/// A dedicated menu for displaying server-related links.
server_links,
};
/// Represents an element in the main content area of a dialog.
pub const DialogBody = union(enum) {
/// A simple text message.
plain_message: TextComponent,
/// A visual item display.
item: ItemStack,
};
/// A checkbox input control.
pub const DialogInputBool = struct {
label: TextComponent,
default_value: bool,
};
/// A text field input control.
pub const DialogInputText = struct {
label: TextComponent,
placeholder: TextComponent,
default_value: []const u8,
};
/// A numerical range slider input control.
pub const DialogInputNumberRange = struct {
label: TextComponent,
min_value: f32,
max_value: f32,
initial_value: f32,
step: f32,
label_format: ?[]const u8,
};
/// A multiple-choice selection input control.
pub const DialogInputSingleOption = struct {
label: TextComponent,
options: []const TextComponent,
initial_index: u32,
};
/// Represents an interactive input control in a dialog.
pub const DialogInput = union(enum) {
/// A checkbox for true/false states.
bool: DialogInputBool,
/// A simple string input field.
text: DialogInputText,
/// A slider for numerical input within a range.
number_range: DialogInputNumberRange,
/// A multiple-choice selection button.
single_option: DialogInputSingleOption,
};
/// Defines what happens after a dialog is closed.
pub const AfterAction = enum(u8) {
/// Returns the player to the previous screen (e.g., keeping an inventory open).
peek,
/// Closes all open screens.
pop,
};
/// Represents a custom server-side action.
pub const CustomClickAction = struct {
/// A unique identifier for the action (sent back to the server).
id: []const u8,
/// Optional binary data to send with the action.
payload: ?[]const u8,
};
/// Represents an action triggered by a button click.
pub const Action = union(enum) {
/// Opens an external URL.
open_url: []const u8,
/// Triggers a custom server-side callback with an ID and optional payload.
custom_click: CustomClickAction,
};
/// Represents a clickable button in a dialog.
pub const ActionButton = struct {
text: TextComponent,
tooltip: ?TextComponent,
width: ?u32,
action: Action,
};
/// Built-in link types recognized by the Java Edition client.
pub const LinkType = enum(u8) {
bug_report,
community_guidelines,
support,
status,
feedback,
community,
website,
forums,
news,
announcements,
};
/// The label for a link, which can be a built-in type or custom text.
pub const LinkLabel = union(enum) {
/// A built-in link type with a predefined icon/label.
built_in: LinkType,
/// A custom text component label.
custom: TextComponent,
};
/// Represents a link displayed in a Java Edition dialog.
pub const Link = struct {
/// The label to display for the link.
label: LinkLabel,
/// The URL the link points to.
url: []const u8,
};
/// Represents a dialog shown to a Java Edition player.
pub const Dialog = struct {
/// The main title of the dialog.
title: TextComponent,
/// The layout type of the dialog.
type: DialogType,
/// A list of body elements to display in the main area.
body: []const DialogBody,
/// A list of interactive input controls.
inputs: []const DialogInput,
/// A list of clickable buttons (for multi-action and other types).
buttons: []const ActionButton,
/// A list of links (primarily for server-links type).
links: []const Link,
/// What to do after the dialog is closed.
after_action: ?AfterAction,
/// Whether the player can exit the dialog using the Esc key.
can_close_with_escape: bool,
/// The text shown on buttons that link to/open this dialog.
external_title: ?TextComponent,
};

2145
src/wit/java_packets.zig Normal file

File diff suppressed because it is too large Load Diff

26
src/wit/logging.zig Normal file
View File

@ -0,0 +1,26 @@
// Generated by tools/gen.zig, do not edit.
const abi = @import("../abi.zig");
pub const Level = enum(u8) {
trace,
debug,
info,
warn,
@"error",
};
/// log any general purpose message
pub fn log(level: Level, message: []const u8) void {
return abi.call(raw.log, void, .{ level, message });
}
/// This function is meant to be used by the tracing crate.
pub fn logTracing(event: []const u8) void {
return abi.call(raw.@"log-tracing", void, .{event});
}
const raw = struct {
extern "pumpkin:plugin/logging@0.1.0" fn log(i32, i32, i32) void;
extern "pumpkin:plugin/logging@0.1.0" fn @"log-tracing"(i32, i32) void;
};

16
src/wit/metadata.zig Normal file
View File

@ -0,0 +1,16 @@
// Generated by tools/gen.zig, do not edit.
pub const PluginMetadata = struct {
/// Name of the plugin.
name: []const u8,
/// Plugin version (semver).
version: []const u8,
/// Plugin authors.
authors: []const []const u8,
/// Short description of the plugin.
description: []const u8,
/// Plugin dependencies.
dependencies: []const []const u8,
/// Permissions requested by the plugin.
permissions: []const []const u8,
};

129
src/wit/particles.zig Normal file
View File

@ -0,0 +1,129 @@
// Generated by tools/gen.zig, do not edit.
pub const Particle = enum(u8) {
angry_villager,
block,
block_marker,
bubble,
sulfur_bubbles,
noxious_gas,
noxious_gas_cloud,
geyser,
geyser_base,
geyser_poof,
geyser_plume,
cloud,
copper_fire_flame,
crit,
damage_indicator,
dragon_breath,
dripping_lava,
falling_lava,
landing_lava,
dripping_water,
falling_water,
dust,
dust_color_transition,
effect,
elder_guardian,
enchanted_hit,
enchant,
end_rod,
entity_effect,
explosion_emitter,
explosion,
gust,
small_gust,
gust_emitter_large,
gust_emitter_small,
sonic_boom,
falling_dust,
firework,
fishing,
flame,
infested,
cherry_leaves,
pale_oak_leaves,
tinted_leaves,
sculk_soul,
sculk_charge,
sculk_charge_pop,
soul_fire_flame,
soul,
flash,
happy_villager,
composter,
heart,
instant_effect,
item,
vibration,
trail,
pause_mob_growth,
reset_mob_growth,
item_slime,
item_cobweb,
item_snowball,
large_smoke,
lava,
mycelium,
note,
poof,
portal,
rain,
smoke,
white_smoke,
sneeze,
spit,
squid_ink,
sweep_attack,
totem_of_undying,
underwater,
splash,
witch,
bubble_pop,
current_down,
bubble_column_up,
nautilus,
dolphin,
campfire_cosy_smoke,
campfire_signal_smoke,
dripping_honey,
falling_honey,
landing_honey,
falling_nectar,
falling_spore_blossom,
ash,
crimson_spore,
warped_spore,
spore_blossom_air,
dripping_obsidian_tear,
falling_obsidian_tear,
landing_obsidian_tear,
reverse_portal,
white_ash,
small_flame,
snowflake,
dripping_dripstone_lava,
falling_dripstone_lava,
dripping_dripstone_water,
falling_dripstone_water,
glow_squid_ink,
glow,
wax_on,
wax_off,
electric_spark,
scrape,
shriek,
egg_crack,
dust_plume,
trial_spawner_detection,
trial_spawner_detection_ominous,
vault_connection,
dust_pillar,
ominous_spawning,
raid_omen,
trial_omen,
block_crumble,
firefly,
sulfur_cube_goo,
};

46
src/wit/permission.zig Normal file
View File

@ -0,0 +1,46 @@
// Generated by tools/gen.zig, do not edit.
/// Represents the player's permission level
///
/// Permission levels determine the player's access to commands and server operations.
/// Each numeric level corresponds to a specific role:
/// - `Zero`: `normal`: Player can use basic commands.
/// - `One`: `moderator`: Player can bypass spawn protection.
/// - `Two`: `gamemaster`: Player or executor can use more commands and player can use command blocks.
/// - `Three`: `admin`: Player or executor can use commands related to multiplayer management.
/// - `Four`: `owner`: Player or executor can use all of the commands, including commands related to server management.
pub const PermissionLevel = enum(u8) {
zero,
one,
two,
three,
four,
};
/// Describes the default behavior for permissions
pub const PermissionDefault = union(enum) {
/// Permission is not granted by default
deny,
/// Permission is granted by default
allow,
/// Permission is granted by default to operators
op: PermissionLevel,
};
/// A child permission entry
pub const PermissionChild = struct {
node: []const u8,
value: bool,
};
/// Defines a permission node in the system
pub const Permission = struct {
/// The full node name (e.g., "minecraft:command.gamemode")
node: []const u8,
/// Description of what this permission does
description: []const u8,
/// The default value of this permission
default: PermissionDefault,
/// Children nodes that are affected by this permission
children: []const PermissionChild,
};

2193
src/wit/player.zig Normal file

File diff suppressed because it is too large Load Diff

50
src/wit/potions.zig Normal file
View File

@ -0,0 +1,50 @@
// Generated by tools/gen.zig, do not edit.
pub const PotionType = enum(u8) {
water,
mundane,
thick,
awkward,
night_vision,
long_night_vision,
invisibility,
long_invisibility,
leaping,
long_leaping,
strong_leaping,
fire_resistance,
long_fire_resistance,
swiftness,
long_swiftness,
strong_swiftness,
slowness,
long_slowness,
strong_slowness,
turtle_master,
long_turtle_master,
strong_turtle_master,
water_breathing,
long_water_breathing,
healing,
strong_healing,
harming,
strong_harming,
poison,
long_poison,
strong_poison,
regeneration,
long_regeneration,
strong_regeneration,
strength,
long_strength,
strong_strength,
weakness,
long_weakness,
luck,
slow_falling,
long_slow_falling,
wind_charged,
weaving,
oozing,
infested,
};

117
src/wit/recipe.zig Normal file
View File

@ -0,0 +1,117 @@
// Generated by tools/gen.zig, do not edit.
const abi = @import("../abi.zig");
const item_stack = @import("item_stack.zig");
pub const ItemStack = item_stack.ItemStack;
/// Recipe category for the recipe book.
pub const RecipeCategory = enum(u8) {
building,
redstone,
equipment,
misc,
food,
blocks,
};
/// Represents an ingredient in a recipe.
pub const Ingredient = union(enum) {
/// A specific item (e.g. "minecraft:diamond").
item: []const u8,
/// A tag group of items (e.g., "minecraft:logs").
tag: []const u8,
/// One of multiple specific items.
one_of: []const []const u8,
};
/// Represents a shaped crafting recipe.
pub const ShapedRecipe = struct {
/// The pattern of the recipe (e.g., ["# #", " s ", "# #"]).
pattern: []const []const u8,
/// Mapping of characters in the pattern to ingredients.
key: []const struct { []const u8, Ingredient },
/// The resulting item stack.
output: ItemStack,
/// The recipe group (optional).
group: ?[]const u8,
/// The recipe category in the recipe book.
category: ?RecipeCategory,
/// Whether to show a toast notification when the recipe is unlocked.
show_notification: ?bool,
};
/// Represents a shapeless crafting recipe.
pub const ShapelessRecipe = struct {
/// The list of ingredients required.
ingredients: []const Ingredient,
/// The resulting item stack.
output: ItemStack,
/// The recipe group (optional).
group: ?[]const u8,
/// The recipe category in the recipe book.
category: ?RecipeCategory,
};
/// Represents a cooking recipe (Smelting, Blasting, Smoking, Campfire).
pub const CookingRecipe = struct {
/// The ingredient to cook.
ingredient: Ingredient,
/// The resulting item stack.
output: ItemStack,
/// Experience points awarded.
experience: f32,
/// Cooking time in ticks.
cooking_time: u32,
/// The recipe group (optional).
group: ?[]const u8,
/// The recipe category in the recipe book.
category: ?RecipeCategory,
};
/// Types of cooking stations.
pub const CookingType = enum(u8) {
smelting,
blasting,
smoking,
campfire,
};
/// Common interface for registering recipes.
pub const RecipeManager = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]recipe-manager";
/// Registers a shaped crafting recipe.
pub fn registerShaped(self: RecipeManager, id: []const u8, recipe: ShapedRecipe) void {
return abi.call(raw.@"[method]recipe-manager.register-shaped", void, .{ abi.borrow(self), id, recipe });
}
/// Registers a shapeless crafting recipe.
pub fn registerShapeless(self: RecipeManager, id: []const u8, recipe: ShapelessRecipe) void {
return abi.call(raw.@"[method]recipe-manager.register-shapeless", void, .{ abi.borrow(self), id, recipe });
}
/// Registers a cooking recipe.
pub fn registerCooking(self: RecipeManager, id: []const u8, station_type: CookingType, recipe: CookingRecipe) void {
return abi.call(raw.@"[method]recipe-manager.register-cooking", void, .{ abi.borrow(self), id, station_type, recipe });
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: RecipeManager) void {
abi.keep(RecipeManager, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: RecipeManager) void {
abi.drop(RecipeManager, self);
}
};
const raw = struct {
extern "pumpkin:plugin/recipe@0.1.0" fn @"[method]recipe-manager.register-shaped"(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/recipe@0.1.0" fn @"[method]recipe-manager.register-shapeless"(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/recipe@0.1.0" fn @"[method]recipe-manager.register-cooking"(i32, i32, i32, i32, i32, i32, i32, i32, f32, i32, i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/recipe@0.1.0" fn @"[resource-drop]recipe-manager"(i32) void;
};

37
src/wit/scheduler.zig Normal file
View File

@ -0,0 +1,37 @@
// Generated by tools/gen.zig, do not edit.
const abi = @import("../abi.zig");
/// Schedules a task to be executed once after the specified number of ticks.
///
/// * `handler-id`: The unique ID of the handler to call when the task executes.
/// * `delay-ticks`: The number of game ticks to wait before execution (20 ticks = 1 second).
///
/// Returns a unique task ID that can be used to cancel the task.
pub fn scheduleDelayedTask(handler_id: u32, delay_ticks: u64) u32 {
return abi.call(raw.@"schedule-delayed-task", u32, .{ handler_id, delay_ticks });
}
/// Schedules a task to be executed repeatedly.
///
/// * `handler-id`: The unique ID of the handler to call when the task executes.
/// * `delay-ticks`: The number of game ticks to wait before the first execution.
/// * `period-ticks`: The number of game ticks between subsequent executions.
///
/// Returns a unique task ID that can be used to cancel the task.
pub fn scheduleRepeatingTask(handler_id: u32, delay_ticks: u64, period_ticks: u64) u32 {
return abi.call(raw.@"schedule-repeating-task", u32, .{ handler_id, delay_ticks, period_ticks });
}
/// Cancels a scheduled task if it hasn't finished yet.
///
/// * `task-id`: The ID of the task to cancel.
pub fn cancelTask(task_id: u32) void {
return abi.call(raw.@"cancel-task", void, .{task_id});
}
const raw = struct {
extern "pumpkin:plugin/scheduler@0.1.0" fn @"schedule-delayed-task"(i32, i64) i32;
extern "pumpkin:plugin/scheduler@0.1.0" fn @"schedule-repeating-task"(i32, i64, i64) i32;
extern "pumpkin:plugin/scheduler@0.1.0" fn @"cancel-task"(i32) void;
};

255
src/wit/scoreboard.zig Normal file
View File

@ -0,0 +1,255 @@
// Generated by tools/gen.zig, do not edit.
const abi = @import("../abi.zig");
const text = @import("text.zig");
const common = @import("common.zig");
pub const TextComponent = text.TextComponent;
pub const NamedColor = common.NamedColor;
pub const RenderType = enum(u8) {
integer,
hearts,
};
pub const NumberFormat = union(enum) {
blank,
fixed: TextComponent,
};
pub const DisplaySlot = enum(u8) {
player_list,
sidebar,
below_name,
sidebar_team_black,
sidebar_team_dark_blue,
sidebar_team_dark_green,
sidebar_team_dark_aqua,
sidebar_team_dark_red,
sidebar_team_dark_purple,
sidebar_team_gold,
sidebar_team_gray,
sidebar_team_dark_gray,
sidebar_team_blue,
sidebar_team_green,
sidebar_team_aqua,
sidebar_team_red,
sidebar_team_light_purple,
sidebar_team_yellow,
sidebar_team_white,
};
pub const NametagVisibility = enum(u8) {
always,
never,
hide_for_other_teams,
hide_for_own_team,
};
pub const CollisionRule = enum(u8) {
always,
never,
push_other_teams,
push_own_team,
};
pub const TeamSettings = struct {
display_name: TextComponent,
friendly_fire: bool,
see_friendly_invisibles: bool,
nametag_visibility: NametagVisibility,
collision_rule: CollisionRule,
color: NamedColor,
prefix: TextComponent,
suffix: TextComponent,
};
pub const Scoreboard = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]scoreboard";
/// Consumes `display_name`.
pub fn addObjective(self: Scoreboard, name: []const u8, display_name: TextComponent, render_type: RenderType, number_format: ?NumberFormat) void {
return abi.call(raw.@"[method]scoreboard.add-objective", void, .{ abi.borrow(self), name, display_name, render_type, number_format });
}
/// Consumes `display_name`.
pub fn updateObjective(self: Scoreboard, name: []const u8, display_name: TextComponent, render_type: RenderType, number_format: ?NumberFormat) void {
return abi.call(raw.@"[method]scoreboard.update-objective", void, .{ abi.borrow(self), name, display_name, render_type, number_format });
}
pub fn removeObjective(self: Scoreboard, name: []const u8) void {
return abi.call(raw.@"[method]scoreboard.remove-objective", void, .{ abi.borrow(self), name });
}
pub fn setDisplaySlot(self: Scoreboard, slot: DisplaySlot, objective_name: []const u8) void {
return abi.call(raw.@"[method]scoreboard.set-display-slot", void, .{ abi.borrow(self), slot, objective_name });
}
pub fn clearDisplaySlot(self: Scoreboard, slot: DisplaySlot) void {
return abi.call(raw.@"[method]scoreboard.clear-display-slot", void, .{ abi.borrow(self), slot });
}
pub fn updateScore(self: Scoreboard, entity_name: []const u8, objective_name: []const u8, value: i32, number_format: ?NumberFormat) void {
return abi.call(raw.@"[method]scoreboard.update-score", void, .{ abi.borrow(self), entity_name, objective_name, value, number_format });
}
pub fn addScore(self: Scoreboard, entity_name: []const u8, objective_name: []const u8, delta: i32) i32 {
return abi.call(raw.@"[method]scoreboard.add-score", i32, .{ abi.borrow(self), entity_name, objective_name, delta });
}
pub fn removeScore(self: Scoreboard, entity_name: []const u8, objective_name: []const u8) void {
return abi.call(raw.@"[method]scoreboard.remove-score", void, .{ abi.borrow(self), entity_name, objective_name });
}
pub fn resetEntityScores(self: Scoreboard, entity_name: []const u8) void {
return abi.call(raw.@"[method]scoreboard.reset-entity-scores", void, .{ abi.borrow(self), entity_name });
}
pub fn createTeam(self: Scoreboard, name: []const u8, settings: TeamSettings) void {
return abi.call(raw.@"[method]scoreboard.create-team", void, .{ abi.borrow(self), name, settings });
}
pub fn removeTeam(self: Scoreboard, name: []const u8) void {
return abi.call(raw.@"[method]scoreboard.remove-team", void, .{ abi.borrow(self), name });
}
pub fn updateTeam(self: Scoreboard, name: []const u8, settings: TeamSettings) void {
return abi.call(raw.@"[method]scoreboard.update-team", void, .{ abi.borrow(self), name, settings });
}
pub fn addPlayerToTeam(self: Scoreboard, team_name: []const u8, player_name: []const u8) void {
return abi.call(raw.@"[method]scoreboard.add-player-to-team", void, .{ abi.borrow(self), team_name, player_name });
}
pub fn removePlayerFromTeam(self: Scoreboard, team_name: []const u8, player_name: []const u8) void {
return abi.call(raw.@"[method]scoreboard.remove-player-from-team", void, .{ abi.borrow(self), team_name, player_name });
}
pub fn clearTeamPlayers(self: Scoreboard, team_name: []const u8) void {
return abi.call(raw.@"[method]scoreboard.clear-team-players", void, .{ abi.borrow(self), team_name });
}
pub fn getTeams(self: Scoreboard) []const []const u8 {
return abi.call(raw.@"[method]scoreboard.get-teams", []const []const u8, .{abi.borrow(self)});
}
pub fn getTeam(self: Scoreboard, name: []const u8) ?TeamSettings {
return abi.call(raw.@"[method]scoreboard.get-team", ?TeamSettings, .{ abi.borrow(self), name });
}
pub fn getTeamPlayers(self: Scoreboard, team_name: []const u8) []const []const u8 {
return abi.call(raw.@"[method]scoreboard.get-team-players", []const []const u8, .{ abi.borrow(self), team_name });
}
pub fn getPlayerTeam(self: Scoreboard, player_name: []const u8) ?[]const u8 {
return abi.call(raw.@"[method]scoreboard.get-player-team", ?[]const u8, .{ abi.borrow(self), player_name });
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: Scoreboard) void {
abi.keep(Scoreboard, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: Scoreboard) void {
abi.drop(Scoreboard, self);
}
};
pub const BedrockSortOrder = enum(u8) {
ascending,
descending,
};
pub const BedrockDisplaySlot = enum(u8) {
player_list,
sidebar,
below_name,
};
pub const BedrockScoreboard = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]bedrock-scoreboard";
pub fn addObjective(self: BedrockScoreboard, name: []const u8, display_name: []const u8, sort_order: BedrockSortOrder) void {
return abi.call(raw.@"[method]bedrock-scoreboard.add-objective", void, .{ abi.borrow(self), name, display_name, sort_order });
}
pub fn updateObjective(self: BedrockScoreboard, name: []const u8, display_name: []const u8, sort_order: BedrockSortOrder) void {
return abi.call(raw.@"[method]bedrock-scoreboard.update-objective", void, .{ abi.borrow(self), name, display_name, sort_order });
}
pub fn removeObjective(self: BedrockScoreboard, name: []const u8) void {
return abi.call(raw.@"[method]bedrock-scoreboard.remove-objective", void, .{ abi.borrow(self), name });
}
pub fn setDisplaySlot(self: BedrockScoreboard, slot: BedrockDisplaySlot, objective_name: []const u8) void {
return abi.call(raw.@"[method]bedrock-scoreboard.set-display-slot", void, .{ abi.borrow(self), slot, objective_name });
}
pub fn clearDisplaySlot(self: BedrockScoreboard, slot: BedrockDisplaySlot) void {
return abi.call(raw.@"[method]bedrock-scoreboard.clear-display-slot", void, .{ abi.borrow(self), slot });
}
pub fn updateScore(self: BedrockScoreboard, entity_name: []const u8, objective_name: []const u8, value: i32) void {
return abi.call(raw.@"[method]bedrock-scoreboard.update-score", void, .{ abi.borrow(self), entity_name, objective_name, value });
}
pub fn addScore(self: BedrockScoreboard, entity_name: []const u8, objective_name: []const u8, delta: i32) i32 {
return abi.call(raw.@"[method]bedrock-scoreboard.add-score", i32, .{ abi.borrow(self), entity_name, objective_name, delta });
}
pub fn removeScore(self: BedrockScoreboard, entity_name: []const u8, objective_name: []const u8) void {
return abi.call(raw.@"[method]bedrock-scoreboard.remove-score", void, .{ abi.borrow(self), entity_name, objective_name });
}
pub fn resetEntityScores(self: BedrockScoreboard, entity_name: []const u8) void {
return abi.call(raw.@"[method]bedrock-scoreboard.reset-entity-scores", void, .{ abi.borrow(self), entity_name });
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: BedrockScoreboard) void {
abi.keep(BedrockScoreboard, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: BedrockScoreboard) void {
abi.drop(BedrockScoreboard, self);
}
};
const raw = struct {
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]scoreboard.add-objective"(i32, i32, i32, i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]scoreboard.update-objective"(i32, i32, i32, i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]scoreboard.remove-objective"(i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]scoreboard.set-display-slot"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]scoreboard.clear-display-slot"(i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]scoreboard.update-score"(i32, i32, i32, i32, i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]scoreboard.add-score"(i32, i32, i32, i32, i32, i32) i32;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]scoreboard.remove-score"(i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]scoreboard.reset-entity-scores"(i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]scoreboard.create-team"(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]scoreboard.remove-team"(i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]scoreboard.update-team"(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]scoreboard.add-player-to-team"(i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]scoreboard.remove-player-from-team"(i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]scoreboard.clear-team-players"(i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]scoreboard.get-teams"(i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]scoreboard.get-team"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]scoreboard.get-team-players"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]scoreboard.get-player-team"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]bedrock-scoreboard.add-objective"(i32, i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]bedrock-scoreboard.update-objective"(i32, i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]bedrock-scoreboard.remove-objective"(i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]bedrock-scoreboard.set-display-slot"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]bedrock-scoreboard.clear-display-slot"(i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]bedrock-scoreboard.update-score"(i32, i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]bedrock-scoreboard.add-score"(i32, i32, i32, i32, i32, i32) i32;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]bedrock-scoreboard.remove-score"(i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[method]bedrock-scoreboard.reset-entity-scores"(i32, i32, i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[resource-drop]scoreboard"(i32) void;
extern "pumpkin:plugin/scoreboard@0.1.0" fn @"[resource-drop]bedrock-scoreboard"(i32) void;
};

29
src/wit/screens.zig Normal file
View File

@ -0,0 +1,29 @@
// Generated by tools/gen.zig, do not edit.
pub const Screen = enum(u8) {
generic_9x1,
generic_9x2,
generic_9x3,
generic_9x4,
generic_9x5,
generic_9x6,
generic_3x3,
crafter_3x3,
anvil,
beacon,
blast_furnace,
brewing_stand,
crafting,
enchantment,
furnace,
grindstone,
hopper,
lectern,
loom,
merchant,
shulker_box,
smithing,
smoker,
cartography_table,
stonecutter,
};

567
src/wit/server.zig Normal file
View File

@ -0,0 +1,567 @@
// Generated by tools/gen.zig, do not edit.
const abi = @import("../abi.zig");
const player = @import("player.zig");
const common = @import("common.zig");
const world = @import("world.zig");
const text = @import("text.zig");
const uuid = @import("uuid.zig");
const recipe = @import("recipe.zig");
const enchantments = @import("enchantments.zig");
const permission = @import("permission.zig");
const advancement = @import("advancement.zig");
const datapack = @import("datapack.zig");
pub const Player = player.Player;
pub const BanPlayerOptions = player.BanPlayerOptions;
pub const BanIpOptions = player.BanIpOptions;
pub const ServerLink = player.ServerLink;
pub const KnownServerLink = player.KnownServerLink;
pub const ServerLinkLabel = player.ServerLinkLabel;
pub const GameMode = common.GameMode;
pub const World = world.World;
pub const TextComponent = text.TextComponent;
pub const Uuid = uuid.Uuid;
pub const RecipeManager = recipe.RecipeManager;
pub const EnchantmentManager = enchantments.EnchantmentManager;
pub const CustomEnchantment = enchantments.CustomEnchantment;
pub const PermissionLevel = permission.PermissionLevel;
pub const AdvancementInfo = advancement.AdvancementInfo;
pub const DatapackManager = datapack.DatapackManager;
pub const DatapackInfo = datapack.DatapackInfo;
/// Represents an operator entry in the ops configuration.
pub const OpEntry = struct {
uuid: Uuid,
name: []const u8,
level: PermissionLevel,
bypasses_player_limit: bool,
};
/// Interface for querying and managing server operators.
pub const OpManager = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]op-manager";
/// Checks if a player UUID is currently an operator.
pub fn isOp(self: OpManager, id: Uuid) bool {
return abi.call(raw.@"[method]op-manager.is-op", bool, .{ abi.borrow(self), id });
}
/// Retrieves the operator entry for a player UUID, if present.
pub fn getOp(self: OpManager, id: Uuid) ?OpEntry {
return abi.call(raw.@"[method]op-manager.get-op", ?OpEntry, .{ abi.borrow(self), id });
}
/// Gets the effective permission level for a player UUID (returns zero if not op).
pub fn getPermissionLevel(self: OpManager, id: Uuid) PermissionLevel {
return abi.call(raw.@"[method]op-manager.get-permission-level", PermissionLevel, .{ abi.borrow(self), id });
}
/// Grants operator status to a player (online or offline) with the specified level and bypass setting.
pub fn opPlayer(self: OpManager, name: []const u8, id: Uuid, level: PermissionLevel, bypasses_player_limit: bool) void {
return abi.call(raw.@"[method]op-manager.op-player", void, .{ abi.borrow(self), name, id, level, bypasses_player_limit });
}
/// Revokes operator status from a player (online or offline). Returns true if the player was previously op.
pub fn deopPlayer(self: OpManager, id: Uuid) bool {
return abi.call(raw.@"[method]op-manager.deop-player", bool, .{ abi.borrow(self), id });
}
/// Returns a list of all operator entries on the server.
pub fn listOps(self: OpManager) []const OpEntry {
return abi.call(raw.@"[method]op-manager.list-ops", []const OpEntry, .{abi.borrow(self)});
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: OpManager) void {
abi.keep(OpManager, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: OpManager) void {
abi.drop(OpManager, self);
}
};
/// Represents a banned player entry in the ban list.
pub const BannedPlayerEntry = struct {
uuid: Uuid,
name: []const u8,
created: []const u8,
source: []const u8,
expires: ?[]const u8,
reason: []const u8,
};
/// Represents a banned IP entry in the IP ban list.
pub const BannedIpEntry = struct {
ip: []const u8,
created: []const u8,
source: []const u8,
expires: ?[]const u8,
reason: []const u8,
};
/// Global manager for player and IP bans.
pub const BanManager = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]ban-manager";
/// Checks if a player UUID is currently banned.
pub fn isPlayerBanned(self: BanManager, id: Uuid) bool {
return abi.call(raw.@"[method]ban-manager.is-player-banned", bool, .{ abi.borrow(self), id });
}
/// Retrieves the ban entry for a player UUID, if banned.
pub fn getPlayerBan(self: BanManager, id: Uuid) ?BannedPlayerEntry {
return abi.call(raw.@"[method]ban-manager.get-player-ban", ?BannedPlayerEntry, .{ abi.borrow(self), id });
}
/// Bans a player by name and UUID with the specified options (works for online and offline players).
pub fn banPlayer(self: BanManager, name: []const u8, id: Uuid, options: BanPlayerOptions) void {
return abi.call(raw.@"[method]ban-manager.ban-player", void, .{ abi.borrow(self), name, id, options });
}
/// Removes a player ban by UUID. Returns true if the player was banned.
pub fn unbanPlayer(self: BanManager, id: Uuid) bool {
return abi.call(raw.@"[method]ban-manager.unban-player", bool, .{ abi.borrow(self), id });
}
/// Returns all currently active player bans.
pub fn listPlayerBans(self: BanManager) []const BannedPlayerEntry {
return abi.call(raw.@"[method]ban-manager.list-player-bans", []const BannedPlayerEntry, .{abi.borrow(self)});
}
/// Checks if an IP address is currently banned.
pub fn isIpBanned(self: BanManager, ip: []const u8) bool {
return abi.call(raw.@"[method]ban-manager.is-ip-banned", bool, .{ abi.borrow(self), ip });
}
/// Retrieves the ban entry for an IP address, if banned.
pub fn getIpBan(self: BanManager, ip: []const u8) ?BannedIpEntry {
return abi.call(raw.@"[method]ban-manager.get-ip-ban", ?BannedIpEntry, .{ abi.borrow(self), ip });
}
/// Bans an IP address with the specified options.
pub fn banIp(self: BanManager, ip: []const u8, options: BanIpOptions) void {
return abi.call(raw.@"[method]ban-manager.ban-ip", void, .{ abi.borrow(self), ip, options });
}
/// Removes an IP ban. Returns true if the IP was banned.
pub fn unbanIp(self: BanManager, ip: []const u8) bool {
return abi.call(raw.@"[method]ban-manager.unban-ip", bool, .{ abi.borrow(self), ip });
}
/// Returns all currently active IP bans.
pub fn listIpBans(self: BanManager) []const BannedIpEntry {
return abi.call(raw.@"[method]ban-manager.list-ip-bans", []const BannedIpEntry, .{abi.borrow(self)});
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: BanManager) void {
abi.keep(BanManager, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: BanManager) void {
abi.drop(BanManager, self);
}
};
/// Represents a player entry in the server whitelist.
pub const WhitelistEntry = struct {
uuid: Uuid,
name: []const u8,
};
/// Global manager for the server whitelist.
pub const WhitelistManager = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]whitelist-manager";
/// Returns whether the whitelist is currently enabled.
pub fn isEnabled(self: WhitelistManager) bool {
return abi.call(raw.@"[method]whitelist-manager.is-enabled", bool, .{abi.borrow(self)});
}
/// Enables or disables the server whitelist. If enabled and enforce-whitelist is set, kicks non-whitelisted players.
pub fn setEnabled(self: WhitelistManager, enabled: bool) void {
return abi.call(raw.@"[method]whitelist-manager.set-enabled", void, .{ abi.borrow(self), enabled });
}
/// Checks if a player UUID is on the whitelist.
pub fn isWhitelisted(self: WhitelistManager, id: Uuid) bool {
return abi.call(raw.@"[method]whitelist-manager.is-whitelisted", bool, .{ abi.borrow(self), id });
}
/// Adds a player to the whitelist (online or offline). Returns true if newly added.
pub fn addPlayer(self: WhitelistManager, name: []const u8, id: Uuid) bool {
return abi.call(raw.@"[method]whitelist-manager.add-player", bool, .{ abi.borrow(self), name, id });
}
/// Removes a player from the whitelist. Returns true if the player was on the whitelist.
pub fn removePlayer(self: WhitelistManager, id: Uuid) bool {
return abi.call(raw.@"[method]whitelist-manager.remove-player", bool, .{ abi.borrow(self), id });
}
/// Returns all whitelisted player entries.
pub fn listEntries(self: WhitelistManager) []const WhitelistEntry {
return abi.call(raw.@"[method]whitelist-manager.list-entries", []const WhitelistEntry, .{abi.borrow(self)});
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: WhitelistManager) void {
abi.keep(WhitelistManager, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: WhitelistManager) void {
abi.drop(WhitelistManager, self);
}
};
/// Represents the difficulty setting of a Minecraft server.
pub const Difficulty = enum(u8) {
/// Hostile mobs do not spawn. Players regain health automatically.
peaceful,
/// Hostile mobs deal less damage.
easy,
/// Standard Minecraft difficulty.
normal,
/// Hostile mobs deal more damage.
hard,
};
/// Represents the three main Minecraft dimensions.
pub const Dimension = enum(u8) {
overworld,
nether,
end,
};
pub const SysInfo = struct {
cpu_count: ?u32,
total_memory: ?u64,
used_memory: ?u64,
os_name: ?[]const u8,
os_version: ?[]const u8,
pumpkin_version: []const u8,
};
pub const CommandSender = union(enum) {
console,
player: Player,
};
/// Represents the global server instance.
pub const Server = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]server";
/// Returns system information (CPU, Memory, OS).
/// Fields are returned only if the plugin has the corresponding permissions.
pub fn getSysInfo(self: Server) SysInfo {
return abi.call(raw.@"[method]server.get-sys-info", SysInfo, .{abi.borrow(self)});
}
/// Returns the current difficulty level of the server.
pub fn getDifficulty(self: Server) Difficulty {
return abi.call(raw.@"[method]server.get-difficulty", Difficulty, .{abi.borrow(self)});
}
/// Returns the total number of players currently connected to the server.
pub fn getPlayerCount(self: Server) u32 {
return abi.call(raw.@"[method]server.get-player-count", u32, .{abi.borrow(self)});
}
/// Returns the rolling average Milliseconds Per Tick (MSPT).
/// A value below 50.0 is required for a stable 20 TPS.
pub fn getMspt(self: Server) f64 {
return abi.call(raw.@"[method]server.get-mspt", f64, .{abi.borrow(self)});
}
/// Returns the effective Ticks Per Second (TPS).
/// Ideal value is 20.0.
pub fn getTps(self: Server) f64 {
return abi.call(raw.@"[method]server.get-tps", f64, .{abi.borrow(self)});
}
/// Returns a list of all online players across all worlds.
pub fn getAllPlayers(self: Server) []const Player {
return abi.call(raw.@"[method]server.get-all-players", []const Player, .{abi.borrow(self)});
}
/// Searches for a player by their exact name.
pub fn getPlayerByName(self: Server, name: []const u8) ?Player {
return abi.call(raw.@"[method]server.get-player-by-name", ?Player, .{ abi.borrow(self), name });
}
/// Searches for a player by their UUID string.
pub fn getPlayerByUuid(self: Server, id: Uuid) ?Player {
return abi.call(raw.@"[method]server.get-player-by-uuid", ?Player, .{ abi.borrow(self), id });
}
/// Returns a list of all worlds on this server.
pub fn getAllWorlds(self: Server) []const World {
return abi.call(raw.@"[method]server.get-all-worlds", []const World, .{abi.borrow(self)});
}
/// Gets a world by its dimension name (e.g., "minecraft:overworld") or custom world name (e.g., "world", "arena_1").
pub fn getWorldByName(self: Server, name: []const u8) ?World {
return abi.call(raw.@"[method]server.get-world-by-name", ?World, .{ abi.borrow(self), name });
}
/// Checks if a world exists by name or dimension.
pub fn hasWorld(self: Server, name: []const u8) bool {
return abi.call(raw.@"[method]server.has-world", bool, .{ abi.borrow(self), name });
}
pub fn executeCommand(self: Server, command: []const u8, sender: CommandSender) void {
return abi.call(raw.@"[method]server.execute-command", void, .{ abi.borrow(self), command, sender });
}
/// Creates or loads a world by its name and dimension.
/// If a world with the given name already exists, it will be returned.
pub fn createWorld(self: Server, name: []const u8, dimension: Dimension) World {
return abi.call(raw.@"[method]server.create-world", World, .{ abi.borrow(self), name, dimension });
}
/// Unloads and saves a world by its name or dimension.
/// Returns an error if the world cannot be found, is the primary/default world, or still has players inside.
pub fn unloadWorld(self: Server, name: []const u8) abi.Result(void, []const u8) {
return abi.call(raw.@"[method]server.unload-world", abi.Result(void, []const u8), .{ abi.borrow(self), name });
}
/// Saves all players, player advancements, and loaded worlds to disk.
pub fn saveAll(self: Server) abi.Result(void, []const u8) {
return abi.call(raw.@"[method]server.save-all", abi.Result(void, []const u8), .{abi.borrow(self)});
}
/// Returns a list of all online players in a specific world.
///
/// Consumes `world_ref`.
pub fn getPlayersInWorld(self: Server, world_ref: World) []const Player {
return abi.call(raw.@"[method]server.get-players-in-world", []const Player, .{ abi.borrow(self), world_ref });
}
/// Returns the number of online players in a specific world.
///
/// Consumes `world_ref`.
pub fn getPlayerCountInWorld(self: Server, world_ref: World) u32 {
return abi.call(raw.@"[method]server.get-player-count-in-world", u32, .{ abi.borrow(self), world_ref });
}
/// Broadcasts a system chat message to all players.
pub fn broadcast(self: Server, message: []const u8) void {
return abi.call(raw.@"[method]server.broadcast", void, .{ abi.borrow(self), message });
}
/// Deletes a signed chat message from all players' chat windows using its 256-byte signature.
pub fn deleteMessageBySignature(self: Server, signature: []const u8) void {
return abi.call(raw.@"[method]server.delete-message-by-signature", void, .{ abi.borrow(self), signature });
}
/// Deletes a signed chat message from all players' chat windows using its signature cache ID.
pub fn deleteMessageById(self: Server, signature_id: i32) void {
return abi.call(raw.@"[method]server.delete-message-by-id", void, .{ abi.borrow(self), signature_id });
}
/// Broadcasts a tab list header and footer to all players.
///
/// Consumes `header`.
/// Consumes `footer`.
pub fn broadcastTabListHeaderFooter(self: Server, header: TextComponent, footer: TextComponent) void {
return abi.call(raw.@"[method]server.broadcast-tab-list-header-footer", void, .{ abi.borrow(self), header, footer });
}
/// Returns the maximum number of players allowed on the server.
pub fn getMaxPlayers(self: Server) u32 {
return abi.call(raw.@"[method]server.get-max-players", u32, .{abi.borrow(self)});
}
/// Returns whether the server is in hardcore mode.
pub fn isHardcore(self: Server) bool {
return abi.call(raw.@"[method]server.is-hardcore", bool, .{abi.borrow(self)});
}
/// Returns whether the server is in online mode (authenticates with Mojang).
pub fn isOnlineMode(self: Server) bool {
return abi.call(raw.@"[method]server.is-online-mode", bool, .{abi.borrow(self)});
}
/// Returns the server's Message of the Day (MOTD).
pub fn getMotd(self: Server) []const u8 {
return abi.call(raw.@"[method]server.get-motd", []const u8, .{abi.borrow(self)});
}
/// Returns whether the server has a whitelist enabled.
pub fn hasWhitelist(self: Server) bool {
return abi.call(raw.@"[method]server.has-whitelist", bool, .{abi.borrow(self)});
}
/// Returns whether the server allows the Nether dimension.
pub fn getAllowNether(self: Server) bool {
return abi.call(raw.@"[method]server.get-allow-nether", bool, .{abi.borrow(self)});
}
/// Returns whether the server allows the End dimension.
pub fn getAllowEnd(self: Server) bool {
return abi.call(raw.@"[method]server.get-allow-end", bool, .{abi.borrow(self)});
}
/// Returns the server's maximum view distance.
pub fn getViewDistance(self: Server) u8 {
return abi.call(raw.@"[method]server.get-view-distance", u8, .{abi.borrow(self)});
}
/// Returns the server's maximum simulation distance.
pub fn getSimulationDistance(self: Server) u8 {
return abi.call(raw.@"[method]server.get-simulation-distance", u8, .{abi.borrow(self)});
}
/// Returns the server's default gamemode.
pub fn getDefaultGamemode(self: Server) GameMode {
return abi.call(raw.@"[method]server.get-default-gamemode", GameMode, .{abi.borrow(self)});
}
/// Returns the recipe manager to register custom recipes.
pub fn getRecipeManager(self: Server) RecipeManager {
return abi.call(raw.@"[method]server.get-recipe-manager", RecipeManager, .{abi.borrow(self)});
}
/// Returns the operator manager to query or modify server operators.
pub fn getOpManager(self: Server) OpManager {
return abi.call(raw.@"[method]server.get-op-manager", OpManager, .{abi.borrow(self)});
}
/// Returns the global ban manager for querying and modifying player and IP bans.
pub fn getBanManager(self: Server) BanManager {
return abi.call(raw.@"[method]server.get-ban-manager", BanManager, .{abi.borrow(self)});
}
/// Returns the global whitelist manager for querying and modifying the server whitelist.
pub fn getWhitelistManager(self: Server) WhitelistManager {
return abi.call(raw.@"[method]server.get-whitelist-manager", WhitelistManager, .{abi.borrow(self)});
}
/// Returns detailed information about an advancement by its ID (e.g., "minecraft:story/mine_stone" or "story/mine_stone").
pub fn getAdvancement(self: Server, id: []const u8) ?AdvancementInfo {
return abi.call(raw.@"[method]server.get-advancement", ?AdvancementInfo, .{ abi.borrow(self), id });
}
/// Returns a list of all registered advancement IDs on the server.
pub fn getAllAdvancementIds(self: Server) []const []const u8 {
return abi.call(raw.@"[method]server.get-all-advancement-ids", []const []const u8, .{abi.borrow(self)});
}
/// Returns the global enchantment manager for registering and querying custom enchantments.
pub fn getEnchantmentManager(self: Server) EnchantmentManager {
return abi.call(raw.@"[method]server.get-enchantment-manager", EnchantmentManager, .{abi.borrow(self)});
}
/// Returns information about an enchantment by its ID (vanilla or custom).
pub fn getEnchantment(self: Server, id: []const u8) ?CustomEnchantment {
return abi.call(raw.@"[method]server.get-enchantment", ?CustomEnchantment, .{ abi.borrow(self), id });
}
/// Returns a list of all registered enchantment IDs (custom and vanilla) on the server.
pub fn getAllEnchantmentIds(self: Server) []const []const u8 {
return abi.call(raw.@"[method]server.get-all-enchantment-ids", []const []const u8, .{abi.borrow(self)});
}
/// Returns the global datapack manager for inspecting, enabling, disabling, and reloading datapacks.
pub fn getDatapackManager(self: Server) DatapackManager {
return abi.call(raw.@"[method]server.get-datapack-manager", DatapackManager, .{abi.borrow(self)});
}
/// Broadcasts custom server links to all connected players (displayed in the client Esc pause menu in 1.21+).
pub fn setServerLinks(self: Server, links: []const ServerLink) void {
return abi.call(raw.@"[method]server.set-server-links", void, .{ abi.borrow(self), links });
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: Server) void {
abi.keep(Server, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: Server) void {
abi.drop(Server, self);
}
};
const raw = struct {
extern "pumpkin:plugin/server@0.1.0" fn @"[method]op-manager.is-op"(i32, i64, i64) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]op-manager.get-op"(i32, i64, i64, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]op-manager.get-permission-level"(i32, i64, i64) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]op-manager.op-player"(i32, i32, i32, i64, i64, i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]op-manager.deop-player"(i32, i64, i64) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]op-manager.list-ops"(i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]ban-manager.is-player-banned"(i32, i64, i64) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]ban-manager.get-player-ban"(i32, i64, i64, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]ban-manager.ban-player"(i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]ban-manager.unban-player"(i32, i64, i64) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]ban-manager.list-player-bans"(i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]ban-manager.is-ip-banned"(i32, i32, i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]ban-manager.get-ip-ban"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]ban-manager.ban-ip"(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i64, i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]ban-manager.unban-ip"(i32, i32, i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]ban-manager.list-ip-bans"(i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]whitelist-manager.is-enabled"(i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]whitelist-manager.set-enabled"(i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]whitelist-manager.is-whitelisted"(i32, i64, i64) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]whitelist-manager.add-player"(i32, i32, i32, i64, i64) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]whitelist-manager.remove-player"(i32, i64, i64) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]whitelist-manager.list-entries"(i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-sys-info"(i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-difficulty"(i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-player-count"(i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-mspt"(i32) f64;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-tps"(i32) f64;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-all-players"(i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-player-by-name"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-player-by-uuid"(i32, i64, i64, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-all-worlds"(i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-world-by-name"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.has-world"(i32, i32, i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.execute-command"(i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.create-world"(i32, i32, i32, i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.unload-world"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.save-all"(i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-players-in-world"(i32, i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-player-count-in-world"(i32, i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.broadcast"(i32, i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.delete-message-by-signature"(i32, i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.delete-message-by-id"(i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.broadcast-tab-list-header-footer"(i32, i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-max-players"(i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.is-hardcore"(i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.is-online-mode"(i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-motd"(i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.has-whitelist"(i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-allow-nether"(i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-allow-end"(i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-view-distance"(i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-simulation-distance"(i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-default-gamemode"(i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-recipe-manager"(i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-op-manager"(i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-ban-manager"(i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-whitelist-manager"(i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-advancement"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-all-advancement-ids"(i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-enchantment-manager"(i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-enchantment"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-all-enchantment-ids"(i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.get-datapack-manager"(i32) i32;
extern "pumpkin:plugin/server@0.1.0" fn @"[method]server.set-server-links"(i32, i32, i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[resource-drop]op-manager"(i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[resource-drop]ban-manager"(i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[resource-drop]whitelist-manager"(i32) void;
extern "pumpkin:plugin/server@0.1.0" fn @"[resource-drop]server"(i32) void;
};

1986
src/wit/sounds.zig Normal file

File diff suppressed because it is too large Load Diff

93
src/wit/statistics.zig Normal file
View File

@ -0,0 +1,93 @@
// Generated by tools/gen.zig, do not edit.
pub const StatisticCategory = enum(u8) {
mined,
crafted,
used,
broken,
picked_up,
dropped,
killed,
killed_by,
custom,
};
pub const CustomStatistic = enum(u8) {
leave_game,
play_time,
total_world_time,
time_since_death,
time_since_rest,
sneak_time,
walk_one_cm,
crouch_one_cm,
sprint_one_cm,
walk_on_water_one_cm,
fall_one_cm,
climb_one_cm,
fly_one_cm,
walk_under_water_one_cm,
minecart_one_cm,
boat_one_cm,
pig_one_cm,
happy_ghast_one_cm,
horse_one_cm,
aviate_one_cm,
swim_one_cm,
strider_one_cm,
nautilus_one_cm,
jump,
drop,
damage_dealt,
damage_dealt_absorbed,
damage_dealt_resisted,
damage_taken,
damage_blocked_by_shield,
damage_absorbed,
damage_resisted,
deaths,
mob_kills,
animals_bred,
player_kills,
fish_caught,
talked_to_villager,
traded_with_villager,
eat_cake_slice,
fill_cauldron,
use_cauldron,
clean_armor,
clean_banner,
clean_shulker_box,
interact_with_brewingstand,
interact_with_beacon,
inspect_dropper,
inspect_hopper,
inspect_dispenser,
play_noteblock,
tune_noteblock,
pot_flower,
trigger_trapped_chest,
open_enderchest,
enchant_item,
play_record,
interact_with_furnace,
interact_with_crafting_table,
open_chest,
sleep_in_bed,
open_shulker_box,
open_barrel,
interact_with_blast_furnace,
interact_with_smoker,
interact_with_lectern,
interact_with_campfire,
interact_with_cartography_table,
interact_with_loom,
interact_with_stonecutter,
bell_ring,
raid_trigger,
raid_win,
interact_with_anvil,
interact_with_grindstone,
target_hit,
interact_with_smithing_table,
};

52
src/wit/status_effect.zig Normal file
View File

@ -0,0 +1,52 @@
// Generated by tools/gen.zig, do not edit.
pub const StatusEffectType = enum(u8) {
speed,
slowness,
haste,
mining_fatigue,
strength,
instant_health,
instant_damage,
jump_boost,
nausea,
regeneration,
resistance,
fire_resistance,
water_breathing,
invisibility,
blindness,
night_vision,
hunger,
weakness,
poison,
wither,
health_boost,
absorption,
saturation,
glowing,
levitation,
luck,
unluck,
slow_falling,
conduit_power,
dolphins_grace,
bad_omen,
hero_of_the_village,
darkness,
trial_omen,
raid_omen,
wind_charged,
weaving,
oozing,
infested,
};
pub const StatusEffectInstance = struct {
effect_type: StatusEffectType,
duration: u32,
amplifier: u8,
ambient: bool,
show_particles: bool,
show_icon: bool,
};

231
src/wit/text.zig Normal file
View File

@ -0,0 +1,231 @@
// Generated by tools/gen.zig, do not edit.
const abi = @import("../abi.zig");
const common = @import("common.zig");
pub const NamedColor = common.NamedColor;
pub const RgbColor = common.RgbColor;
pub const ArgbColor = common.ArgbColor;
pub const TextComponent = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]text-component";
/// Creates a plain text component.
pub fn text(plain: []const u8) TextComponent {
return abi.call(raw.@"[static]text-component.text", TextComponent, .{plain});
}
/// Creates a translation component.
pub fn translate(key: []const u8, with: []const TextComponent) TextComponent {
return abi.call(raw.@"[static]text-component.translate", TextComponent, .{ key, with });
}
/// Creates a translation component with a Bedrock-specific fallback key.
pub fn translateCross(java_key: []const u8, bedrock_key: []const u8, with: []const TextComponent) TextComponent {
return abi.call(raw.@"[static]text-component.translate-cross", TextComponent, .{ java_key, bedrock_key, with });
}
/// Creates an entity names / selector text component.
pub fn entityNames(selector: []const u8, separator: ?[]const u8) TextComponent {
return abi.call(raw.@"[static]text-component.entity-names", TextComponent, .{ selector, separator });
}
/// Creates a keybind text component (e.g., "key.jump").
pub fn keybind(keybind_: []const u8) TextComponent {
return abi.call(raw.@"[static]text-component.keybind", TextComponent, .{keybind_});
}
/// Creates a custom translation component.
pub fn custom(namespace: []const u8, key: []const u8, locale: []const u8, with: []const TextComponent) TextComponent {
return abi.call(raw.@"[static]text-component.custom", TextComponent, .{ namespace, key, locale, with });
}
/// Parses a legacy Minecraft formatted string (using section signs '§' by default) into a text component.
pub fn fromLegacyString(input: []const u8) TextComponent {
return abi.call(raw.@"[static]text-component.from-legacy-string", TextComponent, .{input});
}
/// Parses a legacy formatted string using a custom color code symbol (e.g. '&' or '§').
pub fn fromLegacyStringWithCode(input: []const u8, code_symbol: u21) TextComponent {
return abi.call(raw.@"[static]text-component.from-legacy-string-with-code", TextComponent, .{ input, code_symbol });
}
/// Parses a standard Minecraft JSON text component.
pub fn fromJson(json: []const u8) abi.Result(TextComponent, []const u8) {
return abi.call(raw.@"[static]text-component.from-json", abi.Result(TextComponent, []const u8), .{json});
}
/// Serializes the text component to JSON format.
pub fn toJson(self: TextComponent) []const u8 {
return abi.call(raw.@"[method]text-component.to-json", []const u8, .{abi.borrow(self)});
}
/// Consumes `child`.
pub fn addChild(self: TextComponent, child: TextComponent) void {
return abi.call(raw.@"[method]text-component.add-child", void, .{ abi.borrow(self), child });
}
pub fn addText(self: TextComponent, text_: []const u8) void {
return abi.call(raw.@"[method]text-component.add-text", void, .{ abi.borrow(self), text_ });
}
pub fn getText(self: TextComponent) []const u8 {
return abi.call(raw.@"[method]text-component.get-text", []const u8, .{abi.borrow(self)});
}
pub fn encode(self: TextComponent) []const u8 {
return abi.call(raw.@"[method]text-component.encode", []const u8, .{abi.borrow(self)});
}
pub fn toPrettyConsole(self: TextComponent) []const u8 {
return abi.call(raw.@"[method]text-component.to-pretty-console", []const u8, .{abi.borrow(self)});
}
/// Style
pub fn colorNamed(self: TextComponent, color: NamedColor) void {
return abi.call(raw.@"[method]text-component.color-named", void, .{ abi.borrow(self), color });
}
pub fn colorRgb(self: TextComponent, color: RgbColor) void {
return abi.call(raw.@"[method]text-component.color-rgb", void, .{ abi.borrow(self), color });
}
pub fn gradientNamed(self: TextComponent, colors: []const NamedColor) void {
return abi.call(raw.@"[method]text-component.gradient-named", void, .{ abi.borrow(self), colors });
}
pub fn gradient(self: TextComponent, colors: []const RgbColor) void {
return abi.call(raw.@"[method]text-component.gradient", void, .{ abi.borrow(self), colors });
}
pub fn rainbow(self: TextComponent) void {
return abi.call(raw.@"[method]text-component.rainbow", void, .{abi.borrow(self)});
}
pub fn bold(self: TextComponent, value: bool) void {
return abi.call(raw.@"[method]text-component.bold", void, .{ abi.borrow(self), value });
}
pub fn italic(self: TextComponent, value: bool) void {
return abi.call(raw.@"[method]text-component.italic", void, .{ abi.borrow(self), value });
}
pub fn underlined(self: TextComponent, value: bool) void {
return abi.call(raw.@"[method]text-component.underlined", void, .{ abi.borrow(self), value });
}
pub fn strikethrough(self: TextComponent, value: bool) void {
return abi.call(raw.@"[method]text-component.strikethrough", void, .{ abi.borrow(self), value });
}
pub fn obfuscated(self: TextComponent, value: bool) void {
return abi.call(raw.@"[method]text-component.obfuscated", void, .{ abi.borrow(self), value });
}
/// Text inserted into chat when shift-clicked
pub fn insertion(self: TextComponent, text_: []const u8) void {
return abi.call(raw.@"[method]text-component.insertion", void, .{ abi.borrow(self), text_ });
}
pub fn font(self: TextComponent, font_: []const u8) void {
return abi.call(raw.@"[method]text-component.font", void, .{ abi.borrow(self), font_ });
}
pub fn shadowColor(self: TextComponent, color: ArgbColor) void {
return abi.call(raw.@"[method]text-component.shadow-color", void, .{ abi.borrow(self), color });
}
/// Click events
pub fn clickOpenUrl(self: TextComponent, url: []const u8) void {
return abi.call(raw.@"[method]text-component.click-open-url", void, .{ abi.borrow(self), url });
}
pub fn clickOpenFile(self: TextComponent, path: []const u8) void {
return abi.call(raw.@"[method]text-component.click-open-file", void, .{ abi.borrow(self), path });
}
pub fn clickRunCommand(self: TextComponent, command: []const u8) void {
return abi.call(raw.@"[method]text-component.click-run-command", void, .{ abi.borrow(self), command });
}
pub fn clickSuggestCommand(self: TextComponent, command: []const u8) void {
return abi.call(raw.@"[method]text-component.click-suggest-command", void, .{ abi.borrow(self), command });
}
pub fn clickChangePage(self: TextComponent, page: u32) void {
return abi.call(raw.@"[method]text-component.click-change-page", void, .{ abi.borrow(self), page });
}
pub fn clickCopyToClipboard(self: TextComponent, text_: []const u8) void {
return abi.call(raw.@"[method]text-component.click-copy-to-clipboard", void, .{ abi.borrow(self), text_ });
}
/// Hover events
///
/// Consumes `text_`.
pub fn hoverShowText(self: TextComponent, text_: TextComponent) void {
return abi.call(raw.@"[method]text-component.hover-show-text", void, .{ abi.borrow(self), text_ });
}
/// Item data as SNBT string
pub fn hoverShowItem(self: TextComponent, item: []const u8) void {
return abi.call(raw.@"[method]text-component.hover-show-item", void, .{ abi.borrow(self), item });
}
pub fn hoverShowEntity(self: TextComponent, entity_type: []const u8, id: []const u8, name: ?TextComponent) void {
return abi.call(raw.@"[method]text-component.hover-show-entity", void, .{ abi.borrow(self), entity_type, id, name });
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: TextComponent) void {
abi.keep(TextComponent, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: TextComponent) void {
abi.drop(TextComponent, self);
}
};
const raw = struct {
extern "pumpkin:plugin/text@0.1.0" fn @"[static]text-component.text"(i32, i32) i32;
extern "pumpkin:plugin/text@0.1.0" fn @"[static]text-component.translate"(i32, i32, i32, i32) i32;
extern "pumpkin:plugin/text@0.1.0" fn @"[static]text-component.translate-cross"(i32, i32, i32, i32, i32, i32) i32;
extern "pumpkin:plugin/text@0.1.0" fn @"[static]text-component.entity-names"(i32, i32, i32, i32, i32) i32;
extern "pumpkin:plugin/text@0.1.0" fn @"[static]text-component.keybind"(i32, i32) i32;
extern "pumpkin:plugin/text@0.1.0" fn @"[static]text-component.custom"(i32, i32, i32, i32, i32, i32, i32, i32) i32;
extern "pumpkin:plugin/text@0.1.0" fn @"[static]text-component.from-legacy-string"(i32, i32) i32;
extern "pumpkin:plugin/text@0.1.0" fn @"[static]text-component.from-legacy-string-with-code"(i32, i32, i32) i32;
extern "pumpkin:plugin/text@0.1.0" fn @"[static]text-component.from-json"(i32, i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.to-json"(i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.add-child"(i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.add-text"(i32, i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.get-text"(i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.encode"(i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.to-pretty-console"(i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.color-named"(i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.color-rgb"(i32, i32, i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.gradient-named"(i32, i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.gradient"(i32, i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.rainbow"(i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.bold"(i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.italic"(i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.underlined"(i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.strikethrough"(i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.obfuscated"(i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.insertion"(i32, i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.font"(i32, i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.shadow-color"(i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.click-open-url"(i32, i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.click-open-file"(i32, i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.click-run-command"(i32, i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.click-suggest-command"(i32, i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.click-change-page"(i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.click-copy-to-clipboard"(i32, i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.hover-show-text"(i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.hover-show-item"(i32, i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[method]text-component.hover-show-entity"(i32, i32, i32, i32, i32, i32, i32) void;
extern "pumpkin:plugin/text@0.1.0" fn @"[resource-drop]text-component"(i32) void;
};

30
src/wit/uuid.zig Normal file
View File

@ -0,0 +1,30 @@
// Generated by tools/gen.zig, do not edit.
const abi = @import("../abi.zig");
/// A 128-bit UUID represented as two 64-bit integers.
pub const Uuid = struct {
high: u64,
low: u64,
};
/// Generates a new random v4 UUID.
pub fn generate() Uuid {
return abi.call(raw.generate, Uuid, .{});
}
/// Parses a UUID from a string. Returns none if the string is not a valid UUID.
pub fn parse(s: []const u8) ?Uuid {
return abi.call(raw.parse, ?Uuid, .{s});
}
/// Converts a UUID to its standard string representation.
pub fn toString(id: Uuid) []const u8 {
return abi.call(raw.@"to-string", []const u8, .{id});
}
const raw = struct {
extern "pumpkin:plugin/uuid@0.1.0" fn generate(i32) void;
extern "pumpkin:plugin/uuid@0.1.0" fn parse(i32, i32, i32) void;
extern "pumpkin:plugin/uuid@0.1.0" fn @"to-string"(i64, i64, i32) void;
};

2055
src/wit/world.zig Normal file

File diff suppressed because it is too large Load Diff

818
tools/gen.zig Normal file
View File

@ -0,0 +1,818 @@
//! Generates the Zig bindings from the output of `wasm-tools component wit --json`.
//!
//! usage: gen <wit.json> <src-dir>
//!
//! Writes <src-dir>/wit.zig, one file per interface under <src-dir>/wit/ and
//! <src-dir>/wit/exports.zig for the world's exports.
const std = @import("std");
const Allocator = std.mem.Allocator;
const Value = std.json.Value;
const Writer = std.Io.Writer;
pub const File = struct {
path: []const u8,
data: []const u8,
};
const header = "// Generated by tools/gen.zig, do not edit.\n\n";
fn fatal(comptime fmt: []const u8, args: anytype) noreturn {
std.debug.print("gen: " ++ fmt ++ "\n", args);
std.process.exit(1);
}
fn oom() noreturn {
fatal("out of memory", .{});
}
fn maybe(value: Value, key: []const u8) ?Value {
if (value != .object) fatal("expected an object around '{s}'", .{key});
const found = value.object.get(key) orelse return null;
return if (found == .null) null else found;
}
fn member(value: Value, key: []const u8) Value {
return maybe(value, key) orelse fatal("missing '{s}'", .{key});
}
fn items(value: Value) []const Value {
if (value != .array) fatal("expected an array", .{});
return value.array.items;
}
fn text(value: Value) []const u8 {
if (value != .string) fatal("expected a string", .{});
return value.string;
}
fn index(value: Value) usize {
if (value != .integer) fatal("expected an index", .{});
return @intCast(value.integer);
}
const Kind = enum {
record,
variant,
@"enum",
flags,
tuple,
list,
option,
result,
handle,
resource,
/// `use` or `type x = y`
type,
};
const Def = struct { kind: Kind, payload: Value };
const Core = enum { i32, i64, f32, f64 };
const Signature = struct {
params: []const Core,
result: ?Core,
/// The result doesn't fit in a single value and goes through memory.
indirect: bool,
};
const Scope = std.StringArrayHashMapUnmanaged(void);
const Gen = struct {
arena: Allocator,
types: []const Value,
interfaces: []const Value,
world: Value,
/// "pumpkin:plugin"
namespace: []const u8,
/// "0.1.0"
version: []const u8,
/// Interfaces the world imports. The rest only contribute types.
imports: std.AutoArrayHashMapUnmanaged(usize, void) = .empty,
/// Interface being written, null for exports.zig.
current: ?usize = null,
/// Other interfaces the file being written refers to.
used: std.AutoArrayHashMapUnmanaged(usize, void) = .empty,
/// `used` of the previous pass over the same file. A parameter can't share
/// its name with an imported module, and the imports are only known once
/// the file has been written.
imported: std.AutoArrayHashMapUnmanaged(usize, void) = .empty,
fn init(arena: Allocator, root: Value) Gen {
const packages = items(member(root, "packages"));
const worlds = items(member(root, "worlds"));
if (packages.len != 1) fatal("expected one package, found {d}", .{packages.len});
if (worlds.len != 1) fatal("expected one world, found {d}", .{worlds.len});
const package = text(member(packages[0], "name"));
const at = std.mem.indexOfScalar(u8, package, '@') orelse fatal("package '{s}' has no version", .{package});
var g: Gen = .{
.arena = arena,
.types = items(member(root, "types")),
.interfaces = items(member(root, "interfaces")),
.world = worlds[0],
.namespace = package[0..at],
.version = package[at + 1 ..],
};
for (member(g.world, "imports").object.values()) |item| {
const iface = maybe(item, "interface") orelse continue;
g.imports.put(arena, index(member(iface, "id")), {}) catch oom();
}
g.validate();
return g;
}
fn def(g: *const Gen, id: usize) Def {
if (id >= g.types.len) fatal("type {d} out of range", .{id});
switch (member(g.types[id], "kind")) {
.string => |name| {
if (std.mem.eql(u8, name, "resource")) return .{ .kind = .resource, .payload = .null };
fatal("unsupported type kind '{s}'", .{name});
},
.object => |o| {
const name = o.keys()[0];
const kind = std.meta.stringToEnum(Kind, name) orelse fatal("unsupported type kind '{s}'", .{name});
return .{ .kind = kind, .payload = o.values()[0] };
},
else => fatal("malformed type {d}", .{id}),
}
}
fn nameOf(g: *const Gen, id: usize) ?[]const u8 {
const name = maybe(g.types[id], "name") orelse return null;
return text(name);
}
fn ownerOf(g: *const Gen, id: usize) ?usize {
const owner = maybe(g.types[id], "owner") orelse return null;
if (owner != .object) return null;
return index(owner.object.get("interface") orelse return null);
}
fn interfaceName(g: *const Gen, iface: usize) []const u8 {
return text(member(g.interfaces[iface], "name"));
}
fn isBorrow(g: *const Gen, ref: Value) bool {
if (ref != .integer) return false;
const d = g.def(index(ref));
return d.kind == .handle and d.payload.object.contains("borrow");
}
fn isOwn(g: *const Gen, ref: Value) bool {
if (ref != .integer) return false;
const d = g.def(index(ref));
return d.kind == .handle and d.payload.object.contains("own");
}
/// The type references a definition is built from.
fn children(g: *const Gen, d: Def) []const Value {
var out: std.ArrayList(Value) = .empty;
switch (d.kind) {
.record => for (items(member(d.payload, "fields"))) |f| out.append(g.arena, member(f, "type")) catch oom(),
.variant => for (items(member(d.payload, "cases"))) |c| {
if (maybe(c, "type")) |t| out.append(g.arena, t) catch oom();
},
.tuple => out.appendSlice(g.arena, items(member(d.payload, "types"))) catch oom(),
.list, .option, .type => out.append(g.arena, d.payload) catch oom(),
.result => {
if (maybe(d.payload, "ok")) |t| out.append(g.arena, t) catch oom();
if (maybe(d.payload, "err")) |t| out.append(g.arena, t) catch oom();
},
.@"enum", .flags, .handle, .resource => {},
}
return out.items;
}
/// Everything the generator or src/abi.zig can't express ends up here
/// instead of in bindings that are quietly wrong.
fn validate(g: *const Gen) void {
for (0..g.types.len) |id| {
const d = g.def(id);
const label = g.nameOf(id) orelse "(anonymous)";
if (d.kind == .flags and items(member(d.payload, "flags")).len > 32) {
fatal("flags '{s}' has more than 32 members", .{label});
}
if (d.kind == .resource) {
const owner = g.ownerOf(id) orelse fatal("resource '{s}' has no interface", .{label});
if (!g.imports.contains(owner)) fatal("resource '{s}' is exported by the guest, which is not supported", .{label});
}
for (g.children(d)) |child| {
if (g.isBorrow(child)) fatal("'{s}' contains a borrowed handle, only parameters may", .{label});
}
}
for (g.interfaces) |iface| {
var it = member(iface, "functions").object.iterator();
while (it.next()) |entry| g.validateFunc(entry.key_ptr.*, entry.value_ptr.*);
}
var it = member(g.world, "exports").object.iterator();
while (it.next()) |entry| {
const func = maybe(entry.value_ptr.*, "function") orelse continue;
g.validateFunc(entry.key_ptr.*, func);
for (items(member(func, "params"))) |p| {
if (g.isBorrow(member(p, "type"))) fatal("export '{s}' borrows a handle, which is not supported", .{entry.key_ptr.*});
}
}
}
fn validateFunc(g: *const Gen, name: []const u8, func: Value) void {
const kind = switch (member(func, "kind")) {
.string => |s| s,
.object => |o| o.keys()[0],
else => fatal("malformed function '{s}'", .{name}),
};
const known = [_][]const u8{ "freestanding", "method", "static", "constructor" };
for (known) |k| {
if (std.mem.eql(u8, kind, k)) break;
} else fatal("function '{s}' is '{s}', which is not supported", .{ name, kind });
if (maybe(func, "result")) |result| {
if (g.isBorrow(result)) fatal("function '{s}' returns a borrowed handle", .{name});
}
}
// naming
const Style = enum { snake, camel, title };
fn words(g: *const Gen, name: []const u8, style: Style) []const u8 {
var out: std.ArrayList(u8) = .empty;
var upper = style == .title;
for (name) |c| {
if (c == '-' or c == '.' or c == '_') {
if (style == .snake) out.append(g.arena, '_') catch oom() else upper = true;
continue;
}
out.append(g.arena, if (upper) std.ascii.toUpper(c) else c) catch oom();
upper = false;
}
return out.items;
}
fn moduleName(g: *const Gen, iface: usize) []const u8 {
return g.words(g.interfaceName(iface), .snake);
}
fn funcName(g: *const Gen, func: Value) []const u8 {
const full = text(member(func, "name"));
if (std.mem.startsWith(u8, full, "[constructor]")) return "init";
const base = if (std.mem.indexOfScalar(u8, full, '.')) |dot| full[dot + 1 ..] else full;
return g.words(base, .camel);
}
fn declare(g: *const Gen, scope: *Scope, name: []const u8, where: []const u8) void {
const entry = scope.getOrPut(g.arena, name) catch oom();
if (entry.found_existing) fatal("'{s}' is declared twice in {s}", .{ name, where });
}
fn print(g: *const Gen, comptime fmt: []const u8, args: anytype) []const u8 {
return std.fmt.allocPrint(g.arena, fmt, args) catch oom();
}
// type references
fn primitive(name: []const u8) []const u8 {
const map = std.StaticStringMap([]const u8).initComptime(.{
.{ "bool", "bool" }, .{ "string", "[]const u8" }, .{ "char", "u21" },
.{ "u8", "u8" }, .{ "u16", "u16" }, .{ "u32", "u32" },
.{ "u64", "u64" }, .{ "s8", "i8" }, .{ "s16", "i16" },
.{ "s32", "i32" }, .{ "s64", "i64" }, .{ "f32", "f32" },
.{ "f64", "f64" },
});
return map.get(name) orelse fatal("unknown primitive '{s}'", .{name});
}
fn writeRef(g: *Gen, w: *Writer, ref: ?Value) Writer.Error!void {
switch (ref orelse return w.writeAll("void")) {
.string => |s| try w.writeAll(primitive(s)),
.integer => try g.writeTypeId(w, index(ref.?)),
else => fatal("malformed type reference", .{}),
}
}
fn writeTypeId(g: *Gen, w: *Writer, id: usize) Writer.Error!void {
const d = g.def(id);
if (g.nameOf(id)) |name| {
if (g.ownerOf(id)) |owner| {
if (g.current != owner) {
g.used.put(g.arena, owner, {}) catch oom();
try w.print("{s}.", .{g.moduleName(owner)});
}
return w.writeAll(g.words(name, .title));
}
// Owned by the world, which only ever aliases interface types.
if (d.kind != .type) fatal("the world defines '{s}' itself, which is not supported", .{name});
return g.writeRef(w, d.payload);
}
if (d.kind == .handle) return g.writeTypeId(w, index(d.payload.object.values()[0]));
try g.writeShape(w, d);
}
/// list, option, result, tuple or an alias, the kinds that may be anonymous.
fn writeShape(g: *Gen, w: *Writer, d: Def) Writer.Error!void {
switch (d.kind) {
.type => try g.writeRef(w, d.payload),
.list => {
try w.writeAll("[]const ");
try g.writeRef(w, d.payload);
},
.option => {
try w.writeAll("?");
try g.writeRef(w, d.payload);
},
.result => {
try w.writeAll("abi.Result(");
try g.writeRef(w, maybe(d.payload, "ok"));
try w.writeAll(", ");
try g.writeRef(w, maybe(d.payload, "err"));
try w.writeAll(")");
},
.tuple => {
try w.writeAll("struct { ");
for (items(member(d.payload, "types")), 0..) |item, i| {
if (i != 0) try w.writeAll(", ");
try g.writeRef(w, item);
}
try w.writeAll(" }");
},
else => fatal("'{s}' needs a name", .{@tagName(d.kind)}),
}
}
// Flattening. src/abi.zig does the same from the Zig types at comptime and
// refuses to compile when the two disagree.
fn join(a: Core, b: Core) Core {
if (a == b) return a;
if ((a == .i32 and b == .f32) or (a == .f32 and b == .i32)) return .i32;
return .i64;
}
fn flatten(g: *const Gen, ref: ?Value, out: *std.ArrayList(Core)) void {
switch (ref orelse return) {
.string => |s| {
const eql = std.mem.eql;
if (eql(u8, s, "string")) return out.appendSlice(g.arena, &.{ .i32, .i32 }) catch oom();
const core: Core = if (eql(u8, s, "u64") or eql(u8, s, "s64"))
.i64
else if (eql(u8, s, "f32"))
.f32
else if (eql(u8, s, "f64"))
.f64
else
.i32;
out.append(g.arena, core) catch oom();
},
.integer => {
const d = g.def(index(ref.?));
switch (d.kind) {
.type => g.flatten(d.payload, out),
.record, .tuple => for (g.children(d)) |child| g.flatten(child, out),
.list => out.appendSlice(g.arena, &.{ .i32, .i32 }) catch oom(),
.variant => {
var payloads: std.ArrayList(?Value) = .empty;
for (items(member(d.payload, "cases"))) |c| payloads.append(g.arena, maybe(c, "type")) catch oom();
g.flattenCases(payloads.items, out);
},
.option => g.flattenCases(&.{ null, d.payload }, out),
.result => g.flattenCases(&.{ maybe(d.payload, "ok"), maybe(d.payload, "err") }, out),
.@"enum", .flags, .handle, .resource => out.append(g.arena, .i32) catch oom(),
}
},
else => fatal("malformed type reference", .{}),
}
}
fn flattenCases(g: *const Gen, payloads: []const ?Value, out: *std.ArrayList(Core)) void {
var joined: std.ArrayList(Core) = .empty;
for (payloads) |payload| {
var flat: std.ArrayList(Core) = .empty;
g.flatten(payload, &flat);
for (flat.items, 0..) |c, i| {
if (i < joined.items.len) {
joined.items[i] = join(joined.items[i], c);
} else {
joined.append(g.arena, c) catch oom();
}
}
}
out.append(g.arena, .i32) catch oom();
out.appendSlice(g.arena, joined.items) catch oom();
}
fn signature(g: *const Gen, func: Value, side: enum { import, @"export" }) Signature {
var params: std.ArrayList(Core) = .empty;
for (items(member(func, "params"))) |p| g.flatten(member(p, "type"), &params);
if (params.items.len > 16) {
params.clearRetainingCapacity();
params.append(g.arena, .i32) catch oom();
}
var results: std.ArrayList(Core) = .empty;
g.flatten(maybe(func, "result"), &results);
if (results.items.len <= 1) {
const result = if (results.items.len == 1) results.items[0] else null;
return .{ .params = params.items, .result = result, .indirect = false };
}
if (side == .import) {
params.append(g.arena, .i32) catch oom();
return .{ .params = params.items, .result = null, .indirect = true };
}
return .{ .params = params.items, .result = .i32, .indirect = true };
}
// declarations
fn writeDocs(w: *Writer, item: Value, indent: []const u8) Writer.Error!void {
const docs = maybe(item, "docs") orelse return;
const contents = maybe(docs, "contents") orelse return;
var lines = std.mem.splitScalar(u8, text(contents), '\n');
while (lines.next()) |line| {
const trimmed = std.mem.trimEnd(u8, line, " \r");
if (trimmed.len == 0) try w.print("{s}///\n", .{indent}) else try w.print("{s}/// {s}\n", .{ indent, trimmed });
}
}
fn fieldName(g: *const Gen, item: Value) std.zig.FormatId {
return std.zig.fmtId(g.words(text(member(item, "name")), .snake));
}
fn isPlainAlias(g: *const Gen, id: usize) bool {
const d = g.def(id);
return d.kind == .type and d.payload == .integer and maybe(g.types[id], "docs") == null;
}
fn writeType(g: *Gen, w: *Writer, id: usize, funcs: Value) Writer.Error!void {
const wit_name = g.nameOf(id).?;
const name = g.words(wit_name, .title);
const d = g.def(id);
try writeDocs(w, g.types[id], "");
try w.print("pub const {s} = ", .{name});
switch (d.kind) {
.record => {
try w.writeAll("struct {\n");
for (items(member(d.payload, "fields"))) |f| {
try writeDocs(w, f, " ");
try w.print(" {f}: ", .{g.fieldName(f)});
try g.writeRef(w, member(f, "type"));
try w.writeAll(",\n");
}
try w.writeAll("};\n\n");
},
.variant => {
try w.writeAll("union(enum) {\n");
for (items(member(d.payload, "cases"))) |c| {
try writeDocs(w, c, " ");
try w.print(" {f}", .{g.fieldName(c)});
if (maybe(c, "type")) |t| {
try w.writeAll(": ");
try g.writeRef(w, t);
}
try w.writeAll(",\n");
}
try w.writeAll("};\n\n");
},
.@"enum" => {
const cases = items(member(d.payload, "cases"));
const tag = if (cases.len <= 1 << 8) "u8" else if (cases.len <= 1 << 16) "u16" else "u32";
try w.print("enum({s}) {{\n", .{tag});
for (cases) |c| {
try writeDocs(w, c, " ");
try w.print(" {f},\n", .{g.fieldName(c)});
}
try w.writeAll("};\n\n");
},
.flags => {
const flags = items(member(d.payload, "flags"));
const bits: usize = if (flags.len <= 8) 8 else if (flags.len <= 16) 16 else 32;
try w.print("packed struct(u{d}) {{\n", .{bits});
for (flags) |f| {
try writeDocs(w, f, " ");
try w.print(" {f}: bool = false,\n", .{g.fieldName(f)});
}
if (flags.len != bits) try w.print(" _: u{d} = 0,\n", .{bits - flags.len});
try w.writeAll("};\n\n");
},
.resource => {
try w.print(
\\enum(u32) {{
\\ _,
\\
\\ pub const resource_drop = &raw.{f};
\\
\\
, .{std.zig.fmtId(g.print("[resource-drop]{s}", .{wit_name}))});
try g.writeMethods(w, id, name, funcs);
try w.print(
\\ /// Keeps the handle alive past the current callback. Release it with `deinit`.
\\ pub fn keep(self: {0s}) void {{
\\ abi.keep({0s}, self);
\\ }}
\\
\\ /// Releases the handle now instead of when the callback returns.
\\ pub fn deinit(self: {0s}) void {{
\\ abi.drop({0s}, self);
\\ }}
\\}};
\\
\\
, .{name});
},
.handle => fatal("'{s}' names a handle type", .{wit_name}),
.type, .list, .option, .result, .tuple => {
try g.writeShape(w, d);
try w.writeAll(if (g.isPlainAlias(id)) ";\n" else ";\n\n");
},
}
}
fn methodOwner(func: Value) ?usize {
const kind = member(func, "kind");
if (kind != .object) return null;
return index(kind.object.values()[0]);
}
fn writeMethods(g: *Gen, w: *Writer, resource: usize, resource_name: []const u8, funcs: Value) Writer.Error!void {
var scope: Scope = .empty;
for ([_][]const u8{ "resource_drop", "keep", "deinit" }) |reserved| g.declare(&scope, reserved, resource_name);
for (funcs.object.values()) |func| {
if (methodOwner(func) == resource) g.declare(&scope, g.funcName(func), resource_name);
}
for (funcs.object.values()) |func| {
if (methodOwner(func) == resource) try g.writeFunc(w, func, " ", resource_name, &scope);
}
}
fn paramName(g: *const Gen, param: Value, scope: *const Scope) []const u8 {
const name = g.words(text(member(param, "name")), .snake);
if (scope.contains(name)) return g.print("{s}_", .{name});
if (std.mem.eql(u8, name, "abi") or std.mem.eql(u8, name, "raw")) return g.print("{s}_", .{name});
for (g.imported.keys()) |iface| {
if (std.mem.eql(u8, g.moduleName(iface), name)) return g.print("{s}_", .{name});
}
return name;
}
fn writeFunc(
g: *Gen,
w: *Writer,
func: Value,
indent: []const u8,
self_type: ?[]const u8,
scope: *const Scope,
) Writer.Error!void {
const params = items(member(func, "params"));
const is_method = std.mem.startsWith(u8, text(member(func, "name")), "[method]");
var names: std.ArrayList([]const u8) = .empty;
for (params) |p| names.append(g.arena, g.paramName(p, scope)) catch oom();
try writeDocs(w, func, indent);
var consumes = false;
for (params, names.items) |p, name| {
if (!g.isOwn(member(p, "type"))) continue;
if (!consumes and maybe(func, "docs") != null) try w.print("{s}///\n", .{indent});
consumes = true;
try w.print("{s}/// Consumes `{s}`.\n", .{ indent, name });
}
try w.print("{s}pub fn {f}(", .{ indent, std.zig.fmtId(g.funcName(func)) });
for (params, names.items, 0..) |p, name, i| {
if (i != 0) try w.writeAll(", ");
try w.print("{f}: ", .{std.zig.fmtId(name)});
if (is_method and i == 0) try w.writeAll(self_type.?) else try g.writeRef(w, member(p, "type"));
}
try w.writeAll(") ");
try g.writeRef(w, maybe(func, "result"));
try w.print(" {{\n{s} return abi.call(raw.{f}, ", .{ indent, std.zig.fmtId(text(member(func, "name"))) });
try g.writeRef(w, maybe(func, "result"));
try w.writeAll(if (params.len > 1) ", .{ " else ", .{");
for (params, names.items, 0..) |p, name, i| {
if (i != 0) try w.writeAll(", ");
if (g.isBorrow(member(p, "type"))) {
try w.print("abi.borrow({f})", .{std.zig.fmtId(name)});
} else {
try w.print("{f}", .{std.zig.fmtId(name)});
}
}
try w.print("{s});\n{s}}}\n\n", .{ if (params.len > 1) " }" else "}", indent });
}
fn writeRaw(w: *Writer, module: []const u8, name: []const u8, sig: Signature) Writer.Error!void {
try w.print(" extern \"{s}\" fn {f}(", .{ module, std.zig.fmtId(name) });
for (sig.params, 0..) |p, i| {
if (i != 0) try w.writeAll(", ");
try w.writeAll(@tagName(p));
}
try w.print(") {s};\n", .{if (sig.result) |r| @tagName(r) else "void"});
}
fn writeImports(g: *const Gen, w: *Writer) Writer.Error!void {
for (g.used.keys()) |iface| {
const name = g.moduleName(iface);
try w.print("const {f} = @import(\"{s}.zig\");\n", .{ std.zig.fmtId(name), name });
}
}
// files
fn interfaceFile(g: *Gen, iface: usize) Writer.Error![]const u8 {
g.current = iface;
g.imported = .empty;
_ = try g.interfacePass(iface);
g.imported = g.used;
return g.interfacePass(iface);
}
fn interfacePass(g: *Gen, iface: usize) Writer.Error![]const u8 {
g.used = .empty;
const where = g.interfaceName(iface);
const types = member(g.interfaces[iface], "types").object;
const no_funcs: Value = .{ .object = .empty };
const funcs = if (g.imports.contains(iface)) member(g.interfaces[iface], "functions") else no_funcs;
const module = g.print("{s}/{s}@{s}", .{ g.namespace, where, g.version });
var scope: Scope = .empty;
for (types.keys()) |name| g.declare(&scope, g.words(name, .title), where);
for (funcs.object.values()) |func| {
if (methodOwner(func) == null) g.declare(&scope, g.funcName(func), where);
}
var body: Writer.Allocating = .init(g.arena);
const w = &body.writer;
// `use`d types come first in the JSON, keep them together like an import block.
var in_aliases = false;
for (types.values()) |id| {
const is_alias = g.isPlainAlias(index(id));
if (in_aliases and !is_alias) try w.writeAll("\n");
in_aliases = is_alias;
try g.writeType(w, index(id), funcs);
}
if (in_aliases) try w.writeAll("\n");
for (funcs.object.values()) |func| {
if (methodOwner(func) == null) try g.writeFunc(w, func, "", null, &scope);
}
var raw: Writer.Allocating = .init(g.arena);
for (funcs.object.keys(), funcs.object.values()) |name, func| {
try writeRaw(&raw.writer, module, name, g.signature(func, .import));
}
for (types.keys(), types.values()) |name, id| {
if (g.def(index(id)).kind != .resource) continue;
const drop: Signature = .{ .params = &.{.i32}, .result = null, .indirect = false };
try writeRaw(&raw.writer, module, g.print("[resource-drop]{s}", .{name}), drop);
}
var out: Writer.Allocating = .init(g.arena);
try out.writer.writeAll(header);
const uses_abi = raw.written().len != 0 or std.mem.indexOf(u8, body.written(), "abi.") != null;
if (uses_abi) try out.writer.writeAll("const abi = @import(\"../abi.zig\");\n");
try g.writeImports(&out.writer);
if (uses_abi or g.used.count() != 0) try out.writer.writeAll("\n");
try out.writer.writeAll(body.written());
if (raw.written().len != 0) try out.writer.print("const raw = struct {{\n{s}}};\n", .{raw.written()});
return g.print("{s}\n", .{std.mem.trimEnd(u8, out.written(), "\n")});
}
fn writeExport(g: *Gen, w: *Writer, export_name: []const u8, func: Value) Writer.Error!void {
const sig = g.signature(func, .@"export");
const params = items(member(func, "params"));
const base = if (std.mem.indexOfScalar(u8, export_name, '#')) |hash| export_name[hash + 1 ..] else export_name;
try writeDocs(w, func, "");
try w.print("pub fn {f}(comptime impl: fn (", .{std.zig.fmtId(g.words(base, .camel))});
for (params, 0..) |p, i| {
if (i != 0) try w.writeAll(", ");
try g.writeRef(w, member(p, "type"));
}
try w.writeAll(") ");
try g.writeRef(w, maybe(func, "result"));
try w.writeAll(") void {\n const glue = struct {\n fn call(");
for (sig.params, 0..) |p, i| {
if (i != 0) try w.writeAll(", ");
try w.print("p{d}: {s}", .{ i, @tagName(p) });
}
try w.print(") callconv(.c) {s} {{\n", .{if (sig.result) |r| @tagName(r) else "void"});
try w.writeAll(" const args = abi.liftArgs(std.meta.ArgsTuple(@TypeOf(impl)), ");
try w.writeAll(if (sig.params.len > 1) ".{ " else ".{");
for (sig.params, 0..) |_, i| {
if (i != 0) try w.writeAll(", ");
try w.print("p{d}", .{i});
}
try w.writeAll(if (sig.params.len > 1) " });\n" else "});\n");
if (!sig.indirect) try w.writeAll(" defer abi.endCall();\n");
try w.writeAll(" return abi.lowerReturn(");
try g.writeRef(w, maybe(func, "result"));
try w.writeAll(", @call(.auto, impl, args));\n }\n");
// The host reads an indirect result after we return, cleanup has to wait for post-return.
if (sig.indirect) try w.writeAll("\n fn post(_: i32) callconv(.c) void {\n abi.endCall();\n }\n");
try w.print(" }};\n @export(&glue.call, .{{ .name = \"{s}\" }});\n", .{export_name});
if (sig.indirect) try w.print(" @export(&glue.post, .{{ .name = \"cabi_post_{s}\" }});\n", .{export_name});
try w.writeAll("}\n\n");
}
fn exportsFile(g: *Gen) Writer.Error![]const u8 {
g.current = null;
g.used = .empty;
g.imported = .empty;
var scope: Scope = .empty;
var body: Writer.Allocating = .init(g.arena);
const exports = member(g.world, "exports").object;
for (exports.keys(), exports.values()) |name, item| {
if (maybe(item, "function")) |func| {
g.declare(&scope, g.words(name, .camel), "the world's exports");
try g.writeExport(&body.writer, name, func);
continue;
}
const iface = index(member(member(item, "interface"), "id"));
const funcs = member(g.interfaces[iface], "functions").object;
for (funcs.keys(), funcs.values()) |func_name, func| {
g.declare(&scope, g.words(func_name, .camel), "the world's exports");
const full = g.print("{s}/{s}@{s}#{s}", .{ g.namespace, g.interfaceName(iface), g.version, func_name });
try g.writeExport(&body.writer, full, func);
}
}
var out: Writer.Allocating = .init(g.arena);
try out.writer.writeAll(header ++ "const std = @import(\"std\");\nconst abi = @import(\"../abi.zig\");\n");
try g.writeImports(&out.writer);
try out.writer.print("\n{s}\n", .{std.mem.trimEnd(u8, body.written(), "\n")});
return out.written();
}
};
/// Paths are relative to the source directory.
pub fn generate(arena: Allocator, source: []const u8) ![]const File {
const root = try std.json.parseFromSliceLeaky(Value, arena, source, .{});
var g: Gen = .init(arena, root);
var files: std.ArrayList(File) = .empty;
var root_file: Writer.Allocating = .init(arena);
try root_file.writer.writeAll(header);
for (0..g.interfaces.len) |iface| {
const name = g.moduleName(iface);
if (std.mem.eql(u8, name, "exports")) fatal("an interface is called 'exports'", .{});
try files.append(arena, .{ .path = g.print("wit/{s}.zig", .{name}), .data = try g.interfaceFile(iface) });
try root_file.writer.print("pub const {f} = @import(\"wit/{s}.zig\");\n", .{ std.zig.fmtId(name), name });
}
try files.append(arena, .{ .path = "wit/exports.zig", .data = try g.exportsFile() });
try root_file.writer.writeAll("pub const exports = @import(\"wit/exports.zig\");\n");
try files.append(arena, .{ .path = "wit.zig", .data = root_file.written() });
return files.items;
}
pub fn main(init: std.process.Init) !void {
const arena = init.arena.allocator();
const io = init.io;
const args = try init.minimal.args.toSlice(arena);
if (args.len != 3) fatal("usage: gen <wit.json> <src-dir>", .{});
const cwd = std.Io.Dir.cwd();
const source = cwd.readFileAlloc(io, args[1], arena, .unlimited) catch |err| {
fatal("can't read {s}: {t}", .{ args[1], err });
};
const out = try cwd.createDirPathOpen(io, args[2], .{});
try out.createDirPath(io, "wit");
for (try generate(arena, source)) |file| {
try out.writeFile(io, .{ .sub_path = file.path, .data = file.data });
}
}
test "demo world" {
var arena_state: std.heap.ArenaAllocator = .init(std.testing.allocator);
defer arena_state.deinit();
const expected = [_]File{
.{ .path = "wit/types.zig", .data = @embedFile("testdata/expected/wit/types.zig") },
.{ .path = "wit/host.zig", .data = @embedFile("testdata/expected/wit/host.zig") },
.{ .path = "wit/guest.zig", .data = @embedFile("testdata/expected/wit/guest.zig") },
.{ .path = "wit/exports.zig", .data = @embedFile("testdata/expected/wit/exports.zig") },
.{ .path = "wit.zig", .data = @embedFile("testdata/expected/wit.zig") },
};
const files = try generate(arena_state.allocator(), @embedFile("testdata/demo.json"));
try std.testing.expectEqual(expected.len, files.len);
for (expected, files) |want, got| {
try std.testing.expectEqualStrings(want.path, got.path);
try std.testing.expectEqualStrings(want.data, got.data);
}
}

507
tools/testdata/demo.json vendored Normal file
View File

@ -0,0 +1,507 @@
{
"worlds": [
{
"name": "plugin",
"imports": {
"interface-0": {
"interface": {
"id": 0
}
},
"interface-1": {
"interface": {
"id": 1
}
},
"canvas": {
"type": 17
},
"shape": {
"type": 18
},
"pair": {
"type": 19
}
},
"exports": {
"start": {
"function": {
"name": "start",
"kind": "freestanding",
"params": [
{
"name": "canvas",
"type": 21
}
],
"result": 20
}
},
"measure": {
"function": {
"name": "measure",
"kind": "freestanding",
"params": [
{
"name": "shape",
"type": 18
},
{
"name": "scale",
"type": 19
}
],
"result": "f32"
}
},
"stop": {
"function": {
"name": "stop",
"kind": "freestanding",
"params": []
}
},
"interface-2": {
"interface": {
"id": 2
}
}
},
"package": 0
}
],
"interfaces": [
{
"name": "types",
"types": {
"point": 1,
"color": 2,
"access": 3,
"shape": 5,
"pair": 6,
"names": 7
},
"functions": {},
"package": 0
},
{
"name": "host",
"types": {
"point": 8,
"color": 9,
"shape": 10,
"access": 11,
"canvas": 12
},
"functions": {
"[constructor]canvas": {
"name": "[constructor]canvas",
"kind": {
"constructor": 12
},
"params": [
{
"name": "width",
"type": "u32"
},
{
"name": "height",
"type": "u32"
}
],
"result": 13
},
"[static]canvas.open": {
"name": "[static]canvas.open",
"kind": {
"static": 12
},
"params": [
{
"name": "path",
"type": "string"
}
],
"result": 14,
"docs": {
"contents": "Opens a canvas stored on disk."
}
},
"[method]canvas.draw": {
"name": "[method]canvas.draw",
"kind": {
"method": 12
},
"params": [
{
"name": "self",
"type": 15
},
{
"name": "shape",
"type": 10
},
{
"name": "at",
"type": 8
},
{
"name": "color",
"type": 9
}
]
},
"[method]canvas.blend": {
"name": "[method]canvas.blend",
"kind": {
"method": 12
},
"params": [
{
"name": "self",
"type": 15
},
{
"name": "other",
"type": 15
},
{
"name": "types",
"type": 11
}
],
"docs": {
"contents": "Copies `other` on top of this canvas."
}
},
"[method]canvas.name": {
"name": "[method]canvas.name",
"kind": {
"method": 12
},
"params": [
{
"name": "self",
"type": 15
}
],
"result": 0
},
"present": {
"name": "present",
"kind": "freestanding",
"params": [
{
"name": "canvas",
"type": 13
}
],
"result": "u64",
"docs": {
"contents": "Shows the canvas and closes it."
}
},
"log": {
"name": "log",
"kind": "freestanding",
"params": [
{
"name": "message",
"type": "string"
}
]
}
},
"package": 0
},
{
"name": "guest",
"types": {
"point": 16
},
"functions": {
"describe": {
"name": "describe",
"kind": "freestanding",
"params": [],
"result": 16
}
},
"package": 0
}
],
"types": [
{
"name": null,
"kind": {
"option": "string"
},
"owner": null
},
{
"name": "point",
"kind": {
"record": {
"fields": [
{
"name": "x",
"type": "s32"
},
{
"name": "y",
"type": "s32"
},
{
"name": "label",
"type": 0,
"docs": {
"contents": "Optional label."
}
}
]
}
},
"owner": {
"interface": 0
},
"docs": {
"contents": "Where something is."
}
},
{
"name": "color",
"kind": {
"enum": {
"cases": [
{
"name": "red"
},
{
"name": "green"
},
{
"name": "blue"
}
]
}
},
"owner": {
"interface": 0
}
},
{
"name": "access",
"kind": {
"flags": {
"flags": [
{
"name": "read"
},
{
"name": "write"
},
{
"name": "exec"
}
]
}
},
"owner": {
"interface": 0
}
},
{
"name": null,
"kind": {
"list": 1
},
"owner": null
},
{
"name": "shape",
"kind": {
"variant": {
"cases": [
{
"name": "nothing",
"type": null
},
{
"name": "circle",
"type": "f32"
},
{
"name": "polygon",
"type": 4
}
]
}
},
"owner": {
"interface": 0
}
},
{
"name": "pair",
"kind": {
"tuple": {
"types": [
"u8",
"f64"
]
}
},
"owner": {
"interface": 0
}
},
{
"name": "names",
"kind": {
"list": "string"
},
"owner": {
"interface": 0
}
},
{
"name": "point",
"kind": {
"type": 1
},
"owner": {
"interface": 1
}
},
{
"name": "color",
"kind": {
"type": 2
},
"owner": {
"interface": 1
}
},
{
"name": "shape",
"kind": {
"type": 5
},
"owner": {
"interface": 1
}
},
{
"name": "access",
"kind": {
"type": 3
},
"owner": {
"interface": 1
}
},
{
"name": "canvas",
"kind": "resource",
"owner": {
"interface": 1
}
},
{
"name": null,
"kind": {
"handle": {
"own": 12
}
},
"owner": null
},
{
"name": null,
"kind": {
"result": {
"ok": 13,
"err": "string"
}
},
"owner": null
},
{
"name": null,
"kind": {
"handle": {
"borrow": 12
}
},
"owner": null
},
{
"name": "point",
"kind": {
"type": 1
},
"owner": {
"interface": 2
}
},
{
"name": "canvas",
"kind": {
"type": 12
},
"owner": {
"world": 0
}
},
{
"name": "shape",
"kind": {
"type": 5
},
"owner": {
"world": 0
}
},
{
"name": "pair",
"kind": {
"type": 6
},
"owner": {
"world": 0
}
},
{
"name": null,
"kind": {
"result": {
"ok": null,
"err": "string"
}
},
"owner": null
},
{
"name": null,
"kind": {
"handle": {
"own": 17
}
},
"owner": null
}
],
"packages": [
{
"name": "demo:gen@1.2.0",
"interfaces": {
"types": 0,
"host": 1,
"guest": 2
},
"worlds": {
"plugin": 0
}
}
]
}

6
tools/testdata/expected/wit.zig vendored Normal file
View File

@ -0,0 +1,6 @@
// Generated by tools/gen.zig, do not edit.
pub const types = @import("wit/types.zig");
pub const host = @import("wit/host.zig");
pub const guest = @import("wit/guest.zig");
pub const exports = @import("wit/exports.zig");

59
tools/testdata/expected/wit/exports.zig vendored Normal file
View File

@ -0,0 +1,59 @@
// Generated by tools/gen.zig, do not edit.
const std = @import("std");
const abi = @import("../abi.zig");
const host = @import("host.zig");
const types = @import("types.zig");
const guest = @import("guest.zig");
pub fn start(comptime impl: fn (host.Canvas) abi.Result(void, []const u8)) void {
const glue = struct {
fn call(p0: i32) callconv(.c) i32 {
const args = abi.liftArgs(std.meta.ArgsTuple(@TypeOf(impl)), .{p0});
return abi.lowerReturn(abi.Result(void, []const u8), @call(.auto, impl, args));
}
fn post(_: i32) callconv(.c) void {
abi.endCall();
}
};
@export(&glue.call, .{ .name = "start" });
@export(&glue.post, .{ .name = "cabi_post_start" });
}
pub fn measure(comptime impl: fn (types.Shape, types.Pair) f32) void {
const glue = struct {
fn call(p0: i32, p1: i32, p2: i32, p3: i32, p4: f64) callconv(.c) f32 {
const args = abi.liftArgs(std.meta.ArgsTuple(@TypeOf(impl)), .{ p0, p1, p2, p3, p4 });
defer abi.endCall();
return abi.lowerReturn(f32, @call(.auto, impl, args));
}
};
@export(&glue.call, .{ .name = "measure" });
}
pub fn stop(comptime impl: fn () void) void {
const glue = struct {
fn call() callconv(.c) void {
const args = abi.liftArgs(std.meta.ArgsTuple(@TypeOf(impl)), .{});
defer abi.endCall();
return abi.lowerReturn(void, @call(.auto, impl, args));
}
};
@export(&glue.call, .{ .name = "stop" });
}
pub fn describe(comptime impl: fn () guest.Point) void {
const glue = struct {
fn call() callconv(.c) i32 {
const args = abi.liftArgs(std.meta.ArgsTuple(@TypeOf(impl)), .{});
return abi.lowerReturn(guest.Point, @call(.auto, impl, args));
}
fn post(_: i32) callconv(.c) void {
abi.endCall();
}
};
@export(&glue.call, .{ .name = "demo:gen/guest@1.2.0#describe" });
@export(&glue.post, .{ .name = "cabi_post_demo:gen/guest@1.2.0#describe" });
}

5
tools/testdata/expected/wit/guest.zig vendored Normal file
View File

@ -0,0 +1,5 @@
// Generated by tools/gen.zig, do not edit.
const types = @import("types.zig");
pub const Point = types.Point;

69
tools/testdata/expected/wit/host.zig vendored Normal file
View File

@ -0,0 +1,69 @@
// Generated by tools/gen.zig, do not edit.
const abi = @import("../abi.zig");
const types = @import("types.zig");
pub const Point = types.Point;
pub const Color = types.Color;
pub const Shape = types.Shape;
pub const Access = types.Access;
pub const Canvas = enum(u32) {
_,
pub const resource_drop = &raw.@"[resource-drop]canvas";
pub fn init(width: u32, height: u32) Canvas {
return abi.call(raw.@"[constructor]canvas", Canvas, .{ width, height });
}
/// Opens a canvas stored on disk.
pub fn open(path: []const u8) abi.Result(Canvas, []const u8) {
return abi.call(raw.@"[static]canvas.open", abi.Result(Canvas, []const u8), .{path});
}
pub fn draw(self: Canvas, shape: Shape, at: Point, color: Color) void {
return abi.call(raw.@"[method]canvas.draw", void, .{ abi.borrow(self), shape, at, color });
}
/// Copies `other` on top of this canvas.
pub fn blend(self: Canvas, other: Canvas, types_: Access) void {
return abi.call(raw.@"[method]canvas.blend", void, .{ abi.borrow(self), abi.borrow(other), types_ });
}
pub fn name(self: Canvas) ?[]const u8 {
return abi.call(raw.@"[method]canvas.name", ?[]const u8, .{abi.borrow(self)});
}
/// Keeps the handle alive past the current callback. Release it with `deinit`.
pub fn keep(self: Canvas) void {
abi.keep(Canvas, self);
}
/// Releases the handle now instead of when the callback returns.
pub fn deinit(self: Canvas) void {
abi.drop(Canvas, self);
}
};
/// Shows the canvas and closes it.
///
/// Consumes `canvas`.
pub fn present(canvas: Canvas) u64 {
return abi.call(raw.present, u64, .{canvas});
}
pub fn log(message: []const u8) void {
return abi.call(raw.log, void, .{message});
}
const raw = struct {
extern "demo:gen/host@1.2.0" fn @"[constructor]canvas"(i32, i32) i32;
extern "demo:gen/host@1.2.0" fn @"[static]canvas.open"(i32, i32, i32) void;
extern "demo:gen/host@1.2.0" fn @"[method]canvas.draw"(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32) void;
extern "demo:gen/host@1.2.0" fn @"[method]canvas.blend"(i32, i32, i32) void;
extern "demo:gen/host@1.2.0" fn @"[method]canvas.name"(i32, i32) void;
extern "demo:gen/host@1.2.0" fn present(i32) i64;
extern "demo:gen/host@1.2.0" fn log(i32, i32) void;
extern "demo:gen/host@1.2.0" fn @"[resource-drop]canvas"(i32) void;
};

32
tools/testdata/expected/wit/types.zig vendored Normal file
View File

@ -0,0 +1,32 @@
// Generated by tools/gen.zig, do not edit.
/// Where something is.
pub const Point = struct {
x: i32,
y: i32,
/// Optional label.
label: ?[]const u8,
};
pub const Color = enum(u8) {
red,
green,
blue,
};
pub const Access = packed struct(u8) {
read: bool = false,
write: bool = false,
exec: bool = false,
_: u5 = 0,
};
pub const Shape = union(enum) {
nothing,
circle: f32,
polygon: []const Point,
};
pub const Pair = struct { u8, f64 };
pub const Names = []const []const u8;

60
tools/testdata/wit/demo.wit vendored Normal file
View File

@ -0,0 +1,60 @@
package demo:gen@1.2.0;
interface types {
/// Where something is.
record point {
x: s32,
y: s32,
/// Optional label.
label: option<string>,
}
enum color { red, green, blue }
flags access { read, write, exec }
variant shape {
nothing,
circle(f32),
polygon(list<point>),
}
type pair = tuple<u8, f64>;
type names = list<string>;
}
interface host {
use types.{point, color, shape, access};
resource canvas {
constructor(width: u32, height: u32);
/// Opens a canvas stored on disk.
open: static func(path: string) -> result<canvas, string>;
draw: func(shape: shape, at: point, color: color);
/// Copies `other` on top of this canvas.
blend: func(other: borrow<canvas>, types: access);
name: func() -> option<string>;
}
/// Shows the canvas and closes it.
present: func(canvas: canvas) -> u64;
log: func(message: string);
}
interface guest {
use types.{point};
describe: func() -> point;
}
world plugin {
use host.{canvas};
use types.{shape, pair};
import host;
export guest;
export start: func(canvas: canvas) -> result<_, string>;
export measure: func(shape: shape, scale: pair) -> f32;
export stop: func();
}

1
wit Submodule

@ -0,0 +1 @@
Subproject commit 2366bb9edb3d9a093e3f579c3dac541eba70e6d7