Merge
This commit is contained in:
@@ -927,12 +927,6 @@ static void replaycore_step(EngineWrapper *w) {
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if defined(__linux__)
|
||||
#define DLLEXPORT __attribute__((visibility("default")))
|
||||
#elif defined(_WIN32)
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#endif
|
||||
|
||||
static void init_engine_wrapper_helper(EngineWrapper *w) {
|
||||
static bool called_initializer;
|
||||
assert(DrivenEngineInitializerReg::func != nullptr);
|
||||
@@ -973,6 +967,12 @@ static void init_engine_wrapper_helper(EngineWrapper *w) {
|
||||
w->release = release;
|
||||
};
|
||||
|
||||
#if defined(__linux__)
|
||||
#define DLLEXPORT __attribute__((visibility("default")))
|
||||
#elif defined(_WIN32)
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
DLLEXPORT void init_engine_wrapper(EngineWrapper *w) {
|
||||
init_engine_wrapper_helper(w);
|
||||
|
||||
@@ -744,51 +744,51 @@ void HttpClientRequest::set_defaults() {
|
||||
void HttpClientRequest::configure(LuaKeywordParser &kp) {
|
||||
LuaVar val;
|
||||
LuaExtStack LS(kp.state(), val);
|
||||
if (kp.parse(val, "method")) {
|
||||
if (kp.optional(val, "method")) {
|
||||
set_method(LS, val);
|
||||
}
|
||||
if (kp.parse(val, "host")) {
|
||||
if (kp.optional(val, "host")) {
|
||||
set_host(LS, val);
|
||||
}
|
||||
if (kp.parse(val, "port")) {
|
||||
if (kp.optional(val, "port")) {
|
||||
set_port(LS, val);
|
||||
}
|
||||
if (kp.parse(val, "path")) {
|
||||
if (kp.optional(val, "path")) {
|
||||
set_path(LS, val);
|
||||
}
|
||||
if (kp.parse(val, "params")) {
|
||||
if (kp.optional(val, "params")) {
|
||||
set_params(LS, val);
|
||||
}
|
||||
if (kp.parse(val, "url")) {
|
||||
if (kp.optional(val, "url")) {
|
||||
set_url(LS, val);
|
||||
}
|
||||
if (kp.parse(val, "verifycertificate")) {
|
||||
if (kp.optional(val, "verifycertificate")) {
|
||||
set_verify_certificate(LS, val);
|
||||
}
|
||||
if (kp.parse(val, "mimetype")) {
|
||||
if (kp.optional(val, "mimetype")) {
|
||||
set_mime_type(LS, val);
|
||||
}
|
||||
if (kp.parse(val, "content")) {
|
||||
if (kp.optional(val, "content")) {
|
||||
set_content(LS, val);
|
||||
}
|
||||
if (kp.parse(val, "html")) {
|
||||
if (kp.optional(val, "html")) {
|
||||
set_content(LS, val);
|
||||
set_mime_type("text/html");
|
||||
}
|
||||
|
||||
if (kp.parse(val, "text")) {
|
||||
if (kp.optional(val, "text")) {
|
||||
set_content(LS, val);
|
||||
set_mime_type("text/plain");
|
||||
}
|
||||
if (kp.parse(val, "json")) {
|
||||
if (kp.optional(val, "json")) {
|
||||
set_content(LS, val);
|
||||
set_mime_type("application/json");
|
||||
}
|
||||
if (kp.parse(val, "bytes")) {
|
||||
if (kp.optional(val, "bytes")) {
|
||||
set_content(LS, val);
|
||||
set_mime_type("application/octet-stream");
|
||||
}
|
||||
if (kp.parse(val, "jsonvalue")) {
|
||||
if (kp.optional(val, "jsonvalue")) {
|
||||
set_jsonvalue(LS, val);
|
||||
}
|
||||
}
|
||||
@@ -1044,35 +1044,35 @@ void HttpServerResponse::set_jsonvalue(LuaCoreStack &LS, LuaSlot val) {
|
||||
void HttpServerResponse::configure(LuaKeywordParser &kp) {
|
||||
LuaVar val;
|
||||
LuaExtStack LS(kp.state(), val);
|
||||
if (kp.parse(val, "status")) {
|
||||
if (kp.optional(val, "status")) {
|
||||
set_status(LS, val);
|
||||
}
|
||||
if (kp.parse(val, "maxage")) {
|
||||
if (kp.optional(val, "maxage")) {
|
||||
set_max_age(LS, val);
|
||||
}
|
||||
if (kp.parse(val, "mimetype")) {
|
||||
if (kp.optional(val, "mimetype")) {
|
||||
set_mime_type(LS, val);
|
||||
}
|
||||
if (kp.parse(val, "content")) {
|
||||
if (kp.optional(val, "content")) {
|
||||
set_content(LS, val);
|
||||
}
|
||||
if (kp.parse(val, "html")) {
|
||||
if (kp.optional(val, "html")) {
|
||||
set_content(LS, val);
|
||||
set_mime_type("text/html");
|
||||
}
|
||||
if (kp.parse(val, "text")) {
|
||||
if (kp.optional(val, "text")) {
|
||||
set_content(LS, val);
|
||||
set_mime_type("text/plain");
|
||||
}
|
||||
if (kp.parse(val, "json")) {
|
||||
if (kp.optional(val, "json")) {
|
||||
set_content(LS, val);
|
||||
set_mime_type("application/json");
|
||||
}
|
||||
if (kp.parse(val, "bytes")) {
|
||||
if (kp.optional(val, "bytes")) {
|
||||
set_content(LS, val);
|
||||
set_mime_type("application/octet-stream");
|
||||
}
|
||||
if (kp.parse(val, "jsonvalue")) {
|
||||
if (kp.optional(val, "jsonvalue")) {
|
||||
set_jsonvalue(LS, val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
LuaSpecial LuaRegistry(LUA_REGISTRYINDEX);
|
||||
LuaNilMarker LuaNil;
|
||||
LuaNewTableMarker LuaNewTable;
|
||||
static LuaToken token_error("error");
|
||||
static LuaToken token_found("found");
|
||||
|
||||
|
||||
LuaFunctionReg::LuaFunctionReg(const char *n, const char *a, const char *d, bool s, lua_CFunction f) {
|
||||
name_ = n;
|
||||
@@ -655,52 +658,119 @@ void LuaCoreStack::guard_nopredict(const char *fn) {
|
||||
}
|
||||
}
|
||||
|
||||
LuaKeywordParser::LuaKeywordParser(lua_State *L, int slot) {
|
||||
L_ = L;
|
||||
slot_ = slot;
|
||||
not_table_ = !lua_istable(L_, slot_);
|
||||
if (not_table_) {
|
||||
lua_newtable(L_);
|
||||
lua_replace(L_, slot_);
|
||||
static int tailcall_continuation(lua_State *L)
|
||||
{
|
||||
int base;
|
||||
lua_getctx(L, &base);
|
||||
return lua_gettop(L) - base;
|
||||
}
|
||||
|
||||
int LuaDefStack::tailcall_internal(bool passup, int base, int nargs) {
|
||||
lua_callk(L_, nargs, passup ? LUA_MULTRET : 0, base, tailcall_continuation);
|
||||
return lua_gettop(L_) - base;
|
||||
}
|
||||
|
||||
LuaKeywordParser::LuaKeywordParser(const LuaCoreStack &LS0, LuaSlot slot)
|
||||
: keytab(slot.index()), LS(LS0.state()) {
|
||||
lua_State *L = LS0.state();
|
||||
lua_pushnil(L); found.index_ = lua_gettop(L);
|
||||
lua_pushnil(L); error.index_ = lua_gettop(L);
|
||||
lua_pushnil(L); key.index_ = lua_gettop(L);
|
||||
lua_pushnil(L); val.index_ = lua_gettop(L);
|
||||
|
||||
istable = LS.istable(keytab);
|
||||
if (istable) {
|
||||
LS.rawget(found, keytab, token_found);
|
||||
if (!LS.istable(found)) {
|
||||
LS.set(found, LuaNewTable);
|
||||
LS.rawset(keytab, token_found, found);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LuaKeywordParser::parse(LuaSlot out, const char *kw) {
|
||||
lua_pushstring(L_, kw);
|
||||
lua_rawget(L_, slot_);
|
||||
lua_replace(L_, out.index());
|
||||
if (!lua_isnil(L_, out.index())) {
|
||||
parsed_.insert(kw);
|
||||
bool LuaKeywordParser::optional(LuaSlot out, std::string_view kw) {
|
||||
if (!istable) {
|
||||
LS.set(out, LuaNil);
|
||||
return false;
|
||||
}
|
||||
|
||||
LS.rawget(out, keytab, kw);
|
||||
if (!LS.isnil(out)) {
|
||||
LS.rawset(found, kw, true);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
eng::string LuaKeywordParser::final_check() {
|
||||
if (not_table_) {
|
||||
return "expected a keyword table";
|
||||
bool LuaKeywordParser::required(LuaSlot out, std::string_view kw) {
|
||||
if (!istable) {
|
||||
LS.set(out, LuaNil);
|
||||
return false;
|
||||
}
|
||||
lua_pushnil(L_);
|
||||
while (lua_next(L_, slot_) != 0) {
|
||||
lua_pop(L_, 1); // Don't need the value.
|
||||
if (!lua_isstring(L_, -1)) {
|
||||
return "keyword table contains non-string key";
|
||||
|
||||
LS.rawget(out, keytab, kw);
|
||||
if (!LS.isnil(out)) {
|
||||
LS.rawset(found, kw, true);
|
||||
return true;
|
||||
} else {
|
||||
LS.rawget(error, keytab, token_error);
|
||||
if (!LS.isstring(error)) {
|
||||
LS.rawset(keytab, token_error, util::ss("required keyword argument not present: ", kw));
|
||||
}
|
||||
const char *kw = lua_tostring(L_, -1);
|
||||
if (parsed_.find(kw) == parsed_.end()) {
|
||||
eng::ostringstream oss;
|
||||
oss << "keyword " << kw << " not known";
|
||||
return oss.str();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
eng::string LuaKeywordParser::check() {
|
||||
if (!istable) {
|
||||
return "keyword arguments must be a table";
|
||||
}
|
||||
|
||||
LS.rawget(error, keytab, token_error);
|
||||
auto str = LS.trystring(error);
|
||||
if (str.has_value()) return str.value();
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
eng::string LuaKeywordParser::final_check() {
|
||||
if (!istable) {
|
||||
return "keyword arguments must be a table";
|
||||
}
|
||||
|
||||
LS.rawget(error, keytab, token_error);
|
||||
auto str = LS.trystring(error);
|
||||
if (str.has_value()) return str.value();
|
||||
|
||||
LS.set(key, LuaNil);
|
||||
while (LS.next(keytab, key, val)) {
|
||||
if (LS.istoken(key)) {
|
||||
continue;
|
||||
}
|
||||
auto kw = LS.trystringview(key);
|
||||
if (!kw.has_value()) {
|
||||
return "keyword arguments include a non-string key";
|
||||
}
|
||||
LS.rawget(val, found, key);
|
||||
if (!LS.rawequal(val, true)) {
|
||||
return util::ss("unrecognized keyword argument: ", kw.value());
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void LuaKeywordParser::check_throw() {
|
||||
eng::string err = check();
|
||||
if (!err.empty()) {
|
||||
luaL_error(LS.state(), "%s", err.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void LuaKeywordParser::final_check_throw() {
|
||||
eng::string err = final_check();
|
||||
if (!err.empty()) {
|
||||
luaL_error(L_, "%s", err.c_str());
|
||||
luaL_error(LS.state(), "%s", err.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -712,3 +782,127 @@ const char *LuaByteReader::lua_reader(lua_State *L, void *ud, size_t *size) {
|
||||
reader->size_ = 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
static const char *kwdoc =
|
||||
"|Parse lua keyword arguments."
|
||||
"|"
|
||||
"|The keywords module is used to help parse keyword arguments"
|
||||
"|for functions. The error handling is strong, it throws useful"
|
||||
"|error messages when the caller passes in incorrect arguments."
|
||||
"|This is a typical example of how it is used:"
|
||||
"|"
|
||||
"| function drawrect(args)"
|
||||
"| local x1,y1 = keywords.required(args, 'x1', 'y1')"
|
||||
"| local x2,y2 = keywords.required(args, 'x2', 'y2')"
|
||||
"| local color = keywords.optional(args, 'color')"
|
||||
"| keywords.finalcheckthrow(args)"
|
||||
"| ..."
|
||||
"| end"
|
||||
"|"
|
||||
"|The function above expects four required keyword arguments and one"
|
||||
"|optional one. The example code above handles all the following"
|
||||
"|error cases:"
|
||||
"|"
|
||||
"| * That 'args' actually is a table."
|
||||
"| * That the required keywords x1,y1,x2,y2 are all present."
|
||||
"| * That the table doesn't contain extraneous unknown keywords."
|
||||
"|"
|
||||
"|The functions keywords.required and keywords.optional don't throw"
|
||||
"|lua errors. Instead, they store status information in the keyword"
|
||||
"|table itself. Using the status information, the function"
|
||||
"|keywords.finalcheckthrow will throw an error if anything went wrong"
|
||||
"|along the way."
|
||||
"|"
|
||||
"|You can check errors when you're not finished parsing using the"
|
||||
"|function keywords.checkthrow() instead of keywords.finalcheckthrow()."
|
||||
"|Unlike finalcheckthrow, this function doesn't verify the absence of"
|
||||
"|extraneous keywords, which can only be done at the end."
|
||||
"|"
|
||||
"|If you don't want to throw errors at all, you can use"
|
||||
"|keywords.finalcheck() instead of keywords.finalcheckthrow()."
|
||||
"|This returns the error message, rather than throwing it."
|
||||
"|";
|
||||
|
||||
|
||||
LuaDefine(keywords_optional, "table, keyword, keyword, keyword...", kwdoc) {
|
||||
LuaArg table;
|
||||
LuaExtraArgs keywords;
|
||||
LuaDefStack LS(L, table, keywords);
|
||||
LuaKeywordParser KP(LS, table);
|
||||
|
||||
if (keywords.size() < 1) {
|
||||
luaL_error(L, "expected at least one keyword");
|
||||
return 0;
|
||||
}
|
||||
for (int i = 0; i < keywords.size(); i++) {
|
||||
eng::string kw = LS.ckstring(keywords[i], "keyword");
|
||||
KP.optional(keywords[i], kw);
|
||||
}
|
||||
|
||||
// Return the results without using LS.result, because it doesn't
|
||||
// support multiple return values.
|
||||
lua_settop(L, keywords[keywords.size() - 1].index());
|
||||
return keywords.size();
|
||||
}
|
||||
|
||||
LuaDefine(keywords_required, "table, keyword, keyword, keyword...", kwdoc) {
|
||||
LuaArg table;
|
||||
LuaExtraArgs keywords;
|
||||
LuaDefStack LS(L, table, keywords);
|
||||
LuaKeywordParser KP(LS, table);
|
||||
|
||||
if (keywords.size() < 1) {
|
||||
luaL_error(L, "expected at least one keyword");
|
||||
return 0;
|
||||
}
|
||||
for (int i = 0; i < keywords.size(); i++) {
|
||||
eng::string kw = LS.ckstring(keywords[i], "keyword");
|
||||
KP.required(keywords[i], kw);
|
||||
}
|
||||
|
||||
// Return the results without using LS.result, because it doesn't
|
||||
// support multiple return values.
|
||||
lua_settop(L, keywords[keywords.size() - 1].index());
|
||||
return keywords.size();
|
||||
}
|
||||
|
||||
LuaDefine(keywords_check, "table", kwdoc) {
|
||||
LuaArg table;
|
||||
LuaRet result;
|
||||
LuaDefStack LS(L, table, result);
|
||||
LuaKeywordParser KP(LS, table);
|
||||
eng::string err = KP.check();
|
||||
if (!err.empty()) {
|
||||
LS.set(result, err);
|
||||
}
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
LuaDefine(keywords_finalcheck, "table", kwdoc) {
|
||||
LuaArg table;
|
||||
LuaRet result;
|
||||
LuaDefStack LS(L, table, result);
|
||||
LuaKeywordParser KP(LS, table);
|
||||
eng::string err = KP.final_check();
|
||||
if (!err.empty()) {
|
||||
LS.set(result, err);
|
||||
}
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
LuaDefine(keywords_checkthrow, "table", kwdoc) {
|
||||
LuaArg table;
|
||||
LuaDefStack LS(L, table);
|
||||
LuaKeywordParser KP(LS, table);
|
||||
KP.check_throw();
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
LuaDefine(keywords_finalcheckthrow, "table", kwdoc) {
|
||||
LuaArg table;
|
||||
LuaDefStack LS(L, table);
|
||||
LuaKeywordParser KP(LS, table);
|
||||
KP.final_check_throw();
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
|
||||
@@ -538,6 +538,7 @@ private:
|
||||
friend class LuaCoreStack;
|
||||
friend class LuaDefStack;
|
||||
friend class LuaExtStack;
|
||||
friend class LuaKeywordParser;
|
||||
};
|
||||
|
||||
class LuaArg : public LuaSlot {};
|
||||
@@ -563,10 +564,8 @@ private:
|
||||
int size_;
|
||||
|
||||
public:
|
||||
LuaExtraArgs() {
|
||||
index_ = 0;
|
||||
size_ = 0;
|
||||
}
|
||||
LuaExtraArgs() : index_(0), size_(0) {}
|
||||
LuaExtraArgs(int i, int s) : index_(i), size_(s) {}
|
||||
|
||||
LuaSpecial operator[] (int n) const { return LuaSpecial(index_ + n); }
|
||||
int size() const { return size_; }
|
||||
@@ -1183,7 +1182,33 @@ public:
|
||||
return nret_;
|
||||
}
|
||||
|
||||
// Tail-call into lua.
|
||||
//
|
||||
// This is meant to be used as follows: return LS.tailcall(passup, func, arg, arg...)
|
||||
//
|
||||
// If passup is true, the return value to our caller consists of our
|
||||
// LuaRet arguments concatenated to the return values from the tail-call.
|
||||
// If passup is false, the return value to our caller consists solely
|
||||
// of our LuaRet arguments.
|
||||
//
|
||||
template<typename... T>
|
||||
int tailcall(bool passup, LuaSlot func, T... args) {
|
||||
lua_checkstack(L_, nret_ + 20);
|
||||
int base = lua_gettop(L_);
|
||||
for (int i = 1; i <= nret_; i++) {
|
||||
lua_pushvalue(L_, i);
|
||||
}
|
||||
push_any_value(func);
|
||||
int argbase = lua_gettop(L_);
|
||||
push_any_values(args...);
|
||||
int nargs = lua_gettop(L_) - argbase;
|
||||
return tailcall_internal(passup, base, nargs);
|
||||
}
|
||||
|
||||
~LuaDefStack() { }
|
||||
|
||||
private:
|
||||
int tailcall_internal(bool passup, int base, int nargs);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
@@ -1231,7 +1256,9 @@ public:
|
||||
|
||||
~LuaExtStack() {
|
||||
if (!lua_isthrowing(L_)) {
|
||||
lua_settop(L_, oldtop_);
|
||||
if (lua_gettop(L_) > oldtop_) {
|
||||
lua_settop(L_, oldtop_);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1246,49 +1273,85 @@ public:
|
||||
//
|
||||
// * It makes sure the keyword table actually is a table.
|
||||
//
|
||||
// * It makes sure that you didn't put an unrecognized keyword
|
||||
// into the keyword table. Unrecognized keywords are defined
|
||||
// as keywords that are never checked using 'parse'.
|
||||
// * It makes sure that all required keywords are present.
|
||||
//
|
||||
// * It makes sure that you didn't put anything that isn't a
|
||||
// keyword into the keyword table.
|
||||
// known keyword into the keyword table.
|
||||
//
|
||||
// This module adds two fields to the table:
|
||||
//
|
||||
// [ERROR] - stores an error message, initially nil.
|
||||
// [FOUND] - the set of keywords successfully parsed.
|
||||
//
|
||||
// If at any time, this module detects an error, it doesn't throw.
|
||||
// Instead, it stores an error report in the table key [ERROR].
|
||||
// Later, you can check for an error string using the function
|
||||
// final_check or final_check_throw. If an error is
|
||||
// detected when there is already an error report in the table,
|
||||
// the error report is not overwritten, so therefore, the error
|
||||
// reported is always the first error detected.
|
||||
//
|
||||
// When this module finds a keyword in the table, it adds the keyword
|
||||
// to the [FOUND] set of all keywords successfully parsed.
|
||||
//
|
||||
// If the keyword table that you pass in isn't a table at all,
|
||||
// then the keyword-fetching functions will always return false.
|
||||
// Later, when you call 'check', an appropriate error will be
|
||||
// generated.
|
||||
//
|
||||
// The lua module 'keywords' contains the same functions as this
|
||||
// C++ class. You can write code where a C++ function does some
|
||||
// of the parsing, and the lua code does the rest of the parsing.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
class LuaKeywordParser {
|
||||
struct cmp_char {
|
||||
bool operator () (const char *s1, const char *s2) const {
|
||||
return strcmp(s1, s2) < 0;
|
||||
};
|
||||
};
|
||||
private:
|
||||
bool not_table_;
|
||||
lua_State *L_;
|
||||
int slot_;
|
||||
eng::set<const char *, cmp_char> parsed_;
|
||||
LuaVar found, error, key, val;
|
||||
LuaSpecial keytab;
|
||||
LuaCoreStack LS;
|
||||
bool istable;
|
||||
|
||||
void init(const lua_State *L, int slot);
|
||||
public:
|
||||
// If the slot is not a table, sets the not_table
|
||||
// flag and creates a dummy table in the slot.
|
||||
LuaKeywordParser(lua_State *L, int slot);
|
||||
LuaKeywordParser(const LuaCoreStack &LS, LuaSlot slot) : LuaKeywordParser(LS.state(), slot.index()) {}
|
||||
LuaKeywordParser(const LuaCoreStack &LS, LuaSlot slot);
|
||||
|
||||
// Fetch a value from the table. This never throws.
|
||||
// Return true if the value is non-nil.
|
||||
bool parse(LuaSlot slot, const char *kw);
|
||||
// Fetch the value of the keyword. If the keyword is found, then the
|
||||
// keyword is added to the [FOUND] set, the value is returned in slot,
|
||||
// and returns true. Otherwise, sets slot to nil and returns false.
|
||||
bool optional(LuaSlot slot, std::string_view kw);
|
||||
|
||||
// Check if there were any errors. If so, return an
|
||||
// error message.
|
||||
// Fetch the value of the keyword. If the keyword is found, then the
|
||||
// keyword is added to the [FOUND] set, the value is returned in slot,
|
||||
// and returns true. Otherwise, sets slot to nil, returns false, and
|
||||
// stores an [ERROR] report in the keyword table.
|
||||
bool required(LuaSlot slot, std::string_view kw);
|
||||
|
||||
// Check if there are any errors so far, by checking for an [ERROR]
|
||||
// report in the keyword table. If any error has been
|
||||
// detected, returns an error message, otherwise, returns empty
|
||||
// string.
|
||||
eng::string check();
|
||||
|
||||
// Check if there are any errors so far, by checking for an [ERROR]
|
||||
// report in the keyword table. Also check that all keyword
|
||||
// arguments present in the table are in the [FOUND] set. If there are
|
||||
// any errors, returns an error message, otherwise returns empty string.
|
||||
eng::string final_check();
|
||||
|
||||
// Check if there are any errors. If so, throw a lua error.
|
||||
// If check() returns an error, throws the error using luaL_error.
|
||||
void check_throw();
|
||||
|
||||
// If final_check() returns an error, throws the error using luaL_error.
|
||||
void final_check_throw();
|
||||
|
||||
// Fetch the state pointer.
|
||||
lua_State *state() const { return L_; }
|
||||
lua_State *state() const { return LS.state(); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Lua Byte Reader
|
||||
|
||||
@@ -834,18 +834,20 @@ void PlaneScan::configure(LuaKeywordParser &kp) {
|
||||
bool have_shape = false;
|
||||
bool have_near = false;
|
||||
|
||||
if (kp.parse(val, "plane")) {
|
||||
kp.check_throw();
|
||||
|
||||
if (kp.optional(val, "plane")) {
|
||||
plane_ = LS.ckstring(val, "plane");
|
||||
have_plane = true;
|
||||
}
|
||||
|
||||
if (kp.parse(val, "center")) {
|
||||
if (kp.optional(val, "center")) {
|
||||
util::DXYZ xyz = LS.ckxyz(val, "center");
|
||||
center_ = xyz;
|
||||
have_center = true;
|
||||
}
|
||||
|
||||
if (kp.parse(val, "radius")) {
|
||||
if (kp.optional(val, "radius")) {
|
||||
auto simple = LS.trynumber(val);
|
||||
if (simple) {
|
||||
radius_ = *simple;
|
||||
@@ -862,7 +864,7 @@ void PlaneScan::configure(LuaKeywordParser &kp) {
|
||||
}
|
||||
}
|
||||
|
||||
if (kp.parse(val, "shape")) {
|
||||
if (kp.optional(val, "shape")) {
|
||||
eng::string shape = LS.ckstring(val, "shape");
|
||||
if (shape == "box") {
|
||||
shape_ = BOX;
|
||||
@@ -876,7 +878,7 @@ void PlaneScan::configure(LuaKeywordParser &kp) {
|
||||
have_shape = true;
|
||||
}
|
||||
|
||||
if (kp.parse(val, "near")) {
|
||||
if (kp.optional(val, "near")) {
|
||||
int64_t id = LS.tanid(val);
|
||||
if (id == 0) {
|
||||
luaL_error(L, "scan configuration: 'near' must be a tangible");
|
||||
@@ -893,14 +895,14 @@ void PlaneScan::configure(LuaKeywordParser &kp) {
|
||||
have_near = true;
|
||||
}
|
||||
|
||||
if (kp.parse(val, "include")) {
|
||||
if (kp.optional(val, "include")) {
|
||||
if (!have_near) {
|
||||
luaL_error(L, "scan configuration: 'include' specified without 'near'");
|
||||
}
|
||||
include_near_ = LS.ckboolean(val, "include");
|
||||
}
|
||||
|
||||
if (kp.parse(val, "wholeplane")) {
|
||||
if (kp.optional(val, "wholeplane")) {
|
||||
if (have_plane || have_center || have_radius || have_shape) {
|
||||
luaL_error(L, "scan configuration: do not specify plane, center, shape, or radius with 'wholeplane'");
|
||||
}
|
||||
|
||||
@@ -173,6 +173,8 @@ public:
|
||||
tabify(level + 1);
|
||||
if (LS.isstring(key) && sv::is_lua_id(LS.ckstring(key))) {
|
||||
(*output_) << LS.ckstring(key);
|
||||
} else if (LS.istoken(key)) {
|
||||
atomic_print(LUA_TLIGHTUSERDATA, key, false);
|
||||
} else {
|
||||
(*output_) << "[";
|
||||
pprint_r(level + 1, false, key);
|
||||
@@ -243,13 +245,14 @@ public:
|
||||
void PrettyPrintOptions::parse(LuaKeywordParser &kp) {
|
||||
LuaVar option;
|
||||
LuaExtStack LS(kp.state(), option);
|
||||
if (kp.parse(option, "indent")) {
|
||||
kp.check_throw();
|
||||
if (kp.optional(option, "indent")) {
|
||||
indent = LS.ckboolean(option);
|
||||
}
|
||||
if (kp.parse(option, "level")) {
|
||||
if (kp.optional(option, "level")) {
|
||||
level = LS.ckint(option);
|
||||
}
|
||||
if (kp.parse(option, "expand")) {
|
||||
if (kp.optional(option, "expand")) {
|
||||
expand = LS.ckboolean(option);
|
||||
}
|
||||
}
|
||||
@@ -306,7 +309,7 @@ LuaDefine(string_pprintx, "options",
|
||||
PrettyPrintOptions options;
|
||||
LuaKeywordParser kp(LS, loptions);
|
||||
options.parse(kp);
|
||||
if (!kp.parse(value, "value")) {
|
||||
if (!kp.optional(value, "value")) {
|
||||
LS.set(value, LuaNil);
|
||||
}
|
||||
kp.final_check_throw();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
#include "world.hpp"
|
||||
#include "pprint.hpp"
|
||||
#include "serializelua.hpp"
|
||||
#include <cmath>
|
||||
|
||||
static void tangible_getall(LuaCoreStack &LS0, LuaSlot list, const util::IdVector &idv) {
|
||||
@@ -108,8 +109,8 @@ LuaDefine(tangible_animinit, "tan,config",
|
||||
"|get persisted: they stay the same unless you change them in"
|
||||
"|the 'animate' command."
|
||||
"|"
|
||||
"|There are five hardwired persistent variables: plane,xyz,facing,bp,model."
|
||||
"|These five variables are persistent no matter what. This function,"
|
||||
"|There are four hardwired persistent variables: plane,xyz,facing,bp."
|
||||
"|These variables are persistent no matter what. This function,"
|
||||
"|tangible.animinit, optionally allows you to create more persistent"
|
||||
"|variables. For example, let's say you have a pirate chest. You might"
|
||||
"|want to add two persistent variables in addition to the usual set:"
|
||||
@@ -182,7 +183,7 @@ LuaDefine(tangible_animate, "tan,options,config",
|
||||
bool replace = false;
|
||||
if (!LS.isnil(options)) {
|
||||
LuaKeywordParser kp(LS, options);
|
||||
if (kp.parse(option, "replace")) {
|
||||
if (kp.optional(option, "replace")) {
|
||||
replace = LS.ckboolean(option);
|
||||
}
|
||||
kp.final_check_throw();
|
||||
@@ -273,27 +274,48 @@ LuaDefine(tangible_delete, "tan",
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
LuaDefine(tangible_build, "config",
|
||||
LuaDefine(tangible_build, "config",
|
||||
"|Build a new tangible object."
|
||||
"|"
|
||||
"|The config table must contain: class,animstate."
|
||||
"|" ){
|
||||
"|The configuration table must contain the keyword 'class', which"
|
||||
"|must be the name of a class created with 'makeclass'. It may also"
|
||||
"|contain the following values:"
|
||||
"|"
|
||||
"| bp - the unreal blueprint, defaults to class name."
|
||||
"| plane - the plane, defaults to actor.plane"
|
||||
"| xyz - the xyz coordinate, defaults to actor.xyz"
|
||||
"| facing - the rotation, defaults to actor.facing"
|
||||
"|"
|
||||
"|Tangible.build will create an initial animstate containing only"
|
||||
"|bp, plane, xyz, and facing."
|
||||
"|"
|
||||
"|After creating the tangible and setting up the initial animation"
|
||||
"|state, build will call the constructor for the tangible."
|
||||
"|The constructor must have this prototype:"
|
||||
"|"
|
||||
"| function myclass.init(place, actor, config)"
|
||||
"|"
|
||||
"|The configuration table passed to tangible.build is then passed"
|
||||
"|directly to the init function. The constructor should use the keywords"
|
||||
"|module to parse the keyword arguments."
|
||||
"|"
|
||||
"|The constructor is not allowed to block."
|
||||
){
|
||||
LuaArg config;
|
||||
LuaVar classname, classtab, mt, animstate;
|
||||
LuaVar classname, classtab, bp, plane, xyz, facing, mt, func;
|
||||
LuaRet database;
|
||||
LuaDefStack LS(L, config, classname, classtab, database, mt, animstate);
|
||||
LuaDefStack LS(L, config, classname, classtab, bp, plane, xyz, facing, mt, func, database);
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
|
||||
LuaKeywordParser kp(LS, config);
|
||||
kp.required(classname, "class");
|
||||
kp.optional(bp, "bp");
|
||||
kp.optional(plane, "plane");
|
||||
kp.optional(xyz, "xyz");
|
||||
kp.optional(facing, "facing");
|
||||
kp.check_throw();
|
||||
|
||||
// Get the keyword arguments.
|
||||
if (!kp.parse(classname, "class")) {
|
||||
luaL_error(L, "You must specify a class for the tangible");
|
||||
}
|
||||
if (!kp.parse(animstate, "animstate")) {
|
||||
luaL_error(L, "You must specify an animstate table");
|
||||
}
|
||||
kp.final_check_throw();
|
||||
|
||||
// Find the class.
|
||||
// Verify the class.
|
||||
eng::string err = LS.getclass(classtab, classname);
|
||||
if (err != "") {
|
||||
luaL_error(L, "%s", err.c_str());
|
||||
@@ -301,19 +323,29 @@ LuaDefine(tangible_build, "config",
|
||||
|
||||
// Calculate the initial animation state.
|
||||
AnimState state;
|
||||
err = state.from_lua(LS, animstate, true, false);
|
||||
if (err != "") {
|
||||
luaL_error(L, "%s", err.c_str());
|
||||
if (!LS.isnil(bp)) {
|
||||
state.set_string("bp", LS.ckstring(bp));
|
||||
} else {
|
||||
state.set_string("bp", LS.ckstring(classname));
|
||||
}
|
||||
if (!state.contains("xyz") || !state.contains("plane")) {
|
||||
luaL_error(L, "You must specify both xyz and plane in animstate");
|
||||
if (!LS.isnil(plane)) {
|
||||
state.set_string("plane", LS.ckstring(plane));
|
||||
}
|
||||
err = state.add_defaults(nullptr);
|
||||
if (!LS.isnil(xyz)) {
|
||||
state.set_dxyz("xyz", LS.ckxyz(xyz));
|
||||
}
|
||||
if (!LS.isnil(facing)) {
|
||||
state.set_number("facing", LS.cknumber(facing));
|
||||
}
|
||||
|
||||
// Add default values from the actor. Set persistent flags.
|
||||
Tangible *actor = w->tangible_get(w->lthread_actor_id_);
|
||||
AnimState actorstate = actor->anim_queue_.get_final_persistent();
|
||||
err = state.add_defaults(&actorstate);
|
||||
if (err != "") {
|
||||
luaL_error(L, "%s", err.c_str());
|
||||
}
|
||||
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
int64_t new_id = w->alloc_id_predictable();
|
||||
Tangible *tan = w->tangible_make(LS, database, new_id);
|
||||
|
||||
@@ -321,10 +353,16 @@ LuaDefine(tangible_build, "config",
|
||||
LS.getmetatable(mt, database);
|
||||
LS.rawset(mt, "__index", classtab);
|
||||
|
||||
// Initialize the animstate of the new tangible.
|
||||
tan->anim_queue_.clear(state);
|
||||
tan->update_plane_item();
|
||||
|
||||
return LS.result();
|
||||
|
||||
// Call the constructor and finish.
|
||||
LS.rawget(func, classtab, "init");
|
||||
if (!LS.isfunction(func)) {
|
||||
return LS.result();
|
||||
}
|
||||
return LS.tailcall(false, func, database, config);
|
||||
}
|
||||
|
||||
LuaDefine(tangible_get, "id",
|
||||
@@ -838,7 +876,7 @@ LuaDefine(pprintx, "options",
|
||||
PrettyPrintOptions options;
|
||||
LuaKeywordParser kp(LS, loptions);
|
||||
options.parse(kp);
|
||||
if (!kp.parse(value, "value")) {
|
||||
if (!kp.optional(value, "value")) {
|
||||
LS.set(value, LuaNil);
|
||||
}
|
||||
kp.final_check_throw();
|
||||
@@ -949,14 +987,7 @@ LuaDefine(global_set, "varname, value",
|
||||
"|lua identifier."
|
||||
"|"
|
||||
"|You can store global data using global.set, then you can"
|
||||
"|retrieve it using global.get. You can also retrieve data using"
|
||||
"|gv.varname, which is just shorthand for global.get. You may not"
|
||||
"|store data using gv.varname=value, this yields the error 'Use "
|
||||
"|global.set to store data in the global data table.'"
|
||||
"|"
|
||||
"|Values stored using global.set are transmitted to all"
|
||||
"|connected clients immediately. When a new client connects,"
|
||||
"|he will receive all the global data."
|
||||
"|retrieve it using global.get."
|
||||
"|"
|
||||
"|The global data table is not the same thing as the lua "
|
||||
"|environment table. Trying to store data in the lua environment"
|
||||
@@ -966,18 +997,16 @@ LuaDefine(global_set, "varname, value",
|
||||
"|global data table (using global.set) instead of in the lua"
|
||||
"|environment table."
|
||||
"|"
|
||||
"|There are certain restrictions on the values that you store."
|
||||
"|Only data that can be serialized according to doc(table.serialize)"
|
||||
"|can be stored."
|
||||
"|Data is stored in a serialized form. The data is serialized"
|
||||
"|at the moment you call global.set. It is deserialized when you"
|
||||
"|call global.get. Therefore, each time you call global.get, you"
|
||||
"|construct another copy of the value."
|
||||
"|"
|
||||
"|When you store the value, it is immediately serialized and then"
|
||||
"|deserialized again, and the deserialized copy is stored in the"
|
||||
"|variable."
|
||||
"|"
|
||||
// "|When you call global.get, you obtain the copy. Any attempt to"
|
||||
// "|mutate the copy will fail with this lua error message: 'Tables"
|
||||
// "|returned by global.get are immutable.' This rule prevents'"
|
||||
// "|aliasing between global data and other data structures."
|
||||
"|The serialization routine can only serialize certain kinds of"
|
||||
"|values. For example, closures cannot be serialized at all."
|
||||
"|Serializing a tangible serializes the tangible's ID, but"
|
||||
"|none of the contents of the tangible. See doc(table.serialize)"
|
||||
"|for more information about what can and can't be serialized."
|
||||
"|") {
|
||||
LuaArg varname;
|
||||
LuaArg value;
|
||||
@@ -990,8 +1019,16 @@ LuaDefine(global_set, "varname, value",
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
// Serialize the data.
|
||||
StreamBuffer sb;
|
||||
eng::string error = serialize_lua(LS, value, &sb);
|
||||
if (!error.empty()) {
|
||||
luaL_error(L, "%s", error.c_str());
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
w->set_global(LS, gvar, value);
|
||||
w->set_global(gvar, sb.view());
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
@@ -1000,16 +1037,33 @@ LuaDefine(global_get, "varname",
|
||||
"|"
|
||||
"|See doc(global.set) for information on how to store global data."
|
||||
"|"
|
||||
"|Do not mutate data returned by global.get: doing so will produce"
|
||||
"|unpredictable results. Instead, using global.set to mutate global"
|
||||
"|variables."
|
||||
"|Performance note: each time you call global.get, you deserialize the"
|
||||
"|stored serialized version, constructing another copy of the data."
|
||||
"|") {
|
||||
LuaArg varname;
|
||||
LuaRet value;
|
||||
LuaVar globaldb;
|
||||
LuaDefStack LS(L, varname, value, globaldb);
|
||||
LS.rawget(globaldb, LuaRegistry, "globaldb");
|
||||
LS.rawget(value, globaldb, varname);
|
||||
LuaDefStack LS(L, varname, value);
|
||||
LS.set(value, LuaNil);
|
||||
|
||||
// Check the varname argument.
|
||||
eng::string gvar = LS.ckstring(varname);
|
||||
if (!sv::is_lua_id(gvar)) {
|
||||
luaL_error(L, "variable name must be a valid lua identifier: %s", gvar.c_str());
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
// Fetch the serialized blob.
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
std::string_view data = w->get_global(gvar);
|
||||
if (data.empty()) return LS.result();
|
||||
|
||||
// Deserialize the blob.
|
||||
StreamBuffer sb(data);
|
||||
eng::string error = deserialize_lua(LS, value, &sb);
|
||||
if (!error.empty()) {
|
||||
luaL_error(L, "%s", error.c_str());
|
||||
return LS.result();
|
||||
}
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
@@ -1022,8 +1076,8 @@ LuaDefine(global_once, "varname",
|
||||
"|") {
|
||||
LuaArg varname;
|
||||
LuaRet result;
|
||||
LuaVar globaldb, flag;
|
||||
LuaDefStack LS(L, varname, flag, result, globaldb);
|
||||
LuaDefStack LS(L, varname, result);
|
||||
LS.set(result, false);
|
||||
|
||||
// Check the varname argument.
|
||||
eng::string gvar = LS.ckstring(varname);
|
||||
@@ -1033,17 +1087,12 @@ LuaDefine(global_once, "varname",
|
||||
}
|
||||
gvar += ":once";
|
||||
|
||||
LS.rawget(globaldb, LuaRegistry, "globaldb");
|
||||
LS.rawget(flag, globaldb, gvar);
|
||||
|
||||
if (!LS.isnil(flag)) {
|
||||
LS.set(result, false);
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
LS.set(result, true);
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
w->set_global(LS, gvar, result);
|
||||
LS.set(result, false);
|
||||
if (w->get_global(gvar).empty()) {
|
||||
LS.set(result, true);
|
||||
w->set_global(gvar, "x");
|
||||
}
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
@@ -1055,8 +1104,7 @@ LuaDefine(global_clearonce, "varname",
|
||||
"|uses global.set under the covers."
|
||||
"|") {
|
||||
LuaArg varname;
|
||||
LuaVar null;
|
||||
LuaDefStack LS(L, varname, null);
|
||||
LuaDefStack LS(L, varname);
|
||||
|
||||
// Check the varname argument.
|
||||
eng::string gvar = LS.ckstring(varname);
|
||||
@@ -1066,8 +1114,7 @@ LuaDefine(global_clearonce, "varname",
|
||||
}
|
||||
gvar += ":once";
|
||||
|
||||
LS.set(null, LuaNil);
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
w->set_global(LS, gvar, null);
|
||||
w->set_global(gvar, "");
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "traceback.hpp"
|
||||
#include "pprint.hpp"
|
||||
#include "util.hpp"
|
||||
#include "serializelua.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
@@ -332,7 +331,7 @@ int64_t World::create_login_actor() {
|
||||
tan->print_buffer_.clear();
|
||||
|
||||
if (is_authoritative()) {
|
||||
LS.rawget(func, classtab, "initialize");
|
||||
LS.rawget(func, classtab, "init");
|
||||
spawn(LS, id, id, func, true, 0, false);
|
||||
}
|
||||
}
|
||||
@@ -1101,31 +1100,10 @@ std::ostream *World::lthread_print_stream() const {
|
||||
}
|
||||
}
|
||||
|
||||
void World::set_global(LuaCoreStack &LS0, const eng::string &gvar, LuaSlot value) {
|
||||
lua_State *L = LS0.state();
|
||||
LuaVar globaldb, copy;
|
||||
LuaExtStack LS(L, globaldb, copy);
|
||||
|
||||
// Serialize then deserialize the data, to produce a copy.
|
||||
StreamBuffer sb;
|
||||
eng::string error = serialize_lua(LS, value, &sb);
|
||||
if (!error.empty()) {
|
||||
luaL_error(L, "%s", error.c_str());
|
||||
return;
|
||||
}
|
||||
eng::string serialized(sb.view());
|
||||
error = deserialize_lua(LS, copy, &sb);
|
||||
if (!error.empty()) {
|
||||
luaL_error(L, "%s", error.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the copy in the globalDB.
|
||||
LS.rawget(globaldb, LuaRegistry, "globaldb");
|
||||
LS.rawset(globaldb, gvar, copy);
|
||||
|
||||
void World::set_global(const eng::string &gvar, std::string_view value) {
|
||||
// Store the serialized blob.
|
||||
gvname_to_serial_[gvar] = serialized;
|
||||
//
|
||||
gvname_to_serial_[gvar] = value;
|
||||
|
||||
// Implement the tracking so that we can rapidly determine which global
|
||||
// variables need to be difference transmitted.
|
||||
@@ -1147,6 +1125,15 @@ void World::set_global(LuaCoreStack &LS0, const eng::string &gvar, LuaSlot value
|
||||
}
|
||||
}
|
||||
|
||||
const eng::string &World::get_global(const eng::string &gvar) {
|
||||
static eng::string empty;
|
||||
auto iter = gvname_to_serial_.find(gvar);
|
||||
if (iter == gvname_to_serial_.end()) {
|
||||
return empty;
|
||||
} else {
|
||||
return iter->second;
|
||||
}
|
||||
}
|
||||
|
||||
void World::serialize(StreamBuffer *sb) {
|
||||
assert(stack_is_clear());
|
||||
|
||||
@@ -305,36 +305,15 @@ void World::diff_source(World *master, StreamBuffer *sb) {
|
||||
assert(tsb.empty());
|
||||
}
|
||||
|
||||
const eng::string &World::get_gvname_serial(const eng::string &gvar) {
|
||||
static eng::string empty;
|
||||
auto iter = gvname_to_serial_.find(gvar);
|
||||
if (iter == gvname_to_serial_.end()) {
|
||||
return empty;
|
||||
} else {
|
||||
return iter->second;
|
||||
}
|
||||
}
|
||||
|
||||
void World::patch_globals(StreamBuffer *sb, DebugCollector *dbc) {
|
||||
DebugBlock dbb(dbc, "patch_globals");
|
||||
int64_t seqno = sb->read_int64();
|
||||
int32_t total = sb->read_int32();
|
||||
if (total > 0) {
|
||||
lua_State *L = state();
|
||||
LuaVar globaldb, copy;
|
||||
LuaExtStack LS(L, globaldb, copy);
|
||||
LS.rawget(globaldb, LuaRegistry, "globaldb");
|
||||
for (int i = 0; i < total; i++) {
|
||||
eng::string gvar = sb->read_string();
|
||||
eng::string serial = sb->read_string();
|
||||
gvname_to_serial_[gvar] = serial;
|
||||
StreamBuffer sb(serial);
|
||||
eng::string error = deserialize_lua(LS, copy, &sb);
|
||||
if (error.empty()) {
|
||||
LS.rawset(globaldb, gvar, copy);
|
||||
} else {
|
||||
DebugLine(dbc) << "Invalid global serialized data: " << gvar << ":" << error;
|
||||
}
|
||||
}
|
||||
}
|
||||
assign_seqno_ = seqno;
|
||||
@@ -350,8 +329,8 @@ void World::diff_globals(World *master, StreamBuffer *sb) {
|
||||
int64_t count_pos = tsb.total_writes();
|
||||
int32_t total_mods = 0;
|
||||
for (const eng::string &gvar : gvname_modified_) {
|
||||
const eng::string &mval = master->get_gvname_serial(gvar);
|
||||
const eng::string &sval = get_gvname_serial(gvar);
|
||||
const eng::string &mval = master->get_global(gvar);
|
||||
const eng::string &sval = get_global(gvar);
|
||||
if (mval != sval) {
|
||||
total_mods += 1;
|
||||
tsb.write_string(gvar);
|
||||
@@ -362,8 +341,8 @@ void World::diff_globals(World *master, StreamBuffer *sb) {
|
||||
while (iter != master->seqno_to_gvname_.end()) {
|
||||
const auto &gvar = iter->second;
|
||||
if (gvname_modified_.find(gvar) == gvname_modified_.end()) {
|
||||
const eng::string &mval = master->get_gvname_serial(gvar);
|
||||
const eng::string &sval = get_gvname_serial(gvar);
|
||||
const eng::string &mval = master->get_global(gvar);
|
||||
const eng::string &sval = get_global(gvar);
|
||||
if (mval != sval) {
|
||||
total_mods += 1;
|
||||
tsb.write_string(gvar);
|
||||
|
||||
@@ -241,32 +241,6 @@ void World::tangible_set_class(int64_t id, const eng::string &c) const {
|
||||
LS.rawset(meta, "__index", sclass);
|
||||
}
|
||||
|
||||
void World::set_global_json(const eng::string &gvar, const eng::string &json) {
|
||||
LuaVar decoded;
|
||||
LuaExtStack LS(state(), decoded);
|
||||
bool ok = json::decode(LS, decoded, json);
|
||||
if (!ok) {
|
||||
luaL_error(state(), "invalid json");
|
||||
return;
|
||||
}
|
||||
set_global(LS, gvar, decoded);
|
||||
}
|
||||
|
||||
eng::string World::get_global_json(const eng::string &gvar) {
|
||||
LuaVar value, globaldb;
|
||||
LuaExtStack LS(state(), globaldb, value);
|
||||
LS.rawget(globaldb, LuaRegistry, "globaldb");
|
||||
LS.rawget(value, globaldb, gvar);
|
||||
eng::string out;
|
||||
eng::string error = json::encode(LS, value, out, false, 10000);
|
||||
if (!error.empty()) {
|
||||
luaL_error(state(), "%s", error.c_str());
|
||||
return "";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
eng::string World::tangible_get_class(int64_t id) const {
|
||||
LuaVar tangibles, tan, meta, sclass;
|
||||
LuaExtStack LS(state(), tangibles, tan, meta, sclass);
|
||||
@@ -495,24 +469,24 @@ LuaDefine(unittests_world5diffglobals, "", "some unit tests") {
|
||||
UniqueWorld cs(new World(WORLD_TYPE_PREDICTIVE));
|
||||
StreamBuffer sb;
|
||||
|
||||
m->set_global_json("x", "3");
|
||||
m->set_global("x", "3");
|
||||
ss->diff_globals(m.get(), &sb);
|
||||
cs->patch_globals(&sb, nullptr);
|
||||
LuaAssertStrEq(L, cs->get_global_json("x"), "3");
|
||||
LuaAssertStrEq(L, cs->get_global("x"), "3");
|
||||
|
||||
m->set_global_json("x", "4");
|
||||
m->set_global_json("x", "5");
|
||||
m->set_global_json("y", "6");
|
||||
m->set_global("x", "4");
|
||||
m->set_global("x", "5");
|
||||
m->set_global("y", "6");
|
||||
ss->diff_globals(m.get(), &sb);
|
||||
cs->patch_globals(&sb, nullptr);
|
||||
LuaAssertStrEq(L, cs->get_global_json("x"), "5");
|
||||
LuaAssertStrEq(L, cs->get_global_json("y"), "6");
|
||||
LuaAssertStrEq(L, cs->get_global("x"), "5");
|
||||
LuaAssertStrEq(L, cs->get_global("y"), "6");
|
||||
|
||||
cs->set_global_json("x", "2");
|
||||
ss->set_global_json("x", "2");
|
||||
cs->set_global("x", "2");
|
||||
ss->set_global("x", "2");
|
||||
ss->diff_globals(m.get(), &sb);
|
||||
cs->patch_globals(&sb, nullptr);
|
||||
LuaAssertStrEq(L, cs->get_global_json("x"), "5");
|
||||
LuaAssertStrEq(L, cs->get_global("x"), "5");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -357,17 +357,18 @@ public:
|
||||
|
||||
std::ostream *lthread_print_stream() const;
|
||||
|
||||
// Set a global variable.
|
||||
// Set a lua global variable.
|
||||
//
|
||||
// This will throw lua errors if there's a problem.
|
||||
// The table just stores strings, and the difference transmitter
|
||||
// just difference transmits those strings. The strings are meant
|
||||
// to be serialized lua data structures, but there is no enforcement
|
||||
// of that here.
|
||||
//
|
||||
void set_global(LuaCoreStack &LS0, const eng::string &gvar, LuaSlot value);
|
||||
void set_global(const eng::string &var, std::string_view value);
|
||||
|
||||
// Get the serialized value of a global variable.
|
||||
// Get a lua global variable.
|
||||
//
|
||||
// This accessor is used during difference transmission.
|
||||
//
|
||||
const eng::string &get_gvname_serial(const eng::string &gvar);
|
||||
const eng::string &get_global(const eng::string &var);
|
||||
|
||||
// Allocate a single ID.
|
||||
//
|
||||
@@ -491,14 +492,6 @@ public:
|
||||
//
|
||||
eng::string tangible_get_class(int64_t id) const;
|
||||
|
||||
// Store json in a global variable.
|
||||
//
|
||||
void set_global_json(const eng::string &gvar, const eng::string &json);
|
||||
|
||||
// Get a global variable as json.
|
||||
//
|
||||
eng::string get_global_json(const eng::string &gvar);
|
||||
|
||||
public:
|
||||
///////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -671,7 +664,6 @@ private:
|
||||
std::unique_ptr<eng::ostringstream> lthread_prints_;
|
||||
|
||||
friend class Tangible;
|
||||
friend void global_set(LuaCoreStack &LS0, const eng::string &gvar, LuaSlot value);
|
||||
friend int lfn_tangible_animate(lua_State *L);
|
||||
friend int lfn_tangible_build(lua_State *L);
|
||||
friend int lfn_tangible_redirect(lua_State *L);
|
||||
|
||||
Reference in New Issue
Block a user