Physics fixes; editor fixes
This commit is contained in:
parent
0b4231c0b6
commit
17ad44e5a5
|
@ -458,7 +458,7 @@ var editor = {
|
|||
render.point(x[1].screenpos(), 1, Color.red);
|
||||
});
|
||||
|
||||
var mg = physics.pos_query(input.mouse.worldpos(),10);
|
||||
var mg = physics.pos_query(input.mouse.worldpos());
|
||||
|
||||
if (mg && mg._ed?.selectable) {
|
||||
var p = mg.path_from(thiso);
|
||||
|
@ -655,7 +655,7 @@ editor.inputs.drop = function(str) {
|
|||
return;
|
||||
}
|
||||
|
||||
var mg = physics.pos_query(input.mouse.worldpos(),10);
|
||||
var mg = physics.pos_query(input.mouse.worldpos());
|
||||
if (!mg) return;
|
||||
var img = mg.get_comp_by_name('sprite');
|
||||
if (!img) return;
|
||||
|
@ -785,16 +785,14 @@ editor.inputs['C-f'] = function() {
|
|||
};
|
||||
editor.inputs['C-f'].doc = "Tunnel into the selected level object to edit it.";
|
||||
|
||||
editor.inputs['C-F'] = function() {
|
||||
console.info("PRESSED C-F");
|
||||
if (editor.edit_level.master === world) return;
|
||||
editor.inputs['M-f'] = function() {
|
||||
if (editor.edit_level.master === world) return;
|
||||
|
||||
editor.edit_level = editor.edit_level.master;
|
||||
editor.unselect();
|
||||
editor.reset_undos();
|
||||
};
|
||||
editor.inputs['C-F'].doc = "Tunnel out of the level you are editing, saving it in the process.";
|
||||
|
||||
editor.inputs['M-f'].doc = "Tunnel out of the level you are editing, saving it in the process.";
|
||||
editor.inputs['C-r'] = function() { editor.selectlist.forEach(function(x) { x.rotate(-x.angle*2); }); }
|
||||
editor.inputs['C-r'].doc = "Negate the selected's angle.";
|
||||
|
||||
|
@ -1532,7 +1530,7 @@ var replpanel = Object.copy(inputpanel, {
|
|||
this.caret = 0;
|
||||
var ret = function() {return eval(ecode);}.call(repl_obj);
|
||||
if (typeof ret === 'object') ret = json.encode(ret,null,1);
|
||||
say(ret);
|
||||
if (ret) say(ret);
|
||||
},
|
||||
|
||||
resetscroll() {
|
||||
|
|
|
@ -312,17 +312,20 @@ game.timescale = 1;
|
|||
|
||||
var eachobj = function(obj,fn)
|
||||
{
|
||||
fn(obj);
|
||||
var val = fn(obj);
|
||||
if (val) return val;
|
||||
for (var o in obj.objects) {
|
||||
if (obj.objects[o] === obj) {
|
||||
//console.error(`Object ${obj.toString()} is referenced by itself.`);
|
||||
continue;
|
||||
}
|
||||
eachobj(obj.objects[o],fn);
|
||||
if (obj.objects[o] === obj)
|
||||
console.error(`Object ${obj.toString()} is referenced by itself.`);
|
||||
val = eachobj(obj.objects[o],fn);
|
||||
if (val) return val;
|
||||
}
|
||||
}
|
||||
|
||||
game.all_objects = function(fn, startobj = world) { eachobj(startobj,fn); };
|
||||
game.all_objects = function(fn, startobj = world) { return eachobj(startobj,fn); };
|
||||
game.find_object = function(fn, startobj = world) {
|
||||
|
||||
}
|
||||
|
||||
game.tags = {};
|
||||
game.tag_add = function(tag, obj) {
|
||||
|
|
|
@ -238,8 +238,10 @@ var gameobject = {
|
|||
Object.merge(ent, json.decode(Resources.replstrs(config)));
|
||||
else if (Array.isArray(config))
|
||||
config.forEach(function(path) {
|
||||
if (typeof path === 'string')
|
||||
if (typeof path === 'string') {
|
||||
console.info(`ingesting ${path} ...`);
|
||||
Object.merge(ent, json.decode(Resources.replstrs(path)));
|
||||
}
|
||||
else if (typeof path === 'object')
|
||||
Object.merge(ent,path);
|
||||
});
|
||||
|
@ -278,6 +280,7 @@ var gameobject = {
|
|||
if (!Object.empty(ent.objects)) {
|
||||
var o = ent.objects;
|
||||
delete ent.objects;
|
||||
ent.objects = {};
|
||||
for (var i in o) {
|
||||
console.info(`creating ${i} on ${ent.toString()}`);
|
||||
var newur = o[i].ur;
|
||||
|
@ -302,6 +305,7 @@ var gameobject = {
|
|||
/* Reparent 'this' to be 'parent's child */
|
||||
reparent(parent) {
|
||||
assert(parent, `Tried to reparent ${this.toString()} to nothing.`);
|
||||
console.info(`parenting ${this.toString()} to ${parent.toString()}`);
|
||||
if (this.master === parent) {
|
||||
console.warn("not reparenting ...");
|
||||
console.warn(`${this.master} is the same as ${parent}`);
|
||||
|
@ -363,18 +367,7 @@ var gameobject = {
|
|||
},
|
||||
|
||||
/* Make a unique object the same as its prototype */
|
||||
revert() {
|
||||
var jobj = this.json_obj();
|
||||
var lobj = this.master.__proto__.objects[this.toString()];
|
||||
delete jobj.objects;
|
||||
Object.keys(jobj).forEach(function(x) {
|
||||
if (lobj && x in lobj)
|
||||
this[x] = lobj[x];
|
||||
else
|
||||
this[x] = this.__proto__[x];
|
||||
}, this);
|
||||
this.sync();
|
||||
},
|
||||
revert() { Object.merge(this, this.ur.fresh); },
|
||||
|
||||
toString() { return "new_object"; },
|
||||
|
||||
|
@ -488,9 +481,6 @@ var gameobject = {
|
|||
this.master = undefined;
|
||||
}
|
||||
|
||||
if (this.__proto__.instances)
|
||||
delete this.__proto__.instances[this.toString()];
|
||||
|
||||
for (var key in this.components) {
|
||||
this.components[key].kill?.();
|
||||
this.components[key].gameobject = undefined;
|
||||
|
@ -664,7 +654,7 @@ function apply_ur(u, ent) {
|
|||
if (typeof data === 'string')
|
||||
Object.merge(ent, json.decode(Resources.replstrs(data)));
|
||||
else if (Array.isArray(data)) {
|
||||
data.forEach(function(path)) {
|
||||
data.forEach(function(path) {
|
||||
if (typeof path === 'string')
|
||||
Object.merge(ent, json.decode(Resources.replstrs(data)));
|
||||
else if (typeof path === 'object')
|
||||
|
@ -716,14 +706,13 @@ game.loadurs = function() {
|
|||
if (file[0] === '.' || file[0] === '_') continue;
|
||||
var newur = ur_from_file(file);
|
||||
if (!newur) continue;
|
||||
var datastr = file.set_ext(".json");
|
||||
var data;
|
||||
if (io.exists(datastr))
|
||||
data = datastr;
|
||||
Object.assign(newur, {
|
||||
text: file,
|
||||
data: datastr
|
||||
};
|
||||
newur.text = file;
|
||||
|
||||
var data = file.set_ext(".json");
|
||||
if (io.exists(data)) {
|
||||
console.info(`Found matching json ${data} for ${file}`);
|
||||
newur.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
for (var file of io.glob("**.json")) {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
gui functions take screen space coordinates
|
||||
*/
|
||||
|
||||
gui.scissor_win = function() { gui.scissor(0,0,window.width,window.height); }
|
||||
gui.scissor_win = function() { gui.scissor(0,0,window.size.x,window.y); }
|
||||
|
||||
gui.input_lmouse_pressed = function() {
|
||||
if (gui.selected)
|
||||
|
@ -174,7 +174,6 @@ Mum.window = Mum.extend({
|
|||
render.window(p,this.wh, this.color);
|
||||
this.bb = bbox.blwh(p, this.wh);
|
||||
gui.flush();
|
||||
gui.scissor(p.x,p.y,this.wh.x,this.wh.y);
|
||||
this.max_width = this.width;
|
||||
if (this.selectable) gui.controls.check_bb(this);
|
||||
var pos = [this.bb.l, this.bb.t].add(this.padding);
|
||||
|
|
|
@ -9,8 +9,18 @@ var HIT = {
|
|||
};
|
||||
*/
|
||||
|
||||
var pq = physics.pos_query;
|
||||
physics.pos_query = function(pos,give = 25) { return pq(pos,give); }
|
||||
physics.pos_query = function(pos, start = world, give = 10) {
|
||||
var ret;
|
||||
ret = physics.point_query_nearest(pos, 0);
|
||||
|
||||
if (ret)
|
||||
return ret.entity;
|
||||
|
||||
return game.all_objects(function(o) {
|
||||
var dist = Vector.length(o.pos.sub(pos));
|
||||
if (dist <= give) return o;
|
||||
});
|
||||
}
|
||||
|
||||
physics.box_point_query = function(box,points) {
|
||||
if (!box || !points) return [];
|
||||
|
|
|
@ -67,6 +67,12 @@ void bbhit(cpShape *shape, int *data)
|
|||
qhit++;
|
||||
}
|
||||
|
||||
cpShapeFilter nofilter = {
|
||||
.group = CP_NO_GROUP,
|
||||
.mask = ~CP_ALL_CATEGORIES,
|
||||
.categories = ~CP_ALL_CATEGORIES
|
||||
};
|
||||
|
||||
cpShapeFilter allfilter = {
|
||||
.group = CP_NO_GROUP,
|
||||
.mask = CP_ALL_CATEGORIES,
|
||||
|
@ -508,10 +514,13 @@ void phys2d_dbgdrawedge(struct phys2d_edge *edge) {
|
|||
|
||||
/************ COLLIDER ****************/
|
||||
void shape_enabled(struct phys2d_shape *shape, int enabled) {
|
||||
if (enabled)
|
||||
cpShapeSetFilter(shape->shape, CP_SHAPE_FILTER_ALL);
|
||||
else
|
||||
cpShapeSetFilter(shape->shape, CP_SHAPE_FILTER_NONE);
|
||||
cpShapeFilter set = enabled ? CP_SHAPE_FILTER_ALL : CP_SHAPE_FILTER_NONE;
|
||||
if (!shape->shape) {
|
||||
struct phys2d_edge *edge = shape->data;
|
||||
for (int i = 0; i < arrlen(edge->shapes[i]); i++)
|
||||
cpShapeSetFilter(edge->shapes[i], set);
|
||||
} else
|
||||
cpShapeSetFilter(shape->shape, set);
|
||||
}
|
||||
|
||||
int shape_is_enabled(struct phys2d_shape *shape) {
|
||||
|
@ -578,6 +587,7 @@ void phys_run_post(cpSpace *space, JSValue *fn, JSValue *hit)
|
|||
|
||||
void register_hit(cpArbiter *arb, gameobject *go, const char *name)
|
||||
{
|
||||
if (JS_IsUndefined(go->ref)) return;
|
||||
JSValue cb = JS_GetPropertyStr(js, go->ref, name);
|
||||
if (!JS_IsUndefined(cb)) {
|
||||
JSValue jarb = arb2js(arb);
|
||||
|
@ -589,6 +599,8 @@ void register_hit(cpArbiter *arb, gameobject *go, const char *name)
|
|||
|
||||
cpShape *s1, *s2;
|
||||
cpArbiterGetShapes(arb, &s1, &s2);
|
||||
if (JS_IsUndefined(shape2go(s1)->ref)) return;
|
||||
if (JS_IsUndefined(shape2go(s2)->ref)) return;
|
||||
struct phys2d_shape *pshape1 = cpShapeGetUserData(s1);
|
||||
|
||||
if (JS_IsUndefined(pshape1->ref)) return;
|
||||
|
@ -603,12 +615,11 @@ void register_hit(cpArbiter *arb, gameobject *go, const char *name)
|
|||
}
|
||||
|
||||
void script_phys_cb_begin(cpArbiter *arb, cpSpace *space, gameobject *go) { register_hit(arb, go, "collide"); }
|
||||
|
||||
void script_phys_cb_separate(cpArbiter *arb, cpSpace *space, gameobject *go) { register_hit(arb, go, "separate"); }
|
||||
|
||||
void phys2d_setup_handlers(gameobject *go) {
|
||||
cpCollisionHandler *handler = cpSpaceAddWildcardHandler(space, (cpCollisionType)go);
|
||||
handler->userData = go;
|
||||
handler->postSolveFunc = script_phys_cb_begin;
|
||||
handler->beginFunc = script_phys_cb_begin;
|
||||
handler->separateFunc = script_phys_cb_separate;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ extern struct rgba static_color;
|
|||
extern struct rgba sleep_color;
|
||||
|
||||
extern cpShapeFilter allfilter;
|
||||
extern cpShapeFilter nofilter;
|
||||
|
||||
typedef struct constraint {
|
||||
cpConstraint *c;
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
|
||||
#include "stb_ds.h"
|
||||
|
||||
static gameobject **gameobjects;
|
||||
|
||||
int go_count() { return arrlen(gameobjects); }
|
||||
|
||||
gameobject *body2go(cpBody *body) { return cpBodyGetUserData(body); }
|
||||
gameobject *shape2go(cpShape *shape) { return ((struct phys2d_shape *)cpShapeGetUserData(shape))->go; }
|
||||
gameobject *shape2go(cpShape *shape) {
|
||||
struct phys2d_shape *pshape = cpShapeGetUserData(shape);
|
||||
if (!pshape) return NULL;
|
||||
return pshape->go;
|
||||
}
|
||||
|
||||
HMM_Vec2 go_pos(gameobject *go)
|
||||
{
|
||||
|
@ -44,20 +44,6 @@ HMM_Mat3 t_world2go(gameobject *go) { return HMM_InvGeneralM3(t_go2world(go)); }
|
|||
HMM_Mat4 t3d_go2world(gameobject *go) { return transform3d2mat(go2t3(go)); }
|
||||
HMM_Mat4 t3d_world2go(gameobject *go) { return HMM_InvGeneralM4(t3d_go2world(go)); }
|
||||
|
||||
gameobject *pos2gameobject(HMM_Vec2 pos, float give) {
|
||||
cpShape *hit = phys2d_query_pos(pos.cp);
|
||||
|
||||
if (hit)
|
||||
return shape2go(hit);
|
||||
|
||||
for (int i = 0; i < arrlen(gameobjects); i++) {
|
||||
float dist = HMM_DistV2(go_pos(gameobjects[i]),pos);
|
||||
if (dist <= give) return gameobjects[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
transform2d go2t(gameobject *go)
|
||||
{
|
||||
transform2d t;
|
||||
|
@ -151,34 +137,34 @@ gameobject *MakeGameobject() {
|
|||
.mass = 1.f,
|
||||
.next = -1,
|
||||
.drawlayer = 0,
|
||||
.shape_cbs = NULL,
|
||||
.damping = INFINITY,
|
||||
.timescale = 1.0,
|
||||
.ref = JS_UNDEFINED,
|
||||
};
|
||||
|
||||
go.cbs.begin = JS_UNDEFINED;
|
||||
go.cbs.separate = JS_UNDEFINED;
|
||||
|
||||
go.body = cpSpaceAddBody(space, cpBodyNew(go.mass, 1.f));
|
||||
cpBodySetVelocityUpdateFunc(go.body, velocityFn);
|
||||
|
||||
*ngo = go;
|
||||
cpBodySetUserData(go.body, ngo);
|
||||
phys2d_setup_handlers(ngo);
|
||||
arrpush(gameobjects, ngo);
|
||||
return ngo;
|
||||
}
|
||||
|
||||
void rm_body_shapes(cpBody *body, cpShape *shape, void *data) {
|
||||
struct phys2d_shape *s = cpShapeGetUserData(shape);
|
||||
|
||||
if (s) {
|
||||
JS_FreeValue(js, s->ref);
|
||||
s->ref = JS_UNDEFINED;
|
||||
if (s->free)
|
||||
s->free(s->data);
|
||||
else
|
||||
free(s->data);
|
||||
}
|
||||
|
||||
cpShapeSetFilter(shape, nofilter);
|
||||
|
||||
cpSpaceRemoveShape(space, shape);
|
||||
cpShapeFree(shape);
|
||||
}
|
||||
|
@ -189,22 +175,12 @@ void rm_body_constraints(cpBody *body, cpConstraint *constraint, void *data)
|
|||
}
|
||||
|
||||
void gameobject_free(gameobject *go) {
|
||||
arrfree(go->shape_cbs);
|
||||
go->ref = JS_UNDEFINED;
|
||||
cpBodyEachShape(go->body, rm_body_shapes, NULL);
|
||||
cpBodyEachConstraint(go->body, rm_body_constraints, NULL);
|
||||
cpSpaceRemoveBody(space, go->body);
|
||||
cpBodyFree(go->body);
|
||||
|
||||
go->body = NULL;
|
||||
|
||||
free(go);
|
||||
for (int i = arrlen(gameobjects)-1; i >= 0; i--) {
|
||||
if (gameobjects[i] == go) {
|
||||
arrdelswap(gameobjects,i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gameobject_setangle(gameobject *go, float angle) {
|
||||
|
|
|
@ -41,8 +41,6 @@ struct gameobject {
|
|||
unsigned int layer;
|
||||
cpShapeFilter filter;
|
||||
unsigned int warp_filter;
|
||||
struct phys_cbs cbs;
|
||||
struct shape_cb *shape_cbs;
|
||||
JSValue ref;
|
||||
HMM_Mat4 world;
|
||||
float drawlayer;
|
||||
|
@ -66,7 +64,6 @@ struct gameobject {
|
|||
typedef struct gameobject gameobject;
|
||||
|
||||
gameobject *MakeGameobject();
|
||||
int go_count();
|
||||
void gameobject_apply(gameobject *go);
|
||||
void gameobject_free(gameobject *go);
|
||||
|
||||
|
@ -92,7 +89,6 @@ gameobject *shape2go(cpShape *shape);
|
|||
void go_shape_apply(cpBody *body, cpShape *shape, gameobject *go);
|
||||
|
||||
/* Tries a few methods to select a gameobject; if none is selected returns -1 */
|
||||
gameobject *pos2gameobject(HMM_Vec2 pos, float give);
|
||||
|
||||
void gameobject_draw_debug(gameobject *go);
|
||||
#endif
|
||||
|
|
|
@ -517,13 +517,6 @@ int point2segindex(HMM_Vec2 p, HMM_Vec2 *segs, double slop) {
|
|||
return best;
|
||||
}
|
||||
|
||||
void gameobject_add_shape_collider(gameobject *go, JSValue fn, struct phys2d_shape *shape) {
|
||||
struct shape_cb shapecb;
|
||||
shapecb.shape = shape;
|
||||
shapecb.cbs.begin = fn;
|
||||
arrpush(go->shape_cbs, shapecb);
|
||||
}
|
||||
|
||||
circle2d *js2circle2d(JSValue v) { return js2ptr(v); }
|
||||
|
||||
JSC_CCALL(circle2d_set_radius, js2circle2d(argv[0])->radius = js2number(argv[1]))
|
||||
|
@ -653,7 +646,7 @@ static const JSCFunctionListEntry js_render_funcs[] = {
|
|||
};
|
||||
|
||||
JSC_CCALL(gui_flush, text_flush(&useproj));
|
||||
JSC_CCALL(gui_scissor, sg_apply_scissor_rectf(js2number(argv[0]), js2number(argv[1]), js2number(argv[2]), js2number(argv[3]), 0))
|
||||
JSC_CCALL(gui_scissor, sg_apply_scissor_rect(js2number(argv[0]), js2number(argv[1]), js2number(argv[2]), js2number(argv[3]), 0))
|
||||
JSC_CCALL(gui_text,
|
||||
const char *s = JS_ToCString(js, argv[0]);
|
||||
HMM_Vec2 pos = js2vec2(argv[1]);
|
||||
|
@ -733,11 +726,8 @@ static const JSCFunctionListEntry js_vector_funcs[] = {
|
|||
|
||||
JSC_CCALL(game_engine_start, engine_start(argv[0],argv[1]))
|
||||
|
||||
JSValue js_game_object_count(JSContext *js, JSValue this) { return number2js(go_count()); }
|
||||
|
||||
static const JSCFunctionListEntry js_game_funcs[] = {
|
||||
MIST_FUNC_DEF(game, engine_start, 2),
|
||||
MIST_FUNC_DEF(game, object_count, 0)
|
||||
};
|
||||
|
||||
JSC_CCALL(input_show_keyboard, sapp_show_keyboard(js2boolean(argv[0])))
|
||||
|
@ -930,11 +920,6 @@ JSC_CCALL(physics_sgscale,
|
|||
|
||||
JSC_CCALL(physics_set_cat_mask, set_cat_mask(js2number(argv[0]), js2bitmask(argv[1])))
|
||||
|
||||
JSC_CCALL(physics_pos_query,
|
||||
gameobject *go = pos2gameobject(js2vec2(argv[0]), js2number(argv[1]));
|
||||
return go ? JS_DupValue(js,go->ref) : JS_UNDEFINED;
|
||||
)
|
||||
|
||||
JSC_CCALL(physics_closest_point,
|
||||
void *v1 = js2cpvec2arr(argv[1]);
|
||||
JSValue ret = number2js(point2segindex(js2vec2(argv[0]), v1, js2number(argv[2])));
|
||||
|
@ -1024,10 +1009,9 @@ JSC_CCALL(physics_point_query_nearest,
|
|||
static const JSCFunctionListEntry js_physics_funcs[] = {
|
||||
MIST_FUNC_DEF(physics, sgscale, 2),
|
||||
MIST_FUNC_DEF(physics, set_cat_mask, 2),
|
||||
MIST_FUNC_DEF(physics, pos_query, 2),
|
||||
MIST_FUNC_DEF(physics, point_query, 3),
|
||||
MIST_FUNC_DEF(physics, point_query_nearest, 3),
|
||||
MIST_FUNC_DEF(physics, ray_query, 2),
|
||||
MIST_FUNC_DEF(physics, point_query_nearest, 2),
|
||||
MIST_FUNC_DEF(physics, ray_query, 4),
|
||||
MIST_FUNC_DEF(physics, box_query, 2),
|
||||
MIST_FUNC_DEF(physics, shape_query, 1),
|
||||
MIST_FUNC_DEF(physics, closest_point, 3),
|
||||
|
@ -1471,7 +1455,7 @@ JSC_CCALL(os_make_circle2d,
|
|||
JSValue circleval = JS_NewObject(js);
|
||||
js_setprop_str(circleval, "id", ptr2js(circle));
|
||||
js_setprop_str(circleval, "shape", ptr2js(&circle->shape));
|
||||
circle->shape.ref = argv[1];
|
||||
circle->shape.ref = JS_DupValue(js,argv[1]);
|
||||
return circleval;
|
||||
)
|
||||
|
||||
|
@ -1482,7 +1466,7 @@ JSC_CCALL(os_make_poly2d,
|
|||
JSValue polyval = JS_NewObject(js);
|
||||
js_setprop_str(polyval, "id", ptr2js(poly));
|
||||
js_setprop_str(polyval, "shape", ptr2js(&poly->shape));
|
||||
poly->shape.ref = argv[1];
|
||||
poly->shape.ref = JS_DupValue(js,argv[1]);
|
||||
return polyval;
|
||||
)
|
||||
|
||||
|
@ -1496,7 +1480,7 @@ JSC_CCALL(os_make_edge2d,
|
|||
JSValue edgeval = JS_NewObject(js);
|
||||
js_setprop_str(edgeval, "id", ptr2js(edge));
|
||||
js_setprop_str(edgeval, "shape", ptr2js(&edge->shape));
|
||||
edge->shape.ref = argv[1];
|
||||
edge->shape.ref = JS_DupValue(js, argv[1]);
|
||||
return edgeval;
|
||||
)
|
||||
|
||||
|
|
|
@ -203,7 +203,7 @@ SG_TRACE_SET(attachments)
|
|||
void trace_append_buffer(sg_buffer id, sg_range *data, void *user)
|
||||
{
|
||||
sg_buffer_desc desc = sg_query_buffer_desc(id);
|
||||
YughSpam("Appending buffer %d [%s]", id, desc.label);
|
||||
// YughSpam("Appending buffer %d [%s]", id, desc.label);
|
||||
}
|
||||
|
||||
static sg_trace_hooks hooks = {
|
||||
|
|
Loading…
Reference in a new issue