This commit is contained in:
John Alanbrook 2024-03-18 14:27:52 -05:00
parent 261b373a75
commit 2d8bb9be39
13 changed files with 146 additions and 162 deletions

View file

@ -3,6 +3,7 @@ var a_db = {};
actor.spawn = function(script, config){ actor.spawn = function(script, config){
if (typeof script !== 'string') return undefined; if (typeof script !== 'string') return undefined;
console.info(`spawning actor with script ${script}`);
if (!a_db[script]) a_db[script] = io.slurp(script); if (!a_db[script]) a_db[script] = io.slurp(script);
var padawan = Object.create(actor); var padawan = Object.create(actor);
eval_env(a_db[script], padawan, script); eval_env(a_db[script], padawan, script);

View file

@ -1501,60 +1501,53 @@ bbox.fromobjs = function(objs)
}; };
/* VECTORS */ /* VECTORS */
var Vector = { var Vector = {};
length(v) { Vector.length = function(v) {
var sum = v.reduce(function(acc, val) { return acc + val**2; }, 0); var sum = v.reduce(function(acc, val) { return acc + val**2; }, 0);
return Math.sqrt(sum); return Math.sqrt(sum);
}, }
Vector.norm = function(v) {
var len = Vector.length(v);
if (!len) return [0,0];
return [v.x/len, v.y/len];
}
Vector.project = function(a, b) { return cmd(85, a, b);}
Vector.dot = function(a, b) { return vector.dot(a,b); },
Vector.random = function() {
var vec = [Math.random()-0.5, Math.random()-0.5];
return Vector.norm(vec);
}
norm(v) { Vector.angle = function(v) { return Math.rad2turn(Math.atan2(v.y, v.x)); }
var len = Vector.length(v); Vector.rotate = function(v,angle) {
return [v.x/len, v.y/len]; var r = Vector.length(v);
}, angle += Vector.angle(v);
angle = Math.turn2rad(angle);
return [r*Math.cos(angle), r*Math.sin(angle)];
}
project(a, b) { return cmd(85, a, b);}, Vector.equal = function(v1, v2, tol) {
dot(a, b) { return vector.dot(a,b); }, if (!tol)
return v1.equal(v2);
random() {
var vec = [Math.random()-0.5, Math.random()-0.5];
return Vector.norm(vec);
},
angle(v) { return Math.rad2turn(Math.atan2(v.y, v.x)); },
rotate(v,angle) {
var r = Vector.length(v);
angle += Vector.angle(v);
angle = Math.turn2rad(angle);
return [r*Math.cos(angle), r*Math.sin(angle)];
},
equal(v1, v2, tol) {
if (!tol)
return v1.equal(v2);
var eql = true; var eql = true;
var c = v1.sub(v2); var c = v1.sub(v2);
c.forEach(function(x) { c.forEach(function(x) {
if (!eql) return; if (!eql) return;
if (Math.abs(x) > tol) if (Math.abs(x) > tol)
eql = false; eql = false;
}); });
return eql; return eql;
}, }
reflect(vec, plane) { Vector.reflect = function(vec, plane) {
var p = Vector.norm(plane); var p = Vector.norm(plane);
return vec.sub(p.scale(2*Vector.dot(vec, p))); return vec.sub(p.scale(2*Vector.dot(vec, p)));
}, }
reflect_point(vec, point) { Vector.reflect_point = function(vec, point) { return point.add(vec.sub(point).scale(-1)); }
return point.add(vec.sub(point).scale(-1));
},
};
/* POINT ASSISTANCE */ /* POINT ASSISTANCE */

View file

@ -165,9 +165,7 @@ var timescale = 1;
var gggstart = game.engine_start; var gggstart = game.engine_start;
game.engine_start = function(s) { game.engine_start = function(s) {
gggstart(process); gggstart(function() { world_start(); s(); }, process);
world_start();
s();
} }
function process() function process()
@ -177,6 +175,7 @@ function process()
prosperon.appupdate(dt); prosperon.appupdate(dt);
prosperon.emitters_step(dt); prosperon.emitters_step(dt);
input.procdown();
if (sim.mode === "play" || sim.mode === "step") { if (sim.mode === "play" || sim.mode === "step") {
prosperon.update(dt*game.timescale); prosperon.update(dt*game.timescale);
@ -191,8 +190,19 @@ function process()
prosperon.phys2d_step(physMS*timescale); prosperon.phys2d_step(physMS*timescale);
prosperon.physupdate(physMS*timescale); prosperon.physupdate(physMS*timescale);
} }
prosperon.window_render(); prosperon.window_render();
render.sprites();
render.models();
render.emitters();
prosperon.draw();
render.flush();
render.pass();
prosperon.gui();
render.flush_hud();
render.end_pass();
render.commit();
} }
game.timescale = 1; game.timescale = 1;

