fix resource loading

This commit is contained in:
2025-01-25 17:15:25 -06:00
parent 612111067b
commit 0a69bb96ba
20 changed files with 361 additions and 615 deletions

View File

@@ -1,5 +1,5 @@
var render = use('render')
var debug = os.use('debug')
var debug = this
var util = use('util')
debug.build = function (fn) {

View File

@@ -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)

View File

@@ -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 arent in source at all
for (let key of Object.keys(target)) {
if (!(key in source)) {
delete target[key];
}
}
// Then shallow-copy the sources 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")

View File

@@ -1,4 +1,4 @@
var geometry = os.use('geometry')
var geometry = this
var vector = use('vector')
var shape = {};

View File

@@ -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];

View File

@@ -1,4 +1,5 @@
var input = os.use('input')
var input = this
var util = use('util')
var downkeys = {};

View File

@@ -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

13
scripts/json.js Normal file
View File

@@ -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

3
scripts/log.js Normal file
View File

@@ -0,0 +1,3 @@
var log = {}
return log

View File

@@ -1,4 +1,4 @@
var profile = os.use('profile')
var profile = this
var pmath = use('pmath')

View File

@@ -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"

58
scripts/resources.js Normal file
View File

@@ -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

View File

@@ -1,5 +1,6 @@
//var soloud = use('soloud')
var tween = use('tween')
var io = use('io')
soloud.init();
var audio = {};

View File

@@ -204,6 +204,7 @@ return sprite;
---
var Color = use('color')
var os = use('os')
this.transform = os.make_transform();
if (this.overling.transform)

View File

@@ -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 ...",

View File

@@ -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, {

View File

@@ -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];

View File

@@ -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);

View File

@@ -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); \

View File

@@ -2,6 +2,8 @@
#include "script.h"
#include <string.h>
#include <SDL3/SDL.h>
#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;