Compare commits
2 Commits
06bc4fcd55
...
5f34cb34b2
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f34cb34b2 | |||
| b9d2fe60fd |
@@ -3,23 +3,21 @@
|
|||||||
// This software is released under the MIT License.
|
// This software is released under the MIT License.
|
||||||
// https://opensource.org/licenses/MIT
|
// https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
// Player: dynamic capsule body, moved relative to the camera by PLAYER's
|
class Player extends Entity {
|
||||||
// own update callback (see entityplayer.c).
|
|
||||||
class Player {
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.entity = new Entity();
|
super();
|
||||||
|
|
||||||
this.position = this.entity.add(POSITION);
|
this.position = this.add(POSITION);
|
||||||
this.position.setLocalPosition(0.0, 2.0, 0.0);
|
this.position.setLocalPosition(0.0, 2.0, 0.0);
|
||||||
|
|
||||||
this.physics = this.entity.add(PHYSICS);
|
this.physics = this.add(PHYSICS);
|
||||||
this.physics.setShape(PHYSICS_SHAPE_CAPSULE, 0.5, 0.5);
|
this.physics.setShape(PHYSICS_SHAPE_CAPSULE, 0.5, 0.5);
|
||||||
|
|
||||||
this.renderable = this.entity.add(RENDERABLE);
|
this.renderable = this.add(RENDERABLE);
|
||||||
this.renderable.setMesh(0, MESH_CAPSULE);
|
this.renderable.setMesh(0, MESH_CAPSULE);
|
||||||
this.renderable.setColor(0, 0, 255, 255);
|
this.renderable.setColor(0, 0, 255, 255);
|
||||||
|
|
||||||
this.entity.add(PLAYER);
|
this.add(PLAYER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) 2026 Dominic Masters
|
||||||
|
//
|
||||||
|
// This software is released under the MIT License.
|
||||||
|
// https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
var CAMERA_OFFSET_ANGLE = 0.0;
|
||||||
|
var CAMERA_OFFSET_RADIUS = 18.0;
|
||||||
|
var CAMERA_OFFSET_HEIGHT = 10.0;
|
||||||
|
|
||||||
|
class PlayerCamera {
|
||||||
|
constructor(target) {
|
||||||
|
this.target = target;
|
||||||
|
|
||||||
|
this.entity = new Entity();
|
||||||
|
this.position = this.entity.add(POSITION);
|
||||||
|
this.entity.add(CAMERA);
|
||||||
|
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
var targetPosition = this.target.position.getLocalPosition();
|
||||||
|
|
||||||
|
var eyeX = targetPosition.x + Math.cos(CAMERA_OFFSET_ANGLE) *
|
||||||
|
CAMERA_OFFSET_RADIUS;
|
||||||
|
var eyeY = targetPosition.y + CAMERA_OFFSET_HEIGHT;
|
||||||
|
var eyeZ = targetPosition.z + Math.sin(CAMERA_OFFSET_ANGLE) *
|
||||||
|
CAMERA_OFFSET_RADIUS;
|
||||||
|
|
||||||
|
this.position.lookAt(
|
||||||
|
eyeX, eyeY, eyeZ,
|
||||||
|
targetPosition.x, targetPosition.y, targetPosition.z,
|
||||||
|
0.0, 1.0, 0.0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = PlayerCamera;
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// Copyright (c) 2026 Dominic Masters
|
||||||
|
//
|
||||||
|
// This software is released under the MIT License.
|
||||||
|
// https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
class TestPlane {
|
||||||
|
constructor() {
|
||||||
|
this.entity = new Entity();
|
||||||
|
|
||||||
|
this.position = this.entity.add(POSITION);
|
||||||
|
this.position.setLocalPosition(-10.0, 0.0, -10.0);
|
||||||
|
this.position.setLocalScale(20.0, 1.0, 20.0);
|
||||||
|
|
||||||
|
this.physics = this.entity.add(PHYSICS);
|
||||||
|
this.physics.setBodyType(PHYSICS_BODY_STATIC);
|
||||||
|
this.physics.setShape(PHYSICS_SHAPE_PLANE, 0.0, 1.0, 0.0, 0.0);
|
||||||
|
|
||||||
|
this.renderable = this.entity.add(RENDERABLE);
|
||||||
|
this.renderable.setMesh(0, MESH_PLANE);
|
||||||
|
this.renderable.setColor(128, 128, 128, 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = TestPlane;
|
||||||
@@ -3,72 +3,24 @@
|
|||||||
// This software is released under the MIT License.
|
// This software is released under the MIT License.
|
||||||
// https://opensource.org/licenses/MIT
|
// https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
// Radius must stay well outside the floor's footprint (a 20x20 plane has a
|
|
||||||
// corner-to-center distance of 10*sqrt(2) =~ 14.1) -- orbiting inside that
|
|
||||||
// puts parts of the floor's own geometry near/behind the camera's view
|
|
||||||
// direction, which the PSP's legacy GU pipeline can't clip properly and
|
|
||||||
// drops the whole triangle instead of clipping it.
|
|
||||||
var CAMERA_ORBIT_RADIUS = 18.0;
|
|
||||||
var CAMERA_ORBIT_HEIGHT = 10.0;
|
|
||||||
var CAMERA_ORBIT_SPEED = 0.5;
|
|
||||||
|
|
||||||
var Player = require('./Player.js');
|
var Player = require('./Player.js');
|
||||||
|
var PlayerCamera = require('./PlayerCamera.js');
|
||||||
|
var TestPlane = require('./TestPlane.js');
|
||||||
|
|
||||||
var cameraOrbitAngle = 0.0;
|
var camera = null;
|
||||||
var cameraPosition = null;
|
|
||||||
|
|
||||||
// Orbits the camera around the world origin at a fixed radius/height/
|
|
||||||
// speed, always looking back at the origin. Called once up front (so the
|
|
||||||
// very first rendered frame is already positioned correctly) and then
|
|
||||||
// once per frame via update() below.
|
|
||||||
function updateCameraOrbit() {
|
|
||||||
cameraOrbitAngle += Time.delta * CAMERA_ORBIT_SPEED;
|
|
||||||
|
|
||||||
var eyeX = Math.cos(cameraOrbitAngle) * CAMERA_ORBIT_RADIUS;
|
|
||||||
var eyeY = CAMERA_ORBIT_HEIGHT;
|
|
||||||
var eyeZ = Math.sin(cameraOrbitAngle) * CAMERA_ORBIT_RADIUS;
|
|
||||||
|
|
||||||
cameraPosition.lookAt(eyeX, eyeY, eyeZ, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
// Called once by Scene.set(), right after it creates and activates the
|
|
||||||
// scene this module owns.
|
|
||||||
init: function() {
|
init: function() {
|
||||||
// Camera, orbiting the origin (see updateCameraOrbit above).
|
var player = new Player();
|
||||||
var camera = new Entity();
|
new TestPlane();
|
||||||
cameraPosition = camera.add(POSITION);
|
camera = new PlayerCamera(player);
|
||||||
camera.add(CAMERA);
|
|
||||||
updateCameraOrbit();
|
|
||||||
|
|
||||||
// Static ground plane. Physics ignores the entity's position
|
|
||||||
// component -- the shape's own normal/distance fully define the
|
|
||||||
// plane in world space.
|
|
||||||
var plane = new Entity();
|
|
||||||
var planePosition = plane.add(POSITION);
|
|
||||||
planePosition.setLocalPosition(-10.0, 0.0, -10.0);
|
|
||||||
planePosition.setLocalScale(20.0, 1.0, 20.0);
|
|
||||||
|
|
||||||
var planePhysics = plane.add(PHYSICS);
|
|
||||||
planePhysics.setBodyType(PHYSICS_BODY_STATIC);
|
|
||||||
planePhysics.setShape(PHYSICS_SHAPE_PLANE, 0.0, 1.0, 0.0, 0.0);
|
|
||||||
|
|
||||||
var planeRenderable = plane.add(RENDERABLE);
|
|
||||||
planeRenderable.setMesh(0, MESH_PLANE);
|
|
||||||
planeRenderable.setColor(128, 128, 128, 255);
|
|
||||||
|
|
||||||
new Player();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Called once per engine frame while this module is the active scene
|
|
||||||
// (see Scene.set(), engineUpdate() -> moduleSceneUpdateCurrent()).
|
|
||||||
update: function() {
|
update: function() {
|
||||||
if(cameraPosition) updateCameraOrbit();
|
if(camera) camera.update();
|
||||||
},
|
},
|
||||||
|
|
||||||
// Called once by Scene.set() when this module is replaced by another,
|
|
||||||
// right before the scene it owns is destroyed.
|
|
||||||
dispose: function() {
|
dispose: function() {
|
||||||
cameraPosition = null;
|
camera = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|||||||
PUBLIC
|
PUBLIC
|
||||||
scriptmanager.c
|
scriptmanager.c
|
||||||
scriptproto.c
|
scriptproto.c
|
||||||
|
scriptvalue.c
|
||||||
|
scriptdef.c
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(module)
|
add_subdirectory(module)
|
||||||
|
|||||||
@@ -0,0 +1,224 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "scriptdef.h"
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
|
||||||
|
#define SCRIPT_DEF_BINDING_MAX 256
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const scriptproto_t *proto;
|
||||||
|
const scriptfuncdef_t *funcDef;
|
||||||
|
const scriptpropdef_t *propDef;
|
||||||
|
} scriptdefbinding_t;
|
||||||
|
|
||||||
|
static scriptdefbinding_t SCRIPT_DEF_BINDINGS[SCRIPT_DEF_BINDING_MAX];
|
||||||
|
static size_t SCRIPT_DEF_BINDING_COUNT = 0;
|
||||||
|
|
||||||
|
static const jerry_object_native_info_t SCRIPT_DEF_NATIVE_INFO = {
|
||||||
|
.free_cb = NULL,
|
||||||
|
.number_of_references = 0,
|
||||||
|
.offset_of_references = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
void scriptProtoDefineFuncDefs(
|
||||||
|
scriptproto_t *proto,
|
||||||
|
const scriptfuncdef_t *defs
|
||||||
|
) {
|
||||||
|
assertNotNull(proto, "Script prototype struct must not be null");
|
||||||
|
scriptDefDefineFuncsOnTarget(proto->prototype, proto, defs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scriptProtoDefinePropDefs(
|
||||||
|
scriptproto_t *proto,
|
||||||
|
const scriptpropdef_t *defs
|
||||||
|
) {
|
||||||
|
assertNotNull(proto, "Script prototype struct must not be null");
|
||||||
|
scriptDefDefinePropsOnTarget(proto->prototype, proto, defs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scriptDefDefineGlobalFuncs(
|
||||||
|
const jerry_value_t target,
|
||||||
|
const scriptfuncdef_t *defs
|
||||||
|
) {
|
||||||
|
scriptDefDefineFuncsOnTarget(target, NULL, defs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scriptDefDefineGlobalProps(
|
||||||
|
const jerry_value_t target,
|
||||||
|
const scriptpropdef_t *defs
|
||||||
|
) {
|
||||||
|
scriptDefDefinePropsOnTarget(target, NULL, defs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scriptDefDisposeAll(void) {
|
||||||
|
SCRIPT_DEF_BINDING_COUNT = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *scriptDefResolveHandle(
|
||||||
|
const scriptproto_t *proto,
|
||||||
|
const jerry_value_t thisValue
|
||||||
|
) {
|
||||||
|
if(!proto) return NULL;
|
||||||
|
return scriptProtoGetValue(proto, thisValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(scriptDefFuncTrampoline) {
|
||||||
|
const scriptdefbinding_t *binding =
|
||||||
|
(const scriptdefbinding_t *)jerry_object_get_native_ptr(
|
||||||
|
callInfo->function, &SCRIPT_DEF_NATIVE_INFO
|
||||||
|
);
|
||||||
|
assertNotNull(binding, "Missing script function binding");
|
||||||
|
const scriptfuncdef_t *def = binding->funcDef;
|
||||||
|
|
||||||
|
void *handle = scriptDefResolveHandle(binding->proto, callInfo->this_value);
|
||||||
|
if(binding->proto && !handle) return jerry_undefined();
|
||||||
|
|
||||||
|
scriptvalue_t params[SCRIPT_DEF_PARAMS_MAX];
|
||||||
|
uint32_t consumed = 0;
|
||||||
|
for(uint32_t i = 0; i < def->paramCount; i++) {
|
||||||
|
jerry_value_t thrown;
|
||||||
|
if(!scriptDefReadArg(
|
||||||
|
def->paramTypes[i], args, argc, &consumed, ¶ms[i], &thrown
|
||||||
|
)) {
|
||||||
|
return thrown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptvalue_t ret = def->fn(handle, params, def->paramCount);
|
||||||
|
return scriptDefValueToJerry(&ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(scriptDefPropGetTrampoline) {
|
||||||
|
const scriptdefbinding_t *binding =
|
||||||
|
(const scriptdefbinding_t *)jerry_object_get_native_ptr(
|
||||||
|
callInfo->function, &SCRIPT_DEF_NATIVE_INFO
|
||||||
|
);
|
||||||
|
assertNotNull(binding, "Missing script property binding");
|
||||||
|
|
||||||
|
void *handle = scriptDefResolveHandle(binding->proto, callInfo->this_value);
|
||||||
|
if(binding->proto && !handle) return jerry_undefined();
|
||||||
|
|
||||||
|
scriptvalue_t ret = binding->propDef->getter(handle);
|
||||||
|
return scriptDefValueToJerry(&ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(scriptDefPropSetTrampoline) {
|
||||||
|
const scriptdefbinding_t *binding =
|
||||||
|
(const scriptdefbinding_t *)jerry_object_get_native_ptr(
|
||||||
|
callInfo->function, &SCRIPT_DEF_NATIVE_INFO
|
||||||
|
);
|
||||||
|
assertNotNull(binding, "Missing script property binding");
|
||||||
|
|
||||||
|
void *handle = scriptDefResolveHandle(binding->proto, callInfo->this_value);
|
||||||
|
if(binding->proto && !handle) return jerry_undefined();
|
||||||
|
|
||||||
|
if(argc < 1) return moduleBaseThrow("Expected a value to set");
|
||||||
|
|
||||||
|
scriptvalue_t value;
|
||||||
|
jerry_value_t thrown;
|
||||||
|
if(!scriptDefReadValue(binding->propDef->type, args[0], &value, &thrown)) {
|
||||||
|
return thrown;
|
||||||
|
}
|
||||||
|
|
||||||
|
binding->propDef->setter(handle, &value);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
void scriptDefDefineFuncsOnTarget(
|
||||||
|
const jerry_value_t target,
|
||||||
|
const scriptproto_t *proto,
|
||||||
|
const scriptfuncdef_t *defs
|
||||||
|
) {
|
||||||
|
assertNotNull(defs, "Function definitions must not be null");
|
||||||
|
|
||||||
|
for(size_t i = 0; defs[i].name != NULL; i++) {
|
||||||
|
assertTrue(
|
||||||
|
defs[i].paramCount <= SCRIPT_DEF_PARAMS_MAX,
|
||||||
|
"Too many params declared for script function"
|
||||||
|
);
|
||||||
|
assertTrue(
|
||||||
|
SCRIPT_DEF_BINDING_COUNT < SCRIPT_DEF_BINDING_MAX,
|
||||||
|
"Script def binding capacity exceeded"
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptdefbinding_t *binding =
|
||||||
|
&SCRIPT_DEF_BINDINGS[SCRIPT_DEF_BINDING_COUNT++];
|
||||||
|
binding->proto = proto;
|
||||||
|
binding->funcDef = &defs[i];
|
||||||
|
binding->propDef = NULL;
|
||||||
|
|
||||||
|
jerry_value_t fn = jerry_function_external(scriptDefFuncTrampoline);
|
||||||
|
jerry_object_set_native_ptr(fn, &SCRIPT_DEF_NATIVE_INFO, binding);
|
||||||
|
|
||||||
|
jerry_value_t key = jerry_string_sz(defs[i].name);
|
||||||
|
jerry_object_set(target, key, fn);
|
||||||
|
jerry_value_free(key);
|
||||||
|
jerry_value_free(fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scriptDefDefinePropsOnTarget(
|
||||||
|
const jerry_value_t target,
|
||||||
|
const scriptproto_t *proto,
|
||||||
|
const scriptpropdef_t *defs
|
||||||
|
) {
|
||||||
|
assertNotNull(defs, "Property definitions must not be null");
|
||||||
|
|
||||||
|
for(size_t i = 0; defs[i].name != NULL; i++) {
|
||||||
|
assertTrue(
|
||||||
|
SCRIPT_DEF_BINDING_COUNT < SCRIPT_DEF_BINDING_MAX,
|
||||||
|
"Script def binding capacity exceeded"
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptdefbinding_t *getBinding =
|
||||||
|
&SCRIPT_DEF_BINDINGS[SCRIPT_DEF_BINDING_COUNT++];
|
||||||
|
getBinding->proto = proto;
|
||||||
|
getBinding->funcDef = NULL;
|
||||||
|
getBinding->propDef = &defs[i];
|
||||||
|
|
||||||
|
jerry_property_descriptor_t desc;
|
||||||
|
memoryZero(&desc, sizeof(desc));
|
||||||
|
desc.flags = (uint16_t)(
|
||||||
|
JERRY_PROP_IS_GET_DEFINED |
|
||||||
|
JERRY_PROP_IS_ENUMERABLE_DEFINED | JERRY_PROP_IS_ENUMERABLE |
|
||||||
|
JERRY_PROP_IS_CONFIGURABLE_DEFINED | JERRY_PROP_IS_CONFIGURABLE
|
||||||
|
);
|
||||||
|
desc.getter = jerry_function_external(scriptDefPropGetTrampoline);
|
||||||
|
jerry_object_set_native_ptr(
|
||||||
|
desc.getter, &SCRIPT_DEF_NATIVE_INFO, getBinding
|
||||||
|
);
|
||||||
|
|
||||||
|
if(defs[i].setter != NULL) {
|
||||||
|
assertTrue(
|
||||||
|
SCRIPT_DEF_BINDING_COUNT < SCRIPT_DEF_BINDING_MAX,
|
||||||
|
"Script def binding capacity exceeded"
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptdefbinding_t *setBinding =
|
||||||
|
&SCRIPT_DEF_BINDINGS[SCRIPT_DEF_BINDING_COUNT++];
|
||||||
|
setBinding->proto = proto;
|
||||||
|
setBinding->funcDef = NULL;
|
||||||
|
setBinding->propDef = &defs[i];
|
||||||
|
|
||||||
|
desc.flags |= JERRY_PROP_IS_SET_DEFINED;
|
||||||
|
desc.setter = jerry_function_external(scriptDefPropSetTrampoline);
|
||||||
|
jerry_object_set_native_ptr(
|
||||||
|
desc.setter, &SCRIPT_DEF_NATIVE_INFO, setBinding
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_value_t key = jerry_string_sz(defs[i].name);
|
||||||
|
jerry_value_t result = jerry_object_define_own_prop(target, key, &desc);
|
||||||
|
jerry_value_free(result);
|
||||||
|
jerry_value_free(key);
|
||||||
|
jerry_value_free(desc.getter);
|
||||||
|
if(defs[i].setter != NULL) jerry_value_free(desc.setter);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "scriptproto.h"
|
||||||
|
#include "scriptvalue.h"
|
||||||
|
|
||||||
|
/** Max params a single scriptfuncdef_t may declare. */
|
||||||
|
#define SCRIPT_DEF_PARAMS_MAX 8
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Native handler for a scriptfuncdef_t. handle is the native pointer the
|
||||||
|
* owning scriptproto_t wraps (or NULL for a proto-less/global
|
||||||
|
* function), already resolved by the trampoline. args holds argc
|
||||||
|
* already-decoded, already-type-checked values (one per paramTypes
|
||||||
|
* entry -- a SCRIPT_TYPE_VEC3 param still counts as a single args[]
|
||||||
|
* entry even though it consumed 3 raw JS arguments).
|
||||||
|
*/
|
||||||
|
typedef scriptvalue_t (*scriptfuncdeffn_t)(
|
||||||
|
void *handle,
|
||||||
|
const scriptvalue_t *args,
|
||||||
|
const uint32_t argc
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declarative description of one JS-callable method. Terminate an array
|
||||||
|
* of these with a zeroed entry ({ .name = NULL }).
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
const char_t *name;
|
||||||
|
scriptvaluetype_t returnType;
|
||||||
|
scriptvaluetype_t paramTypes[SCRIPT_DEF_PARAMS_MAX];
|
||||||
|
uint32_t paramCount;
|
||||||
|
scriptfuncdeffn_t fn;
|
||||||
|
} scriptfuncdef_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Native getter for a scriptpropdef_t. handle is as in
|
||||||
|
* scriptfuncdeffn_t.
|
||||||
|
*/
|
||||||
|
typedef scriptvalue_t (*scriptpropdefgetter_t)(void *handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Native setter for a scriptpropdef_t. value is already decoded/type-
|
||||||
|
* checked according to the property's declared type.
|
||||||
|
*/
|
||||||
|
typedef void (*scriptpropdefsetter_t)(
|
||||||
|
void *handle,
|
||||||
|
const scriptvalue_t *value
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declarative description of one JS-visible property. setter may be
|
||||||
|
* NULL for a read-only property. Terminate an array of these with a
|
||||||
|
* zeroed entry ({ .name = NULL }).
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
const char_t *name;
|
||||||
|
scriptvaluetype_t type;
|
||||||
|
scriptpropdefgetter_t getter;
|
||||||
|
scriptpropdefsetter_t setter;
|
||||||
|
} scriptpropdef_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define instance methods on a scriptproto_t's prototype. Each
|
||||||
|
* handler's handle param resolves via scriptProtoGetValue(proto, this).
|
||||||
|
*
|
||||||
|
* @param proto The class prototype to attach methods to.
|
||||||
|
* @param defs Sentinel-terminated array of method definitions.
|
||||||
|
*/
|
||||||
|
void scriptProtoDefineFuncDefs(
|
||||||
|
scriptproto_t *proto,
|
||||||
|
const scriptfuncdef_t *defs
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define instance properties on a scriptproto_t's prototype. Each
|
||||||
|
* getter/setter's handle param resolves via scriptProtoGetValue(proto,
|
||||||
|
* this).
|
||||||
|
*
|
||||||
|
* @param proto The class prototype to attach properties to.
|
||||||
|
* @param defs Sentinel-terminated array of property definitions.
|
||||||
|
*/
|
||||||
|
void scriptProtoDefinePropDefs(
|
||||||
|
scriptproto_t *proto,
|
||||||
|
const scriptpropdef_t *defs
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define functions directly on an arbitrary JS object (e.g. a plain
|
||||||
|
* jerry_object() used as a global namespace, or proto->constructor for
|
||||||
|
* statics) with no owning scriptproto_t -- every handler's handle param
|
||||||
|
* is always NULL.
|
||||||
|
*
|
||||||
|
* @param target The JS object to attach functions to.
|
||||||
|
* @param defs Sentinel-terminated array of method definitions.
|
||||||
|
*/
|
||||||
|
void scriptDefDefineGlobalFuncs(
|
||||||
|
const jerry_value_t target,
|
||||||
|
const scriptfuncdef_t *defs
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define properties directly on an arbitrary JS object, with no owning
|
||||||
|
* scriptproto_t -- every getter/setter's handle param is always NULL.
|
||||||
|
*
|
||||||
|
* @param target The JS object to attach properties to.
|
||||||
|
* @param defs Sentinel-terminated array of property definitions.
|
||||||
|
*/
|
||||||
|
void scriptDefDefineGlobalProps(
|
||||||
|
const jerry_value_t target,
|
||||||
|
const scriptpropdef_t *defs
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forget every binding registered via the functions above. Must be
|
||||||
|
* called during script manager teardown (alongside
|
||||||
|
* scriptProtoDisposeAll()) so a later scriptManagerInit() cycle can
|
||||||
|
* re-register from a clean slate instead of overflowing the binding
|
||||||
|
* registry.
|
||||||
|
*/
|
||||||
|
void scriptDefDisposeAll(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal. Resolves the native handle for a call, or NULL if proto is
|
||||||
|
* NULL (the global-target case).
|
||||||
|
*
|
||||||
|
* @param proto The owning prototype, or NULL.
|
||||||
|
* @param thisValue The this_value from the call's jerry_call_info_t.
|
||||||
|
* @return The resolved native handle, or NULL.
|
||||||
|
*/
|
||||||
|
void *scriptDefResolveHandle(
|
||||||
|
const scriptproto_t *proto,
|
||||||
|
const jerry_value_t thisValue
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal. Shared implementation behind scriptProtoDefineFuncDefs()
|
||||||
|
* and scriptDefDefineGlobalFuncs().
|
||||||
|
*
|
||||||
|
* @param target The JS object to attach functions to.
|
||||||
|
* @param proto The owning prototype for handle resolution, or NULL.
|
||||||
|
* @param defs Sentinel-terminated array of method definitions.
|
||||||
|
*/
|
||||||
|
void scriptDefDefineFuncsOnTarget(
|
||||||
|
const jerry_value_t target,
|
||||||
|
const scriptproto_t *proto,
|
||||||
|
const scriptfuncdef_t *defs
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal. Shared implementation behind scriptProtoDefinePropDefs()
|
||||||
|
* and scriptDefDefineGlobalProps().
|
||||||
|
*
|
||||||
|
* @param target The JS object to attach properties to.
|
||||||
|
* @param proto The owning prototype for handle resolution, or NULL.
|
||||||
|
* @param defs Sentinel-terminated array of property definitions.
|
||||||
|
*/
|
||||||
|
void scriptDefDefinePropsOnTarget(
|
||||||
|
const jerry_value_t target,
|
||||||
|
const scriptproto_t *proto,
|
||||||
|
const scriptpropdef_t *defs
|
||||||
|
);
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
#include "scriptproto.h"
|
#include "scriptproto.h"
|
||||||
|
#include "scriptdef.h"
|
||||||
#include "script/module/modulelist.h"
|
#include "script/module/modulelist.h"
|
||||||
#include "script/module/require/modulerequire.h"
|
#include "script/module/require/modulerequire.h"
|
||||||
|
|
||||||
@@ -163,6 +164,7 @@ errorret_t scriptManagerCallValue(
|
|||||||
errorret_t scriptManagerDispose(void) {
|
errorret_t scriptManagerDispose(void) {
|
||||||
moduleListDispose();
|
moduleListDispose();
|
||||||
scriptProtoDisposeAll();
|
scriptProtoDisposeAll();
|
||||||
|
scriptDefDisposeAll();
|
||||||
|
|
||||||
for(uint8_t i = 0; i < SCRIPT_MANAGER.globalKeyCacheCount; i++) {
|
for(uint8_t i = 0; i < SCRIPT_MANAGER.globalKeyCacheCount; i++) {
|
||||||
jerry_value_free(SCRIPT_MANAGER.globalKeyCache[i].key);
|
jerry_value_free(SCRIPT_MANAGER.globalKeyCache[i].key);
|
||||||
|
|||||||
@@ -0,0 +1,279 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "scriptvalue.h"
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "util/string.h"
|
||||||
|
|
||||||
|
scriptvalue_t scriptValueVoid(void) {
|
||||||
|
scriptvalue_t v;
|
||||||
|
memoryZero(&v, sizeof(v));
|
||||||
|
v.type = SCRIPT_TYPE_VOID;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptvalue_t scriptValueNumber(const double value) {
|
||||||
|
scriptvalue_t v;
|
||||||
|
memoryZero(&v, sizeof(v));
|
||||||
|
v.type = SCRIPT_TYPE_NUMBER;
|
||||||
|
v.as.number = value;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptvalue_t scriptValueInt(const int32_t value) {
|
||||||
|
scriptvalue_t v;
|
||||||
|
memoryZero(&v, sizeof(v));
|
||||||
|
v.type = SCRIPT_TYPE_INT;
|
||||||
|
v.as.intValue = value;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptvalue_t scriptValueBool(const bool_t value) {
|
||||||
|
scriptvalue_t v;
|
||||||
|
memoryZero(&v, sizeof(v));
|
||||||
|
v.type = SCRIPT_TYPE_BOOL;
|
||||||
|
v.as.boolValue = value;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptvalue_t scriptValueString(const char_t *value) {
|
||||||
|
scriptvalue_t v;
|
||||||
|
memoryZero(&v, sizeof(v));
|
||||||
|
v.type = SCRIPT_TYPE_STRING;
|
||||||
|
stringCopy(v.as.string, value, SCRIPT_VALUE_STRING_MAX - 1);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptvalue_t scriptValueVec3(const vec3 value) {
|
||||||
|
scriptvalue_t v;
|
||||||
|
memoryZero(&v, sizeof(v));
|
||||||
|
v.type = SCRIPT_TYPE_VEC3;
|
||||||
|
v.as.vec3Value[0] = value[0];
|
||||||
|
v.as.vec3Value[1] = value[1];
|
||||||
|
v.as.vec3Value[2] = value[2];
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptvalue_t scriptValuePointer(void *value) {
|
||||||
|
scriptvalue_t v;
|
||||||
|
memoryZero(&v, sizeof(v));
|
||||||
|
v.type = SCRIPT_TYPE_POINTER;
|
||||||
|
v.as.pointer = value;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t scriptDefReadArg(
|
||||||
|
const scriptvaluetype_t type,
|
||||||
|
const jerry_value_t args[],
|
||||||
|
const jerry_length_t argc,
|
||||||
|
uint32_t *consumed,
|
||||||
|
scriptvalue_t *out,
|
||||||
|
jerry_value_t *outThrown
|
||||||
|
) {
|
||||||
|
switch(type) {
|
||||||
|
case SCRIPT_TYPE_NUMBER:
|
||||||
|
if(*consumed >= argc || !jerry_value_is_number(args[*consumed])) {
|
||||||
|
*outThrown = moduleBaseThrow("Expected number argument");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out->type = SCRIPT_TYPE_NUMBER;
|
||||||
|
out->as.number = jerry_value_as_number(args[(*consumed)++]);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case SCRIPT_TYPE_INT:
|
||||||
|
if(*consumed >= argc || !jerry_value_is_number(args[*consumed])) {
|
||||||
|
*outThrown = moduleBaseThrow("Expected number argument");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out->type = SCRIPT_TYPE_INT;
|
||||||
|
out->as.intValue = (int32_t)jerry_value_as_number(args[(*consumed)++]);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case SCRIPT_TYPE_BOOL:
|
||||||
|
if(*consumed >= argc) {
|
||||||
|
*outThrown = moduleBaseThrow("Expected boolean argument");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out->type = SCRIPT_TYPE_BOOL;
|
||||||
|
out->as.boolValue = jerry_value_is_true(args[(*consumed)++]);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case SCRIPT_TYPE_STRING:
|
||||||
|
if(*consumed >= argc || !jerry_value_is_string(args[*consumed])) {
|
||||||
|
*outThrown = moduleBaseThrow("Expected string argument");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out->type = SCRIPT_TYPE_STRING;
|
||||||
|
moduleBaseToString(
|
||||||
|
args[(*consumed)++], out->as.string, SCRIPT_VALUE_STRING_MAX
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case SCRIPT_TYPE_VEC3: {
|
||||||
|
if(*consumed + 3 > argc) {
|
||||||
|
*outThrown = moduleBaseThrow("Expected 3 number arguments");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for(uint32_t i = 0; i < 3; i++) {
|
||||||
|
if(!jerry_value_is_number(args[*consumed + i])) {
|
||||||
|
*outThrown = moduleBaseThrow("Expected number argument");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out->type = SCRIPT_TYPE_VEC3;
|
||||||
|
out->as.vec3Value[0] = (float_t)jerry_value_as_number(args[*consumed]);
|
||||||
|
out->as.vec3Value[1] =
|
||||||
|
(float_t)jerry_value_as_number(args[*consumed + 1]);
|
||||||
|
out->as.vec3Value[2] =
|
||||||
|
(float_t)jerry_value_as_number(args[*consumed + 2]);
|
||||||
|
*consumed += 3;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SCRIPT_TYPE_POINTER:
|
||||||
|
if(*consumed >= argc || !jerry_value_is_object(args[*consumed])) {
|
||||||
|
*outThrown = moduleBaseThrow("Expected object argument");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out->type = SCRIPT_TYPE_POINTER;
|
||||||
|
out->as.pointer = moduleBaseUnwrapPointer(args[(*consumed)++]);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case SCRIPT_TYPE_CALLBACK:
|
||||||
|
if(*consumed >= argc || !jerry_value_is_function(args[*consumed])) {
|
||||||
|
*outThrown = moduleBaseThrow("Expected function argument");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out->type = SCRIPT_TYPE_CALLBACK;
|
||||||
|
out->as.value = jerry_value_copy(args[(*consumed)++]);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case SCRIPT_TYPE_VALUE:
|
||||||
|
if(*consumed >= argc) {
|
||||||
|
*outThrown = moduleBaseThrow("Expected argument");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out->type = SCRIPT_TYPE_VALUE;
|
||||||
|
out->as.value = args[(*consumed)++];
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
*outThrown = moduleBaseThrow("Unsupported script argument type");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t scriptDefReadValue(
|
||||||
|
const scriptvaluetype_t type,
|
||||||
|
const jerry_value_t value,
|
||||||
|
scriptvalue_t *out,
|
||||||
|
jerry_value_t *outThrown
|
||||||
|
) {
|
||||||
|
switch(type) {
|
||||||
|
case SCRIPT_TYPE_NUMBER:
|
||||||
|
if(!jerry_value_is_number(value)) {
|
||||||
|
*outThrown = moduleBaseThrow("Expected number value");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out->type = SCRIPT_TYPE_NUMBER;
|
||||||
|
out->as.number = jerry_value_as_number(value);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case SCRIPT_TYPE_INT:
|
||||||
|
if(!jerry_value_is_number(value)) {
|
||||||
|
*outThrown = moduleBaseThrow("Expected number value");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out->type = SCRIPT_TYPE_INT;
|
||||||
|
out->as.intValue = (int32_t)jerry_value_as_number(value);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case SCRIPT_TYPE_BOOL:
|
||||||
|
out->type = SCRIPT_TYPE_BOOL;
|
||||||
|
out->as.boolValue = jerry_value_is_true(value);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case SCRIPT_TYPE_STRING:
|
||||||
|
if(!jerry_value_is_string(value)) {
|
||||||
|
*outThrown = moduleBaseThrow("Expected string value");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out->type = SCRIPT_TYPE_STRING;
|
||||||
|
moduleBaseToString(value, out->as.string, SCRIPT_VALUE_STRING_MAX);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case SCRIPT_TYPE_VEC3: {
|
||||||
|
if(!jerry_value_is_object(value)) {
|
||||||
|
*outThrown = moduleBaseThrow("Expected {x, y, z} object value");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
jerry_value_t xVal = moduleBaseGetProp(value, "x");
|
||||||
|
jerry_value_t yVal = moduleBaseGetProp(value, "y");
|
||||||
|
jerry_value_t zVal = moduleBaseGetProp(value, "z");
|
||||||
|
out->type = SCRIPT_TYPE_VEC3;
|
||||||
|
out->as.vec3Value[0] = moduleBaseValueFloat(xVal);
|
||||||
|
out->as.vec3Value[1] = moduleBaseValueFloat(yVal);
|
||||||
|
out->as.vec3Value[2] = moduleBaseValueFloat(zVal);
|
||||||
|
jerry_value_free(xVal);
|
||||||
|
jerry_value_free(yVal);
|
||||||
|
jerry_value_free(zVal);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SCRIPT_TYPE_POINTER:
|
||||||
|
if(!jerry_value_is_object(value)) {
|
||||||
|
*outThrown = moduleBaseThrow("Expected object value");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out->type = SCRIPT_TYPE_POINTER;
|
||||||
|
out->as.pointer = moduleBaseUnwrapPointer(value);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case SCRIPT_TYPE_CALLBACK:
|
||||||
|
if(!jerry_value_is_function(value)) {
|
||||||
|
*outThrown = moduleBaseThrow("Expected function value");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out->type = SCRIPT_TYPE_CALLBACK;
|
||||||
|
out->as.value = jerry_value_copy(value);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case SCRIPT_TYPE_VALUE:
|
||||||
|
out->type = SCRIPT_TYPE_VALUE;
|
||||||
|
out->as.value = value;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
*outThrown = moduleBaseThrow("Unsupported script value type");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_value_t scriptDefValueToJerry(const scriptvalue_t *value) {
|
||||||
|
switch(value->type) {
|
||||||
|
case SCRIPT_TYPE_VOID:
|
||||||
|
return jerry_undefined();
|
||||||
|
case SCRIPT_TYPE_NUMBER:
|
||||||
|
return jerry_number(value->as.number);
|
||||||
|
case SCRIPT_TYPE_INT:
|
||||||
|
return jerry_number(value->as.intValue);
|
||||||
|
case SCRIPT_TYPE_BOOL:
|
||||||
|
return jerry_boolean(value->as.boolValue);
|
||||||
|
case SCRIPT_TYPE_STRING:
|
||||||
|
return jerry_string_sz(value->as.string);
|
||||||
|
case SCRIPT_TYPE_VEC3:
|
||||||
|
return moduleBaseVec3ToObject(value->as.vec3Value);
|
||||||
|
case SCRIPT_TYPE_POINTER:
|
||||||
|
return moduleBaseWrapPointer(value->as.pointer);
|
||||||
|
case SCRIPT_TYPE_CALLBACK:
|
||||||
|
case SCRIPT_TYPE_VALUE:
|
||||||
|
return value->as.value;
|
||||||
|
default:
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
#include <jerryscript.h>
|
||||||
|
|
||||||
|
/** Max chars (incl. null terminator) a SCRIPT_TYPE_STRING value holds. */
|
||||||
|
#define SCRIPT_VALUE_STRING_MAX 128
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of a scriptvalue_t, and of a single function param/property.
|
||||||
|
*
|
||||||
|
* SCRIPT_TYPE_VEC3 is read differently depending on context: as a
|
||||||
|
* function param it consumes 3 consecutive JS number arguments (e.g.
|
||||||
|
* `setPosition(x, y, z)`); as a property value it reads/writes a single
|
||||||
|
* `{x, y, z}` JS object.
|
||||||
|
*
|
||||||
|
* SCRIPT_TYPE_CALLBACK validates the incoming value is a JS function and
|
||||||
|
* takes a jerry_value_copy() of it for you -- the receiving handler owns
|
||||||
|
* that reference (store it, invoke it later with the existing
|
||||||
|
* scriptManagerCallValue(), and jerry_value_free() it when done).
|
||||||
|
*
|
||||||
|
* SCRIPT_TYPE_VALUE is a raw passthrough jerry_value_t, borrowed only
|
||||||
|
* for the duration of the call -- do not store it without copying it
|
||||||
|
* yourself first.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
SCRIPT_TYPE_VOID,
|
||||||
|
SCRIPT_TYPE_NUMBER,
|
||||||
|
SCRIPT_TYPE_INT,
|
||||||
|
SCRIPT_TYPE_BOOL,
|
||||||
|
SCRIPT_TYPE_STRING,
|
||||||
|
SCRIPT_TYPE_VEC3,
|
||||||
|
SCRIPT_TYPE_POINTER,
|
||||||
|
SCRIPT_TYPE_CALLBACK,
|
||||||
|
SCRIPT_TYPE_VALUE
|
||||||
|
} scriptvaluetype_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A typed value crossing the JS/C boundary -- a function param/return
|
||||||
|
* value, or a property's get/set value.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
scriptvaluetype_t type;
|
||||||
|
union {
|
||||||
|
double number;
|
||||||
|
int32_t intValue;
|
||||||
|
bool_t boolValue;
|
||||||
|
char_t string[SCRIPT_VALUE_STRING_MAX];
|
||||||
|
vec3 vec3Value;
|
||||||
|
void *pointer;
|
||||||
|
jerry_value_t value;
|
||||||
|
} as;
|
||||||
|
} scriptvalue_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a SCRIPT_TYPE_VOID value (e.g. to return from a fn with no
|
||||||
|
* meaningful result).
|
||||||
|
*/
|
||||||
|
scriptvalue_t scriptValueVoid(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a SCRIPT_TYPE_NUMBER value.
|
||||||
|
*/
|
||||||
|
scriptvalue_t scriptValueNumber(const double value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a SCRIPT_TYPE_INT value.
|
||||||
|
*/
|
||||||
|
scriptvalue_t scriptValueInt(const int32_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a SCRIPT_TYPE_BOOL value.
|
||||||
|
*/
|
||||||
|
scriptvalue_t scriptValueBool(const bool_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a SCRIPT_TYPE_STRING value. Copies value (truncating to
|
||||||
|
* SCRIPT_VALUE_STRING_MAX - 1 chars) into the returned scriptvalue_t --
|
||||||
|
* value need not outlive this call.
|
||||||
|
*/
|
||||||
|
scriptvalue_t scriptValueString(const char_t *value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a SCRIPT_TYPE_VEC3 value.
|
||||||
|
*/
|
||||||
|
scriptvalue_t scriptValueVec3(const vec3 value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a SCRIPT_TYPE_POINTER value.
|
||||||
|
*/
|
||||||
|
scriptvalue_t scriptValuePointer(void *value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal. Decodes one function-call argument (advancing *consumed by
|
||||||
|
* 1, or by 3 for SCRIPT_TYPE_VEC3) into a typed scriptvalue_t.
|
||||||
|
*
|
||||||
|
* @param type The declared param type.
|
||||||
|
* @param args The raw JS arguments array.
|
||||||
|
* @param argc Number of raw JS arguments.
|
||||||
|
* @param consumed In/out cursor into args.
|
||||||
|
* @param out Receives the decoded value on success.
|
||||||
|
* @param outThrown Receives a thrown JS error value on failure.
|
||||||
|
* @return true on success, false if outThrown was set.
|
||||||
|
*/
|
||||||
|
bool_t scriptDefReadArg(
|
||||||
|
const scriptvaluetype_t type,
|
||||||
|
const jerry_value_t args[],
|
||||||
|
const jerry_length_t argc,
|
||||||
|
uint32_t *consumed,
|
||||||
|
scriptvalue_t *out,
|
||||||
|
jerry_value_t *outThrown
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal. Decodes a single incoming JS value (e.g. a property
|
||||||
|
* setter's argument) into a typed scriptvalue_t. Unlike
|
||||||
|
* scriptDefReadArg, SCRIPT_TYPE_VEC3 here decodes a single {x, y, z}
|
||||||
|
* object rather than 3 raw arguments.
|
||||||
|
*
|
||||||
|
* @param type The declared value type.
|
||||||
|
* @param value The raw incoming JS value.
|
||||||
|
* @param out Receives the decoded value on success.
|
||||||
|
* @param outThrown Receives a thrown JS error value on failure.
|
||||||
|
* @return true on success, false if outThrown was set.
|
||||||
|
*/
|
||||||
|
bool_t scriptDefReadValue(
|
||||||
|
const scriptvaluetype_t type,
|
||||||
|
const jerry_value_t value,
|
||||||
|
scriptvalue_t *out,
|
||||||
|
jerry_value_t *outThrown
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal. Converts a typed scriptvalue_t into a JS value (the
|
||||||
|
* return/get direction).
|
||||||
|
*
|
||||||
|
* @param value The value to convert.
|
||||||
|
* @return The equivalent JS value.
|
||||||
|
*/
|
||||||
|
jerry_value_t scriptDefValueToJerry(const scriptvalue_t *value);
|
||||||
@@ -7,6 +7,8 @@ include(dusktest)
|
|||||||
|
|
||||||
dusktest(test_modulerequire.c)
|
dusktest(test_modulerequire.c)
|
||||||
|
|
||||||
|
dusktest(test_scriptdef.c)
|
||||||
|
|
||||||
dusktest(test_overworldscene.c)
|
dusktest(test_overworldscene.c)
|
||||||
target_compile_definitions(test_overworldscene PRIVATE
|
target_compile_definitions(test_overworldscene PRIVATE
|
||||||
DUSK_ASSETS_DIR="${DUSK_ASSETS_DIR}"
|
DUSK_ASSETS_DIR="${DUSK_ASSETS_DIR}"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
#include "dusktest.h"
|
#include "dusktest.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "time/time.h"
|
#include "asset/asset.h"
|
||||||
#include "scene/scene.h"
|
#include "scene/scene.h"
|
||||||
#include "entity/entitymanager.h"
|
#include "entity/entitymanager.h"
|
||||||
#include "entity/component.h"
|
#include "entity/component.h"
|
||||||
@@ -18,7 +18,8 @@
|
|||||||
#include "display/mesh/capsule.h"
|
#include "display/mesh/capsule.h"
|
||||||
#include "script/scriptmanager.h"
|
#include "script/scriptmanager.h"
|
||||||
#include "script/module/scene/modulescene.h"
|
#include "script/module/scene/modulescene.h"
|
||||||
#include <math.h>
|
#include "script/module/require/modulerequire.h"
|
||||||
|
#include <zip.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@@ -60,17 +61,93 @@ static errorret_t installOverworldScene(void) {
|
|||||||
);
|
);
|
||||||
memoryFree(fileSrc);
|
memoryFree(fileSrc);
|
||||||
|
|
||||||
|
// overworldscene.js now require()s sibling files (Player.js etc.) --
|
||||||
|
// push the same base directory scriptManagerExecFile() would push for
|
||||||
|
// a real load, since this test bypasses that path to exec a wrapped
|
||||||
|
// copy of the source directly instead.
|
||||||
|
moduleRequireDirPush("scripts/");
|
||||||
errorret_t ret = scriptManagerExec(wrapped, NULL);
|
errorret_t ret = scriptManagerExec(wrapped, NULL);
|
||||||
|
moduleRequireDirPop();
|
||||||
memoryFree(wrapped);
|
memoryFree(wrapped);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static zip_t *g_zip = NULL;
|
||||||
|
|
||||||
|
// overworldscene.js require()s sibling files (Player.js/TestPlane.js/
|
||||||
|
// PlayerCamera.js), which resolve through the asset system's ASSET.zip --
|
||||||
|
// there's no real-filesystem fallback (see assetFileInit()). Package the
|
||||||
|
// real, shipped copies of those files (read straight off disk, not
|
||||||
|
// hardcoded here) into an in-memory zip so require() finds the same
|
||||||
|
// content a real build would.
|
||||||
static int overworld_setup(void **state) {
|
static int overworld_setup(void **state) {
|
||||||
sceneInit();
|
sceneInit();
|
||||||
|
|
||||||
errorret_t ret = scriptManagerInit();
|
errorret_t ret = scriptManagerInit();
|
||||||
if(errorIsNotOk(ret)) { errorCatch(ret); return -1; }
|
if(errorIsNotOk(ret)) { errorCatch(ret); return -1; }
|
||||||
|
|
||||||
|
zip_error_t err;
|
||||||
|
zip_error_init(&err);
|
||||||
|
|
||||||
|
zip_source_t *write_src = zip_source_buffer_create(NULL, 0, 1, &err);
|
||||||
|
if(!write_src) return -1;
|
||||||
|
|
||||||
|
zip_t *za = zip_open_from_source(write_src, ZIP_TRUNCATE, &err);
|
||||||
|
if(!za) { zip_source_free(write_src); return -1; }
|
||||||
|
|
||||||
|
const char_t *requiredScripts[] = {
|
||||||
|
"scripts/Player.js", "scripts/TestPlane.js", "scripts/PlayerCamera.js"
|
||||||
|
};
|
||||||
|
char_t *scriptSrcs[3];
|
||||||
|
|
||||||
|
for(size_t i = 0; i < 3; i++) {
|
||||||
|
scriptSrcs[i] = readFile(requiredScripts[i]);
|
||||||
|
zip_source_t *s = zip_source_buffer(
|
||||||
|
za, scriptSrcs[i], strlen(scriptSrcs[i]), 0
|
||||||
|
);
|
||||||
|
if(zip_file_add(za, requiredScripts[i], s, ZIP_FL_OVERWRITE) < 0) {
|
||||||
|
zip_close(za);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zip_source_keep(write_src);
|
||||||
|
if(zip_close(za) != 0) { zip_source_free(write_src); return -1; }
|
||||||
|
|
||||||
|
// zip_close() has fully read every source added above by now, so the
|
||||||
|
// backing buffers are safe to free.
|
||||||
|
for(size_t i = 0; i < 3; i++) memoryFree(scriptSrcs[i]);
|
||||||
|
|
||||||
|
zip_stat_t zs;
|
||||||
|
memset(&zs, 0, sizeof(zs));
|
||||||
|
if(zip_source_stat(write_src, &zs) != 0 || !(zs.valid & ZIP_STAT_SIZE)) {
|
||||||
|
zip_source_free(write_src);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *zipbuf = malloc((size_t)zs.size);
|
||||||
|
if(!zipbuf) { zip_source_free(write_src); return -1; }
|
||||||
|
|
||||||
|
if(zip_source_open(write_src) != 0) {
|
||||||
|
free(zipbuf);
|
||||||
|
zip_source_free(write_src);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
zip_source_read(write_src, zipbuf, (zip_uint64_t)zs.size);
|
||||||
|
zip_source_close(write_src);
|
||||||
|
zip_source_free(write_src);
|
||||||
|
|
||||||
|
zip_error_init(&err);
|
||||||
|
zip_source_t *read_src = zip_source_buffer_create(
|
||||||
|
zipbuf, (zip_uint64_t)zs.size, 1, &err
|
||||||
|
);
|
||||||
|
if(!read_src) { free(zipbuf); return -1; }
|
||||||
|
|
||||||
|
g_zip = zip_open_from_source(read_src, 0, &err);
|
||||||
|
if(!g_zip) { zip_source_free(read_src); return -1; }
|
||||||
|
|
||||||
|
ASSET.zip = g_zip;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +157,9 @@ static int overworld_teardown(void **state) {
|
|||||||
|
|
||||||
sceneDispose();
|
sceneDispose();
|
||||||
|
|
||||||
|
if(g_zip) { zip_close(g_zip); g_zip = NULL; }
|
||||||
|
ASSET.zip = NULL;
|
||||||
|
|
||||||
// JerryScript defers freeing native-wrapped handles (Entity/Scene/
|
// JerryScript defers freeing native-wrapped handles (Entity/Scene/
|
||||||
// Position/Physics/Renderable instances) until GC/jerry_cleanup() runs,
|
// Position/Physics/Renderable instances) until GC/jerry_cleanup() runs,
|
||||||
// so the leak check can only be meaningful after scriptManagerDispose()
|
// so the leak check can only be meaningful after scriptManagerDispose()
|
||||||
@@ -96,26 +176,53 @@ static void test_overworldscene_builds_expected_entities(void **state) {
|
|||||||
assert_true(sceneId != SCENE_ID_INVALID);
|
assert_true(sceneId != SCENE_ID_INVALID);
|
||||||
entitymanager_t *mgr = sceneGetEntities(sceneId);
|
entitymanager_t *mgr = sceneGetEntities(sceneId);
|
||||||
|
|
||||||
// Entity 0: camera. Position + Camera components, initial orbit
|
// Entity 0: player (new Player() runs first in init()).
|
||||||
// position placed with angle=0 (Time.delta defaults to 0 with no
|
entityid_t playerEntity = 0;
|
||||||
// timeInit()/timeUpdate() in this test), i.e. eye=(radius, height, 0).
|
componentid_t playerPos = entityGetComponent(
|
||||||
entityid_t camEntity = 0;
|
mgr, playerEntity, COMPONENT_TYPE_POSITION
|
||||||
componentid_t camPos = entityGetComponent(
|
|
||||||
mgr, camEntity, COMPONENT_TYPE_POSITION
|
|
||||||
);
|
);
|
||||||
assert_true(camPos != COMPONENT_ID_INVALID);
|
assert_true(playerPos != COMPONENT_ID_INVALID);
|
||||||
|
|
||||||
|
vec3 playerPosition;
|
||||||
|
entityPositionGetLocalPosition(mgr, playerEntity, playerPos, playerPosition);
|
||||||
|
assert_float_equal(playerPosition[0], 0.0f, 0.0001f);
|
||||||
|
assert_float_equal(playerPosition[1], 2.0f, 0.0001f);
|
||||||
|
assert_float_equal(playerPosition[2], 0.0f, 0.0001f);
|
||||||
|
|
||||||
|
componentid_t playerPhysics = entityGetComponent(
|
||||||
|
mgr, playerEntity, COMPONENT_TYPE_PHYSICS
|
||||||
|
);
|
||||||
|
assert_true(playerPhysics != COMPONENT_ID_INVALID);
|
||||||
|
assert_int_equal(
|
||||||
|
entityPhysicsGetBodyType(mgr, playerEntity, playerPhysics),
|
||||||
|
PHYSICS_BODY_DYNAMIC
|
||||||
|
);
|
||||||
|
physicsshape_t playerShape = entityPhysicsGetShape(
|
||||||
|
mgr, playerEntity, playerPhysics
|
||||||
|
);
|
||||||
|
assert_int_equal(playerShape.type, PHYSICS_SHAPE_CAPSULE);
|
||||||
|
assert_float_equal(playerShape.data.capsule.radius, 0.5f, 0.0001f);
|
||||||
|
assert_float_equal(playerShape.data.capsule.halfHeight, 0.5f, 0.0001f);
|
||||||
|
|
||||||
|
componentid_t playerRenderable = entityGetComponent(
|
||||||
|
mgr, playerEntity, COMPONENT_TYPE_RENDERABLE
|
||||||
|
);
|
||||||
|
assert_true(playerRenderable != COMPONENT_ID_INVALID);
|
||||||
|
entityrenderable_t *playerR = componentGetData(
|
||||||
|
mgr, playerEntity, playerRenderable, COMPONENT_TYPE_RENDERABLE
|
||||||
|
);
|
||||||
|
assert_ptr_equal(playerR->data.material.meshes[0], &CAPSULE_MESH_SIMPLE);
|
||||||
|
assert_int_equal(playerR->data.material.material.unlit.color.r, 0);
|
||||||
|
assert_int_equal(playerR->data.material.material.unlit.color.g, 0);
|
||||||
|
assert_int_equal(playerR->data.material.material.unlit.color.b, 255);
|
||||||
|
assert_int_equal(playerR->data.material.material.unlit.color.a, 255);
|
||||||
|
|
||||||
assert_true(
|
assert_true(
|
||||||
entityGetComponent(mgr, camEntity, COMPONENT_TYPE_CAMERA) !=
|
entityGetComponent(mgr, playerEntity, COMPONENT_TYPE_PLAYER) !=
|
||||||
COMPONENT_ID_INVALID
|
COMPONENT_ID_INVALID
|
||||||
);
|
);
|
||||||
|
|
||||||
vec3 camPosition;
|
// Entity 1: static ground plane (new TestPlane() runs second).
|
||||||
entityPositionGetLocalPosition(mgr, camEntity, camPos, camPosition);
|
|
||||||
assert_float_equal(camPosition[0], 18.0f, 0.0001f);
|
|
||||||
assert_float_equal(camPosition[1], 10.0f, 0.0001f);
|
|
||||||
assert_float_equal(camPosition[2], 0.0f, 0.0001f);
|
|
||||||
|
|
||||||
// Entity 1: static ground plane.
|
|
||||||
entityid_t planeEntity = 1;
|
entityid_t planeEntity = 1;
|
||||||
componentid_t planePos = entityGetComponent(
|
componentid_t planePos = entityGetComponent(
|
||||||
mgr, planeEntity, COMPONENT_TYPE_POSITION
|
mgr, planeEntity, COMPONENT_TYPE_POSITION
|
||||||
@@ -162,83 +269,59 @@ static void test_overworldscene_builds_expected_entities(void **state) {
|
|||||||
assert_int_equal(planeR->data.material.material.unlit.color.b, 128);
|
assert_int_equal(planeR->data.material.material.unlit.color.b, 128);
|
||||||
assert_int_equal(planeR->data.material.material.unlit.color.a, 255);
|
assert_int_equal(planeR->data.material.material.unlit.color.a, 255);
|
||||||
|
|
||||||
// Entity 2: player. Dynamic capsule body (default body type), PLAYER
|
// Entity 2: camera (new PlayerCamera(player) runs last). Position +
|
||||||
// component present.
|
// Camera components, positioned at the player's local position plus
|
||||||
entityid_t playerEntity = 2;
|
// PlayerCamera.js's fixed offset (angle=0, radius=18, height=10) --
|
||||||
componentid_t playerPos = entityGetComponent(
|
// player is at (0, 2, 0), so eye=(18, 12, 0).
|
||||||
mgr, playerEntity, COMPONENT_TYPE_POSITION
|
entityid_t camEntity = 2;
|
||||||
|
componentid_t camPos = entityGetComponent(
|
||||||
|
mgr, camEntity, COMPONENT_TYPE_POSITION
|
||||||
);
|
);
|
||||||
assert_true(playerPos != COMPONENT_ID_INVALID);
|
assert_true(camPos != COMPONENT_ID_INVALID);
|
||||||
|
|
||||||
vec3 playerPosition;
|
|
||||||
entityPositionGetLocalPosition(mgr, playerEntity, playerPos, playerPosition);
|
|
||||||
assert_float_equal(playerPosition[0], 0.0f, 0.0001f);
|
|
||||||
assert_float_equal(playerPosition[1], 2.0f, 0.0001f);
|
|
||||||
assert_float_equal(playerPosition[2], 0.0f, 0.0001f);
|
|
||||||
|
|
||||||
componentid_t playerPhysics = entityGetComponent(
|
|
||||||
mgr, playerEntity, COMPONENT_TYPE_PHYSICS
|
|
||||||
);
|
|
||||||
assert_true(playerPhysics != COMPONENT_ID_INVALID);
|
|
||||||
assert_int_equal(
|
|
||||||
entityPhysicsGetBodyType(mgr, playerEntity, playerPhysics),
|
|
||||||
PHYSICS_BODY_DYNAMIC
|
|
||||||
);
|
|
||||||
physicsshape_t playerShape = entityPhysicsGetShape(
|
|
||||||
mgr, playerEntity, playerPhysics
|
|
||||||
);
|
|
||||||
assert_int_equal(playerShape.type, PHYSICS_SHAPE_CAPSULE);
|
|
||||||
assert_float_equal(playerShape.data.capsule.radius, 0.5f, 0.0001f);
|
|
||||||
assert_float_equal(playerShape.data.capsule.halfHeight, 0.5f, 0.0001f);
|
|
||||||
|
|
||||||
componentid_t playerRenderable = entityGetComponent(
|
|
||||||
mgr, playerEntity, COMPONENT_TYPE_RENDERABLE
|
|
||||||
);
|
|
||||||
assert_true(playerRenderable != COMPONENT_ID_INVALID);
|
|
||||||
entityrenderable_t *playerR = componentGetData(
|
|
||||||
mgr, playerEntity, playerRenderable, COMPONENT_TYPE_RENDERABLE
|
|
||||||
);
|
|
||||||
assert_ptr_equal(playerR->data.material.meshes[0], &CAPSULE_MESH_SIMPLE);
|
|
||||||
assert_int_equal(playerR->data.material.material.unlit.color.r, 0);
|
|
||||||
assert_int_equal(playerR->data.material.material.unlit.color.g, 0);
|
|
||||||
assert_int_equal(playerR->data.material.material.unlit.color.b, 255);
|
|
||||||
assert_int_equal(playerR->data.material.material.unlit.color.a, 255);
|
|
||||||
|
|
||||||
assert_true(
|
assert_true(
|
||||||
entityGetComponent(mgr, playerEntity, COMPONENT_TYPE_PLAYER) !=
|
entityGetComponent(mgr, camEntity, COMPONENT_TYPE_CAMERA) !=
|
||||||
COMPONENT_ID_INVALID
|
COMPONENT_ID_INVALID
|
||||||
);
|
);
|
||||||
|
|
||||||
|
vec3 camPosition;
|
||||||
|
entityPositionGetLocalPosition(mgr, camEntity, camPos, camPosition);
|
||||||
|
assert_float_equal(camPosition[0], 18.0f, 0.0001f);
|
||||||
|
assert_float_equal(camPosition[1], 12.0f, 0.0001f);
|
||||||
|
assert_float_equal(camPosition[2], 0.0f, 0.0001f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_overworldscene_update_orbits_camera(void **state) {
|
static void test_overworldscene_camera_follows_player(void **state) {
|
||||||
errorret_t ret = installOverworldScene();
|
errorret_t ret = installOverworldScene();
|
||||||
assert_true(errorIsOk(ret));
|
assert_true(errorIsOk(ret));
|
||||||
|
|
||||||
sceneid_t sceneId = sceneGetActive();
|
sceneid_t sceneId = sceneGetActive();
|
||||||
entitymanager_t *mgr = sceneGetEntities(sceneId);
|
entitymanager_t *mgr = sceneGetEntities(sceneId);
|
||||||
entityid_t camEntity = 0;
|
|
||||||
|
entityid_t playerEntity = 0;
|
||||||
|
componentid_t playerPos = entityGetComponent(
|
||||||
|
mgr, playerEntity, COMPONENT_TYPE_POSITION
|
||||||
|
);
|
||||||
|
|
||||||
|
entityid_t camEntity = 2;
|
||||||
componentid_t camPos = entityGetComponent(
|
componentid_t camPos = entityGetComponent(
|
||||||
mgr, camEntity, COMPONENT_TYPE_POSITION
|
mgr, camEntity, COMPONENT_TYPE_POSITION
|
||||||
);
|
);
|
||||||
|
|
||||||
// Drive one frame with a known delta and confirm the camera orbited by
|
// Move the player and confirm the camera's next update() re-centers on
|
||||||
// exactly angle = delta * speed (0.1 * 0.5 = 0.05 rad).
|
// the player's new position at the same fixed offset (PlayerCamera.js
|
||||||
TIME.delta = 0.1f;
|
// no longer orbits over time -- it just tracks the player).
|
||||||
|
entityPositionSetLocalPosition(
|
||||||
|
mgr, playerEntity, playerPos, (vec3){ 5.0f, 2.0f, -3.0f }
|
||||||
|
);
|
||||||
|
|
||||||
ret = moduleSceneUpdateCurrent();
|
ret = moduleSceneUpdateCurrent();
|
||||||
assert_true(errorIsOk(ret));
|
assert_true(errorIsOk(ret));
|
||||||
|
|
||||||
vec3 camPosition;
|
vec3 camPosition;
|
||||||
entityPositionGetLocalPosition(mgr, camEntity, camPos, camPosition);
|
entityPositionGetLocalPosition(mgr, camEntity, camPos, camPosition);
|
||||||
const float_t expectedAngle = 0.1f * 0.5f;
|
assert_float_equal(camPosition[0], 5.0f + 18.0f, 0.0001f);
|
||||||
assert_float_equal(
|
assert_float_equal(camPosition[1], 2.0f + 10.0f, 0.0001f);
|
||||||
camPosition[0], cosf(expectedAngle) * 18.0f, 0.0001f
|
assert_float_equal(camPosition[2], -3.0f, 0.0001f);
|
||||||
);
|
|
||||||
assert_float_equal(camPosition[1], 10.0f, 0.0001f);
|
|
||||||
assert_float_equal(
|
|
||||||
camPosition[2], sinf(expectedAngle) * 18.0f, 0.0001f
|
|
||||||
);
|
|
||||||
|
|
||||||
TIME.delta = 0.0f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_scene_set_switches_and_disposes(void **state) {
|
static void test_scene_set_switches_and_disposes(void **state) {
|
||||||
@@ -309,7 +392,7 @@ int main(void) {
|
|||||||
overworld_setup, overworld_teardown
|
overworld_setup, overworld_teardown
|
||||||
),
|
),
|
||||||
cmocka_unit_test_setup_teardown(
|
cmocka_unit_test_setup_teardown(
|
||||||
test_overworldscene_update_orbits_camera,
|
test_overworldscene_camera_follows_player,
|
||||||
overworld_setup, overworld_teardown
|
overworld_setup, overworld_teardown
|
||||||
),
|
),
|
||||||
cmocka_unit_test_setup_teardown(
|
cmocka_unit_test_setup_teardown(
|
||||||
|
|||||||
@@ -0,0 +1,447 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dusktest.h"
|
||||||
|
#include "script/scriptmanager.h"
|
||||||
|
#include "script/scriptdef.h"
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "util/string.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
double numberValue;
|
||||||
|
int32_t intValue;
|
||||||
|
bool_t boolValue;
|
||||||
|
char_t stringValue[SCRIPT_VALUE_STRING_MAX];
|
||||||
|
vec3 vec3Value;
|
||||||
|
void *pointerValue;
|
||||||
|
jerry_value_t callbackValue;
|
||||||
|
} testwidgethandle_t;
|
||||||
|
|
||||||
|
static scriptproto_t TEST_WIDGET_PROTO;
|
||||||
|
static uint8_t TEST_SENTINEL;
|
||||||
|
static double TEST_GLOBAL_VALUE;
|
||||||
|
|
||||||
|
moduleBaseFunction(testWidgetConstructor) {
|
||||||
|
testwidgethandle_t *inst = (testwidgethandle_t *)memoryAllocate(
|
||||||
|
sizeof(testwidgethandle_t)
|
||||||
|
);
|
||||||
|
memoryZero(inst, sizeof(testwidgethandle_t));
|
||||||
|
inst->callbackValue = jerry_undefined();
|
||||||
|
|
||||||
|
jerry_object_set_native_ptr(
|
||||||
|
callInfo->this_value, &TEST_WIDGET_PROTO.info, inst
|
||||||
|
);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
static scriptvalue_t testWidgetSetNumber(
|
||||||
|
void *handle, const scriptvalue_t *args, const uint32_t argc
|
||||||
|
) {
|
||||||
|
((testwidgethandle_t *)handle)->numberValue = args[0].as.number;
|
||||||
|
return scriptValueVoid();
|
||||||
|
}
|
||||||
|
|
||||||
|
static scriptvalue_t testWidgetGetNumberValue(void *handle) {
|
||||||
|
return scriptValueNumber(((testwidgethandle_t *)handle)->numberValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static scriptvalue_t testWidgetGetNumberMethod(
|
||||||
|
void *handle, const scriptvalue_t *args, const uint32_t argc
|
||||||
|
) {
|
||||||
|
return testWidgetGetNumberValue(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
static scriptvalue_t testWidgetSetInt(
|
||||||
|
void *handle, const scriptvalue_t *args, const uint32_t argc
|
||||||
|
) {
|
||||||
|
((testwidgethandle_t *)handle)->intValue = args[0].as.intValue;
|
||||||
|
return scriptValueVoid();
|
||||||
|
}
|
||||||
|
|
||||||
|
static scriptvalue_t testWidgetSetBool(
|
||||||
|
void *handle, const scriptvalue_t *args, const uint32_t argc
|
||||||
|
) {
|
||||||
|
((testwidgethandle_t *)handle)->boolValue = args[0].as.boolValue;
|
||||||
|
return scriptValueVoid();
|
||||||
|
}
|
||||||
|
|
||||||
|
static scriptvalue_t testWidgetGetBoolMethod(
|
||||||
|
void *handle, const scriptvalue_t *args, const uint32_t argc
|
||||||
|
) {
|
||||||
|
return scriptValueBool(((testwidgethandle_t *)handle)->boolValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static scriptvalue_t testWidgetSetString(
|
||||||
|
void *handle, const scriptvalue_t *args, const uint32_t argc
|
||||||
|
) {
|
||||||
|
stringCopy(
|
||||||
|
((testwidgethandle_t *)handle)->stringValue, args[0].as.string,
|
||||||
|
SCRIPT_VALUE_STRING_MAX - 1
|
||||||
|
);
|
||||||
|
return scriptValueVoid();
|
||||||
|
}
|
||||||
|
|
||||||
|
static scriptvalue_t testWidgetGetStringMethod(
|
||||||
|
void *handle, const scriptvalue_t *args, const uint32_t argc
|
||||||
|
) {
|
||||||
|
return scriptValueString(((testwidgethandle_t *)handle)->stringValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static scriptvalue_t testWidgetSetVec3(
|
||||||
|
void *handle, const scriptvalue_t *args, const uint32_t argc
|
||||||
|
) {
|
||||||
|
testwidgethandle_t *inst = (testwidgethandle_t *)handle;
|
||||||
|
inst->vec3Value[0] = args[0].as.vec3Value[0];
|
||||||
|
inst->vec3Value[1] = args[0].as.vec3Value[1];
|
||||||
|
inst->vec3Value[2] = args[0].as.vec3Value[2];
|
||||||
|
return scriptValueVoid();
|
||||||
|
}
|
||||||
|
|
||||||
|
static scriptvalue_t testWidgetGetVec3Value(void *handle) {
|
||||||
|
return scriptValueVec3(((testwidgethandle_t *)handle)->vec3Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static scriptvalue_t testWidgetGetVec3Method(
|
||||||
|
void *handle, const scriptvalue_t *args, const uint32_t argc
|
||||||
|
) {
|
||||||
|
return testWidgetGetVec3Value(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
static scriptvalue_t testWidgetSetPointer(
|
||||||
|
void *handle, const scriptvalue_t *args, const uint32_t argc
|
||||||
|
) {
|
||||||
|
((testwidgethandle_t *)handle)->pointerValue = args[0].as.pointer;
|
||||||
|
return scriptValueVoid();
|
||||||
|
}
|
||||||
|
|
||||||
|
static scriptvalue_t testWidgetGetSentinelPointer(
|
||||||
|
void *handle, const scriptvalue_t *args, const uint32_t argc
|
||||||
|
) {
|
||||||
|
return scriptValuePointer(&TEST_SENTINEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static scriptvalue_t testWidgetPointerMatches(
|
||||||
|
void *handle, const scriptvalue_t *args, const uint32_t argc
|
||||||
|
) {
|
||||||
|
return scriptValueBool(
|
||||||
|
((testwidgethandle_t *)handle)->pointerValue == args[0].as.pointer
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static scriptvalue_t testWidgetSetCallback(
|
||||||
|
void *handle, const scriptvalue_t *args, const uint32_t argc
|
||||||
|
) {
|
||||||
|
testwidgethandle_t *inst = (testwidgethandle_t *)handle;
|
||||||
|
if(jerry_value_is_function(inst->callbackValue)) {
|
||||||
|
jerry_value_free(inst->callbackValue);
|
||||||
|
}
|
||||||
|
inst->callbackValue = args[0].as.value;
|
||||||
|
return scriptValueVoid();
|
||||||
|
}
|
||||||
|
|
||||||
|
static scriptvalue_t testWidgetInvokeCallback(
|
||||||
|
void *handle, const scriptvalue_t *args, const uint32_t argc
|
||||||
|
) {
|
||||||
|
testwidgethandle_t *inst = (testwidgethandle_t *)handle;
|
||||||
|
if(jerry_value_is_function(inst->callbackValue)) {
|
||||||
|
errorret_t ret = scriptManagerCallValue(
|
||||||
|
jerry_undefined(), inst->callbackValue, "TestWidget", "callback"
|
||||||
|
);
|
||||||
|
errorCatch(ret);
|
||||||
|
}
|
||||||
|
return scriptValueVoid();
|
||||||
|
}
|
||||||
|
|
||||||
|
static scriptvalue_t testWidgetDispose(
|
||||||
|
void *handle, const scriptvalue_t *args, const uint32_t argc
|
||||||
|
) {
|
||||||
|
testwidgethandle_t *inst = (testwidgethandle_t *)handle;
|
||||||
|
if(jerry_value_is_function(inst->callbackValue)) {
|
||||||
|
jerry_value_free(inst->callbackValue);
|
||||||
|
}
|
||||||
|
inst->callbackValue = jerry_undefined();
|
||||||
|
return scriptValueVoid();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testWidgetSetNumberProp(void *handle, const scriptvalue_t *value) {
|
||||||
|
((testwidgethandle_t *)handle)->numberValue = value->as.number;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testWidgetSetVec3Prop(void *handle, const scriptvalue_t *value) {
|
||||||
|
testwidgethandle_t *inst = (testwidgethandle_t *)handle;
|
||||||
|
inst->vec3Value[0] = value->as.vec3Value[0];
|
||||||
|
inst->vec3Value[1] = value->as.vec3Value[1];
|
||||||
|
inst->vec3Value[2] = value->as.vec3Value[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
static scriptfuncdef_t TEST_WIDGET_FUNCS[] = {
|
||||||
|
{ "setNumber", SCRIPT_TYPE_VOID, { SCRIPT_TYPE_NUMBER }, 1,
|
||||||
|
testWidgetSetNumber },
|
||||||
|
{ "getNumber", SCRIPT_TYPE_NUMBER, { 0 }, 0, testWidgetGetNumberMethod },
|
||||||
|
{ "setInt", SCRIPT_TYPE_VOID, { SCRIPT_TYPE_INT }, 1, testWidgetSetInt },
|
||||||
|
{ "setBool", SCRIPT_TYPE_VOID, { SCRIPT_TYPE_BOOL }, 1, testWidgetSetBool },
|
||||||
|
{ "getBool", SCRIPT_TYPE_BOOL, { 0 }, 0, testWidgetGetBoolMethod },
|
||||||
|
{ "setString", SCRIPT_TYPE_VOID, { SCRIPT_TYPE_STRING }, 1,
|
||||||
|
testWidgetSetString },
|
||||||
|
{ "getString", SCRIPT_TYPE_STRING, { 0 }, 0, testWidgetGetStringMethod },
|
||||||
|
{ "setVec3", SCRIPT_TYPE_VOID, { SCRIPT_TYPE_VEC3 }, 1, testWidgetSetVec3 },
|
||||||
|
{ "getVec3", SCRIPT_TYPE_VEC3, { 0 }, 0, testWidgetGetVec3Method },
|
||||||
|
{ "setPointer", SCRIPT_TYPE_VOID, { SCRIPT_TYPE_POINTER }, 1,
|
||||||
|
testWidgetSetPointer },
|
||||||
|
{ "getSentinelPointer", SCRIPT_TYPE_POINTER, { 0 }, 0,
|
||||||
|
testWidgetGetSentinelPointer },
|
||||||
|
{ "pointerMatches", SCRIPT_TYPE_BOOL, { SCRIPT_TYPE_POINTER }, 1,
|
||||||
|
testWidgetPointerMatches },
|
||||||
|
{ "setCallback", SCRIPT_TYPE_VOID, { SCRIPT_TYPE_CALLBACK }, 1,
|
||||||
|
testWidgetSetCallback },
|
||||||
|
{ "invokeCallback", SCRIPT_TYPE_VOID, { 0 }, 0, testWidgetInvokeCallback },
|
||||||
|
{ "dispose", SCRIPT_TYPE_VOID, { 0 }, 0, testWidgetDispose },
|
||||||
|
{ NULL, SCRIPT_TYPE_VOID, { 0 }, 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static scriptpropdef_t TEST_WIDGET_PROPS[] = {
|
||||||
|
{ "numberProp", SCRIPT_TYPE_NUMBER, testWidgetGetNumberValue,
|
||||||
|
testWidgetSetNumberProp },
|
||||||
|
{ "vec3Prop", SCRIPT_TYPE_VEC3, testWidgetGetVec3Value,
|
||||||
|
testWidgetSetVec3Prop },
|
||||||
|
{ NULL, SCRIPT_TYPE_VOID, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static scriptvalue_t testGlobalGetValue(void *handle) {
|
||||||
|
return scriptValueNumber(TEST_GLOBAL_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testGlobalSetValue(void *handle, const scriptvalue_t *value) {
|
||||||
|
TEST_GLOBAL_VALUE = value->as.number;
|
||||||
|
}
|
||||||
|
|
||||||
|
static scriptvalue_t testGlobalCompute(
|
||||||
|
void *handle, const scriptvalue_t *args, const uint32_t argc
|
||||||
|
) {
|
||||||
|
return scriptValueNumber(args[0].as.number + args[1].as.number);
|
||||||
|
}
|
||||||
|
|
||||||
|
static scriptpropdef_t TEST_GLOBAL_PROPS[] = {
|
||||||
|
{ "value", SCRIPT_TYPE_NUMBER, testGlobalGetValue, testGlobalSetValue },
|
||||||
|
{ NULL, SCRIPT_TYPE_VOID, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static scriptfuncdef_t TEST_GLOBAL_FUNCS[] = {
|
||||||
|
{ "compute", SCRIPT_TYPE_NUMBER,
|
||||||
|
{ SCRIPT_TYPE_NUMBER, SCRIPT_TYPE_NUMBER }, 2, testGlobalCompute },
|
||||||
|
{ NULL, SCRIPT_TYPE_VOID, { 0 }, 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static int scriptdef_setup(void **state) {
|
||||||
|
errorret_t ret = scriptManagerInit();
|
||||||
|
if(errorIsNotOk(ret)) { errorCatch(ret); return -1; }
|
||||||
|
|
||||||
|
scriptProtoInit(
|
||||||
|
&TEST_WIDGET_PROTO, "TestWidget", sizeof(testwidgethandle_t),
|
||||||
|
testWidgetConstructor
|
||||||
|
);
|
||||||
|
scriptProtoDefineFuncDefs(&TEST_WIDGET_PROTO, TEST_WIDGET_FUNCS);
|
||||||
|
scriptProtoDefinePropDefs(&TEST_WIDGET_PROTO, TEST_WIDGET_PROPS);
|
||||||
|
|
||||||
|
TEST_GLOBAL_VALUE = 0.0;
|
||||||
|
jerry_value_t globalObj = jerry_object();
|
||||||
|
scriptDefDefineGlobalProps(globalObj, TEST_GLOBAL_PROPS);
|
||||||
|
scriptDefDefineGlobalFuncs(globalObj, TEST_GLOBAL_FUNCS);
|
||||||
|
moduleBaseSetValue("TestGlobal", globalObj);
|
||||||
|
jerry_value_free(globalObj);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int scriptdef_teardown(void **state) {
|
||||||
|
errorret_t ret = scriptManagerDispose();
|
||||||
|
if(errorIsNotOk(ret)) errorCatch(ret);
|
||||||
|
|
||||||
|
assert_int_equal(memoryGetAllocatedCount(), 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_scriptdef_number_and_int_and_bool_roundtrip(void **state) {
|
||||||
|
jerry_value_t result;
|
||||||
|
errorret_t ret = scriptManagerExec(
|
||||||
|
"var w = new TestWidget();"
|
||||||
|
"w.setNumber(3.5); w.setInt(7); w.setBool(true);"
|
||||||
|
"w.getNumber() + ',' + w.getBool();",
|
||||||
|
&result
|
||||||
|
);
|
||||||
|
assert_true(errorIsOk(ret));
|
||||||
|
char_t buf[64];
|
||||||
|
moduleBaseToString(result, buf, sizeof(buf));
|
||||||
|
assert_string_equal(buf, "3.5,true");
|
||||||
|
jerry_value_free(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_scriptdef_string_method_roundtrip(void **state) {
|
||||||
|
jerry_value_t result;
|
||||||
|
errorret_t ret = scriptManagerExec(
|
||||||
|
"var w = new TestWidget();"
|
||||||
|
"w.setString('hello'); w.getString();",
|
||||||
|
&result
|
||||||
|
);
|
||||||
|
assert_true(errorIsOk(ret));
|
||||||
|
char_t buf[32];
|
||||||
|
moduleBaseToString(result, buf, sizeof(buf));
|
||||||
|
assert_string_equal(buf, "hello");
|
||||||
|
jerry_value_free(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_scriptdef_vec3_method_roundtrip(void **state) {
|
||||||
|
jerry_value_t result;
|
||||||
|
errorret_t ret = scriptManagerExec(
|
||||||
|
"var w = new TestWidget();"
|
||||||
|
"w.setVec3(4, 5, 6);"
|
||||||
|
"var v = w.getVec3();"
|
||||||
|
"v.x + ',' + v.y + ',' + v.z;",
|
||||||
|
&result
|
||||||
|
);
|
||||||
|
assert_true(errorIsOk(ret));
|
||||||
|
char_t buf[32];
|
||||||
|
moduleBaseToString(result, buf, sizeof(buf));
|
||||||
|
assert_string_equal(buf, "4,5,6");
|
||||||
|
jerry_value_free(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_scriptdef_vec3_property_roundtrip(void **state) {
|
||||||
|
jerry_value_t result;
|
||||||
|
errorret_t ret = scriptManagerExec(
|
||||||
|
"var w = new TestWidget();"
|
||||||
|
"w.vec3Prop = { x: 1, y: 2, z: 3 };"
|
||||||
|
"var v = w.vec3Prop;"
|
||||||
|
"v.x + ',' + v.y + ',' + v.z;",
|
||||||
|
&result
|
||||||
|
);
|
||||||
|
assert_true(errorIsOk(ret));
|
||||||
|
char_t buf[32];
|
||||||
|
moduleBaseToString(result, buf, sizeof(buf));
|
||||||
|
assert_string_equal(buf, "1,2,3");
|
||||||
|
jerry_value_free(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_scriptdef_number_property_roundtrip(void **state) {
|
||||||
|
jerry_value_t result;
|
||||||
|
errorret_t ret = scriptManagerExec(
|
||||||
|
"var w = new TestWidget(); w.numberProp = 42; w.numberProp;", &result
|
||||||
|
);
|
||||||
|
assert_true(errorIsOk(ret));
|
||||||
|
assert_true(jerry_value_is_number(result));
|
||||||
|
assert_int_equal((int)jerry_value_as_number(result), 42);
|
||||||
|
jerry_value_free(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_scriptdef_pointer_roundtrip(void **state) {
|
||||||
|
jerry_value_t result;
|
||||||
|
errorret_t ret = scriptManagerExec(
|
||||||
|
"var w = new TestWidget();"
|
||||||
|
"var p = w.getSentinelPointer();"
|
||||||
|
"w.setPointer(p);"
|
||||||
|
"w.pointerMatches(p);",
|
||||||
|
&result
|
||||||
|
);
|
||||||
|
assert_true(errorIsOk(ret));
|
||||||
|
assert_true(jerry_value_is_true(result));
|
||||||
|
jerry_value_free(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_scriptdef_callback_can_be_invoked_later(void **state) {
|
||||||
|
jerry_value_t result;
|
||||||
|
errorret_t ret = scriptManagerExec(
|
||||||
|
"var calls = 0;"
|
||||||
|
"var w = new TestWidget();"
|
||||||
|
"w.setCallback(function() { calls++; });"
|
||||||
|
"w.invokeCallback(); w.invokeCallback();"
|
||||||
|
"w.dispose();"
|
||||||
|
"calls;",
|
||||||
|
&result
|
||||||
|
);
|
||||||
|
assert_true(errorIsOk(ret));
|
||||||
|
assert_true(jerry_value_is_number(result));
|
||||||
|
assert_int_equal((int)jerry_value_as_number(result), 2);
|
||||||
|
jerry_value_free(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_scriptdef_wrong_type_args_throw(void **state) {
|
||||||
|
errorret_t ret = scriptManagerExec(
|
||||||
|
"var w = new TestWidget(); w.setNumber('nope');", NULL
|
||||||
|
);
|
||||||
|
assert_true(errorIsNotOk(ret));
|
||||||
|
errorCatch(ret);
|
||||||
|
|
||||||
|
ret = scriptManagerExec(
|
||||||
|
"var w = new TestWidget(); w.setString(123);", NULL
|
||||||
|
);
|
||||||
|
assert_true(errorIsNotOk(ret));
|
||||||
|
errorCatch(ret);
|
||||||
|
|
||||||
|
ret = scriptManagerExec(
|
||||||
|
"var w = new TestWidget(); w.setPointer(42);", NULL
|
||||||
|
);
|
||||||
|
assert_true(errorIsNotOk(ret));
|
||||||
|
errorCatch(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_scriptdef_global_object_props_and_funcs(void **state) {
|
||||||
|
jerry_value_t result;
|
||||||
|
errorret_t ret = scriptManagerExec(
|
||||||
|
"TestGlobal.value = 10;"
|
||||||
|
"TestGlobal.value + ',' + TestGlobal.compute(2, 3);",
|
||||||
|
&result
|
||||||
|
);
|
||||||
|
assert_true(errorIsOk(ret));
|
||||||
|
char_t buf[32];
|
||||||
|
moduleBaseToString(result, buf, sizeof(buf));
|
||||||
|
assert_string_equal(buf, "10,5");
|
||||||
|
jerry_value_free(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
assertInit();
|
||||||
|
const struct CMUnitTest tests[] = {
|
||||||
|
cmocka_unit_test_setup_teardown(
|
||||||
|
test_scriptdef_number_and_int_and_bool_roundtrip,
|
||||||
|
scriptdef_setup, scriptdef_teardown
|
||||||
|
),
|
||||||
|
cmocka_unit_test_setup_teardown(
|
||||||
|
test_scriptdef_string_method_roundtrip,
|
||||||
|
scriptdef_setup, scriptdef_teardown
|
||||||
|
),
|
||||||
|
cmocka_unit_test_setup_teardown(
|
||||||
|
test_scriptdef_vec3_method_roundtrip,
|
||||||
|
scriptdef_setup, scriptdef_teardown
|
||||||
|
),
|
||||||
|
cmocka_unit_test_setup_teardown(
|
||||||
|
test_scriptdef_vec3_property_roundtrip,
|
||||||
|
scriptdef_setup, scriptdef_teardown
|
||||||
|
),
|
||||||
|
cmocka_unit_test_setup_teardown(
|
||||||
|
test_scriptdef_number_property_roundtrip,
|
||||||
|
scriptdef_setup, scriptdef_teardown
|
||||||
|
),
|
||||||
|
cmocka_unit_test_setup_teardown(
|
||||||
|
test_scriptdef_pointer_roundtrip,
|
||||||
|
scriptdef_setup, scriptdef_teardown
|
||||||
|
),
|
||||||
|
cmocka_unit_test_setup_teardown(
|
||||||
|
test_scriptdef_callback_can_be_invoked_later,
|
||||||
|
scriptdef_setup, scriptdef_teardown
|
||||||
|
),
|
||||||
|
cmocka_unit_test_setup_teardown(
|
||||||
|
test_scriptdef_wrong_type_args_throw,
|
||||||
|
scriptdef_setup, scriptdef_teardown
|
||||||
|
),
|
||||||
|
cmocka_unit_test_setup_teardown(
|
||||||
|
test_scriptdef_global_object_props_and_funcs,
|
||||||
|
scriptdef_setup, scriptdef_teardown
|
||||||
|
),
|
||||||
|
};
|
||||||
|
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user