View file

@ -598,7 +598,6 @@ var gameobject = {
if (this.__kill) return; if (this.__kill) return;
this.__kill = true; this.__kill = true;
console.info(`killing ${this.toString()}`);
this.timers.forEach(t => t()); this.timers.forEach(t => t());
this.timers = []; this.timers = [];
Event.rm_obj(this); Event.rm_obj(this);

View file

@ -20,7 +20,7 @@ var keycodes = {
var codekeys = {}; var codekeys = {};
for (var code in keycodes) for (var code in keycodes)
codekeys[keycodes[code]] = code; codekeys[keycodes[code]] = code;
var mod = { var mod = {
shift: 0, shift: 0,
ctrl: 0, ctrl: 0,
@ -67,7 +67,7 @@ function modstr()
prosperon.keydown = function(key, repeat) prosperon.keydown = function(key, repeat)
{ {
prosperon.keys[key] = true; prosperon.keys[key] = key;
if (key == 341 || key == 345) if (key == 341 || key == 345)
mod.ctrl = 1; mod.ctrl = 1;
@ -204,6 +204,14 @@ input.print_pawn_kbm = function(pawn) {
return str; return str;
}; };
input.procdown = function()
{
for (var k of prosperon.keys) {
if (!k) continue;
player[0].raw_input(keyname_extd(k), "down");
}
}
input.print_md_kbm = function(pawn) { input.print_md_kbm = function(pawn) {
if (!('inputs' in pawn)) return; if (!('inputs' in pawn)) return;
@ -277,7 +285,7 @@ var Player = {
continue; continue;
} }
var fn = null; var fn = null;
switch (state) { switch (state) {
case 'pressed': case 'pressed':
@ -364,7 +372,6 @@ var player = Player;
return { return {
Mouse, Mouse,
Keys, Keys,
input,
Player, Player,
player, player,
keycodes, keycodes,

View file

@ -43,7 +43,7 @@ render.device.doc = `Device resolutions given as [x,y,inches diagonal].`;
/* All draw in screen space */ /* All draw in screen space */
render.point = function(pos,size,color) { render.point = function(pos,size,color) {
color ??= Color.blue; color ??= Color.blue;
render.circle(pos,size,color); render.circle(pos,size,size,color);
}; };
var tmpline = render.line; var tmpline = render.line;

View file

@ -295,6 +295,7 @@ Cmdline.register_order("play", function(argv) {
console.info(`Starting game with window size ${window.size} and render ${window.rendersize}.`); console.info(`Starting game with window size ${window.size} and render ${window.rendersize}.`);
game.engine_start(function() { game.engine_start(function() {
console.info(`eng start`);
global.mixin("scripts/sound.js"); global.mixin("scripts/sound.js");
global.game = actor.spawn("game.js"); global.game = actor.spawn("game.js");
if (project.icon) window.set_icon(project.icon); if (project.icon) window.set_icon(project.icon);

View file

@ -1206,6 +1206,11 @@ GETSET_PAIR(warp_gravity, planar_force, vec3)
#define GGETSET_ADD(ENTRY) #define GGETSET_ADD(ENTRY)
#define JSC_CCALL(NAME, FN) JSValue js_##NAME (JSContext *js, JSValueConst this, int argc, JSValue *argv) { \
{FN;} \
return JS_UNDEFINED; \
} \
static const JSCFunctionListEntry js_warp_gravity_funcs [] = { static const JSCFunctionListEntry js_warp_gravity_funcs [] = {
CGETSET_ADD(warp_gravity, strength), CGETSET_ADD(warp_gravity, strength),
CGETSET_ADD(warp_gravity, decay), CGETSET_ADD(warp_gravity, decay),
@ -1294,13 +1299,12 @@ JSValue js_os_sys(JSContext *js, JSValueConst this, int argc, JSValue *argv)
return JS_UNDEFINED; return JS_UNDEFINED;
} }
JSValue js_os_quit(JSContext *js, JSValueConst this) { quit(); return JS_UNDEFINED; } JSC_CCALL(os_quit, quit();)
JSValue js_os_reindex_static(JSContext *js, JSValueConst this) { cpSpaceReindexStatic(space); return JS_UNDEFINED; } JSC_CCALL(os_reindex_static, cpSpaceReindexStatic(space));
JSC_CCALL(os_gc, script_gc());
#define RETUN return JS_UNDEFINED #define RETUN return JS_UNDEFINED
JSValue js_os_gc(JSContext *js, JSValueConst this) { script_gc(); RETUN; }
JSValue js_os_capture(JSContext *js, JSValueConst this, int argc, JSValue *argv) { JSValue js_os_capture(JSContext *js, JSValueConst this, int argc, JSValue *argv) {
char *str = js2str(argv[0]); char *str = js2str(argv[0]);
capture_screen(js2number(argv[1]), js2number(argv[2]), js2number(argv[4]), js2number(argv[5]), str); capture_screen(js2number(argv[1]), js2number(argv[2]), js2number(argv[4]), js2number(argv[5]), str);
@ -1318,24 +1322,32 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, capture, 5), MIST_FUNC_DEF(os, capture, 5),
}; };
JSValue js_render_normal(JSContext *js, JSValueConst this) { opengl_rendermode(LIT); RETUN; } JSC_CCALL(render_normal, opengl_rendermode(LIT))
JSValue js_render_wireframe(JSContext *js, JSValueConst this) { opengl_rendermode(WIREFRAME); RETUN; } JSC_CCALL(render_wireframe, opengl_rendermode(WIREFRAME))
JSValue js_render_grid(JSContext *js, JSValueConst this, int argc, JSValue *argv) { draw_grid(js2number(argv[0]), js2number(argv[1]), js2color(argv[2])); RETUN; } JSC_CCALL(render_grid, draw_grid(js2number(argv[0]), js2number(argv[1]), js2color(argv[2]));)
JSValue js_render_point(JSContext *js, JSValueConst this, int argc, JSValue *argv) { draw_cppoint(js2vec2(argv[0]), js2number(argv[1]), js2color(argv[2])); RETUN;} JSC_CCALL(render_point, draw_cppoint(js2vec2(argv[0]), js2number(argv[1]), js2color(argv[2])))
JSValue js_render_circle(JSContext *js, JSValueConst this, int argc, JSValue *argv) { draw_circle(js2vec2(argv[0]), js2number(argv[1]), js2number(argv[2]), js2color(argv[3]), -1); RETUN; } JSC_CCALL(render_circle, draw_circle(js2vec2(argv[0]), js2number(argv[1]), js2number(argv[2]), js2color(argv[3]), -1);)
JSValue js_render_poly(JSContext *js, JSValueConst this, int argc, JSValue *argv) { JSC_CCALL(render_poly,
int n = js_arrlen(argv[0]); int n = js_arrlen(argv[0]);
HMM_Vec2 points[n]; HMM_Vec2 points[n];
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
points[i] = js2vec2(js_arridx(argv[0], i)); points[i] = js2vec2(js_arridx(argv[0], i));
draw_poly(points, n, js2color(argv[1])); draw_poly(points, n, js2color(argv[1]));
RETUN; )
}
JSValue js_render_line(JSContext *js, JSValueConst this, int argc, JSValue *argv) { JSC_CCALL(render_line,
void *v1 = js2cpvec2arr(argv[0]); void *v1 = js2cpvec2arr(argv[0]);
draw_edge(v1, js_arrlen(argv[0]), js2color(argv[1]), js2number(argv[2]), 0, js2color(argv[1]), 10); draw_edge(v1, js_arrlen(argv[0]), js2color(argv[1]), js2number(argv[2]), 0, js2color(argv[1]), 10);
RETUN; )
}
JSC_CCALL(render_sprites, sprite_draw_all())
JSC_CCALL(render_models, model_draw_all())
JSC_CCALL(render_emitters, emitters_draw())
JSC_CCALL(render_flush, debug_flush(&projection); text_flush(&projection))
JSC_CCALL(render_flush_hud, debug_flush(&hudproj); debug_flush(&hudproj); sprite_flush();)
JSC_CCALL(render_pass, debug_nextpass())
JSC_CCALL(render_end_pass, sg_end_pass())
JSC_CCALL(render_commit, sg_commit(); debug_newframe();)
static const JSCFunctionListEntry js_render_funcs[] = { static const JSCFunctionListEntry js_render_funcs[] = {
MIST_FUNC_DEF(render, normal, 0), MIST_FUNC_DEF(render, normal, 0),
@ -1345,13 +1357,18 @@ static const JSCFunctionListEntry js_render_funcs[] = {
MIST_FUNC_DEF(render, circle, 3), MIST_FUNC_DEF(render, circle, 3),
MIST_FUNC_DEF(render, poly, 2), MIST_FUNC_DEF(render, poly, 2),
MIST_FUNC_DEF(render, line, 3), MIST_FUNC_DEF(render, line, 3),
MIST_FUNC_DEF(render, sprites, 0),
MIST_FUNC_DEF(render, models, 0),
MIST_FUNC_DEF(render, emitters, 0),
MIST_FUNC_DEF(render, flush, 0),
MIST_FUNC_DEF(render, flush_hud, 0),
MIST_FUNC_DEF(render, pass, 0),
MIST_FUNC_DEF(render, end_pass, 0),
MIST_FUNC_DEF(render, commit, 0)
}; };
JSValue js_gui_flush(JSContext *js, JSValueConst this) { text_flush(&hudproj); RETUN; } JSC_CCALL(gui_flush, text_flush(&hudproj));
JSValue js_gui_scissor(JSContext *js, JSValueConst this, int argc, JSValue *argv) { JSC_CCALL(gui_scissor, sg_apply_scissor_rectf(js2number(argv[0]), js2number(argv[1]), js2number(argv[2]), js2number(argv[3]), 0))
sg_apply_scissor_rectf(js2number(argv[0]), js2number(argv[1]), js2number(argv[2]), js2number(argv[3]), 0);
RETUN;
}
DEF_FN_STR(font_set) DEF_FN_STR(font_set)
@ -1368,7 +1385,7 @@ static const JSCFunctionListEntry js_vector_funcs[] = {
}; };
JSValue js_game_engine_start(JSContext *js, JSValueConst this, int argc, JSValue *argv) { JSValue js_game_engine_start(JSContext *js, JSValueConst this, int argc, JSValue *argv) {
engine_start(argv[0]); engine_start(argv[0],argv[1]);
RETUN; RETUN;
} }
JSValue js_game_object_count(JSContext *js, JSValueConst this) { return number2js(go_count()); } JSValue js_game_object_count(JSContext *js, JSValueConst this) { return number2js(go_count()); }
@ -1378,10 +1395,10 @@ static const JSCFunctionListEntry js_game_funcs[] = {
MIST_FUNC_DEF(game, object_count, 0) MIST_FUNC_DEF(game, object_count, 0)
}; };
JSValue js_input_show_keyboard(JSContext *js, JSValueConst this, int argc, JSValue *argv) { sapp_show_keyboard(js2bool(argv[0])); RETUN; } JSC_CCALL(input_show_keyboard, sapp_show_keyboard(js2bool(argv[0])))
JSValue js_input_keyboard_shown(JSContext *js, JSValueConst this) { return bool2js(sapp_keyboard_shown()); } JSValue js_input_keyboard_shown(JSContext *js, JSValueConst this) { return bool2js(sapp_keyboard_shown()); }
JSValue js_input_mouse_mode(JSContext *js, JSValueConst this, int argc, JSValue *argv) { set_mouse_mode(js2int(argv[0])); RETUN; } JSC_CCALL(input_mouse_mode, set_mouse_mode(js2int(argv[0])))
JSValue js_input_mouse_cursor(JSContext *js, JSValueConst this, int argc, JSValue *argv) { sapp_set_mouse_cursor(js2int(argv[0])); RETUN; } JSC_CCALL(input_mouse_cursor, sapp_set_mouse_cursor(js2int(argv[0])))
DEF_FN_STR(cursor_img) DEF_FN_STR(cursor_img)
@ -1403,23 +1420,9 @@ JSValue global_get_##ENTRY (JSContext *js, JSValue this) { \
return TYPE##2js(ENTRY); \ return TYPE##2js(ENTRY); \
} \ } \
JSValue js_prosperon_emitters_step(JSContext *js, JSValue this, int argc, JSValue *argv) JSC_CCALL(prosperon_emitters_step, emitters_step(js2number(argv[0])))
{ JSC_CCALL(prosperon_phys2d_step, phys2d_update(js2number(argv[0])))
emitters_step(js2number(argv[0])); JSC_CCALL(prosperon_window_render, openglRender(&mainwin))
return JS_UNDEFINED;
}
JSValue js_prosperon_phys2d_step(JSContext *js, JSValue this, int argc, JSValue *argv)
{
phys2d_update(js2number(argv[0]));
return JS_UNDEFINED;
}
JSValue js_prosperon_window_render(JSContext *js, JSValue this, int argc, JSValue *argv)
{
window_render(&mainwin);
return JS_UNDEFINED;
}
static const JSCFunctionListEntry js_prosperon_funcs[] = { static const JSCFunctionListEntry js_prosperon_funcs[] = {
MIST_FUNC_DEF(prosperon, emitters_step, 1), MIST_FUNC_DEF(prosperon, emitters_step, 1),
@ -1599,7 +1602,6 @@ JSValue js_io_chmod(JSContext *js, JSValueConst this, int argc, JSValue *argv)
return JS_UNDEFINED; return JS_UNDEFINED;
} }
DEF_FN_STR(save_qoa) DEF_FN_STR(save_qoa)
JSValue js_io_compile(JSContext *js, JSValueConst this, int argc, JSValue *argv) { JSValue js_io_compile(JSContext *js, JSValueConst this, int argc, JSValue *argv) {

View file

@ -393,8 +393,36 @@ HMM_Vec3 dirl_pos = {4, 100, 20};
#define MODE_EXPAND 4 #define MODE_EXPAND 4
#define MODE_FULL 5 #define MODE_FULL 5
void full_2d_pass(struct window *window) void full_3d_pass(struct window *window)
{ {
HMM_Mat4 model = HMM_M4D(1.f);
float scale = 0.08;
model = HMM_MulM4(model, HMM_Scale((HMM_Vec3){scale,scale,scale}));
// Shadow pass
// sg_begin_pass(sg_shadow.pass, &sg_shadow.pass_action);
// sg_apply_pipeline(sg_shadow.pipe);
HMM_Mat4 light_proj = HMM_Orthographic_RH_ZO(-100.f, 100.f, -100.f, 100.f, 1.f, 100.f);
HMM_Mat4 light_view = HMM_LookAt_RH(dirl_pos, (HMM_Vec3){0,0,0}, (HMM_Vec3){0,1,0});
HMM_Mat4 lsm = HMM_MulM4(light_proj, light_view);
HMM_Mat4 subo[2];
subo[0] = lsm;
subo[1] = model;
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(subo));
}
void openglRender(struct window *window) {
sg_swapchain sch = sglue_swapchain();
sg_begin_pass(&(sg_pass){
.action = pass_action,
.swapchain = sglue_swapchain(),
.label = "window pass"
});
HMM_Vec2 usesize = window->rendersize; HMM_Vec2 usesize = window->rendersize;
switch(window->mode) { switch(window->mode) {
@ -432,54 +460,6 @@ void full_2d_pass(struct window *window)
hudproj = HMM_Orthographic_LH_ZO(0, usesize.x, 0, usesize.y, -1.f, 1.f); hudproj = HMM_Orthographic_LH_ZO(0, usesize.x, 0, usesize.y, -1.f, 1.f);
sprite_draw_all();
model_draw_all();
emitters_draw();
script_evalf("prosperon.draw();");
debug_flush(&projection);
text_flush(&projection);
////// TEXT && GUI
debug_nextpass();
script_evalf("prosperon.gui();");
debug_flush(&hudproj);
text_flush(&hudproj);
sprite_flush();
}
void full_3d_pass(struct window *window)
{
HMM_Mat4 model = HMM_M4D(1.f);
float scale = 0.08;
model = HMM_MulM4(model, HMM_Scale((HMM_Vec3){scale,scale,scale}));
// Shadow pass
// sg_begin_pass(sg_shadow.pass, &sg_shadow.pass_action);
// sg_apply_pipeline(sg_shadow.pipe);
HMM_Mat4 light_proj = HMM_Orthographic_RH_ZO(-100.f, 100.f, -100.f, 100.f, 1.f, 100.f);
HMM_Mat4 light_view = HMM_LookAt_RH(dirl_pos, (HMM_Vec3){0,0,0}, (HMM_Vec3){0,1,0});
HMM_Mat4 lsm = HMM_MulM4(light_proj, light_view);
HMM_Mat4 subo[2];
subo[0] = lsm;
subo[1] = model;
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(subo));
}
void openglRender(struct window *window) {
sg_swapchain sch = sglue_swapchain();
sg_begin_pass(&(sg_pass){
.action = pass_action,
.swapchain = sglue_swapchain(),
.label = "window pass"
});
full_2d_pass(window);
sg_end_pass();
/* if (gif.rec && (apptime() - gif.timer) > gif.spf) { /* if (gif.rec && (apptime() - gif.timer) > gif.spf) {
sg_begin_pass(&(sg_pass){ sg_begin_pass(&(sg_pass){
@ -496,10 +476,6 @@ void openglRender(struct window *window) {
msf_gif_frame(&gif_state, gif.buffer, gif.cpf, gif.depth, gif.w * -4); msf_gif_frame(&gif_state, gif.buffer, gif.cpf, gif.depth, gif.w * -4);
} }
*/ */
sg_commit();
debug_newframe();
} }
sg_shader sg_compile_shader(const char *v, const char *f, sg_shader_desc *d) sg_shader sg_compile_shader(const char *v, const char *f, sg_shader_desc *d)
@ -519,7 +495,6 @@ sg_shader sg_compile_shader(const char *v, const char *f, sg_shader_desc *d)
return ret; return ret;
} }
struct boundingbox cwh2bb(HMM_Vec2 c, HMM_Vec2 wh) { struct boundingbox cwh2bb(HMM_Vec2 c, HMM_Vec2 wh) {
struct boundingbox bb = { struct boundingbox bb = {
.t = c.Y + wh.Y/2, .t = c.Y + wh.Y/2,

View file

@ -105,10 +105,6 @@ void window_seticon(struct window *w, struct texture *tex)
free(sizes[i].data); free(sizes[i].data);
} }
void window_render(struct window *w) {
openglRender(w);
}
void window_free(window *w) void window_free(window *w)
{ {

View file

@ -41,6 +41,4 @@ void window_setfullscreen(window *w, int f);
void set_icon(const char *png); void set_icon(const char *png);
void window_seticon(struct window *w, struct texture *icon); void window_seticon(struct window *w, struct texture *icon);
void window_render(struct window *w);
#endif #endif

View file

@ -52,15 +52,16 @@ static int sim_play = SIM_PLAY;
static int argc; static int argc;
static char **args; static char **args;
static JSValue c_start;
static JSValue c_process_fn; static JSValue c_process_fn;
void c_init() { void c_init() {
mainwin.start = 1; mainwin.start = 1;
window_resize(sapp_width(), sapp_height()); window_resize(sapp_width(), sapp_height());
// script_evalf("world_start();");
render_init(); render_init();
set_icon("icons/moon.gif"); set_icon("icons/moon.gif");
particle_init(); particle_init();
script_call_sym(c_start,0,NULL);
} }
void c_frame() { script_call_sym(c_process_fn,0,NULL); } void c_frame() { script_call_sym(c_process_fn,0,NULL); }
@ -230,8 +231,9 @@ int main(int argc, char **argv) {
return 0; return 0;
} }
void engine_start(JSValue procfn) void engine_start(JSValue start, JSValue procfn)
{ {
c_start = start;
c_process_fn = procfn; c_process_fn = procfn;
sound_init(); sound_init();

View file

@ -5,7 +5,7 @@
double apptime(); double apptime();
void print_stacktrace(); void print_stacktrace();
void engine_start(JSValue proc_fn); /* fn runs after the engine starts */ void engine_start(JSValue start_fn, JSValue proc_fn); /* fn runs after the engine starts */
void quit(); void quit();