global.set has been implemented, but not diff xmit for globals

This commit is contained in:
2023-04-05 18:41:03 -04:00
parent 74f7686b85
commit f0f4ad8609
10 changed files with 260 additions and 152 deletions

View File

@@ -1,6 +1,7 @@
#include "world.hpp"
#include "pprint.hpp"
#include "serializelua.hpp"
#include <cmath>
#include <iostream>
@@ -854,3 +855,171 @@ LuaDefine(http_post, "request",
"|See doc(http.clientrequest) and doc(http.clientresponse).") {
return lfn_http_request(L, "POST");
}
void global_set(LuaStack &LS0, const eng::string &gvar, LuaSlot value) {
lua_State *L = LS0.state();
World *w = World::fetch_global_pointer(L);
LuaVar globaldb, copy;
LuaStack 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);
// Store the serialized blob in the master model.
w->gvar_to_serial_.emplace(gvar, serialized);
// In an authoritative model only, update the assignment counters.
if (w->is_authoritative()) {
int64_t newassign = w->next_gvar_assign_++;
auto oldassigniter = w->gvar_to_assign_.find(gvar);
if (oldassigniter != w->gvar_to_assign_.end()) {
int64_t oldassign = oldassigniter->second;
w->assign_to_gvar_.erase(oldassign);
}
w->assign_to_gvar_.emplace(newassign, gvar);
w->gvar_to_assign_[gvar] = newassign;
}
LS.result();
}
LuaDefine(global_set, "varname, value",
"|Store data in the global data table."
"|"
"|The variable name must be a string which is a valid"
"|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."
"|"
"|The global data table is not the same thing as the lua "
"|environment table. Trying to store data in the lua environment"
"|table will seem to work, at first, but the data will not get"
"|difference transmitted, and will eventually be cleared and lost."
"|Therefore, it is essential that global data be stored in the"
"|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."
"|"
"|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."
"|") {
LuaArg varname;
LuaArg value;
LuaStack LS(L, varname, value);
// 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();
}
global_set(LS, gvar, value);
return LS.result();
}
LuaDefine(global_get, "varname",
"|Get data stored using global.set"
"|"
"|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."
"|") {
LuaArg varname;
LuaRet value;
LuaVar globaldb;
LuaStack LS(L, varname, value, globaldb);
LS.rawget(globaldb, LuaRegistry, "globaldb");
LS.rawget(value, globaldb, varname);
return LS.result();
}
LuaDefine(global_once, "varname",
"|For a given string, returns true exactly once"
"|"
"|The semantics and difference transmission behavior of global.once"
"|are identical to the semantics of global.set, since global.once"
"|uses global.set under the covers."
"|") {
LuaArg varname;
LuaRet result;
LuaVar globaldb, flag;
LuaStack LS(L, varname, flag, result, globaldb);
// 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();
}
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);
global_set(LS, gvar, result);
return LS.result();
}
LuaDefine(global_clearonce, "varname",
"|Reset the specified once-flag"
"|"
"|The semantics and difference transmission behavior of global.clearonce"
"|are identical to the semantics of global.set, since global.once"
"|uses global.set under the covers."
"|") {
LuaArg varname;
LuaVar null;
LuaStack LS(L, varname, null);
// 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();
}
gvar += ":once";
LS.set(null, LuaNil);
global_set(LS, gvar, null);
return LS.result();
}