ona/source/runner.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)},
});
}