Reconfigure logging; add markdown output for input controls and important api

This commit is contained in:
John Alanbrook 2023-10-23 13:08:11 +00:00
parent 29698dbca5
commit 264a365a1a
17 changed files with 328 additions and 79 deletions

View file

@ -135,6 +135,7 @@ DISTDIR = ./dist
.DEFAULT_GOAL := all
all: $(BIN)/$(NAME)
cp $(BIN)/$(NAME) .
DESTDIR ?= ~/.bin
@ -149,7 +150,6 @@ install: $(BIN)/$(NAME)
$(BIN)/$(NAME): $(BIN)/libengine.a $(BIN)/libquickjs.a
@echo Linking $(NAME)
$(LD) $^ $(LDFLAGS) -L$(BIN) $(LDLIBS) -o $@
cp $(BIN)/$(NAME) .
@echo Finished build
$(DISTDIR)/$(DIST): $(BIN)/$(NAME)
@ -170,9 +170,32 @@ tools/libcdb.a:
make -C $(CDB) libcdb.a
mv $(CDB)/libcdb.a tools
DOCOS = Sound gameobject Game Window physics Profile Time Player Mouse IO timer Log ColorMap sprite
DOCHTML := $(addsuffix .api.html, $(DOCOS))
DOCMD := $(addsuffix .api.md, $(DOCOS))
api.md: $(DOCMD)
@(echo "# API"; cat $^) > $@
@rm $^
INPUT = editor component.sprite component.polygon2d component.edge2d component.circle2d
INPUTMD := $(addsuffix .input.md, $(INPUT))
input.md: $(INPUTMD)
@(echo "# Input"; cat $^) > $@
@rm $^
%.input.md: primum $(SCRIPTS)
@echo Printing controls for $*
@./primum -e $* > $@
%.api.md: primum $(SCRIPTS)
@echo Printing api for $*
@./primum -d $* > $@
$(BIN)/libquickjs.a:
make -C quickjs clean
make -C quickjs OPT=$(OPT) HOST_CC=$(CC) AR=$(AR) libquickjs.a libquickjs.lto.a CC=$(CC)
make -C quickjs DBG=$(DBG) OPT=$(OPT) HOST_CC=$(CC) AR=$(AR) libquickjs.a libquickjs.lto.a CC=$(CC)
@mkdir -p $(BIN)
cp -rf quickjs/libquickjs.* $(BIN)

View file

@ -80,6 +80,7 @@
/* dump object free */
#ifdef DBG
//#define DUMP_FREE
//#define DUMP_CLOSURE
/* dump the bytecode of the compiled functions: combination of bits
@ -105,7 +106,7 @@
//#define DUMP_MODULE_RESOLVE
//#define DUMP_PROMISE
//#define DUMP_READ_OBJECT
#endif
/* test the GC by forcing it before each object allocation */
//#define FORCE_GC_AT_MALLOC

View file

@ -99,6 +99,12 @@ Object.freeze(sprite);
var sprite = component.sprite;
sprite.doc = {
path: "Path to the texture.",
color: "Color to mix with the sprite.",
pos: "The offset position of the sprite, relative to its entity."
};
sprite.inputs = {};
sprite.inputs.kp9 = function() { this.pos = [0,0]; };
sprite.inputs.kp8 = function() { this.pos = [-0.5, 0]; };

View file

