2021-09-09 18:23:17 -04:00
|
|
|
|
|
|
|
|
#include "world.hpp"
|
2021-10-21 13:15:04 -04:00
|
|
|
#include "pprint.hpp"
|
2022-03-31 17:15:15 -04:00
|
|
|
#include <cmath>
|
|
|
|
|
#include <iostream>
|
2021-09-09 18:23:17 -04:00
|
|
|
|
2021-11-23 16:56:55 -05:00
|
|
|
static void tangible_getall(LuaStack &LS0, LuaSlot list, const util::IdVector &idv) {
|
|
|
|
|
LuaVar tangibles, tan;
|
|
|
|
|
LuaStack LS(LS0.state(), tangibles, tan);
|
|
|
|
|
LS.rawget(tangibles, LuaRegistry, "tangibles");
|
|
|
|
|
assert(LS.istable(tangibles));
|
|
|
|
|
LS.set(list, LuaNewTable);
|
|
|
|
|
int index = 1;
|
|
|
|
|
for (int64_t id : idv) {
|
|
|
|
|
LS.rawget(tan, tangibles, id);
|
|
|
|
|
assert(LS.istable(tan));
|
|
|
|
|
LS.rawset(list, index++, tan);
|
|
|
|
|
}
|
|
|
|
|
LS.result();
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-23 14:38:01 -05:00
|
|
|
LuaDefine(tangible_animstate, "tan",
|
|
|
|
|
"|Get the entire animation state of the tangible."
|
|
|
|
|
"|Returns six values: graphic,plane,x,y,z,facing.") {
|
2021-09-09 18:23:17 -04:00
|
|
|
LuaArg tanobj;
|
|
|
|
|
LuaRet graphic, plane, x, y, z, facing;
|
|
|
|
|
LuaStack LS(L, tanobj, graphic, plane, x, y, z, facing);
|
|
|
|
|
World *w = World::fetch_global_pointer(L);
|
|
|
|
|
Tangible *tan = w->tangible_get(LS, tanobj);
|
|
|
|
|
const AnimStep &aqback = tan->anim_queue_.back();
|
|
|
|
|
LS.set(graphic, aqback.graphic());
|
|
|
|
|
LS.set(plane, aqback.plane());
|
|
|
|
|
LS.set(x, aqback.xyz().x);
|
|
|
|
|
LS.set(y, aqback.xyz().y);
|
|
|
|
|
LS.set(z, aqback.xyz().z);
|
|
|
|
|
LS.set(facing, aqback.facing());
|
|
|
|
|
return LS.result();
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-23 14:38:01 -05:00
|
|
|
LuaDefine(tangible_xyz, "tan",
|
|
|
|
|
"|Get the current coordinates of the tangible."
|
|
|
|
|
"|Returns three values: x, y, z") {
|
|
|
|
|
LuaArg tanobj;
|
|
|
|
|
LuaRet x, y, z;
|
|
|
|
|
LuaStack LS(L, tanobj, x, y, z);
|
|
|
|
|
World *w = World::fetch_global_pointer(L);
|
|
|
|
|
Tangible *tan = w->tangible_get(LS, tanobj);
|
|
|
|
|
const AnimStep &aqback = tan->anim_queue_.back();
|
|
|
|
|
LS.set(x, aqback.xyz().x);
|
|
|
|
|
LS.set(y, aqback.xyz().y);
|
|
|
|
|
LS.set(z, aqback.xyz().z);
|
|
|
|
|
return LS.result();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LuaDefine(tangible_animate, "tan,configtable",
|
|
|
|
|
"|Add an animation step to the tangible."
|
|
|
|
|
"|The configtable is a table containing any of the following:"
|
|
|
|
|
"|action,graphic,plane,x,y,z,facing") {
|
2021-09-09 18:23:17 -04:00
|
|
|
LuaArg tanobj, config;
|
|
|
|
|
LuaStack LS(L, tanobj, config);
|
2022-07-22 16:22:04 -04:00
|
|
|
LuaKeywordParser kp(LS, config);
|
2021-09-09 18:23:17 -04:00
|
|
|
World *w = World::fetch_global_pointer(L);
|
|
|
|
|
Tangible *tan = w->tangible_get(LS, tanobj);
|
2021-10-14 11:43:16 -04:00
|
|
|
int64_t id = w->alloc_id_predictable();
|
2021-09-09 18:23:17 -04:00
|
|
|
AnimStep step;
|
2022-07-22 16:22:04 -04:00
|
|
|
step.configure(kp, tan->anim_queue_.back());
|
2022-07-22 17:07:40 -04:00
|
|
|
kp.final_check_throw();
|
2021-09-09 18:23:17 -04:00
|
|
|
if (step.action() == "") {
|
|
|
|
|
luaL_error(L, "animation action must be specified");
|
|
|
|
|
}
|
|
|
|
|
tan->anim_queue_.add(id, step);
|
|
|
|
|
tan->update_plane_item();
|
|
|
|
|
return LS.result();
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-23 14:38:01 -05:00
|
|
|
LuaDefine(tangible_setclass, "tan,class",
|
|
|
|
|
"|Set the class of the tangible."
|
|
|
|
|
"|The class can be a 'class table' (ie, a table of methods), "
|
|
|
|
|
"|or it can be a string that names a class. The tangible is "
|
|
|
|
|
"|given an __index metamethod that points at the class table.") {
|
2021-09-09 18:23:17 -04:00
|
|
|
LuaArg tanobj, classname;
|
|
|
|
|
LuaVar classtab, mt;
|
|
|
|
|
LuaStack LS(L, tanobj, classname, classtab, mt);
|
|
|
|
|
World *w = World::fetch_global_pointer(L);
|
|
|
|
|
w->tangible_get(LS, tanobj);
|
2022-02-24 02:17:41 -05:00
|
|
|
eng::string err = LS.getclass(classtab, classname);
|
2021-12-27 16:44:12 -05:00
|
|
|
if (err != "") {
|
|
|
|
|
luaL_error(L, "%s", err.c_str());
|
2021-10-13 19:41:59 -04:00
|
|
|
}
|
2021-09-09 18:23:17 -04:00
|
|
|
LS.getmetatable(mt, tanobj);
|
|
|
|
|
LS.rawset(mt, "__index", classtab);
|
|
|
|
|
return LS.result();
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-23 14:38:01 -05:00
|
|
|
LuaDefine(tangible_getclass, "tan",
|
|
|
|
|
"|Get the class of the tangible, if any."
|
|
|
|
|
"|The return value is a string, the class name, not"
|
|
|
|
|
"|the class table.") {
|
2021-11-23 16:10:48 -05:00
|
|
|
LuaArg tanobj;
|
|
|
|
|
LuaVar mt, classtab;
|
|
|
|
|
LuaRet classname;
|
|
|
|
|
LuaStack LS(L, tanobj, mt, classtab, classname);
|
|
|
|
|
World *w = World::fetch_global_pointer(L);
|
|
|
|
|
w->tangible_get(LS, tanobj);
|
|
|
|
|
LS.getmetatable(mt, tanobj);
|
|
|
|
|
LS.rawget(classtab, mt, "__index");
|
2022-02-24 02:17:41 -05:00
|
|
|
eng::string name = LS.classname(classtab);
|
2021-11-23 16:10:48 -05:00
|
|
|
if (name == "") {
|
|
|
|
|
LS.set(classname, LuaNil);
|
|
|
|
|
} else {
|
|
|
|
|
LS.set(classname, name);
|
|
|
|
|
}
|
|
|
|
|
return LS.result();
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-23 14:38:01 -05:00
|
|
|
LuaDefine(tangible_delete, "tan",
|
|
|
|
|
"|Delete the specified tangible."
|
|
|
|
|
"|This cannot be used to delete player tangibles,"
|
|
|
|
|
"|To delete a player, use tangible.redirect") {
|
2021-09-09 18:23:17 -04:00
|
|
|
LuaArg tanobj;
|
|
|
|
|
LuaStack LS(L, tanobj);
|
|
|
|
|
World *w = World::fetch_global_pointer(L);
|
|
|
|
|
Tangible *tan = w->tangible_get(LS, tanobj);
|
|
|
|
|
assert(tan != nullptr); // this should be checked above.
|
|
|
|
|
if (tan->is_an_actor()) {
|
|
|
|
|
luaL_error(L, "Cannot delete a player using tangible.delete, use tangible.redirect instead.");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
w->tangible_delete(tan->id());
|
|
|
|
|
return LS.result();
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-22 16:22:04 -04:00
|
|
|
LuaDefine(tangible_build, "config",
|
2021-12-23 14:38:01 -05:00
|
|
|
"|Build a new tangible object."
|
2022-07-22 16:22:04 -04:00
|
|
|
"|The config table must contain: class,x,y,z,plane,graphic."
|
|
|
|
|
"|The config table can optionally contain: facing.") {
|
2021-09-09 18:23:17 -04:00
|
|
|
LuaArg config;
|
|
|
|
|
LuaVar classname, classtab, mt;
|
|
|
|
|
LuaRet database;
|
|
|
|
|
LuaStack LS(L, config, classname, classtab, database, mt);
|
2022-07-22 16:22:04 -04:00
|
|
|
LuaKeywordParser kp(LS, config);
|
|
|
|
|
|
2021-09-09 18:23:17 -04:00
|
|
|
// Get the class of the new tangible.
|
2022-07-22 16:22:04 -04:00
|
|
|
if (!kp.parse(classname, "class")) {
|
|
|
|
|
luaL_error(L, "You must specify a class for the tangible");
|
|
|
|
|
}
|
2022-02-24 02:17:41 -05:00
|
|
|
eng::string err = LS.getclass(classtab, classname);
|
2021-12-27 16:44:12 -05:00
|
|
|
if (err != "") {
|
|
|
|
|
luaL_error(L, "%s", err.c_str());
|
2021-09-09 18:23:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parse the initial animation step.
|
|
|
|
|
AnimStep initstep, blank;
|
2022-07-22 16:22:04 -04:00
|
|
|
initstep.configure(kp, blank);
|
2022-07-22 17:07:40 -04:00
|
|
|
kp.final_check_throw();
|
2021-09-09 18:23:17 -04:00
|
|
|
if (!initstep.has_xyz()) {
|
|
|
|
|
luaL_error(L, "You must specify (X,Y,Z) for new tangible");
|
|
|
|
|
}
|
|
|
|
|
if (!initstep.has_plane()) {
|
|
|
|
|
luaL_error(L, "You must specify plane for new tangible");
|
|
|
|
|
}
|
|
|
|
|
if (!initstep.has_graphic()) {
|
|
|
|
|
luaL_error(L, "You must specify graphic for new tangible");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: generate error if there's extra crap in the config table.
|
|
|
|
|
|
|
|
|
|
World *w = World::fetch_global_pointer(L);
|
2021-10-14 11:43:16 -04:00
|
|
|
int64_t new_id = w->alloc_id_predictable();
|
|
|
|
|
Tangible *tan = w->tangible_make(L, new_id, "nowhere", true);
|
2021-09-09 18:23:17 -04:00
|
|
|
lua_replace(L, database.index());
|
|
|
|
|
|
|
|
|
|
// Update the class of the new tangible.
|
|
|
|
|
LS.getmetatable(mt, database);
|
|
|
|
|
LS.rawset(mt, "__index", classtab);
|
|
|
|
|
|
|
|
|
|
// Update the animation queue and planemap of the new tangible.
|
2021-10-14 11:43:16 -04:00
|
|
|
int64_t stepid = w->alloc_id_predictable();
|
2021-09-09 18:23:17 -04:00
|
|
|
tan->anim_queue_.add(stepid, initstep);
|
|
|
|
|
tan->update_plane_item();
|
|
|
|
|
|
|
|
|
|
return LS.result();
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-23 14:38:01 -05:00
|
|
|
LuaDefine(tangible_get, "id",
|
|
|
|
|
"|Get the tangible with the specified id."
|
|
|
|
|
"|This is for debugging only and will be removed in"
|
|
|
|
|
"|the released version.") {
|
2021-09-09 18:23:17 -04:00
|
|
|
LuaArg id;
|
|
|
|
|
LuaVar tangibles;
|
|
|
|
|
LuaRet database;
|
|
|
|
|
LuaStack LS(L, id, tangibles, database);
|
|
|
|
|
int64_t nid = LS.ckinteger(id);
|
|
|
|
|
LS.rawget(tangibles, LuaRegistry, "tangibles");
|
|
|
|
|
LS.rawget(database, tangibles, id);
|
|
|
|
|
if (!LS.istable(database)) {
|
|
|
|
|
luaL_error(L, "Not a tangible ID: %d", nid);
|
|
|
|
|
}
|
|
|
|
|
return LS.result();
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-23 14:38:01 -05:00
|
|
|
LuaDefine(tangible_redirect, "tan1,tan2,bulldozetan1",
|
|
|
|
|
"|Redirect is not working yet") {
|
2021-09-09 18:23:17 -04:00
|
|
|
LuaArg actor1, actor2, bldz;
|
|
|
|
|
LuaStack LS(L, actor1, actor2, bldz);
|
|
|
|
|
World *w = World::fetch_global_pointer(L);
|
|
|
|
|
bool bulldoze = LS.ckboolean(bldz);
|
|
|
|
|
Tangible *tan1 = w->tangible_get(LS, actor1);
|
|
|
|
|
if (!tan1->is_an_actor()) {
|
|
|
|
|
luaL_error(L, "redirect source is not an actor");
|
|
|
|
|
}
|
|
|
|
|
if (LS.isnil(actor2)) {
|
|
|
|
|
w->redirects_[tan1->id()] = 0;
|
|
|
|
|
} else {
|
|
|
|
|
Tangible *tan2 = w->tangible_get(LS, actor2);
|
|
|
|
|
tan2->configure_id_pool_for_actor();
|
|
|
|
|
w->redirects_[tan1->id()] = tan2->id();
|
|
|
|
|
}
|
|
|
|
|
if (bulldoze) {
|
|
|
|
|
w->tangible_delete(tan1->id());
|
|
|
|
|
}
|
|
|
|
|
return LS.result();
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-23 14:38:01 -05:00
|
|
|
LuaDefine(tangible_id, "tan",
|
|
|
|
|
"|Return the tangible's id number."
|
|
|
|
|
"|This is for debugging only and will be removed"
|
|
|
|
|
"|in the released version.") {
|
2021-09-09 18:23:17 -04:00
|
|
|
LuaArg tanobj;
|
|
|
|
|
LuaRet id;
|
|
|
|
|
LuaStack LS(L, tanobj, id);
|
2021-11-26 12:28:59 -05:00
|
|
|
int64_t tid = LS.tanid(tanobj);
|
|
|
|
|
if (tid == 0) {
|
|
|
|
|
luaL_error(L, "Not a tangible");
|
|
|
|
|
}
|
|
|
|
|
LS.set(id, tid);
|
2021-09-09 18:23:17 -04:00
|
|
|
return LS.result();
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-23 14:38:01 -05:00
|
|
|
LuaDefine(tangible_actor, "",
|
|
|
|
|
"|Return the current actor.") {
|
2021-10-13 20:32:43 -04:00
|
|
|
LuaRet actor;
|
2021-10-14 11:43:16 -04:00
|
|
|
LuaVar tangibles;
|
|
|
|
|
LuaStack LS(L, tangibles, actor);
|
|
|
|
|
World *w = World::fetch_global_pointer(L);
|
|
|
|
|
LS.rawget(tangibles, LuaRegistry, "tangibles");
|
|
|
|
|
LS.rawget(actor, tangibles, w->lthread_actor_id_);
|
2021-10-13 20:32:43 -04:00
|
|
|
return LS.result();
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-23 14:38:01 -05:00
|
|
|
LuaDefine(tangible_place, "",
|
|
|
|
|
"|Return the current place.") {
|
2021-10-13 20:32:43 -04:00
|
|
|
LuaRet place;
|
2021-10-14 11:43:16 -04:00
|
|
|
LuaVar tangibles;
|
|
|
|
|
LuaStack LS(L, tangibles, place);
|
|
|
|
|
World *w = World::fetch_global_pointer(L);
|
|
|
|
|
LS.rawget(tangibles, LuaRegistry, "tangibles");
|
|
|
|
|
LS.rawget(place, tangibles, w->lthread_place_id_);
|
2021-10-13 20:32:43 -04:00
|
|
|
return LS.result();
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-23 14:38:01 -05:00
|
|
|
LuaDefine(tangible_near, "tan,radius,omit_nowhere,omit_self",
|
|
|
|
|
"|Scan near the specified tangible."
|
|
|
|
|
"|If omit_nowhere is true, and the tangible is on the nowhere plane,"
|
|
|
|
|
"|then the scan returns empty. If omit_self is true, then the "
|
|
|
|
|
"|tangible passed in is omitted from the results.") {
|
2021-11-23 16:10:48 -05:00
|
|
|
LuaArg ltan, lradius, lomit_nowhere, lomit_self;
|
|
|
|
|
LuaRet list;
|
|
|
|
|
LuaStack LS(L, ltan, lradius, lomit_nowhere, lomit_self, list);
|
|
|
|
|
World *w = World::fetch_global_pointer(L);
|
|
|
|
|
Tangible *tan = w->tangible_get(LS, ltan);
|
|
|
|
|
const AnimStep &aqback = tan->anim_queue_.back();
|
2022-07-11 02:32:12 -04:00
|
|
|
|
|
|
|
|
PlaneScan scan;
|
|
|
|
|
scan.set_plane(aqback.plane());
|
|
|
|
|
scan.set_bbox_given_center_radius(aqback.xyz(), LS.cknumber(lradius));
|
2022-07-13 01:08:54 -04:00
|
|
|
scan.set_shape(PlaneScan::SPHERE);
|
2022-07-11 02:32:12 -04:00
|
|
|
scan.set_sorted(true);
|
2022-07-14 00:57:11 -04:00
|
|
|
scan.set_near(tan->id(), !LS.ckboolean(lomit_self));
|
2022-07-11 02:32:12 -04:00
|
|
|
scan.set_omit_nowhere(LS.ckboolean(lomit_nowhere));
|
|
|
|
|
|
|
|
|
|
util::IdVector idv = w->plane_map_.scan(scan);
|
2021-11-23 16:56:55 -05:00
|
|
|
tangible_getall(LS, list, idv);
|
2021-11-23 16:10:48 -05:00
|
|
|
return LS.result();
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-23 14:38:01 -05:00
|
|
|
LuaDefine(tangible_scan, "plane,x,y,radius,omit_nowhere",
|
|
|
|
|
"|Scan the specified plane."
|
|
|
|
|
"|If omit_nowhere is true, and the plane is 'nowhere', then"
|
|
|
|
|
"|the scan returns empty.") {
|
2021-11-23 16:10:48 -05:00
|
|
|
LuaArg lplane, lx, ly, lradius, lomit_nowhere;
|
|
|
|
|
LuaRet list;
|
|
|
|
|
LuaStack LS(L, lplane, lx, ly, lradius, lomit_nowhere, list);
|
|
|
|
|
World *w = World::fetch_global_pointer(L);
|
2022-07-11 02:32:12 -04:00
|
|
|
|
|
|
|
|
PlaneScan scan;
|
|
|
|
|
scan.set_plane(LS.ckstring(lplane));
|
|
|
|
|
scan.set_bbox_given_center_radius(util::XYZ(LS.cknumber(lx), LS.cknumber(ly), 0), LS.cknumber(lradius));
|
2022-07-13 01:08:54 -04:00
|
|
|
scan.set_shape(PlaneScan::SPHERE);
|
2022-07-11 02:32:12 -04:00
|
|
|
scan.set_sorted(true);
|
|
|
|
|
scan.set_omit_nowhere(LS.ckboolean(lomit_nowhere));
|
|
|
|
|
|
|
|
|
|
util::IdVector idv = w->plane_map_.scan(scan);
|
2021-11-23 16:56:55 -05:00
|
|
|
tangible_getall(LS, list, idv);
|
2021-11-23 16:10:48 -05:00
|
|
|
return LS.result();
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-14 00:57:11 -04:00
|
|
|
LuaDefine(tangible_find, "config",
|
|
|
|
|
"|Find tangibles by their location."
|
|
|
|
|
"|"
|
|
|
|
|
"|There are multiple ways to specify the plane, the bounding"
|
|
|
|
|
"|box, and the shape of the search. The most basic is to"
|
|
|
|
|
"|include these parameters in the table:"
|
|
|
|
|
"|"
|
|
|
|
|
"| plane : the plane to search (a string)"
|
|
|
|
|
"| centerx : x-coordinate of the center of the search"
|
|
|
|
|
"| centery : y-coordinate of the center of the search"
|
|
|
|
|
"| centerz : z-coordinate of the center of the search"
|
|
|
|
|
"| radius : the radius of the search"
|
|
|
|
|
"| shape : 'box', 'sphere', or 'cylinder'"
|
|
|
|
|
"|"
|
|
|
|
|
"|Shape has a default: 'sphere'. The other parameters do"
|
|
|
|
|
"|not have default values."
|
|
|
|
|
"|"
|
|
|
|
|
"|Instead of specifying the radius as a single float,"
|
|
|
|
|
"|you may optionally specify separate radii for each dimension."
|
|
|
|
|
"|"
|
|
|
|
|
"| radiusx : the radius in the x-dimension."
|
|
|
|
|
"| radiusy : the radius in the y-dimension."
|
|
|
|
|
"| radiusz : the radius in the z-dimension."
|
|
|
|
|
"|"
|
|
|
|
|
"|If you specify different radii in each dimension, then the"
|
|
|
|
|
"|'sphere' shape will actually be a spheroid, and the 'cylinder'"
|
|
|
|
|
"|shape will actually be a cylindroid."
|
|
|
|
|
"|"
|
|
|
|
|
"|Instead of specifying the center and plane, you can specify"
|
|
|
|
|
"|a tangible to search near:"
|
|
|
|
|
"|"
|
|
|
|
|
"| near : a tangible in whose vicinity the search occurs"
|
|
|
|
|
"|"
|
|
|
|
|
"|If you specify 'near', then by default, the near tangible is"
|
|
|
|
|
"|filtered out of the search. If you want to include it in the"
|
|
|
|
|
"|results, set the 'include' flag to true. In this case, the"
|
|
|
|
|
"|near tangible will always be the first search result:"
|
|
|
|
|
"|"
|
|
|
|
|
"| include : include the 'near' object in the results"
|
|
|
|
|
"|"
|
|
|
|
|
"|If you want to search an entire plane, you can use the"
|
|
|
|
|
"|wholeplane option, which replaces all the other options:"
|
|
|
|
|
"|"
|
|
|
|
|
"| wholeplane: the name of the plane to scan in its entirety"
|
|
|
|
|
"|"
|
2022-07-22 16:00:37 -04:00
|
|
|
"|It is valid to use 0.0 as a radius. For example, you could"
|
|
|
|
|
"|use shape='cylinder' and radiusz=0.0 to scan a flat"
|
|
|
|
|
"|circular area."
|
|
|
|
|
"|"
|
|
|
|
|
"|It is also valid to use math.huge (infinity) as a radius. For"
|
|
|
|
|
"|example, you could use shape='cylinder' and radiusz=math.huge"
|
|
|
|
|
"|to scan an infinitely tall cylinder."
|
2022-07-14 00:57:11 -04:00
|
|
|
"|"
|
|
|
|
|
"|If you are making a 2D game, it works fine to set all object Z"
|
|
|
|
|
"|coordinates to zero, and then do searches with centerz=0.0"
|
|
|
|
|
"|and radiusz=0.0."
|
|
|
|
|
"|") {
|
|
|
|
|
LuaArg config;
|
|
|
|
|
LuaRet result;
|
|
|
|
|
LuaStack LS(L, config, result);
|
2022-07-22 16:00:37 -04:00
|
|
|
LuaKeywordParser kw(LS, config);
|
2022-07-14 00:57:11 -04:00
|
|
|
PlaneScan scan;
|
2022-07-22 16:00:37 -04:00
|
|
|
scan.configure(kw);
|
2022-07-22 17:07:40 -04:00
|
|
|
kw.final_check_throw();
|
2022-07-22 16:00:37 -04:00
|
|
|
|
2022-07-14 00:57:11 -04:00
|
|
|
// When the configure routine sees the 'near' flag, it stores the tangible
|
|
|
|
|
// ID, but not the center and plane, because doing so would require it to
|
|
|
|
|
// know about world models. We have to handle center and plane for 'near'
|
|
|
|
|
// separately.
|
|
|
|
|
World *w = World::fetch_global_pointer(L);
|
|
|
|
|
int64_t near = scan.near();
|
|
|
|
|
if (near != 0) {
|
|
|
|
|
Tangible *t = w->tangible_get(near);
|
|
|
|
|
assert(t != nullptr); // Should never happen.
|
|
|
|
|
const AnimStep &aqback = t->anim_queue_.back();
|
|
|
|
|
scan.set_plane(aqback.plane());
|
|
|
|
|
scan.set_center(aqback.xyz());
|
|
|
|
|
}
|
|
|
|
|
// Do the scan.
|
|
|
|
|
util::IdVector idv = w->plane_map_.scan(scan);
|
|
|
|
|
tangible_getall(LS, result, idv);
|
|
|
|
|
return LS.result();
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-16 14:21:09 -04:00
|
|
|
LuaDefine(tangible_start, "tangible,function,arg1,arg2...",
|
|
|
|
|
"|Start a thread."
|
|
|
|
|
"|"
|
|
|
|
|
"|Every thread is owned by a tangible. The first argument"
|
|
|
|
|
"|to 'tangible.start' indicates the tangible that owns"
|
2022-05-16 15:17:08 -04:00
|
|
|
"|the new thread. Instead of passing a single tangible,"
|
|
|
|
|
"|you can pass a list of tangibles, in which case a thread"
|
|
|
|
|
"|is started on each tangible."
|
2022-05-16 14:21:09 -04:00
|
|
|
"|"
|
|
|
|
|
"|The function can be a lua closure, or it can be a string."
|
|
|
|
|
"|If it's a string, then the tangible's class will be"
|
|
|
|
|
"|used to look up the relevant closure."
|
|
|
|
|
"|"
|
|
|
|
|
"|The arguments arg1,arg2... will be passed to the"
|
|
|
|
|
"|function."
|
|
|
|
|
"|"
|
|
|
|
|
"|Actor and place aren't passed to the function unless"
|
|
|
|
|
"|you manually include them in the list arg1, arg2, etc."
|
|
|
|
|
"|The new thread can, however, use the builtin "
|
|
|
|
|
"|functions 'tangible.actor' and 'tangible.place' to obtain "
|
|
|
|
|
"|actor and place. Actor will be the same actor who "
|
|
|
|
|
"|called 'tangible.start'. Place will be the tangible that"
|
|
|
|
|
"|owns the thread, ie, the tangible passed to 'tangible.start'."
|
|
|
|
|
"|"
|
|
|
|
|
"|The new thread doesn't start running instantly:"
|
|
|
|
|
"|it waits until the current thread is finished. The"
|
|
|
|
|
"|current thread must either block (eg, 'wait') or terminate"
|
|
|
|
|
"|before the new thread can actually begin execution."
|
|
|
|
|
"|"
|
|
|
|
|
"|If you start a thread, then start another, then both of"
|
|
|
|
|
"|the newly-started threads wait until the current thread"
|
|
|
|
|
"|is finished. At that point, it is undefined which of the"
|
|
|
|
|
"|two new threads runs first."
|
|
|
|
|
"|"
|
|
|
|
|
"|Threads are owned by tangibles. If a tangible is"
|
|
|
|
|
"|deleted, then none of its threads will ever be resumed,"
|
|
|
|
|
"|for any reason."
|
|
|
|
|
"|"
|
|
|
|
|
"|However, if a thread deletes its own place (ie, the tangible"
|
|
|
|
|
"|that owns the thread), then the thread will be allowed"
|
|
|
|
|
"|to continue running until it blocks. But from that point"
|
|
|
|
|
"|forward, the thread will never be resumed for any reason.") {
|
|
|
|
|
|
|
|
|
|
int top = lua_gettop(L);
|
|
|
|
|
if (top < 2) {
|
|
|
|
|
luaL_error(L, "Not enough arguments to tangible.start");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
int varlen = top - 2;
|
|
|
|
|
|
|
|
|
|
World *w = World::fetch_global_pointer(L);
|
2022-05-16 15:17:08 -04:00
|
|
|
w->guard_blockable(L, "tangible.start");
|
2022-05-16 14:21:09 -04:00
|
|
|
|
2022-05-16 15:17:08 -04:00
|
|
|
LuaVar mt, classtab, plthreads, thread, thinfo, func, tanlist;
|
|
|
|
|
LuaStack LS(L, mt, classtab, plthreads, thread, thinfo, func, tanlist);
|
2022-05-16 14:21:09 -04:00
|
|
|
LuaSpecial place(1);
|
2022-05-16 15:17:08 -04:00
|
|
|
LuaSpecial fname(2);
|
2022-05-16 14:21:09 -04:00
|
|
|
|
2022-05-16 15:17:08 -04:00
|
|
|
// If they passed in a single tangible, convert it to a tangible list.
|
2022-05-16 14:21:09 -04:00
|
|
|
int64_t place_id = LS.tanid(place);
|
2022-05-16 15:17:08 -04:00
|
|
|
if (place_id != 0) {
|
|
|
|
|
LS.newtable(tanlist);
|
|
|
|
|
LS.rawset(tanlist, 1, place);
|
|
|
|
|
} else {
|
|
|
|
|
LS.set(tanlist, place);
|
|
|
|
|
if (!LS.istable(tanlist)) {
|
|
|
|
|
luaL_error(L, "tangible.start expects a tangible or list of tangibles");
|
2022-05-16 14:21:09 -04:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-16 15:17:08 -04:00
|
|
|
for (int i = 1; ; i++) {
|
|
|
|
|
LS.rawget(place, tanlist, i);
|
|
|
|
|
if (LS.isnil(place)) break;
|
|
|
|
|
|
|
|
|
|
// Confirm that the place is a valid tangible,
|
|
|
|
|
// and get the tangible ID.
|
|
|
|
|
w->tangible_get(LS, place);
|
|
|
|
|
place_id = LS.tanid(place);
|
|
|
|
|
|
|
|
|
|
// Get place's metatable and threads table.
|
|
|
|
|
LS.getmetatable(mt, place);
|
|
|
|
|
assert(LS.istable(mt));
|
|
|
|
|
LS.rawget(plthreads, mt, "threads");
|
|
|
|
|
assert(LS.istable(plthreads));
|
|
|
|
|
|
|
|
|
|
// Get the function closure.
|
|
|
|
|
if (LS.isfunction(fname)) {
|
|
|
|
|
LS.set(func, fname);
|
|
|
|
|
} else if (LS.isstring(fname)) {
|
|
|
|
|
LS.rawget(classtab, mt, "__index");
|
|
|
|
|
assert(LS.istable(classtab));
|
|
|
|
|
LS.rawget(func, classtab, fname);
|
|
|
|
|
if (!LS.isfunction(func)) {
|
|
|
|
|
eng::string cfname = LS.ckstring(fname);
|
|
|
|
|
luaL_error(L, "tangible doesn't have method: %s", cfname.c_str());
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
luaL_error(L, "invalid function, expected closure or string");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2022-05-16 14:21:09 -04:00
|
|
|
|
2022-05-16 15:17:08 -04:00
|
|
|
// Create a new thread, set up function and arguments.
|
|
|
|
|
lua_State *CO = LS.newthread(thread);
|
|
|
|
|
lua_pushvalue(L, func.index());
|
|
|
|
|
for (int i = 0; i < varlen; i++) {
|
|
|
|
|
lua_pushvalue(L, i + 3);
|
|
|
|
|
}
|
|
|
|
|
lua_xmove(L, CO, varlen + 1);
|
2022-05-16 14:21:09 -04:00
|
|
|
|
2022-05-16 15:17:08 -04:00
|
|
|
// Create the thread info table.
|
|
|
|
|
LS.newtable(thinfo);
|
|
|
|
|
LS.rawset(thinfo, "thread", thread);
|
|
|
|
|
LS.rawset(thinfo, "actorid", w->lthread_actor_id_);
|
|
|
|
|
LS.rawset(thinfo, "isnew", true);
|
|
|
|
|
LS.rawset(thinfo, "useppool", false);
|
|
|
|
|
LS.rawset(thinfo, "print", false);
|
2022-05-16 14:21:09 -04:00
|
|
|
|
2022-05-16 15:17:08 -04:00
|
|
|
// Get a thread ID for the new thread, store it in
|
|
|
|
|
// the thread table.
|
|
|
|
|
int64_t tid = w->alloc_id_predictable();
|
|
|
|
|
LS.rawset(plthreads, tid, thinfo);
|
2022-05-16 14:21:09 -04:00
|
|
|
|
2022-05-16 15:17:08 -04:00
|
|
|
// Push the thread's ID into the runnable thread queue.
|
|
|
|
|
w->schedule(0, tid, place_id);
|
|
|
|
|
}
|
2022-05-16 14:21:09 -04:00
|
|
|
return LS.result();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-12-23 14:38:01 -05:00
|
|
|
LuaDefine(wait, "nticks",
|
|
|
|
|
"|Wait the specified number of ticks.") {
|
2022-04-25 17:17:41 -04:00
|
|
|
World *w = World::fetch_global_pointer(L);
|
2022-05-03 00:36:11 -04:00
|
|
|
w->guard_blockable(L, "wait");
|
|
|
|
|
|
|
|
|
|
// Parse the argument.
|
2022-04-25 17:17:41 -04:00
|
|
|
LuaArg seconds;
|
|
|
|
|
LuaStack LS(L, seconds);
|
|
|
|
|
int64_t n = LS.ckinteger(seconds);
|
|
|
|
|
if ((n < 0) || (n > 1000000)) {
|
|
|
|
|
luaL_error(L, "Argument to wait must be between 0 and 1000000");
|
|
|
|
|
return LS.result();
|
|
|
|
|
}
|
2022-05-03 00:36:11 -04:00
|
|
|
|
|
|
|
|
// Schedule a continuation.
|
|
|
|
|
w->schedule(w->clock_ + n, w->lthread_thread_id_, w->lthread_place_id_);
|
|
|
|
|
return lua_yield(L, 0);
|
2021-09-09 18:23:17 -04:00
|
|
|
}
|
|
|
|
|
|
2022-04-25 17:17:41 -04:00
|
|
|
LuaDefine(nopredict, "",
|
2021-12-23 14:38:01 -05:00
|
|
|
"|Stop predictive execution of this thread.") {
|
2021-11-21 13:35:39 -05:00
|
|
|
World *w = World::fetch_global_pointer(L);
|
2022-05-03 00:36:11 -04:00
|
|
|
w->guard_nopredict(L, "nopredict");
|
|
|
|
|
|
|
|
|
|
if (lua_gettop(L) != 0) {
|
|
|
|
|
luaL_error(L, "nopredict takes no arguments");
|
2021-11-21 13:35:39 -05:00
|
|
|
}
|
2022-04-25 17:17:41 -04:00
|
|
|
return 0;
|
2021-11-21 13:35:39 -05:00
|
|
|
}
|
|
|
|
|
|
2022-03-31 17:15:15 -04:00
|
|
|
LuaDefine(math_random, "(args...)",
|
|
|
|
|
"|Generate random numbers."
|
|
|
|
|
"|"
|
|
|
|
|
"|What it generates depends on the arguments:"
|
|
|
|
|
"|"
|
|
|
|
|
"| () - a float in range [0.0, 1.0)"
|
|
|
|
|
"| (high) - an int between 1 and high inclusive"
|
|
|
|
|
"| (low, high) - an int between low and high inclusive"
|
|
|
|
|
"|"
|
2022-07-14 00:57:11 -04:00
|
|
|
"|You may also pass in a randomstate table"
|
|
|
|
|
"|as an optional first argument."
|
|
|
|
|
"|"
|
2022-03-31 17:15:15 -04:00
|
|
|
"|math.random tries to cooperate with predictive"
|
|
|
|
|
"|reexecution to be as predictable as possible."
|
|
|
|
|
"|To achieve predictability, we used an ad-hoc"
|
|
|
|
|
"|random number generator. It passes a variety of"
|
|
|
|
|
"|statistical tests, but it's not well-studied."
|
|
|
|
|
"|"
|
|
|
|
|
"|If you want actually want nonpredictability, or"
|
|
|
|
|
"|if you need the assurance of a well-studied random"
|
|
|
|
|
"|number generator, use math.mtrandom or"
|
|
|
|
|
"|math.cryptrandom instead.") {
|
|
|
|
|
// Parse the arguments.
|
|
|
|
|
// This is hairy because there's a lot of possibilities.
|
|
|
|
|
bool passed_in_randomstate = false;
|
|
|
|
|
int arg = 1;
|
|
|
|
|
if ((lua_gettop(L) >= arg) && (lua_istable(L, arg))) {
|
|
|
|
|
passed_in_randomstate = true;
|
|
|
|
|
arg += 1;
|
|
|
|
|
}
|
|
|
|
|
bool have_range = false;
|
|
|
|
|
int64_t low, high;
|
|
|
|
|
if ((lua_gettop(L) >= arg) && (lua_type(L, arg) == LUA_TNUMBER)) {
|
|
|
|
|
double lowf, highf;
|
|
|
|
|
if ((lua_gettop(L) >= arg+1) && (lua_type(L, arg+1) == LUA_TNUMBER)) {
|
|
|
|
|
lowf = std::floor(lua_tonumber(L, arg));
|
|
|
|
|
highf = std::floor(lua_tonumber(L, arg + 1));
|
|
|
|
|
arg += 2;
|
|
|
|
|
} else {
|
|
|
|
|
lowf = 1;
|
|
|
|
|
highf = std::floor(lua_tonumber(L, arg));
|
|
|
|
|
arg += 1;
|
|
|
|
|
}
|
|
|
|
|
if ((lowf < -LuaStack::MAXINT) || (highf > LuaStack::MAXINT)) {
|
|
|
|
|
luaL_error(L, "math.random range exceeds MAXINT");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (lowf > highf) {
|
|
|
|
|
luaL_error(L, "math.random range low > high");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
low = int64_t(lowf);
|
|
|
|
|
high = int64_t(highf);
|
|
|
|
|
have_range = true;
|
|
|
|
|
}
|
|
|
|
|
if (lua_gettop(L) >= arg) {
|
|
|
|
|
luaL_error(L, "math.random accepts an optional randomstate and an optional range");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate the seed, count, and salt.
|
|
|
|
|
// The salt prevents accidental duplication between user-specified
|
|
|
|
|
// seeds and system-generated seeds.
|
|
|
|
|
uint64_t seed, count, salt;
|
|
|
|
|
if (passed_in_randomstate) {
|
|
|
|
|
lua_pushstring(L, "seed");
|
|
|
|
|
lua_rawget(L, 1);
|
|
|
|
|
lua_pushstring(L, "count");
|
|
|
|
|
lua_rawget(L, 1);
|
|
|
|
|
if ((lua_type(L, -1) != LUA_TNUMBER) ||
|
|
|
|
|
(lua_type(L, -2) != LUA_TNUMBER)) {
|
|
|
|
|
luaL_error(L, "Not a valid randomstate table");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
double dseed = lua_tonumber(L, -2);
|
|
|
|
|
double dcount = lua_tonumber(L, -1);
|
|
|
|
|
seed = uint64_t(dseed) & LuaStack::MAXINT;
|
|
|
|
|
count = uint64_t(dcount) & LuaStack::MAXINT;
|
|
|
|
|
if (dseed < 0) {
|
|
|
|
|
salt = 0x35c9a6082a097ade;
|
|
|
|
|
} else {
|
|
|
|
|
salt = 0x4785d086ead90c20;
|
|
|
|
|
}
|
|
|
|
|
lua_pop(L, 2);
|
|
|
|
|
lua_pushstring(L, "count");
|
|
|
|
|
lua_pushnumber(L, double((count + 1) & LuaStack::MAXINT));
|
|
|
|
|
lua_rawset(L, 1);
|
|
|
|
|
} else {
|
|
|
|
|
World *w = World::fetch_global_pointer(L);
|
|
|
|
|
if (w->lthread_use_ppool_) {
|
|
|
|
|
Tangible *actor = w->tangible_get(w->lthread_actor_id_);
|
|
|
|
|
seed = w->lthread_actor_id_;
|
|
|
|
|
count = actor->id_player_pool_.get_seqno();
|
|
|
|
|
salt = 0x3ab0fb84aedc3764;
|
|
|
|
|
} else {
|
|
|
|
|
// TODO: maybe throw in a 'donotpredict' here.
|
|
|
|
|
seed = 123456;
|
|
|
|
|
count = w->id_global_pool_.get_seqno();
|
|
|
|
|
salt = 0x6f493c90faf0139d;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!have_range) {
|
2022-04-06 15:09:28 -04:00
|
|
|
// Generate the hash and convert to a double.
|
|
|
|
|
uint64_t hash = util::hash_ints(seed, count, salt, 456);
|
|
|
|
|
lua_pushnumber(L, util::hash_to_double(hash));
|
2022-03-31 17:15:15 -04:00
|
|
|
} else {
|
2022-04-06 15:09:28 -04:00
|
|
|
// Generate the hash and scale it into the desired range.
|
|
|
|
|
// This code is not quite right: the results are not quite
|
|
|
|
|
// uniform, this is especially true for very long ranges.
|
|
|
|
|
uint64_t hash = util::hash_ints(seed, count, salt, 456);
|
2022-03-31 17:15:15 -04:00
|
|
|
uint64_t range = (high - low) + 1;
|
|
|
|
|
uint64_t offset = (hash & 0x7FFFFFFFFFFFFFFF) % range;
|
|
|
|
|
int64_t result = low + int64_t(offset);
|
|
|
|
|
lua_pushnumber(L, result);
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-31 17:16:17 -04:00
|
|
|
LuaDefine(math_randomstate, "seed",
|
2022-03-31 17:15:15 -04:00
|
|
|
"|Create and return a randomstate table."
|
|
|
|
|
"|This is a lua table that stores the state for a random"
|
|
|
|
|
"|number generator. A randomstate table can be passed"
|
|
|
|
|
"|to math.random."
|
|
|
|
|
"|"
|
2022-03-31 17:16:17 -04:00
|
|
|
"|You can optionally omit the seed, in which case a"
|
|
|
|
|
"|seed will be chosen randomly. Automatically-generated"
|
|
|
|
|
"|seeds are guaranteed never to be the same as"
|
|
|
|
|
"|user-specified seeds.") {
|
2022-03-31 17:15:15 -04:00
|
|
|
double seed;
|
|
|
|
|
if (lua_gettop(L) == 0) {
|
|
|
|
|
World *w = World::fetch_global_pointer(L);
|
|
|
|
|
int64_t iseed = (w->id_global_pool_.get_seqno() & LuaStack::MAXINT) + 1;
|
|
|
|
|
seed = -iseed;
|
|
|
|
|
} else if (lua_gettop(L) == 1) {
|
|
|
|
|
if (lua_type(L, 1) != LUA_TNUMBER) {
|
|
|
|
|
luaL_error(L, "math.randomstate takes an optional integer seed");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
seed = lua_tonumber(L, 1);
|
|
|
|
|
if ((seed < 0.0) || (seed > LuaStack::MAXINT) || (std::floor(seed) != seed)) {
|
|
|
|
|
luaL_error(L, "math.randomstate seed must be an integer 0-MAXINT");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
luaL_error(L, "math.randomstate takes an optional integer seed");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lua_newtable(L);
|
|
|
|
|
lua_pushstring(L, "seed");
|
|
|
|
|
lua_pushnumber(L, seed);
|
|
|
|
|
lua_rawset(L, -3);
|
|
|
|
|
lua_pushstring(L, "count");
|
|
|
|
|
lua_pushnumber(L, 0);
|
|
|
|
|
lua_rawset(L, -3);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LuaSandboxBuiltin(math_randomseed, "", "");
|
|
|
|
|
|
2023-03-05 01:51:25 -05:00
|
|
|
LuaDefine(pprint, "obj1, obj2, ...",
|
|
|
|
|
"|Pretty-print the specified objects."
|
|
|
|
|
"|"
|
|
|
|
|
"|See also: pprintx, which has a lot more options."
|
|
|
|
|
"|This function uses the default options: pretty print indented,"
|
|
|
|
|
"|start at indentation level zero, and always expand the"
|
|
|
|
|
"|top-level table."
|
|
|
|
|
"|") {
|
2021-10-21 13:15:04 -04:00
|
|
|
World *w = World::fetch_global_pointer(L);
|
2022-02-25 19:57:23 -05:00
|
|
|
std::ostream *ostream = w->lthread_print_stream();
|
2023-03-05 01:51:25 -05:00
|
|
|
int n = lua_gettop(L);
|
2021-10-21 13:15:04 -04:00
|
|
|
LuaStack LS(L);
|
2023-03-05 01:51:25 -05:00
|
|
|
for (int i = 1; i <= n; i++) {
|
2021-10-21 13:15:04 -04:00
|
|
|
LuaSpecial root(i);
|
2023-03-05 01:51:25 -05:00
|
|
|
pprint(LS, root, PrettyPrintOptions(), ostream);
|
|
|
|
|
if (i < n) (*ostream) << "\n";
|
|
|
|
|
}
|
|
|
|
|
(*ostream) << std::endl;
|
|
|
|
|
return LS.result();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LuaDefine(pprintx, "options",
|
|
|
|
|
"|Pretty-print the specified object, with options"
|
|
|
|
|
"|"
|
|
|
|
|
"|Options is a table with these fields:"
|
|
|
|
|
"|"
|
|
|
|
|
"| value - the object to pretty-print"
|
|
|
|
|
"| indent - if false, suppress newlines and indentation (default: true)"
|
|
|
|
|
"| level - base level of indentation (default: zero)"
|
|
|
|
|
"| expand - if true, force expansion of top-level table (default: false)"
|
|
|
|
|
"|"
|
|
|
|
|
"|About the expand flag: normally, when you print a class, it just "
|
|
|
|
|
"|prints '<class name>', and when you print a tangible, it just"
|
|
|
|
|
"|prints '<tangible id>'. But sometimes, you want to see the details."
|
|
|
|
|
"|The expand flag forces it to expand the top-level table, even if the"
|
|
|
|
|
"|top-level table is a tangible or class."
|
|
|
|
|
"|") {
|
|
|
|
|
World *w = World::fetch_global_pointer(L);
|
|
|
|
|
std::ostream *ostream = w->lthread_print_stream();
|
|
|
|
|
LuaArg loptions;
|
|
|
|
|
LuaVar value;
|
|
|
|
|
LuaStack LS(L, loptions, value);
|
|
|
|
|
PrettyPrintOptions options;
|
|
|
|
|
LuaKeywordParser kp(LS, loptions);
|
|
|
|
|
options.parse(kp);
|
|
|
|
|
if (!kp.parse(value, "value")) {
|
|
|
|
|
LS.set(value, LuaNil);
|
2021-10-21 13:15:04 -04:00
|
|
|
}
|
2023-03-05 01:51:25 -05:00
|
|
|
kp.final_check_throw();
|
|
|
|
|
pprint(LS, value, options, ostream);
|
2021-10-21 13:15:04 -04:00
|
|
|
return LS.result();
|
2021-10-21 14:22:06 -04:00
|
|
|
}
|
|
|
|
|
|
2023-03-05 01:51:25 -05:00
|
|
|
LuaDefine(print, "obj1, obj2, ...",
|
2021-12-23 14:38:01 -05:00
|
|
|
"|Print object or objects.") {
|
2021-10-21 14:22:06 -04:00
|
|
|
World *w = World::fetch_global_pointer(L);
|
2022-02-25 19:57:23 -05:00
|
|
|
std::ostream *ostream = w->lthread_print_stream();
|
2021-10-21 14:22:06 -04:00
|
|
|
LuaStack LS(L);
|
2021-12-23 14:38:01 -05:00
|
|
|
int n = lua_gettop(L);
|
|
|
|
|
for (int i = 1; i <= n; i++) {
|
2021-10-21 14:22:06 -04:00
|
|
|
LuaSpecial root(i);
|
2021-12-15 23:03:43 -05:00
|
|
|
atomic_print(LS, root, false, ostream);
|
2021-12-23 14:38:01 -05:00
|
|
|
if (i < n) (*ostream) << " ";
|
2021-10-21 14:22:06 -04:00
|
|
|
}
|
2021-12-23 14:38:01 -05:00
|
|
|
(*ostream) << std::endl;
|
2021-10-21 14:22:06 -04:00
|
|
|
return LS.result();
|
2021-12-15 23:03:43 -05:00
|
|
|
}
|
|
|
|
|
|
2021-12-23 14:38:01 -05:00
|
|
|
LuaDefine(doc, "function",
|
|
|
|
|
"|Print documentation for specified function.") {
|
2021-12-15 23:03:43 -05:00
|
|
|
World *w = World::fetch_global_pointer(L);
|
2022-02-25 19:57:23 -05:00
|
|
|
std::ostream *ostream = w->lthread_print_stream();
|
2021-12-15 23:03:43 -05:00
|
|
|
LuaArg func;
|
|
|
|
|
LuaStack LS(L, func);
|
2022-02-24 02:17:41 -05:00
|
|
|
eng::string doc = SourceDB::function_docs(LS, func);
|
2021-12-15 23:03:43 -05:00
|
|
|
if (doc == "") {
|
|
|
|
|
(*ostream) << "no doc found" << std::endl;
|
|
|
|
|
}
|
|
|
|
|
(*ostream) << doc;
|
|
|
|
|
return LS.result();
|
2022-05-03 00:36:11 -04:00
|
|
|
}
|
|
|
|
|
|
2022-06-07 01:54:08 -04:00
|
|
|
int lfn_http_request(lua_State *L, const char *method) {
|
2022-05-03 00:36:11 -04:00
|
|
|
World *w = World::fetch_global_pointer(L);
|
|
|
|
|
w->guard_blockable(L, "http.get");
|
|
|
|
|
|
|
|
|
|
LuaArg request;
|
|
|
|
|
LuaRet response;
|
|
|
|
|
LuaStack LS(L, request, response);
|
2022-07-22 17:07:40 -04:00
|
|
|
LuaKeywordParser kp(LS, request);
|
2022-05-06 13:16:27 -04:00
|
|
|
HttpClientRequest req;
|
2022-05-03 00:36:11 -04:00
|
|
|
|
|
|
|
|
// Parse the request and make sure it's valid.
|
2022-05-20 17:12:58 -04:00
|
|
|
// If not, immediately pass a '400 bad request' back to lua.
|
2022-06-07 01:54:08 -04:00
|
|
|
req.set_method(method);
|
2022-07-22 17:07:40 -04:00
|
|
|
req.configure(kp);
|
|
|
|
|
kp.final_check_throw();
|
2022-05-03 00:36:11 -04:00
|
|
|
req.set_defaults();
|
|
|
|
|
eng::string error = req.check();
|
|
|
|
|
if (!error.empty()) {
|
2022-05-20 17:12:58 -04:00
|
|
|
HttpParser::store_fail(LS, response, 400, util::ss("Bad Request: ", error));
|
2022-05-03 00:36:11 -04:00
|
|
|
return LS.result();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Give the request an ID.
|
|
|
|
|
req.set_request_id(w->id_global_pool_.get_one());
|
|
|
|
|
req.set_place_id(w->lthread_place_id_);
|
|
|
|
|
req.set_thread_id(w->lthread_thread_id_);
|
|
|
|
|
|
|
|
|
|
// Store it in the global request table.
|
|
|
|
|
w->http_requests_[req.request_id()] = req;
|
|
|
|
|
|
|
|
|
|
// Block.
|
|
|
|
|
return lua_yield(L, 0);
|
2022-06-07 01:54:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LuaDefine(http_get, "request",
|
|
|
|
|
"|Make an HTTP GET request. Returns an HTTP response."
|
|
|
|
|
"|See doc(http.clientrequest) and doc(http.clientresponse).") {
|
|
|
|
|
return lfn_http_request(L, "GET");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LuaDefine(http_head, "request",
|
|
|
|
|
"|Make an HTTP HEAD request. Returns an HTTP response."
|
|
|
|
|
"|See doc(http.clientrequest) and doc(http.clientresponse).") {
|
|
|
|
|
return lfn_http_request(L, "HEAD");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LuaDefine(http_post, "request",
|
|
|
|
|
"|Make an HTTP POST request. Returns an HTTP response."
|
|
|
|
|
"|See doc(http.clientrequest) and doc(http.clientresponse).") {
|
|
|
|
|
return lfn_http_request(L, "POST");
|
|
|
|
|
}
|