186 lines
5.4 KiB
Zig
186 lines
5.4 KiB
Zig
const coral = @import("coral");
|
|
|
|
const ona = @import("ona");
|
|
|
|
pub const ScriptPlugin = struct {
|
|
env: ona.script.RuntimeEnv,
|
|
events: ?*ona.script.RuntimeObj,
|
|
|
|
const EventsObject = struct {
|
|
updaters: RuntimeObjList,
|
|
loop: *ona.GraphicsLoop,
|
|
|
|
fn get(context: ona.script.Typeinfo.GetContext) ona.script.RuntimeError!?*ona.script.RuntimeObj {
|
|
const index = context.get_index();
|
|
|
|
defer context.env.release(index);
|
|
|
|
inline for ([_][]const u8{"on_update"}) |name| {
|
|
const symbol = (try context.env.new_symbol(name)).pop().?;
|
|
|
|
defer context.env.release(symbol);
|
|
|
|
if (index.equals(symbol)) {
|
|
return (try context.env.new_syscall(@field(EventsObject, name))).pop();
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
fn on_update(env: *ona.script.RuntimeEnv) ona.script.RuntimeError!?*ona.script.RuntimeObj {
|
|
const caller = try env.expect_object(try env.get_caller());
|
|
|
|
defer env.release(caller);
|
|
|
|
const updater = try env.expect_object((try env.arg(0)).pop());
|
|
|
|
errdefer env.release(updater);
|
|
|
|
try @as(*EventsObject, @ptrCast(@alignCast(try env.expect_dynamic(caller, typeinfo)))).updaters.push_one(updater);
|
|
|
|
return null;
|
|
}
|
|
|
|
const typeinfo = &ona.script.Typeinfo{
|
|
.name = "events",
|
|
.size = @sizeOf(EventsObject),
|
|
.get = get,
|
|
};
|
|
|
|
fn update(env: *ona.script.RuntimeEnv, _: *ona.GraphicsLoop) void {
|
|
return ((env.arg(0) catch return).call(0, null) catch return).discard();
|
|
}
|
|
};
|
|
|
|
const RuntimeObjList = coral.list.Stack(*ona.script.RuntimeObj);
|
|
|
|
pub fn deinit(self: *ScriptPlugin) void {
|
|
if (self.events) |object| {
|
|
self.env.release(object);
|
|
}
|
|
|
|
self.env.deinit();
|
|
}
|
|
|
|
fn import_events(self: *ScriptPlugin, env: *ona.script.RuntimeEnv) ona.script.RuntimeError!?*ona.script.RuntimeObj {
|
|
return env.acquire(self.events.?);
|
|
}
|
|
|
|
pub fn init() coral.io.AllocationError!ScriptPlugin {
|
|
var env = try ona.script.RuntimeEnv.init(ona.heap.allocator, 255, .{
|
|
.print_out = ona.log_info,
|
|
.print_err = ona.log_fail,
|
|
});
|
|
|
|
errdefer env.deinit();
|
|
|
|
return .{
|
|
.env = env,
|
|
.events = null,
|
|
};
|
|
}
|
|
|
|
fn load(self: *ScriptPlugin, loop: *ona.GraphicsLoop) void {
|
|
self.events = (self.env.new_dynamic(EventsObject.typeinfo, EventsObject{
|
|
.updaters = RuntimeObjList.init(ona.heap.allocator),
|
|
.loop = loop,
|
|
}) catch {
|
|
return ona.log_fail("failed to instantiate events object");
|
|
}).pop();
|
|
|
|
self.env.override_import("events", ona.script.Importer.bind(ScriptPlugin, self, import_events)) catch {
|
|
return ona.log_fail("failed to overide native import paths");
|
|
};
|
|
|
|
self.reload(loop) catch {};
|
|
}
|
|
|
|
fn reload(self: *ScriptPlugin, loop: *ona.GraphicsLoop) ona.script.RuntimeError!void {
|
|
var title = comptime try coral.utf8.SmallString.from_units("Ona");
|
|
var res_width = @as(u16, 640);
|
|
var res_height = @as(u16, 480);
|
|
var tick_rate = @as(f64, 60);
|
|
|
|
if ((try self.env.import(comptime try ona.file.Path.from_bytes("app.ona"))).pop()) |manifest| {
|
|
defer self.env.release(manifest);
|
|
|
|
if (try ona.script.get_field(&self.env, manifest, "res_width")) |object| {
|
|
defer self.env.release(object);
|
|
|
|
const int = @typeInfo(@TypeOf(res_width)).Int;
|
|
|
|
res_width = coral.math.checked_cast(int, try self.env.expect_fixed(object)) orelse {
|
|
return self.env.raise(error.TypeMismatch, "`res_width` property cannot be greater than `{max}`", .{
|
|
.max = coral.math.max_int(int),
|
|
});
|
|
};
|
|
}
|
|
|
|
if (try ona.script.get_field(&self.env, manifest, "res_height")) |object| {
|
|
defer self.env.release(object);
|
|
|
|
const int = @typeInfo(@TypeOf(res_height)).Int;
|
|
|
|
res_height = coral.math.checked_cast(int, try self.env.expect_fixed(object)) orelse {
|
|
return self.env.raise(error.TypeMismatch, "`res_height` property cannot be greater than `{max}`", .{
|
|
.max = coral.math.max_int(int),
|
|
});
|
|
};
|
|
}
|
|
|
|
if (try ona.script.get_field(&self.env, manifest, "tick_rate")) |object| {
|
|
defer self.env.release(object);
|
|
|
|
tick_rate = try self.env.expect_float(object);
|
|
}
|
|
|
|
if (try ona.script.get_field(&self.env, manifest, "title")) |object| {
|
|
defer self.env.release(object);
|
|
|
|
title = coral.utf8.SmallString.from_units(try self.env.expect_string(object)) catch |from_error| {
|
|
return switch (from_error) {
|
|
error.InvalidUtf8 => self.env.raise(error.TypeMismatch, "`title` cannot contain invalid utf8", .{}),
|
|
error.TooBig => self.env.raise(error.TypeMismatch, "`title` is too long", .{}),
|
|
};
|
|
};
|
|
}
|
|
|
|
loop.set_resolution(res_width, res_height);
|
|
loop.set_title(title);
|
|
loop.set_tick_rate(tick_rate);
|
|
}
|
|
}
|
|
|
|
fn update(self: *ScriptPlugin, graphics_update: ona.GraphicsUpdate) void {
|
|
if (graphics_update.loop.is_key_pressed(ona.Key.f5)) {
|
|
self.reload(graphics_update.loop) catch {};
|
|
}
|
|
|
|
const eventsect = @as(*EventsObject, @ptrCast(@alignCast(self.env.expect_dynamic(self.events.?, EventsObject.typeinfo) catch {
|
|
return;
|
|
})));
|
|
|
|
const dt = (self.env.new_float(graphics_update.delta_time) catch return).pop().?;
|
|
|
|
defer self.env.release(dt);
|
|
|
|
for (eventsect.updaters.values) |updater| {
|
|
(((self.env.push(dt) catch return).push(updater) catch return).call(1, null) catch return).discard();
|
|
}
|
|
}
|
|
};
|
|
|
|
pub fn main() void {
|
|
var script_plugin = ScriptPlugin.init() catch {
|
|
return ona.log_fail("failed to initialize script plugin");
|
|
};
|
|
|
|
defer script_plugin.deinit();
|
|
|
|
ona.start_graphics(&.{
|
|
.{.loader = ona.GraphicsLoader.bind(ScriptPlugin, &script_plugin, ScriptPlugin.load)},
|
|
.{.updater = ona.GraphicsUpdater.bind(ScriptPlugin, &script_plugin, ScriptPlugin.update)},
|
|
});
|
|
}
|