@ -146,6 +146,8 @@ var Profile = {
get fps() { return sys_cmd(8); },
};
Profile.cpu.doc = `Output the time it takes to do a given function n number of times. Provide 'q' as "ns", "us", or "ms" to output the time taken in the requested resolution.`;
/* These controls are available during editing, and during play of debug builds */
var DebugControls = {};
DebugControls.toString = function() { return "Debug"; };
@ -251,6 +253,15 @@ var Time = {
},
};
Time.doc = {};
Time.doc.timescale = "Get and set the timescale. 1 is normal time; 0.5 is half speed; etc.";
Time.doc.updateMS = "Set the ms per game update.";
Time.doc.physMS = "Set the ms per physics update.";
Time.doc.renderMS = "Set the ms per render update.";
Time.doc.time = "Seconds elapsed since the game started.";
Time.doc.pause = "Pause the game by setting the timescale to 0; remembers the current timescale on play.";
Time.doc.play = "Resume the game after using Time.pause.";
Player.players[0].control(DebugControls);
Register.gui.register(Debug.draw, Debug);
@ -265,3 +276,55 @@ console.clear = function()
cmd(146);
}
var API = {};
API.doc_entry = function(obj, key)
{
var d = obj.doc;
var doc = "";
var title = key;
var o = obj[key];
if (typeof o === 'undefined' && obj.impl && typeof obj.impl[key] !== 'undefined')
o = obj.impl[key];
var t = typeof o;
if (t === 'object' && Array.isArray(o)) t = 'array';
if (t === 'function') {
title = o.toString().tofirst(')') + ")";
if (o.doc) doc = o.doc;
t = "";
}
if (t === 'undefined') t = "";
if (t) t = "**" + t + "**";
if (!doc) {
if (d && d[key]) doc = d[key];
else return "";
}
return `### \`${title}\`
${t}
${doc}
`;
}
API.print_doc = function(name)
{
var obj = eval(name);
if (!obj.doc) {
Log.warn(`Object has no doc sidecar.`);
return;
}
var mdoc = "# " + name + " API #\n";
if (obj.doc?.doc) mdoc += obj.doc.doc + "\n";
else if (typeof obj.doc === 'string') mdoc += obj.doc + "\n";
for (var key in obj) {
if (key === 'doc') continue;
mdoc += API.doc_entry(obj, key);
}
return mdoc;
}

View file

@ -1159,6 +1159,7 @@ editor.inputs['M-u'].doc = "Make selected objects unique.";
editor.inputs['C-S-g'] = function() { editor.openpanel(groupsaveaspanel); };
editor.inputs['C-S-g'].doc = "Save selected objects as a new level.";
editor.inputs.g = editor.inputs.mm;/*
editor.inputs.g = function() {
if (editor.selectlist.length === 0) {
var o = editor.try_pick();
@ -1168,6 +1169,7 @@ editor.inputs.g = function() {
editor.grabselect = editor.selectlist.slice();
};
*/
editor.inputs.g.doc = "Move selected objects.";
editor.inputs.g.released = function() { editor.grabselect = []; Mouse.normal(); };
@ -1971,4 +1973,4 @@ Game.stop();
Game.editor_mode(true);
Debug.draw_phys(true);
editor.enter_editor();
//editor.enter_editor();

View file

