diff --git a/scripts/debug.js b/scripts/debug.js index 543d0567..75bada3a 100644 --- a/scripts/debug.js +++ b/scripts/debug.js @@ -1,5 +1,5 @@ var render = use('render') -var debug = os.use('debug') +var debug = this var util = use('util') debug.build = function (fn) { diff --git a/scripts/emitter.js b/scripts/emitter.js index 3aa30f1c..1d7b16c0 100644 --- a/scripts/emitter.js +++ b/scripts/emitter.js @@ -1,4 +1,5 @@ var Color = use('color') +var os = use('os') var ex = {} @@ -112,4 +113,5 @@ this.dead = [] this.transform = this.overling.transform + $.emitters.add(this) diff --git a/scripts/engine.js b/scripts/engine.js index f08fbf3f..ee4dcae6 100644 --- a/scripts/engine.js +++ b/scripts/engine.js @@ -1,162 +1,11 @@ globalThis.prosperon = {} -var io = os.use('io') - -os.mem_limit.doc = "Set the memory limit of the runtime in bytes."; -os.gc_threshold.doc = "Set the threshold before a GC pass is triggered in bytes. This is set to malloc_size + malloc_size>>1 after a GC pass."; -os.max_stacksize.doc = "Set the max stack size in bytes."; - -globalThis.Resources = {}; +var os = use_embed('os') prosperon.SIGINT = function() { os.exit(); } -Resources.rm_fn = function rm_fn(fn, text) { - var reg = new RegExp(fn.source + "\\s*\\("); - var match; - while ((match = text.match(reg))) { - var last = match.index + match[0].length; - var par = 1; - while (par !== 0) { - if (text[last] === "(") par++; - if (text[last] === ")") par--; - last++; - } - text = text.rm(match.index, last); - } - - return 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); - - if (!path) return str; - - var stem = path.dir(); - while (stem) { - var tr = stem + "/" + str; - if (io.exists(tr)) return tr; - stem = stem.updir(); - } - 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); - if (!script) return; - var regexp = /"[^"\s]*?\.[^"\s]+?"/g; - - var stem = path.dir(); - -// if (!console.enabled) script = Resources.rm_fn(/console\.(spam|info|warn|error)/, script); -/* if (!profile.enabled) script = Resources.rm_fn(/profile\.(cache|frame|endcache|endframe)/, script); - - if (!debug.enabled) { - script = Resources.rm_fn(/assert/, script); - script = Resources.rm_fn(/debug\.(build|fn_break)/, script); - } -*/ - script = script.replace(regexp, function (str) { - var newstr = Resources.replpath(os.trimchr(str,'"'), path); - return `"${newstr}"`; - }); - - return script; -} - -Resources.is_sound = function (path) { - var ext = path.ext(); - return Resources.sounds.any(x => x === ext); -}; - -Resources.is_animation = function (path) { - if (path.ext() === "gif" && Resources.gif.frames(path) > 1) return true; - if (path.ext() === "ase") return true; - - return false; -}; - -Resources.is_path = function (str) { - return !/[\\\/:*?"<>|]/.test(str); -}; - -globalThis.json = {}; -json.encode = function json_encode(value, replacer, space = 1) { - return JSON.stringify(value, replacer, space); -}; - -json.decode = function json_decode(text, reviver) { - if (!text) return undefined; - return JSON.parse(text, reviver); -}; - -json.readout = function (obj) { - var j = {}; - for (var k in obj) - if (typeof obj[k] === "function") j[k] = "function " + obj[k].toString(); - else j[k] = obj[k]; - - return json.encode(j); -}; - -json.doc = { - doc: "json implementation.", - encode: "Encode a value to json.", - decode: "Decode a json string to a value.", - readout: "Encode an object fully, including function definitions.", -}; - -Resources.scripts = ["jsoc", "jsc", "jso", "js"]; -Resources.images = ["qoi", "png", "gif", "jpg", "jpeg", "ase", "aseprite"]; -Resources.sounds = ["wav", "flac", "mp3", "qoa"]; -Resources.fonts = ["ttf"]; -Resources.is_image = function (path) { - var ext = path.ext(); - return Resources.images.some(x => x === ext); -}; - -Resources.shaders = ["hlsl", "glsl", "cg"] -Resources.is_shader = function(path) { - var ext = path.ext(); - return Resources.shaders.some(x => x === ext) -} - -var res_cache = {}; - -// ext is a list of extensions to search -function find_ext(file, ext) { - if (!file) return; - - var file_ext = file.ext(); - var has_ext = file_ext.length > 0; - - for (var e of ext) { - var nf = `${file}.${e}`; - if (io.exists(nf)) return nf; - } - - var glob_pat = has_ext ? `**/${file}` : `**/${file}.*`; - - var all_files = io.glob(glob_pat); - var find = undefined; - for (var e of ext) { - var finds = all_files.filter(x => x.ext() === e); - if (finds.length > 1) { - console.warn(`Found conflicting files when searching for '${file}': ${json.encode(finds)}. Returning the topmost one.`); - finds.sort((a,b) => a.length-b.length); - return finds[0]; - } - if (finds.length === 1) return finds[0]; - } - - return find; -} +var use_cache = {} Object.defineProperty(Function.prototype, "hashify", { value: function () { @@ -172,74 +21,72 @@ Object.defineProperty(Function.prototype, "hashify", { }, }); -Resources.find_image = function (file, root = "") { - return find_ext(file, Resources.images, root); +var io = use_embed('io') +io.mount(io.basedir() + "core/scripts/") +io.mount(io.basedir() + "core/") + +var canonical = io.realdir('resources.js') + 'resources.js' +var content = io.slurp('resources.js') +var resources = os.eval('resources.js', `(function setup_resources(){${content}})`).call({}) +console.print(resources.canonical('resources.js')) +use_cache[resources.canonical('resources.js')] = resources + +// path is the path of a module or script to resolve +var script_fn = function script_fn(path) { + var parsed = {} + var file = resources.find_script(path); + if (!file) { + // attempt to bare load + parsed.module_ret = bare_load(path); + + if (!parsed.module_ret) throw new Error(`Module ${path} could not be created`) + return parsed + } + var content = io.slurp(file) + var parsed = parse_file(content); + var module_name = file.name(); + + parsed.module_ret = bare_load(path) + parsed.module_ret ??= {} + + if (parsed.module) { + var mod_script = `(function setup_${module_name}_module(){ var self = this; var $ = this; var exports = {}; var module = {exports: exports}; var define = undefined; ${parsed.module}})`; + var module_fn = os.eval(file, mod_script); + parsed.module_ret = module_fn.call(parsed.module_ret); + if (parsed.module_ret === undefined || parsed.module_ret === null) + throw new Error(`Module ${module_name} must return a value`); + parsed.module_fn = module_fn; + } + + if (parsed.program) { + var prog_script = `(function use_${module_name}() { var self = this; var $ = this.__proto__; ${parsed.program}})`; + parsed.prog_fn = os.eval(file, prog_script); + } + + return parsed; }.hashify(); -Resources.find_sound = function (file, root = "") { - return find_ext(file, Resources.sounds, root); -}.hashify(); - -Resources.find_script = function (file, root = "") { - return find_ext(file, Resources.scripts, root); -}.hashify(); - -Resources.find_font = function(file, root = "") { - return find_ext(file, Resources.fonts, root); -}.hashify(); - -var tmpslurp = io.slurp; -io.slurp = function slurp(path) -{ - var findpath = Resources.replpath(path); - var ret = tmpslurp(findpath, true); //|| core_db.slurp(findpath, true); - return ret; +function bare_load(file) { + try { + return use_embed(file) + } catch (e) { } + try { + return use_dyn(file + so_ext) + } catch(e) { } + return undefined } -io.slurpbytes = function(path) -{ - path = Resources.replpath(path); - var ret = tmpslurp(path);// || core_db.slurp(path); - if (!ret) throw new Error(`Could not find file ${path} anywhere`); - return ret; -} +var res_cache = {}; -var ignore = io.slurp('.prosperonignore').split('\n'); -var allpaths; -io.glob = function glob(pat) { - if (!allpaths) - allpaths = io.globfs(ignore); - - return allpaths.filter(str => io.match(pat,str)).sort(); -} - -io.invalidate = function() -{ - allpaths = undefined; -} - - -console.transcript = ""; -console.say = function (msg) { - console.print(msg); - if (debug.termout) console.term_print(msg); - console.transcript += msg; -}; - -console.rec = function(category, priority, line, file, msg) +function console_rec(category, priority, line, file, msg) { return `${file}:${line}: [${category} ${priority}]: ${msg}` + "\n"; } - - var logfile = io.open('.prosperon/log.txt') -//logfile.buffer(1024*1024) // 1MB buffer - -console.stdout_lvl = 0; function pprint(msg, lvl = 0) { - if (lvl < console.stdout_lvl && !logfile) return; + if (!logfile) return; if (typeof msg === "object") msg = JSON.stringify(msg, null, 2); @@ -255,10 +102,9 @@ function pprint(msg, lvl = 0) { m = md ? md[1] : 0; if (m) line = m; } - var fmt = console.rec("script", lvl, line,file, msg); + var fmt = console_rec("script", lvl, line,file, msg); - if (lvl >= console.stdout_lvl) - console.print(fmt) + console.print(fmt) if (logfile) logfile.write(fmt) @@ -284,8 +130,9 @@ console.log = function(msg) pprint(msg, 2) } +console.log(io.searchpath()) + console.error = function(e) { - console.print(e.message) if (!e) e = new Error(); @@ -294,7 +141,7 @@ ${e.stack}`, 4) }; console.panic = function (e) { - console.pprint(e , 5) + pprint(e , 5) os.quit(); }; @@ -336,7 +183,7 @@ prosperon.SIGSEGV = function() function add_timer(obj, fn, seconds) { - var timers = obj.timers; + var timers = obj[TIMERS] var stop = function () { if (!timer) return @@ -366,6 +213,14 @@ function add_timer(obj, fn, seconds) return stop; } +var DEAD = Symbol() +var GARBAGE = Symbol() +var FILE = Symbol() +var TIMERS = Symbol() +var REGGIES = Symbol() +var UNDERLINGS = Symbol() +var OVERLING = Symbol() + var actor = {}; var so_ext; @@ -378,101 +233,18 @@ switch(os.sys()) { break; } -var use_cache = {} - -function load_mod(file) -{ - try { - var par = script_fn(file) - if (par?.module_ret) { - use_cache[file] = par.module_ - return par.module_ret - } - } catch(e) { - console.error(e) - } - - try { - return os.use('./lib' + file + so_ext) - } catch(e) { console.error(e) } - - return os.use(file) -} var use = function use(file) { + if (use_cache[file]) return use_cache[file]; - use_cache[file] = load_mod(file) + var mod = script_fn(file) + + use_cache[file] = mod.module_ret return use_cache[file] } -use.hotreload = function(file) -{ - console.log(`hot reloading ${file}`) - var oldval = use_cache[file] - var newval = load_mod(file) - if (!oldval) { - use_cache[file] = newval; - return newval; - } - - if (typeof oldval !== 'object' || typeof newval !== 'object' || !oldval || !newval) { - use_cache[file] = newval; - return newval; - } - - use_patch(oldval, newval); - return oldval; -} - -function use_patch(target, source) -{ - // First remove properties that aren’t in source at all - for (let key of Object.keys(target)) { - if (!(key in source)) { - delete target[key]; - } - } - - // Then shallow-copy the source’s own properties - for (let key of Object.keys(source)) { - target[key] = source[key]; - } - - // Update the prototype if needed so that new or changed methods come along - let oldProto = Object.getPrototypeOf(target); - let newProto = Object.getPrototypeOf(source); - if (oldProto !== newProto) { - Object.setPrototypeOf(target, newProto); - } -} - -var script_fn = function script_fn(path) { - var file = Resources.find_script(path) - if (!file) throw new Error(`File ${path} could not be found`) - var content = Resources.replstrs(file); - var parsed = parse_file(content) - var module_name = file.name() - - if (parsed.module) { - var mod_script = `(function setup_${module_name}_module(){ var self = this; var $ = this; var exports = {}; var module = {exports:exports}; var define = undefined; ${parsed.module}})`; - var module_fn = os.eval(file, mod_script) - var module_ret = module_fn.call(); - if (module_ret === undefined || module_ret === null) - throw new Error(`Module ${module_name} must return a value`); - - parsed.module_fn = module_fn; - parsed.module_ret = module_ret; - } else - parsed.module_ret = {} - - if (parsed.program) { - var prog_script = `(function use_${module_name}() { var self = this; var $ = this.__proto__; ${parsed.program}})`; - parsed.prog_fn = os.eval(file, prog_script); - } - - return parsed; -}.hashify() +globalThis.json = use('json') function parse_file(content) { if (!content) return {} @@ -494,68 +266,6 @@ function parse_file(content) { } } -actor.__stats = function () { - var total = 0; - var stats = {}; - search.all_objects(obj => { - if (!actor_spawns[obj._file]) return; - stats[obj._file] ??= 0; - stats[obj._file]++; - total++; - }); -/* for (var i in actor_spawns) { - stats[i] = actor_spawns[i].length; - total += stats[i]; - }*/ -// stats.total = total; - return stats; -}; - -actor.hotreload = function hotreload(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[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); - Register.check_registers(a); - } -*/ -}; - -////////// -/// EVENT -///////// - -var Event = { - events: {}, - - observe(name, obj, fn) { - this.events[name] ??= []; - this.events[name].push([obj, fn]); - }, - - unobserve(name, obj) { - this.events[name] = this.events[name].filter(x => x[0] !== obj); - }, - - rm_obj(obj) { - Object.keys(this.events).forEach(name => Event.unobserve(name, obj)); - }, - - notify(name, ...args) { - if (!this.events[name]) return; - this.events[name].forEach(function (x) { - x[1].call(x[0], ...args); - }); - }, -}; - ////////////////// ///////REGISTRANT ///////////////// @@ -635,15 +345,15 @@ Register.register_obj = function register_obj(obj, reg) var fn = obj[reg].bind(obj); fn.layer = obj[reg].layer; var name = obj.ur ? obj.ur.name : obj.toString(); - obj.timers.push(Register.registries[reg].register(fn, name)); + obj[TIMERS].push(Register.registries[reg].register(fn, name)); if (!obj[reg].name) Object.defineProperty(obj[reg], 'name', {value:`${obj._file}_${reg}`}); } Register.check_registers = function check_registers(obj) { - if (obj.__reggies) { - if (obj.__reggies.length == 0) return; + if (obj[REGGIES]) { + if (obj[REGGIES].length == 0) return; // fast path - for (var reg of obj.__reggies) + for (var reg of obj[REGGIES]) Register.register_obj(obj,reg); return; @@ -653,12 +363,6 @@ Register.check_registers = function check_registers(obj) { if (typeof obj[reg] === "function") Register.register_obj(obj,reg); } - -/* for (var k in obj) { - if (!k.startsWith("on_")) continue; - var signal = k.fromfirst("on_"); - Event.observe(signal, obj, obj[k]); - }*/ }; Register.add_cb("appupdate"); @@ -676,9 +380,9 @@ function cant_kill() throw Error("Can't kill an object in its spawning code. Move the kill command to awake."); } -actor.toString = function() { return this.__file } +actor.toString = function() { return this[FILE] } actor.spawn = function spawn(script, config, callback) { - if (this.__dead__) throw Error("Attempting to spawn on a dead actor") + if (this[DEAD]) throw Error("Attempting to spawn on a dead actor") var prog if (!script) { prog = {} @@ -692,11 +396,43 @@ actor.spawn = function spawn(script, config, callback) { var underling; prog.module_ret.__proto__ = actor; underling = Object.create(prog.module_ret); - underling.overling = this; - underling.__file = script + underling[OVERLING] = this; + underling[FILE] = script + underling[TIMERS] = [] + underling[UNDERLINGS] = new Set() - underling.timers = [] - underling.underlings = new Set() + Object.defineProperty(underling, 'overling', { + get() { return this[OVERLING] }, + enumerable:true, + configurable:false + }) + + Object.defineProperty(underling, 'underlings', { + get() { return new Set(this[UNDERLINGS]) }, + enumerable:true, + configurable:false + }) + + Object.defineProperty(underling, 'spawn', { + value: actor.spawn, + writable:false, + enumerable:true, + configurable:false + }) + + Object.defineProperty(underling, 'kill', { + value: actor.kill, + writable:false, + enumerable:true, + configurable:false + }) + + Object.defineProperty(underling, 'delay', { + value: actor.delay, + writable:false, + enumerable:true, + configurable:false + }) if (callback) callback(underling, { message:"created" @@ -705,28 +441,23 @@ actor.spawn = function spawn(script, config, callback) { try{ prog.prog_fn.call(underling) } catch(e) {throw e} - if (underling.__dead__) + if (underling[DEAD]) return undefined if (typeof config === 'object') Object.assign(underling,config); - if (!underling.__reggies) - underling.__proto__.__reggies = Register.pull_registers(underling) + if (!underling[REGGIES]) + underling.__proto__[REGGIES] = Register.pull_registers(underling) Register.check_registers(underling); if (underling.awake) underling.awake(); - this.underlings.add(underling); + this[UNDERLINGS].add(underling); if (underling.tag) search.tag_add(underling.tag, underling) - Object.defineProperty(underling, 'garbage', { - configurable: false, - writable: false, - value: underling.garbage - }) - + underling[GARBAGE] = underling.garbage return underling; }; @@ -734,25 +465,24 @@ actor.spawn.doc = `Create a new actor, using this actor as the overling, initial actor.clear = function actor_clear() { - this.underlings.forEach(p => { + this[UNDERLINGS].forEach(p => { p.kill(); }) - this.underlings.clear() + this[UNDERLINGS].clear() } var input = use('input') actor.kill = function kill() { - if (this.__dead__) return; - this.__dead__ = true; - this.timers.slice().forEach(t => t()) // slice in case something is removed from timers while running - delete this.timers + if (this[DEAD]) return + this[DEAD] = true + this[TIMERS].slice().forEach(t => t()) // slice in case something is removed from timers while running + delete this[TIMERS] input.do_uncontrol(this); - Event.rm_obj(this); this.clear() - this.overling.underlings.delete(this) - delete this.underlings + this[OVERLING][UNDERLINGS].delete(this) + delete this[UNDERLINGS] if (typeof this.garbage === "function") this.garbage(); if (typeof this.then === "function") this.then(); @@ -765,21 +495,9 @@ actor.kill.doc = `Remove this actor and all its underlings from existence.`; actor.delay = function (fn, seconds) { add_timer(this, fn, seconds) } actor.delay.doc = `Call 'fn' after 'seconds' with 'this' set to the actor.`; -actor.interval = function interval(fn, seconds) { - var self = this; - var stop; - var usefn = function () { - fn(); - stop = self.delay(usefn, seconds); - }; - stop = self.delay(usefn, seconds); - - return stop; -}; - var search = use('search') -actor.underlings = new Set() +actor[UNDERLINGS] = new Set() globalThis.mixin("color"); globalThis.mixin("std") diff --git a/scripts/geometry.js b/scripts/geometry.js index ded41519..838eef62 100644 --- a/scripts/geometry.js +++ b/scripts/geometry.js @@ -1,4 +1,4 @@ -var geometry = os.use('geometry') +var geometry = this var vector = use('vector') var shape = {}; diff --git a/scripts/graphics.js b/scripts/graphics.js index 4da0b970..f9582a3f 100644 --- a/scripts/graphics.js +++ b/scripts/graphics.js @@ -1,4 +1,7 @@ var graphics = {} +var os = use('os') +var io = use('io') +var res = use('resources') function calc_image_size(img) { @@ -97,7 +100,7 @@ graphics.texture = function texture(path) { throw new Error('need a string for graphics.texture') } var parts = path.split(':'); - var ipath = Resources.find_image(parts[0]); + var ipath = res.find_image(parts[0]); graphics.texture.cache[ipath] ??= create_image(ipath); return graphics.texture.cache[ipath]; @@ -134,7 +137,6 @@ graphics.tex_hotreload = function tex_hotreload(file) { var oldimg = graphics.texture.cache[file]; console.log(`new image:${json.encode(img)}`) console.log(`old image: ${json.encode(oldimg)}`) - merge_objects(oldimg,img, ['surface', 'texture', 'loop', 'time']); // graphics.texture.cache[file] = img; @@ -168,11 +170,13 @@ graphics.get_font = function get_font(path,size) path = parts[0]; size = Number(parts[1]); } - path = Resources.find_font(path); - var fontstr = `${path}.${size}`; + var fullpath = res.find_font(path); + if (!fullpath) throw new Error(`Cannot load font ${path}`) + + var fontstr = `${fullpath}.${size}`; if (fontcache[fontstr]) return fontcache[fontstr]; - var data = io.slurpbytes(path); + var data = io.slurpbytes(fullpath); fontcache[fontstr] = os.make_font(data,size); fontcache[fontstr].texture = prosperon.gpu.load_texture(fontcache[fontstr].surface); return fontcache[fontstr]; diff --git a/scripts/input.js b/scripts/input.js index 159c5568..5a73280b 100644 --- a/scripts/input.js +++ b/scripts/input.js @@ -1,4 +1,5 @@ -var input = os.use('input') +var input = this + var util = use('util') var downkeys = {}; diff --git a/scripts/io.js b/scripts/io.js deleted file mode 100644 index 3886b24f..00000000 --- a/scripts/io.js +++ /dev/null @@ -1,79 +0,0 @@ -/* - io path rules. Starts with, meaning: - "@": user path - "/": root game path - "" : relative game path -*/ - -var io = os.use('io') - -var tmpslurp = io.slurp; -io.slurp = function slurp(path) -{ - var findpath = Resources.replpath(path); - var ret = tmpslurp(findpath, true); //|| core_db.slurp(findpath, true); - return ret; -} - -io.slurpbytes = function(path) -{ - path = Resources.replpath(path); - var ret = tmpslurp(path);// || core_db.slurp(path); - if (!ret) throw new Error(`Could not find file ${path} anywhere`); - return ret; -} - -var ignore = io.slurp('.prosperonignore').split('\n'); -var allpaths; -var tmpglob = io.glob; -io.glob = function glob(pat) { - if (!allpaths) - allpaths = io.globfs(ignore); - - return allpaths.filter(str => game.glob(pat,str)).sort(); -} - -/*var ignore; -if (ignore = io.slurp('.prosperonignore')) { - ignore = ignore.split('\n'); - for (var ig of ignore) { - if (!ig) continue; - allpaths = allpaths.filter(x => !x.startsWith(ig)); - } -} -*/ - -io.invalidate = function() -{ - allpaths = undefined; -} - -io.mkpath = function (dir) { - if (!dir) return; - var mkstack = []; - while (!io.exists(dir)) { - mkstack.push(dir.fromlast("/")); - dir = dir.dir(); - } - for (var d of mkstack) { - dir = dir + "/" + d; - say(`making ${dir}`); - io.mkdir(dir); - } -}; - -io.doc = { - doc: "Functions for filesystem input/output commands.", - exists: "Returns true if a file exists.", - slurp: "Returns the contents of given file as a string.", - slurpbytes: "Return the contents of a file as a byte array.", - slurpwrite: "Write a given string to a given file.", - cp: "Copy file f1 to f2.", - mv: "Rename file f1 to f2.", - rm: "Remove file f.", - mkdir: "Make dir.", - ls: "List contents of the game directory.", - glob: "Glob files in game directory.", -}; - -return io diff --git a/scripts/json.js b/scripts/json.js new file mode 100644 index 00000000..f8b0a9dd --- /dev/null +++ b/scripts/json.js @@ -0,0 +1,13 @@ +var json = {} + +json.encode = function(val,space,replacer,whitelist) +{ + return JSON.stringify(val, replacer, space ? 1 : 0) +} + +json.decode = function(text,reviver) +{ + return JSON.parse(text,reviver) +} + +return json diff --git a/scripts/log.js b/scripts/log.js new file mode 100644 index 00000000..9f465f90 --- /dev/null +++ b/scripts/log.js @@ -0,0 +1,3 @@ +var log = {} + +return log diff --git a/scripts/profile.js b/scripts/profile.js index cbe9bed0..ef4427a7 100644 --- a/scripts/profile.js +++ b/scripts/profile.js @@ -1,4 +1,4 @@ -var profile = os.use('profile') +var profile = this var pmath = use('pmath') diff --git a/scripts/render.js b/scripts/render.js index 2610a25f..4396d353 100644 --- a/scripts/render.js +++ b/scripts/render.js @@ -4,6 +4,8 @@ var config = use('config.js') var gizmo = use('gizmos') var vector = use('vector') var search = use('search') +var io = use('io') +var os = use('os') var input = use('input') @@ -30,7 +32,6 @@ appy.inputs["M-f4"] = os.exit; input.player[0].control(appy); - prosperon.window = os.engine_start(config); var driver = "vulkan" diff --git a/scripts/resources.js b/scripts/resources.js new file mode 100644 index 00000000..66b86874 --- /dev/null +++ b/scripts/resources.js @@ -0,0 +1,58 @@ +var Resources = {} + +var so_ext; +switch(os.sys()) { + case 'Windows': + so_ext = '.dll'; + break; + default: + so_ext = '.so'; + break; +} + +Resources.scripts = ["jsoc", "jsc", "jso", "js"] +Resources.images = ["qoi", "png", "gif", "jpg", "jpeg", "ase", "aseprite"] +Resources.sounds = ["wav", "flac", "mp3", "qoa"] +Resources.fonts = ["ttf"] +Resources.lib = [so_ext] + +Resources.canonical = function(file) +{ + return io.realdir(file) + file +} + +// ext is a list of extensions to search +// returns the file to load +function find_ext(file, ext) { + if (!file) return; + + var file_ext = file.ext() + if (file_ext && ext.some(e => e === file_ext)) + if (io.exists(file)) return file + + for (var e of ext) { + var attempt = `${file}.${e}`; + if (io.exists(attempt)) return attempt + } + + return undefined +} + +Resources.find_image = function (file) { + return find_ext(file, Resources.images); +}.hashify(); + +Resources.find_sound = function (file) { + return find_ext(file, Resources.sounds); +}.hashify(); + +Resources.find_script = function (file) { + return find_ext(file, Resources.scripts); +}.hashify(); + +Resources.find_font = function(file) { + return find_ext(file, Resources.fonts); +}.hashify(); + + +return Resources diff --git a/scripts/sound.js b/scripts/sound.js index 64870ba7..d39cadf3 100644 --- a/scripts/sound.js +++ b/scripts/sound.js @@ -1,5 +1,6 @@ //var soloud = use('soloud') var tween = use('tween') +var io = use('io') soloud.init(); var audio = {}; diff --git a/scripts/sprite.js b/scripts/sprite.js index 5410dd39..d6770d62 100644 --- a/scripts/sprite.js +++ b/scripts/sprite.js @@ -204,6 +204,7 @@ return sprite; --- var Color = use('color') +var os = use('os') this.transform = os.make_transform(); if (this.overling.transform) diff --git a/scripts/std.js b/scripts/std.js index 74ccd451..77f590c6 100644 --- a/scripts/std.js +++ b/scripts/std.js @@ -1,3 +1,4 @@ +var profile = use('profile') var io = use('io') var dumpfolder = ".prosperon"; @@ -24,7 +25,7 @@ Cmdline.register_order( "edit", function () { if (!io.exists(projectfile)) { - say("No game to edit. Try making one with 'prosperon init'."); + console.print("No game to edit. Try making one with 'prosperon init'."); return; } }, @@ -36,7 +37,7 @@ Cmdline.register_order( "init", function () { if (io.exists(projectfile)) { - say("Already a game here."); + console.print("Already a game here."); return; } @@ -102,14 +103,14 @@ Cmdline.register_order( return; } else packname = str[0]; - say(`Packing into ${packname}`); + console.print(`Packing into ${packname}`); io.pack_start(packname); files = allfiles.filter(f => !f.startsWith(".git")); files = files.filter(f => !f.startsWith(".nova")); files = files.filter(f => !f.includes(".DS_Store")); files = files.filter(f => !f.startsWith(".gitignore")); - say(files); + console.print(files); for (var f of files) io.pack_add(f); io.pack_end(); }, @@ -122,11 +123,11 @@ Cmdline.register_order( function (argv) { var cdb = "game.zip"; if (!io.exists(cdb)) { - say(`No 'game.zip' present.`); + console.print(`No 'game.zip' present.`); return; } if (argv.length === 0) { - say(`cdb name: ${cdb}`); + console.print(`cdb name: ${cdb}`); } }, "CDB commands.", @@ -138,7 +139,7 @@ Cmdline.register_order( var sounds = Resources.sounds.filter(x => x !== "qoa"); for (var file of argv) { if (!sounds.includes(file.ext())) continue; - say(`converting ${file}`); + console.print(`converting ${file}`); io.save_qoa(file); } }, @@ -149,13 +150,13 @@ Cmdline.register_order( "about", function (argv) { if (!argv[0]) { - say("About your game"); - say(`Prosperon version ${prosperon.version}`); - say(`Total entities ${ur._list.length}`); + console.print("About your game"); + console.print(`Prosperon version ${prosperon.version}`); + console.print(`Total entities ${ur._list.length}`); } switch (argv[0]) { case "entities": - for (var i of ur._list) say(i); + for (var i of ur._list) console.print(i); break; } }, @@ -166,7 +167,7 @@ Cmdline.register_order( "ur", function (argv) { // game.loadurs(); - for (var i of ur._list.sort()) say(i); + for (var i of ur._list.sort()) console.print(i); }, "Get information about the ur types in your game.", ); @@ -177,21 +178,21 @@ Cmdline.register_order( if (argv.length > 2) return; var gg = json.decode(io.slurp(projectfile)); if (argv.length === 0) { - say(json.encode(gg, null, 1)); + console.print(json.encode(gg, null, 1)); return; } if (argv.length === 1) { var v = gg[argv[0]]; if (!v) { - say(`Value ${argv[0]} not found.`); + console.print(`Value ${argv[0]} not found.`); return; } - say(`${argv[0]}:${v}`); + console.print(`${argv[0]}:${v}`); } else { gg[argv[0]] = argv[1]; - say(`Set ${argv[0]}:${v}`); - say(json.encode(gg, null, 1)); + console.print(`Set ${argv[0]}:${v}`); + console.print(json.encode(gg, null, 1)); io.slurpwrite(projectfile, json.encode(gg)); } }, @@ -201,7 +202,7 @@ Cmdline.register_order( Cmdline.register_order( "unpack", function () { - say("Unpacking not implemented."); + console.print("Unpacking not implemented."); }, "Unpack this binary's contents into this folder for editing.", ); @@ -209,7 +210,7 @@ Cmdline.register_order( Cmdline.register_order( "build", function () { - say("Building not implemented."); + console.print("Building not implemented."); }, "Build static assets for this project.", ); @@ -219,7 +220,7 @@ Cmdline.register_order( function (argv) { for (var file of argv) { if (!io.exists(file)) { - say(`File ${file} does not exist.`); + console.print(`File ${file} does not exist.`); continue; } @@ -236,10 +237,10 @@ Cmdline.register_order( function (argv) { for (var file of argv) { if (!io.exists(file)) { - say(`File ${file} does not exist.`); + console.print(`File ${file} does not exist.`); continue; } - say(file.ext()); + console.print(file.ext()); var obj = nota.decode(io.slurp(file)); var nn = json.encode(obj); io.slurpwrite(file.strip_ext() + ".json", nn); @@ -260,7 +261,7 @@ Cmdline.register_order( var api = debug.api.print_doc(obj[0]); if (!api) return; - say(api); + console.print(api); }, "Print the API for an object as markdown. Give it a file to save the output to.", "OBJECT", @@ -270,8 +271,8 @@ Cmdline.register_order( "input", function (pawn) { use("editor.js"); - say(`## Input for ${pawn}`); - eval(`say(input.print_md_kbm(${pawn}));`); + console.print(`## Input for ${pawn}`); + eval(`console.print(input.print_md_kbm(${pawn}));`); }, "Print input documentation for a given object as markdown. Give it a file to save the output to", "OBJECT ?FILE?", @@ -280,13 +281,14 @@ Cmdline.register_order( Cmdline.register_order( "run", function (script) { + var s = os.now() script = script.join(" "); if (!script) { - say("Need something to run."); + console.print("Need something to run."); return; } - say(eval(script)); + console.print(eval(script)); }, "Run a given script. SCRIPT can be the script itself, or a file containing the script", "SCRIPT", @@ -298,8 +300,8 @@ Cmdline.print_order = function (fn) { if (typeof fn === "string") fn = Cmdline.orders[fn]; if (!fn) return; - say(`Usage: prosperon ${fn.usage}`); - say(fn.doc); + console.print(`Usage: prosperon ${fn.usage}`); + console.print(fn.doc); }; Cmdline.register_order( @@ -319,7 +321,7 @@ Cmdline.register_order( Cmdline.print_order("help"); - for (var cmd of Object.keys(Cmdline.orders).sort()) say(cmd); + for (var cmd of Object.keys(Cmdline.orders).sort()) console.print(cmd); Cmdline.orders.version(); }, @@ -330,7 +332,7 @@ Cmdline.register_order( Cmdline.register_order( "version", function () { - say(`Prosperon version ${prosperon.version} [${prosperon.revision}]`); + console.print(`Prosperon version ${prosperon.version} [${prosperon.revision}]`); }, "Display Prosperon info.", ); @@ -346,13 +348,12 @@ function cmd_args(cmdargs) { } Cmdline.orders[cmds[0]](cmds.slice(1)); - if (!game.startengine) os.exit(0); } Cmdline.register_order( "clean", function (argv) { - say("Cleaning not implemented."); + console.print("Cleaning not implemented."); }, "Clean up a given object file.", "JSON ...", diff --git a/scripts/time.js b/scripts/time.js index 9f687055..925deeef 100644 --- a/scripts/time.js +++ b/scripts/time.js @@ -1,4 +1,4 @@ -var time = os.use('time') +var time = this /* Time values are always expressed in terms of real earth-seconds */ Object.assign(time, { diff --git a/scripts/vector.js b/scripts/vector.js index d6b64ea9..531e787d 100644 --- a/scripts/vector.js +++ b/scripts/vector.js @@ -1,5 +1,5 @@ /* VECTORS */ -var vector = os.use('vector') +var vector = this vector.random = function () { var vec = [Math.random() - 0.5, Math.random() - 0.5]; diff --git a/source/jsffi.c b/source/jsffi.c index 6a832b6b..dc903d02 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -5581,6 +5581,22 @@ JSC_SCALL(io_stat, JS_SetPropertyStr(js,ret,"accesstime", number2js(js,stat.accesstime)); ) +JSC_SCALL(io_slurpbytes, + PHYSFS_File *f = PHYSFS_openRead(str); + if (!f) { + ret = JS_ThrowReferenceError(js,"physfs error when slurping %s: %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + goto END; + } + PHYSFS_Stat stat; + PHYSFS_stat(str,&stat); + void *data = malloc(stat.filesize); + PHYSFS_readBytes(f,data,stat.filesize); + PHYSFS_close(f); + ret = JS_NewArrayBufferCopy(js,data,stat.filesize); + + END: +) + JSC_SCALL(io_slurp, PHYSFS_File *f = PHYSFS_openRead(str); if (!f) { @@ -5592,10 +5608,7 @@ JSC_SCALL(io_slurp, void *data = malloc(stat.filesize); PHYSFS_readBytes(f,data,stat.filesize); PHYSFS_close(f); - if (JS_ToBool(js,argv[1])) - ret = JS_NewStringLen(js,data, stat.filesize); - else - ret = JS_NewArrayBufferCopy(js,data,stat.filesize); + ret = JS_NewStringLen(js,data, stat.filesize); END: ) @@ -5621,8 +5634,8 @@ JSC_SCALL(io_slurpwrite, END: ) -JSC_SCALL(io_mount, - if (!PHYSFS_mount(str,NULL,0)) ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); +JSC_SSCALL(io_mount, + if (!PHYSFS_mount(str,str2,0)) ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); ) JSC_SCALL(io_unmount, @@ -5710,13 +5723,27 @@ JSC_CCALL(io_globfs, ) JSC_CCALL(io_basedir, return JS_NewString(js,PHYSFS_getBaseDir())) +JSC_CCALL(io_userdir, return JS_NewString(js,PHYSFS_getUserDir())) JSC_SCALL(io_open, PHYSFS_File *f = PHYSFS_openWrite(str); if (!f) return JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - return PHYSFS_File2js(js,f); + ret = PHYSFS_File2js(js,f); +) + +JSC_SCALL(io_realdir, + const char *real = PHYSFS_getRealDir(str); + if (!real) return JS_UNDEFINED; + ret = JS_NewString(js,real); +) + +JSC_CCALL(io_searchpath, + ret = JS_NewArray(js); + char **paths = PHYSFS_getSearchPath(); + for (int i = 0; paths[i] != NULL; i++) + JS_SetPropertyUint32(js,ret,i,JS_NewString(js,paths[i])); ) static const JSCFunctionListEntry js_io_funcs[] = { @@ -5726,14 +5753,18 @@ static const JSCFunctionListEntry js_io_funcs[] = { MIST_FUNC_DEF(io, globfs, 1), MIST_FUNC_DEF(io, match, 2), MIST_FUNC_DEF(io, exists, 1), - MIST_FUNC_DEF(io, mount, 1), + MIST_FUNC_DEF(io, mount, 2), MIST_FUNC_DEF(io,unmount,1), - MIST_FUNC_DEF(io,slurp,2), + MIST_FUNC_DEF(io,slurp,1), + MIST_FUNC_DEF(io,slurpbytes,1), MIST_FUNC_DEF(io,slurpwrite,2), MIST_FUNC_DEF(io,writepath, 1), MIST_FUNC_DEF(io,basedir, 0), + MIST_FUNC_DEF(io, userdir, 0), + MIST_FUNC_DEF(io, realdir, 1), MIST_FUNC_DEF(io, gamemode, 2), MIST_FUNC_DEF(io, open, 2), + MIST_FUNC_DEF(io, searchpath, 0), }; JSC_CCALL(file_close, @@ -6167,6 +6198,7 @@ JSC_CCALL(os_exit, exit(js2number(js,argv[0]));) JSC_CCALL(os_gc, return JS_RunGC(JS_GetRuntime(js), js) ) +JSC_CCALL(os_now, return number2js(js, (double)SDL_GetTicksNS()/1000000000.0)) JSC_CCALL(os_mem_limit, JS_SetMemoryLimit(JS_GetRuntime(js), js2number(js,argv[0]))) JSC_CCALL(os_gc_threshold, JS_SetGCThreshold(JS_GetRuntime(js), js2number(js,argv[0]))) JSC_CCALL(os_max_stacksize, JS_SetMaxStackSize(JS_GetRuntime(js), js2number(js,argv[0]))) @@ -6308,28 +6340,6 @@ void script_report_gc_time(double t, double startmem, double mem) gc_startmem = startmem; } -static FILE *cycles; - -JSC_CCALL(os_check_cycles, - if (ftell(cycles) == 0) return JS_UNDEFINED; - cycles = tmpfile(); - quickjs_set_cycleout(cycles); - ret = tmp2js(js,cycles); -) - -JSC_CCALL(os_check_gc, - if (gc_t == 0) return JS_UNDEFINED; - - JSValue gc = JS_NewObject(js); - JS_SetPropertyStr(js, gc, "time", number2js(js,gc_t)); - JS_SetPropertyStr(js, gc, "mem", number2js(js,gc_mem)); - JS_SetPropertyStr(js, gc, "startmem", number2js(js,gc_startmem)); - gc_t = 0; - gc_mem = 0; - gc_startmem = 0; - return gc; -) - JSC_SSCALL(os_eval, return ret = script_eval(js,str, str2)) JSC_CCALL(os_make_timer, return timer2js(js,timer_make(js,argv[0]))) JSC_CCALL(os_update_timers, timer_update(js, js2number(js,argv[0]))) @@ -7182,45 +7192,6 @@ typedef struct { size_t fn_count; } ModuleEntry; -#define MISTLINE(NAME) { #NAME, js_##NAME##_funcs, countof(js_##NAME##_funcs) } - -static ModuleEntry module_registry[] = { - MISTLINE(io), - MISTLINE(input), - MISTLINE(time), - MISTLINE(profile), - MISTLINE(debug), - MISTLINE(vector), - MISTLINE(spline), - MISTLINE(performance), - MISTLINE(geometry), - MISTLINE(camera), -}; - -JSC_SCALL(os_use, - SDL_SharedObject *ptr; - if (access(str, F_OK) != 0 || !(ptr = SDL_LoadObject(str))) { - for (int i = 0; i < sizeof(module_registry)/sizeof(module_registry[0]); i++) { - if (strcmp(str,module_registry[i].name) == 0) { - JSValue mod = JS_NewObject(js); - JS_SetPropertyFunctionList(js,mod,module_registry[i].fn, module_registry[i].fn_count); - return mod; - } - } - return JS_ThrowReferenceError(js, "%s\nAnd could not find a static module.", SDL_GetError()); - } - - JSValue (*js_use)(JSContext*); - js_use = (JSValue (*)(JSContext*))SDL_LoadFunction(ptr, "use"); - - if (!js_use) - ret = JS_ThrowReferenceError(js, "Shared library %s has no use function", str); - else - ret = js_use(js); - - SDL_UnloadObject(ptr); -) - JSC_SSCALL(os_trimchr, int len; JS_GETPROP(js,len,argv[0],length,number) @@ -7254,6 +7225,7 @@ static const JSCFunctionListEntry js_os_funcs[] = { MIST_FUNC_DEF(os, system, 1), MIST_FUNC_DEF(os, exit, 1), MIST_FUNC_DEF(os, gc, 0), + MIST_FUNC_DEF(os, now, 0), MIST_FUNC_DEF(os, guid, 0), MIST_FUNC_DEF(os, openurl, 1), MIST_FUNC_DEF(os, push_event, 1), @@ -7291,8 +7263,6 @@ static const JSCFunctionListEntry js_os_funcs[] = { MIST_FUNC_DEF(os, dump_shapes, 0), MIST_FUNC_DEF(os, dump_atoms,0), MIST_FUNC_DEF(os, calc_mem, 1), - MIST_FUNC_DEF(os, check_gc, 0), - MIST_FUNC_DEF(os, check_cycles, 0), MIST_FUNC_DEF(os, memstate, 0), MIST_FUNC_DEF(os, value_id, 1), MIST_FUNC_DEF(os, gltf_buffer, 3), @@ -7309,10 +7279,57 @@ static const JSCFunctionListEntry js_os_funcs[] = { MIST_FUNC_DEF(os, cull_sprites, 2), MIST_FUNC_DEF(os, rects_to_sprites,2), MIST_FUNC_DEF(os, on, 2), - MIST_FUNC_DEF(os, use, 1), MIST_FUNC_DEF(os, trimchr, 2), + }; +#define MISTLINE(NAME) { #NAME, js_##NAME##_funcs, countof(js_##NAME##_funcs) } + +static ModuleEntry module_registry[] = { + MISTLINE(io), + MISTLINE(os), + MISTLINE(input), + MISTLINE(time), + MISTLINE(profile), + MISTLINE(debug), + MISTLINE(vector), + MISTLINE(spline), + MISTLINE(performance), + MISTLINE(geometry), + MISTLINE(camera), +}; + +JSC_SCALL(os_use_embed, + for (int i = 0; i < sizeof(module_registry)/sizeof(module_registry[0]); i++) { + if (strcmp(str,module_registry[i].name) == 0) { + JSValue mod = JS_NewObject(js); + JS_SetPropertyFunctionList(js,mod,module_registry[i].fn, module_registry[i].fn_count); + return mod; + } + } + return JS_ThrowReferenceError(js,"Library %s could not be found embedded"); +) + +JSC_SCALL(os_use_dyn, + SDL_SharedObject *ptr = SDL_LoadObject(str); + if (!ptr) + return JS_ThrowReferenceError(js, "Shared library %s could not be loaded", SDL_GetError()); + + JSValue (*js_use)(JSContext*); + js_use = (JSValue (*)(JSContext*))SDL_LoadFunction(ptr, "use"); + + if (!js_use) + ret = JS_ThrowReferenceError(js, "Shared library %s has no use function", str); + else + ret = js_use(js); + + SDL_UnloadObject(ptr); +) + + + + + JSC_CCALL(qtree_insert, qtree tree = js2qtree(js,self); JSValue v = argv[0]; @@ -7590,9 +7607,6 @@ static void exit_handler() } void ffi_load(JSContext *js) { - cycles = tmpfile(); - quickjs_set_cycleout(cycles); - JSValue globalThis = JS_GetGlobalObject(js); QJSCLASSPREP_FUNCS(qtree) @@ -7628,6 +7642,9 @@ void ffi_load(JSContext *js) { QJSCLASSPREP_FUNCS(datastream); QJSCLASSPREP_FUNCS(timer); + JS_SetPropertyStr(js, globalThis, "use_dyn", JS_NewCFunction(js,js_os_use_dyn,"use_dyn", 1)); + JS_SetPropertyStr(js, globalThis, "use_embed", JS_NewCFunction(js,js_os_use_embed,"use_embed", 1)); + QJSGLOBALCLASS(os); QJSGLOBALCLASS(console); diff --git a/source/qjs_macros.h b/source/qjs_macros.h index 77bac67d..b62a6a0c 100644 --- a/source/qjs_macros.h +++ b/source/qjs_macros.h @@ -19,8 +19,10 @@ ) #define JSC_SSCALL(NAME, ...) JSC_CCALL(NAME, \ - const char *str = JS_ToCString(js,argv[0]); \ - const char *str2 = JS_ToCString(js,argv[1]); \ + const char *str = NULL; \ + const char *str2 = NULL; \ + if (!JS_IsUndefined(argv[0])) str = JS_ToCString(js,argv[0]); \ + if (!JS_IsUndefined(argv[1])) str2 = JS_ToCString(js,argv[1]); \ __VA_ARGS__ ; \ JS_FreeCString(js,str2); \ JS_FreeCString(js,str); \ diff --git a/source/yugine.c b/source/yugine.c index f6afafde..bd4e69a2 100644 --- a/source/yugine.c +++ b/source/yugine.c @@ -2,6 +2,8 @@ #include "script.h" #include +#include + #include "physfs.h" int main(int argc, char **argv) { @@ -9,6 +11,7 @@ int main(int argc, char **argv) { char *base = PHYSFS_getBaseDir(); PHYSFS_setWriteDir(base); PHYSFS_mount(base,NULL,0); + PHYSFS_mount(base,"/",0); script_startup(); // runs engine.js int argsize = 0;