diff --git a/scripts/actor.js b/scripts/actor.js index e67f0caf..0a8d8c28 100644 --- a/scripts/actor.js +++ b/scripts/actor.js @@ -1,7 +1,6 @@ var actor = {}; var actor_urs = {}; -var script_times = {}; var actor_spawns = {}; @@ -17,7 +16,6 @@ globalThis.class_use = function (script, config, base, callback) { if (!actor_urs[file]) { var newur = Object.create(base); actor_urs[file] = newur; - script_times[file] = io.mod(file); actor_spawns[file] = []; } @@ -61,28 +59,23 @@ actor.__stats = function () { return stats; }; -actor.hotreload = function () { - for (var i in script_times) { - if (io.mod(i) > script_times[i]) { - script_times[i] = io.mod(i); - var script = Resources.replstrs(i); - script = `(function() { - var self = this; - var $ = this.__proto__; - ${script}; - })`; - var fn = os.eval(i, script); +actor.hotreload = function (file) { + var script = Resources.replstrs(file); + script = `(function() { + var self = this; + var $ = this.__proto__; + ${script}; + })`; + var fn = os.eval(file, script); - for (var obj of actor_spawns[i]) { - var a = obj; - a.timers.forEachRight(t=>t()); - a.timers = []; - var save = json.decode(json.encode(a)); - fn.call(a); - Object.merge(a, save); - check_registers(a); - } - } + for (var obj of actor_spawns[file]) { + var a = obj; + a.timers.forEachRight(t=>t()); + a.timers = []; + var save = json.decode(json.encode(a)); + fn.call(a); + Object.merge(a, save); + check_registers(a); } }; diff --git a/scripts/engine.js b/scripts/engine.js index 0365c01e..de01024a 100644 --- a/scripts/engine.js +++ b/scripts/engine.js @@ -53,6 +53,7 @@ Resources.rm_fn = function rm_fn(fn, text) { }; Resources.rm_fn.doc = "Remove calls to a given function from a given text script."; +// Normalizes paths for use in prosperon Resources.replpath = function replpath(str, path) { if (!str) return str; if (str[0] === "/") return str.rm(0); @@ -70,6 +71,7 @@ Resources.replpath = function replpath(str, path) { return str; }; +// Given a script path, loads it, and replaces certain function calls to conform to environment Resources.replstrs = function replstrs(path) { if (!path) return; var script = io.slurp(path); @@ -160,7 +162,6 @@ function find_ext(file, ext, root = "") { } var all_files = io.glob(`**/${file}.*`); - all_files = all_files.filter(x => !x.startsWith('.')); var find = undefined; for (var e of ext) { var finds = all_files.filter(x => x.ext() === e); @@ -328,9 +329,9 @@ io.exists = function(path) var tmpslurp = io.slurp; io.slurp = function(path) { - path = Resources.replpath(path); - var ret = tmpslurp(path, true) || core_db.slurp(path, true); - if (!ret) console.info(`could not slurp ${path}`); + var findpath = Resources.replpath(path); + var ret = tmpslurp(findpath, true) || core_db.slurp(findpath, true); + if (!ret) console.info(`could not slurp path ${path} as ${findpath}`) return ret; } @@ -410,13 +411,4 @@ bare_use("preconfig.js"); if (!profile.enabled) use = stripped_use; Object.assign(globalThis, use("prosperon.js")); - -/*app.interval(_ => { - profile.report("hotreload"); - actor.hotreload(); - render.hotreload(); - game.tex_hotreload(); - repl.hotreload(); - profile.endreport("hotreload"); -}, 1); -*/ + diff --git a/scripts/prosperon.js b/scripts/prosperon.js index 1bd1c10e..4ef39493 100644 --- a/scripts/prosperon.js +++ b/scripts/prosperon.js @@ -178,21 +178,42 @@ game.doc.pause = "Pause game simulation."; game.doc.play = "Resume or start game simulation."; game.doc.camera = "Current camera."; -game.tex_hotreload = function () { - return; - for (var path in game.texture.cache) { - if (io.mod(path) > game.texture.time_cache[path]) { - var tex = game.texture.cache[path]; - game.texture.time_cache[path] = io.mod(path); - SpriteAnim.hotreload(path); - os.texture_swap(path, game.texture.cache[path]); - for (var sprite of Object.values(allsprites)) { - if (sprite.texture == tex) { - sprite.tex_sync(); - } +game.tex_hotreload = function (file) { + if (!(file in game.texture.cache)) return; + var data = io.slurpbytes(file); + var tex; + if (file.endsWith('.gif')) { + var anim = os.make_gif(data); + if (anim.frames.length !== 1) return; + console.info(json.encode(anim)); + tex = anim.frames[0].texture; + } else if (file.endsWith('.ase') || file.endsWith('.aseprite')) { + var anim = os.make_aseprite(data); + if (anim.texture) // single picture + tex = anim.texture; + else { + var oldanim = game.texture.cache[file]; + // load all into gpu + for (var a in anim) { + oldanim[a] = anim[a]; + for (let frame of anim[a].frames) + frame.texture.load_gpu(); } + return; } - } + } else + tex = os.make_texture(data); + + var img = game.texture.cache[file]; + tex.load_gpu(); + console.info(`replacing ${json.encode(img)}`) + img.texture = tex; + img.rect = { + x:0, + y:0, + width:1, + height:1 + }; }; var image = {}; @@ -303,6 +324,7 @@ game.is_image = function(obj) // Any request to it returns an image, which is a texture and rect. But they can game.texture = function (path) { + if (typeof path !== 'string') throw new Error('need a string for game.texture') var parts = path.split(':'); path = Resources.find_image(parts[0]); @@ -343,8 +365,16 @@ game.texture = function (path) { var ext = path.ext(); if (ext === 'ase' || ext === 'aseprite') { - anim = os.make_aseprite(path); + console.info(`making out of an aseprite for ${path}`); + anim = os.make_aseprite(io.slurpbytes(path)); + console.info(`raw anim is ${json.encode(anim)}`) if (!anim) return; + if (anim.texture) { + // it was a single image + anim.texture.load_gpu(); + game.texture.cache[path] = anim; + return anim; + } // load all into gpu for (var a in anim) for (let frame of anim[a].frames) @@ -357,7 +387,7 @@ game.texture = function (path) { } if (ext === 'gif') { - anim = os.make_gif(io.slurp(path)); + anim = os.make_gif(io.slurpbytes(path)); if (!anim) return; if (anim.frames.length === 1) { // in this case, it's just a single image diff --git a/scripts/render.js b/scripts/render.js index da5fdbb5..ec06ea48 100644 --- a/scripts/render.js +++ b/scripts/render.js @@ -1433,17 +1433,11 @@ try{ profile.report("imgui"); if (debug.show) imgui_fn(); profile.endreport("imgui"); - -// render.end_pass(); - -// render.commit(); -// profile.report_frame(profile.secs(profile.now()) - frame_t); - -// endframe(); } catch(e) { throw e; } finally { render.end_pass(); + profile.report_frame(profile.secs(profile.now()) - frame_t); render.commit(); endframe(); } @@ -1455,6 +1449,12 @@ function dmon_cb(e) { if (e.file.startsWith('.')) return; console.info(json.encode(e)) + if (e.file.endsWith('.js')) + actor.hotreload(e.file); + else if (Resources.is_image(e.file)) + game.tex_hotreload(e.file); + else if (e.file.endsWith('.cg')) // shader + render.hotreload(e.file); } prosperon.process = function process() { diff --git a/scripts/std.js b/scripts/std.js index dc6fb35b..49806087 100644 --- a/scripts/std.js +++ b/scripts/std.js @@ -235,7 +235,7 @@ Cmdline.register_order( else global.app = actor.spawn("nogame.js"); var icon = game.texture(project.icon); - if (icon) window.set_icon(game.texture(icon.texture)); + if (icon) window.set_icon(icon.texture); game.camera = world.spawn("camera2d"); }); }, diff --git a/source/jsffi.c b/source/jsffi.c index dcc33a42..bca219fb 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -2393,21 +2393,20 @@ JSC_CCALL(os_obj_size, JSC_CCALL(os_make_texture, size_t len; void *raw = JS_GetArrayBuffer(js, &len, argv[0]); - if (!raw) { - JS_ThrowReferenceError(js, "could not load texture with array buffer"); - return JS_EXCEPTION; - } - ret = texture2js(texture_fromdata(raw, len)); + if (!raw) return JS_ThrowReferenceError(js, "could not load texture with array buffer"); + + texture *tex = texture_fromdata(raw, len); + if (!tex) return JS_ThrowReferenceError(js, "unable to make texture from the given array buffer"); + + ret = texture2js(tex); JS_SetPropertyStr(js, ret, "path", JS_DupValue(js,argv[0])); ) JSC_CCALL(os_make_gif, size_t rawlen; void *raw = JS_GetArrayBuffer(js, &rawlen, argv[0]); - if (!raw) { - JS_ThrowReferenceError(js, "could not load gif from supplied array buffer"); - return JS_UNDEFINED; - } + if (!raw) return JS_ThrowReferenceError(js, "could not load gif from supplied array buffer"); + int n; texture *tex = calloc(1,sizeof(*tex)); int frames; @@ -2442,20 +2441,43 @@ JSC_CCALL(os_make_gif, ret = gif; ) +JSValue aseframe2js(JSContext *js, ase_frame_t aframe) +{ + JSValue frame = JS_NewObject(js); + texture *tex = calloc(1,sizeof(*tex)); + tex->width = aframe.ase->w; + tex->height = aframe.ase->h; + tex->data = malloc(tex->width*tex->height*4); + memcpy(tex->data, aframe.pixels, tex->width*tex->height*4); + js_setpropstr(frame, "texture", texture2js(tex)); + js_setpropstr(frame, "rect", rect2js((rect){.x=0,.y=0,.w=1,.h=1})); + js_setpropstr(frame, "time", number2js((float)aframe.duration_milliseconds/1000.0)); + return frame; +} + JSC_CCALL(os_make_aseprite, size_t rawlen; void *raw = JS_GetArrayBuffer(js,&rawlen,argv[0]); - if (!raw) return JS_UNDEFINED; + if (!raw) return JS_ThrowReferenceError(js, "could not load aseprite from supplied array buffer"); ase_t *ase = cute_aseprite_load_from_memory(raw, rawlen, NULL); - JSValue obj = JS_NewObject(js); - int w = ase->w; int h = ase->h; int pixels = w*h; + if (ase->tag_count == 0) { + // we're dealing with a single frame image, or single animation + if (ase->frame_count == 1) { + JSValue obj = aseframe2js(js,ase->frames[0]); + cute_aseprite_free(ase); + return obj; + } + } + + JSValue obj = JS_NewObject(js); + for (int t = 0; t < ase->tag_count; t++) { ase_tag_t tag = ase->tags[t]; JSValue anim = JS_NewObject(js); @@ -2475,17 +2497,7 @@ JSC_CCALL(os_make_aseprite, int _frame = 0; JSValue frames = JS_NewArray(js); for (int f = tag.from_frame; f <= tag.to_frame; f++) { - ase_frame_t aframe = ase->frames[f]; - JSValue frame = JS_NewObject(js); - - texture *tex = calloc(1,sizeof(*tex)); - tex->width = w; - tex->height = h; - tex->data = malloc(w*h*4); - memcpy(tex->data, aframe.pixels, w*h*4); - js_setpropstr(frame, "texture", texture2js(tex)); - js_setpropstr(frame, "rect", rect2js((rect){.x=0,.y=0,.w=1,.h=1})); - js_setpropstr(frame, "time", number2js((float)aframe.duration_milliseconds/1000.0)); + JSValue frame = aseframe2js(js,ase->frames[f]); js_setprop_num(frames, _frame, frame); _frame++; } @@ -2498,13 +2510,6 @@ JSC_CCALL(os_make_aseprite, cute_aseprite_free(ase); ) -JSC_SCALL(os_texture_swap, - texture *old = js2texture(argv[1]); - texture *tex = texture_from_file(str); - JS_SetOpaque(argv[1], tex); - texture_free(old); -) - JSC_CCALL(os_make_tex_data, ret = texture2js(texture_empty(js2number(argv[0]), js2number(argv[1]))) ) @@ -2788,7 +2793,6 @@ static const JSCFunctionListEntry js_os_funcs[] = { MIST_FUNC_DEF(os, make_texture, 1), MIST_FUNC_DEF(os, make_gif, 1), MIST_FUNC_DEF(os, make_aseprite, 1), - MIST_FUNC_DEF(os, texture_swap, 2), MIST_FUNC_DEF(os, make_tex_data, 3), MIST_FUNC_DEF(os, make_font, 2), MIST_FUNC_DEF(os, make_transform, 0), diff --git a/source/script.c b/source/script.c index 44993fe5..08b11ebc 100644 --- a/source/script.c +++ b/source/script.c @@ -69,7 +69,19 @@ static uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filen void script_startup() { rt = JS_NewRuntime(); - js = JS_NewContext(rt); + js = JS_NewContextRaw(rt); + JS_AddIntrinsicBaseObjects(js); + JS_AddIntrinsicEval(js); + JS_AddIntrinsicRegExp(js); + JS_AddIntrinsicJSON(js); + JS_AddIntrinsicMapSet(js); + JS_AddIntrinsicTypedArrays(js); + JS_AddIntrinsicPromise(js); + JS_AddIntrinsicProxy(js); + JS_AddIntrinsicBigInt(js); + JS_AddIntrinsicBigFloat(js); + JS_AddIntrinsicBigDecimal(js); + JS_AddIntrinsicOperators(js); ffi_load();