@ -7,18 +7,6 @@ function load(file) {
load("scripts/base.js");
load("scripts/std.js");
function initialize()
{
if (!Game.edit) {
load("config.js");
load("game.js");
}
else {
load("scripts/editor.js");
load("editorconfig.js");
}
}
function run(file)
{
var modtime = cmd(119, file);
@ -231,6 +219,10 @@ ColorMap.sample = function(t, map)
return map[1];
}
ColorMap.doc = {
sample: "Sample a given colormap at the given percentage (0 to 1).",
};
Object.freeze(ColorMap);
function bb2wh(bb) {
@ -248,6 +240,7 @@ var timer = {
}
var t = Object.create(this);
assign_impl(t, this.impl);
t.callback = fn;
var guardfn = function() {
if (typeof t.callback === 'function')
@ -265,7 +258,7 @@ var timer = {
var t = this.make(fn, secs, obj, 0, app);
t.start();
},
impl: {
get remain() { return cmd(32, this.id); },
get on() { return cmd(33, this.id); },
get loop() { return cmd(34, this.id); },
@ -278,6 +271,30 @@ var timer = {
set time(x) { cmd(28, this.id, x); },
get time() { return cmd(29, this.id); },
get pct() { return this.remain / this.time; },
},
remain: 0,
on: false,
loop: false,
start(){},
stop(){},
kill(){},
pause(){},
time: 0,
pct: 0,
};
timer.doc = {
doc: "Quickly make timers to fire off events once or multiple times.",
oneshot: "Executes a given function after the given number of seconds.",
make: "Returns a timer that can be handled and reused.",
start: "Starts the timer.",
stop: "Stops the timer.",
loop: "Set to true for the timer to repeat when done.",
kill: "Destroys the timer.",
pct: "Get the percentange the timer is complete.",
time: "Set or get the amount of time this timer waits to execute. Does not reset the time, so if it is set to below the elapsed time it will execute immediately.",
remain: "The time remianing before the function is executed.",
};
var animation = {
@ -367,7 +384,11 @@ load("scripts/physics.js");
load("scripts/input.js");
load("scripts/sound.js");
function screen2world(screenpos) { return Game.camera.view2world(screenpos); }
function screen2world(screenpos) {
if (Game.camera)
return Game.camera.view2world(screenpos);
return screenpos;
}
function world2screen(worldpos) { return Game.camera.world2view(worldpos); }
var Register = {
@ -595,6 +616,17 @@ function find_com(objects)
};
var Game = {
init() {
if (!Game.edit) {
load("config.js");
load("game.js");
}
else {
load("scripts/editor.js");
load("editorconfig.js");
editor.enter_editor();
}
},
objects: [],
resolution: [1200,720],
name: "Untitled",
@ -715,11 +747,29 @@ var Game = {
},
};
Game.doc = {};
Game.doc.object = "Returns the entity belonging to a given id.";
Game.doc.quit = "Immediately quit the game.";
Game.doc.pause = "Pause game simulation.";
Game.doc.stop = "Stop game simulation. This does the same thing as 'pause', and if the game is a debug build, starts its editor.";
Game.doc.play = "Resume or start game simulation.";
Game.doc.editor_mode = "Set to true for the game to only update on input; otherwise the game updates every frame.";
Game.doc.dt = "Current frame dt.";
Window.doc = {};
Window.doc.width = "Width of the game window.";
Window.doc.height = "Height of the game window.";
Window.doc.dimensions = "Window width and height packaged in an array [width,height]";
Window.doc.name = "Name in the title bar of the window.";
Window.doc.boundingbox = "Boundingbox of the window, with top and right being its height and width.";
Register.update.register(Game.exec, Game);
load("scripts/entity.js");
var preprimum = Object.create(gameobject);
function world_start() {
globalThis.preprimum = Object.create(gameobject);
var preprimum = globalThis.preprimum;
preprimum.objects = {};
preprimum.worldpos = function() { return [0,0]; };
preprimum.worldangle = function() { return 0; };
@ -730,13 +780,16 @@ preprimum.angle = 0;
preprimum.remove_obj = function() {};
preprimum.instances = [];
preprimum.toString = function() { return "preprimum"; };
var World = preprimum.make(preprimum);
var Primum = World;
globalThis.World = preprimum.make(preprimum);
globalThis.Primum = World;
var Primum = globalThis.Primum;
Primum.level = undefined;
Primum.toString = function() { return "Primum"; };
Primum._ed.selectable = false;
Primum._ed.check_dirty = function() { };
World.reparent = function(parent) { Log.warn("Cannot reparent the Primum."); }
globalThis.World.reparent = function(parent) { Log.warn("Cannot reparent the Primum."); }
Game.view_camera(Primum.spawn(ur.camera2d));
}
/* Load configs */
function load_configs(file) {
@ -796,8 +849,6 @@ Game.view_camera = function(cam)
cam.zoom = cam.zoom;
}
Game.view_camera(Primum.spawn(ur.camera2d));
Window.name = "Primum Machinam (V0.1)";
Window.width = 1280;
Window.height = 720;

View file

@ -275,7 +275,7 @@ var gameobject = {
angularvelocity:0,
layer:0,
worldpos() { return [0,0]; },
save:true,
selectable:true,
ed_locked:false,
@ -466,9 +466,11 @@ var gameobject = {
}
};
if (this.objects)
if (this.objects) {
obj.make_objs(this.objects);
}
obj.level = undefined;
obj.reparent(level);
@ -479,6 +481,12 @@ var gameobject = {
gameobject.check_registers(obj);
if (Game.playing() && typeof obj.start === 'function') obj.start();
/* obj.objects.forEach(function(obj) {
if (Game.playing() && typeof obj.start === 'function') obj.start();
});
*/
return obj;
},
@ -523,6 +531,40 @@ var gameobject = {
gameobject.impl.spawn.doc = `Spawn an entity of type 'ur' on this entity. Returns the spawned entity.`;
gameobject.doc = {
doc: "All objects in the game created through spawning have these attributes.",
pos: "Position of the object, relative to its level.",
angle: "Rotation of this object, relative to its level.",
velocity: "Velocity of the object, relative to world.",
angularvelocity: "Angular velocity of the object, relative to the world.",
scale: "Scale of the object, relative to its level.",
flipx: "Set the object to be flipped on its x axis.",
flipy: "Set the object to be flipped on its y axis.",
elasticity: `When two objects collide, their elasticities are multiplied together. Their velocities are then multiplied by this value to find their resultant velocities.`,
friction: `When one object touches another, friction slows them down.`,
mass: `The higher the mass of the object, the less forces will affect it.`,
phys: `Set to 0, 1, or 2, representing static, kinematic, and dynamic.`,
worldpos: `Function returns the world position of the object.`,
set_worldpos: `Function to set the position of the object in world coordinates.`,
worldangle: `Function to get the angle of the entity in the world.`,
rotate: `Function to rotate this object by x degrees.`,
pulse: `Apply an impulse to this body in world coordinates. Impulse is a short force.`,
shove: `Apply a force to this body in world coordinates. Should be used over many frames.`,
in_air: `Return true if the object is in the air.`,
on_ground: `Return true if the object is on the ground.`,
spawn: `Create an instance of a supplied ur-type on this object. Optionally provide a data object to modify the created entity.`,
hide: `Make this object invisible.`,
show: `Make this object visible.`,
width: `The total width of the object and all its components.`,
height: `The total height of the object.`,
move: `Move this object the given amount.`,
boundingbox: `The boundingbox of the object.`,
dup: `Make an exact copy of this object.`,
transform: `Return an object representing the transform state of this object.`,
kill: `Remove this object from the world.`,
level: "The entity this entity belongs to.",
};
/* Default objects */
var prototypes = {};
prototypes.ur_ext = ".jso";

View file

@ -16,15 +16,17 @@ var Mouse = {
cmd(46, 1);
},
hidden() {
cmd(46, 1);
},
normal() {
cmd(46, 0);
},
};
Mouse.doc = {};
Mouse.doc.pos = "The screen position of the mouse.";
Mouse.doc.worldpos = "The position in the game world of the mouse.";
Mouse.disabled.doc = "Set the mouse to hidden. This locks it to the game and hides it, but still provides movement and click events.";
Mouse.normal.doc = "Set the mouse to show again after hiding.";
var Keys = {
shift() {
return cmd(50, 340);// || cmd(50, 344);
@ -83,7 +85,6 @@ Input.has_bind = function(pawn, bind) {
return (typeof pawn.inputs?.[bind] === 'function');
};
var Action = {
add_new(name) {
var action = Object.create(Action);
@ -188,15 +189,27 @@ var Player = {
var n = Object.create(this);
n.pawns = [];
n.gamepads = [];
n.control = function(pawn) {
n.pawns.push_unique(pawn);
};
n.uncontrol = function(pawn) { n.pawns = n.pawns.filter(x => x !== pawn); };
this.players.push(n);
return n;
},
pawns: [],
control(pawn) {
this.pawns.push_unique(pawn);
},
uncontrol(pawn) {
this.pawns = this.pawns.filter(x => x !== pawn);
},
};
for (var i = 0; i < 4; i++) {
Player.create();
}
Player.control.doc = "Control a provided object, if the object has an 'inputs' object.";
Player.uncontrol.doc = "Uncontrol a previously controlled object.";
Player.print_pawns.doc = "Print out a list of the current pawn control stack.";
Player.doc = {};
Player.doc.players = "A list of current players.";

View file

@ -27,6 +27,13 @@ var physics = {
},
};
physics.doc = {};
physics.doc.gravity = "Gravity expressed in units per second.";
physics.doc.damping = "Damping applied to all physics bodies. Bound between 0 and 1.";
physics.doc.pos_query = "Returns any object colliding with the given point.";
physics.doc.box_query = "Returns an array of body ids that collide with a given box.";
physics.doc.box_point_query = "Returns the subset of points from a given list that are inside a given box.";
var Collision = {
types: {},
num: 10,

View file

@ -36,6 +36,7 @@ var Sound = {
/* Between 0 and 100 */
set volume(x) { cmd(19, x); },
get volume() { 0; },
killall() {
Music.stop();
@ -43,3 +44,10 @@ var Sound = {
/* TODO: Kill all sound effects that may still be running */
},
};
Sound.play.doc = "Play the given file once.";
Sound.music.doc = "Play a midi track with a given soundfont.";
Sound.musicstop.doc = "Stop playing music.";
Sound.doc = {};
Sound.doc.volume = "Set the master volume. 0 is no sound and 100 is loudest.";
Sound.killall.doc = "Kill any playing sounds and music in the game world.";

View file

@ -82,6 +82,23 @@ var Log = {
this.write(stack.slice(n));
},
clear() {
cmd(146);
},
};
Log.doc = {
level: "Set level to output logging to console.",
info: "Output info level message.",
warn: "Output warn level message.",
error: "Output error level message, and print stacktrace.",
critical: "Output critical level message, and exit game immediately.",
write: "Write raw text to console.",
say: "Write raw text to console, plus a newline.",
stack: "Output a stacktrace to console.",
console: "Output directly to in game console.",
clear: "Clear console."
};
var IO = {
@ -111,6 +128,15 @@ var IO = {
},
};
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.",
slurpwrite: "Write a given string to a given file.",
ls: "List contents of the game directory.",
glob: "Glob files in game directory.",
};
var Cmdline = {};
Cmdline.cmds = [];
@ -151,10 +177,13 @@ function cmd_args(cmdargs)
}
}
var STD = {};
STD.exit = function(status)
{
cmd(147,status);
}
Cmdline.register_cmd("p", function() { Game.edit = false; }, "Launch engine in play mode.");
Cmdline.register_cmd("v", function() { Log.say(cmd(120)); Game.quit(); }, "Display engine info.");
Cmdline.register_cmd("c", function() {}, "Redirect logging to console.");
Cmdline.register_cmd("v", function() { Log.say(cmd(120)); STD.exit(0);}, "Display engine info.");
Cmdline.register_cmd("l", function(n) {
Log.level = n;
}, "Set log level.");
@ -162,8 +191,7 @@ Cmdline.register_cmd("h", function(str) {
for (var cmd of Cmdline.cmds) {
Log.say(`-${cmd.flag}: ${cmd.doc}`);
}
Game.quit();
STD.exit(0);
},
"Help.");
Cmdline.register_cmd("b", function(str) {
@ -179,17 +207,23 @@ Cmdline.register_cmd("b", function(str) {
Log.warn(`Packing into ${packname}`);
cmd(124, packname);
Game.quit();
STD.exit(0);
}, "Pack the game into the given name.");
Cmdline.register_cmd("e", function(pawn) {
run("scripts/editor.js");
Log.write(`## Input for ${pawn}\n`);
eval(`Log.write(Input.print_md_kbm(${pawn}));`);
Game.quit();
STD.exit(0);
}, "Print input documentation for a given object in a markdown table." );
Cmdline.register_cmd("t", function() {
Log.warn("Testing not implemented yet.");
Game.quit();
STD.exit(0);
}, "Test suite.");
Cmdline.register_cmd("d", function(obj) {
run("scripts/editor.js");
Log.say(API.print_doc(obj[0]));
STD.exit(0);
}, "Print documentation for an object.");

View file

@ -2,6 +2,7 @@
#define SOKOL_TRACE_HOOKS
#define SOKOL_IMPL
#define SOKOL_NO_ENTRY
#include "sokol/sokol_audio.h"
#include "sokol/sokol_time.h"
#include "sokol/sokol_args.h"

View file

@ -29,34 +29,35 @@ FILE *logfile = NULL;
#define CONSOLE_BUF 1024*1024 /* 5MB */
char *consolelog;
FILE *consolefp;
void log_init()
{
consolelog = malloc(CONSOLE_BUF+1);
consolefp = fmemopen(consolelog, CONSOLE_BUF+1,"w");
}
void mYughLog(int category, int priority, int line, const char *file, const char *message, ...)
{
#ifndef NDEBUG
if (priority >= logLevel) {
time_t now = time(0);
struct tm *tinfo = localtime(&now);
if (priority >= logLevel) {
time_t now = time(0);
struct tm *tinfo = localtime(&now);
double ticks = (double)clock()/CLOCKS_PER_SEC;
double ticks = (double)clock()/CLOCKS_PER_SEC;
va_list args;
va_start(args, message);
char msgbuffer[ERROR_BUFFER] = { '\0' };
vsnprintf(msgbuffer, ERROR_BUFFER, message, args);
va_end(args);
va_list args;
va_start(args, message);
char msgbuffer[ERROR_BUFFER] = { '\0' };
vsnprintf(msgbuffer, ERROR_BUFFER, message, args);
va_end(args);
char buffer[ERROR_BUFFER] = { '\0' };
snprintf(buffer, ERROR_BUFFER, "%s:%d: %s, %s: %s\n", file, line, logstr[priority], catstr[category], msgbuffer);
char buffer[ERROR_BUFFER] = { '\0' };
snprintf(buffer, ERROR_BUFFER, "%s:%d: %s, %s: %s\n", file, line, logstr[priority], catstr[category], msgbuffer);
log_print(buffer);
// if (category == LOG_SCRIPT && priority >= 2)
// js_stacktrace();
fprintf(stderr, buffer);
fflush(stderr);
}
#endif
@ -64,10 +65,10 @@ void mYughLog(int category, int priority, int line, const char *file, const char
void log_print(const char *str)
{
fprintf(stdout, "%s", str);
fflush(stdout);
#ifndef NDEBUG
fprintf(stderr, "%s", str);
fflush(stderr);
strncat(consolelog, str, CONSOLE_BUF);
if (logfile) {

View file

@ -1118,6 +1118,10 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
case 146:
log_clear();
break;
case 147:
exit(js2int(argv[1]));
break;
}
if (str)

View file

@ -40,8 +40,6 @@ const char *DB_NAME = "test.db";
static struct cdb corecdb;
static struct cdb game_cdb;
void resources_init() {
DATA_PATH = malloc(MAXPATH);
getcwd(DATA_PATH, MAXPATH);

View file

@ -131,10 +131,14 @@ static int argc;
static char **args;
void c_init() {
sound_init();
input_init();
script_evalf("world_start();");
render_init();
window_set_icon("icons/moon.gif");
window_resize(sapp_width(), sapp_height());
script_evalf("initialize();");
script_evalf("Game.init();");
}
int frame_fps() {
@ -174,7 +178,6 @@ static void process_frame()
prof_start(&prof_draw);
window_render(&mainwin);
prof(&prof_draw);
gameobjects_cleanup();
}
@ -307,7 +310,7 @@ void app_name(char *name)
start_desc.window_title = strdup(name);
}
sapp_desc sokol_main(int argc, char **argv) {
int main(int argc, char **argv) {
#ifndef NDEBUG
log_init();
// #ifdef __linux__
@ -319,8 +322,6 @@ sapp_desc sokol_main(int argc, char **argv) {
log_setfile(fname);
}
YughInfo("Starting yugine version %s.", VER);
FILE *sysinfo = NULL;
/* sysinfo = popen("uname -a", "r");
if (!sysinfo) {
@ -336,12 +337,9 @@ sapp_desc sokol_main(int argc, char **argv) {
// signal(SIGBUS, seghandle);
#endif
stm_setup(); /* time */
resources_init();
phys2d_init();
resources_init();
phys2d_init();
script_startup();
int argsize = 0;
@ -360,15 +358,11 @@ sapp_desc sokol_main(int argc, char **argv) {
script_evalf("cmd_args('%s');", cmdstr);
sound_init();
input_init();
script_dofile("warmup.js");
start_desc.width = mainwin.width;
start_desc.height = mainwin.height;
start_desc.fullscreen = 0;
return start_desc;
sapp_run(&start_desc);
return 0;
}

View file

@ -10,6 +10,7 @@ void sim_step();
int phys_stepping();
void set_timescale(float val);
void print_stacktrace();
void yugine_init();
const char *engine_info();
void app_name(char *name);