More work on redirect
This commit is contained in:
@@ -45,7 +45,7 @@ public:
|
||||
// the invariant that there's always an actor. When the first difference
|
||||
// transmission arrives, this actor may be deleted, or it may just be
|
||||
// ignored, at the server's discretion.
|
||||
actor_id_ = world_->create_login_actor();
|
||||
actor_id_ = world_->connection_create();
|
||||
|
||||
// Clear the unack command queue.
|
||||
unack_.clear();
|
||||
@@ -70,7 +70,7 @@ public:
|
||||
channel_.reset();
|
||||
|
||||
// Create the standalone actor.
|
||||
actor_id_ = world_->create_login_actor();
|
||||
actor_id_ = world_->connection_create();
|
||||
|
||||
// Clear the unack command queue.
|
||||
unack_.clear();
|
||||
@@ -102,7 +102,7 @@ public:
|
||||
if (world_->snapshot_empty()) {
|
||||
world_->snapshot();
|
||||
for (const Invocation &inv : unack_) {
|
||||
world_->invoke(inv);
|
||||
world_->invoke(0, inv);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,10 +117,10 @@ public:
|
||||
|
||||
void send_invocation(const Invocation &inv) {
|
||||
if (channel_ == nullptr) {
|
||||
world_->invoke(inv);
|
||||
world_->invoke(0, inv);
|
||||
} else {
|
||||
world_to_asynchronous();
|
||||
world_->invoke(inv);
|
||||
world_->invoke(0, inv);
|
||||
unack_.push_back(inv);
|
||||
StreamBuffer *sb = channel_->out();
|
||||
sb->write_uint8(util::MSG_INVOKE);
|
||||
@@ -134,12 +134,6 @@ public:
|
||||
// set_initial_state_connect(util::ss("nocert:", hostname, ":8085"));
|
||||
}
|
||||
|
||||
void change_actor_id(int64_t actor_id) {
|
||||
util::dprint("Actor ID changing: ", actor_id);
|
||||
print_channeler_.reset();
|
||||
actor_id_ = actor_id;
|
||||
}
|
||||
|
||||
void receive_ack_from_server(StreamBuffer *sb) {
|
||||
// An ack is just a single byte, so there's nothing left to read.
|
||||
if (unack_.empty()) {
|
||||
@@ -148,7 +142,7 @@ public:
|
||||
return;
|
||||
}
|
||||
world_to_synchronous();
|
||||
world_->invoke(unack_.front());
|
||||
world_->invoke(0, unack_.front());
|
||||
unack_.pop_front();
|
||||
}
|
||||
|
||||
@@ -157,7 +151,11 @@ public:
|
||||
try {
|
||||
DebugCollector dbc("");
|
||||
int64_t nactor = world_->patch(sb, &dbc);
|
||||
if (nactor != actor_id_) change_actor_id(nactor);
|
||||
if (nactor != actor_id_) {
|
||||
util::dprint("Actor ID changing: ", nactor);
|
||||
print_channeler_.reset();
|
||||
actor_id_ = nactor;
|
||||
}
|
||||
// dbc.dump(...);
|
||||
} catch (const StreamException &sexcept) {
|
||||
abandon_server();
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
class Client : public eng::opnew {
|
||||
public:
|
||||
int64_t client_id_;
|
||||
int64_t actor_id_;
|
||||
SharedChannel channel_;
|
||||
UniqueWorld sync_;
|
||||
@@ -44,7 +45,7 @@ public:
|
||||
world_->expose_world_to_driver(wrapper_);
|
||||
|
||||
// Create the admin actor. Note: there isn't any 'init' function yet.
|
||||
actor_id_ = world_->create_login_actor();
|
||||
actor_id_ = world_->connection_create();
|
||||
|
||||
// Print out admin ID for debugging purposes.
|
||||
util::dprint("Admin actor id = ", actor_id_);
|
||||
@@ -73,12 +74,13 @@ public:
|
||||
}
|
||||
|
||||
void delete_client(UniqueClient &client) {
|
||||
util::dprint("Client closed: actor id=", client->actor_id_);
|
||||
world_->disconnected(client->actor_id_);
|
||||
util::dprint("Client closed: client_id=", client->client_id_, " actor_id=", client->actor_id_);
|
||||
world_->connection_delete(client->client_id_);
|
||||
client.reset();
|
||||
}
|
||||
|
||||
void send_diffs(UniqueClient &client, bool full) {
|
||||
client->actor_id_ = world_->connection_get_actor(client->client_id_);
|
||||
StreamBuffer *sb = client->channel_->out();
|
||||
sb->write_uint8(util::MSG_DIFF);
|
||||
sb->write_uint32(0);
|
||||
@@ -89,27 +91,6 @@ public:
|
||||
sb->overwrite_int32(tw_1, tw_2 - tw_1);
|
||||
}
|
||||
|
||||
void redirect(int64_t id1, int64_t id2) {
|
||||
for (auto &client : clients_) {
|
||||
if (client->actor_id_ == id1) {
|
||||
bool ok = world_->redirected(id1, id2);
|
||||
if (!ok) delete_client(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Redirection is touchy: almost any world->invoke could
|
||||
// call tangible.redirect. After that point, the world is
|
||||
// relying on us not to control the wrong character.
|
||||
void process_redirects() {
|
||||
if (world_->have_redirects()) {
|
||||
World::Redirects redirects = world_->fetch_redirects();
|
||||
for (const auto &pair : redirects) {
|
||||
redirect(pair.first, pair.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool handle_invocation(UniqueClient &client) {
|
||||
if (client == nullptr) return false;
|
||||
StreamBuffer *sb = client->channel_->in();
|
||||
@@ -131,18 +112,12 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the user sent an invalid kind, log them out.
|
||||
if (!Invocation::is_valid_network_kind(inv.kind())) {
|
||||
delete_client(client);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Acknowledge the invocation.
|
||||
client->channel_->out()->write_uint8(util::MSG_ACK);
|
||||
client->channel_->out()->write_uint32(0);
|
||||
|
||||
// Execute the invocation with the sync model.
|
||||
client->sync_->invoke(inv);
|
||||
client->sync_->invoke(0, inv);
|
||||
client->async_diff_ = true;
|
||||
|
||||
// Process the invocation in the master model.
|
||||
@@ -152,8 +127,7 @@ public:
|
||||
// who may not know their new actor_id yet.
|
||||
//
|
||||
if (inv.actor() == client->actor_id_) {
|
||||
world_->invoke(inv);
|
||||
process_redirects();
|
||||
world_->invoke(client->client_id_, inv);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -210,7 +184,7 @@ public:
|
||||
// come from the local command line. We just feed
|
||||
// these directly into the master model.
|
||||
for (const Invocation &inv : delayed_invocations_) {
|
||||
world_->invoke(inv);
|
||||
world_->invoke(0, inv);
|
||||
}
|
||||
delayed_invocations_.clear();
|
||||
|
||||
@@ -220,7 +194,8 @@ public:
|
||||
if (chan == nullptr) break;
|
||||
if (chan->port() == 8085) {
|
||||
Client *client = new Client;
|
||||
client->actor_id_ = world_->create_login_actor();
|
||||
client->actor_id_ = world_->connection_create();
|
||||
client->client_id_ = client->actor_id_;
|
||||
// TODO: initialize the login actor on the master.
|
||||
client->channel_ = std::move(chan);
|
||||
client->async_diff_ = true;
|
||||
@@ -229,7 +204,7 @@ public:
|
||||
client->sync_.reset(new World(WORLD_TYPE_PREDICTIVE));
|
||||
// This login actor is never used, it is just to preserve the invariant that
|
||||
// the client model and the server synchronous model are identical.
|
||||
client->sync_->create_login_actor();
|
||||
client->sync_->connection_create();
|
||||
clients_.emplace_back(client);
|
||||
util::dprint("New client: actor id=", client->actor_id_);
|
||||
} else if (chan->port() == 8080) {
|
||||
@@ -241,7 +216,7 @@ public:
|
||||
|
||||
// If the clock has advanced far enough, tick the master model.
|
||||
if (clock >= next_tick_) {
|
||||
world_->invoke(Invocation(AccessKind::INVOKE_TICK, 0, 0, ""));
|
||||
world_->invoke(0, Invocation(AccessKind::INVOKE_TICK, 0, 0, ""));
|
||||
for (UniqueClient &client : clients_) {
|
||||
client->async_diff_ = true;
|
||||
}
|
||||
@@ -249,11 +224,6 @@ public:
|
||||
if (next_tick_ < clock + 0.3) next_tick_ = clock + 0.3;
|
||||
}
|
||||
|
||||
// This handles any redirects triggered from the server command
|
||||
// line or by the tick function.
|
||||
process_redirects();
|
||||
util::remove_nullptrs(clients_);
|
||||
|
||||
// Traverse all existing channels, process any communication.
|
||||
for (UniqueClient &client : clients_) {
|
||||
if (client->channel_->closed()) {
|
||||
|
||||
@@ -76,6 +76,7 @@ lua_State *LuaCoreStack::newstate (lua_Alloc allocf) {
|
||||
LS.rawset(LuaRegistry, "tangibles", LuaNewTable);
|
||||
LS.rawset(LuaRegistry, "persist", LuaNewTable);
|
||||
LS.rawset(LuaRegistry, "unpersist", LuaNewTable);
|
||||
LS.rawset(LuaRegistry, "funcnames", LuaNewTable);
|
||||
|
||||
// Tag the registry and global environment with their tabletypes.
|
||||
LS.settabletype(LuaRegistry, LUA_TT_REGISTRY);
|
||||
|
||||
@@ -292,8 +292,8 @@ void SourceDB::update(const util::LuaSourceVec &source) {
|
||||
// Clear all the classes in the registry classes table.
|
||||
//
|
||||
static void source_clear_globals(lua_State *L) {
|
||||
LuaVar classname, classtab, key, globtab, classes;
|
||||
LuaExtStack LS(L, classname, classtab, key, globtab, classes);
|
||||
LuaVar classname, classtab, key, globtab, classes, funcnames;
|
||||
LuaExtStack LS(L, classname, classtab, key, globtab, classes, funcnames);
|
||||
|
||||
LS.getglobaltable(globtab);
|
||||
LS.cleartable(globtab, true);
|
||||
@@ -306,8 +306,45 @@ static void source_clear_globals(lua_State *L) {
|
||||
assert(LS.istable(classtab));
|
||||
LS.cleartable(classtab, true);
|
||||
}
|
||||
|
||||
LS.rawget(funcnames, LuaRegistry, "funcnames");
|
||||
assert(LS.istable(funcnames));
|
||||
LS.cleartable(funcnames, true);
|
||||
}
|
||||
|
||||
void SourceDB::rebuild_funcnames() {
|
||||
lua_State *L = lua_state_;
|
||||
LuaVar globtab, funcnames, key, val, key2, val2;
|
||||
LuaExtStack LS(L, globtab, funcnames, key, val, key2, val2);
|
||||
|
||||
LS.getglobaltable(globtab);
|
||||
LS.rawget(funcnames, LuaRegistry, "funcnames");
|
||||
|
||||
// Top-level functions in the global environment.
|
||||
LS.set(key, LuaNil);
|
||||
while (LS.next(globtab, key, val) != 0) {
|
||||
if (!LS.isstring(key)) continue;
|
||||
if (LS.isfunction(val)) {
|
||||
LS.rawset(funcnames, val, key);
|
||||
}
|
||||
}
|
||||
|
||||
// Functions one level deep inside class tables.
|
||||
LS.set(key, LuaNil);
|
||||
while (LS.next(globtab, key, val) != 0) {
|
||||
if (!LS.isstring(key)) continue;
|
||||
if (!LS.istable(val)) continue;
|
||||
eng::string classname = LS.ckstring(key);
|
||||
LS.set(key2, LuaNil);
|
||||
while (LS.next(val, key2, val2) != 0) {
|
||||
if (!LS.isstring(key2)) continue;
|
||||
if (LS.isfunction(val2)) {
|
||||
eng::string fullname = classname + "." + LS.ckstring(key2);
|
||||
LS.rawset(funcnames, val2, fullname);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load all the 'LuaDefine' C functions into the lua state.
|
||||
//
|
||||
@@ -406,7 +443,7 @@ void SourceDB::rebuild_core() {
|
||||
source_clear_globals(lua_state_);
|
||||
source_load_cfunctions(lua_state_);
|
||||
source_load_cconstants(lua_state_);
|
||||
}
|
||||
}
|
||||
|
||||
eng::string SourceDB::rebuild_module(const eng::string &mod) {
|
||||
if (mod == "CORE") {
|
||||
|
||||
@@ -172,6 +172,13 @@ public:
|
||||
//
|
||||
void rebuild_core();
|
||||
|
||||
// rebuild_funcnames
|
||||
//
|
||||
// Traverses the global environment and populates the registry "funcnames"
|
||||
// table, mapping each closure to its name.
|
||||
//
|
||||
void rebuild_funcnames();
|
||||
|
||||
// Difference transmission.
|
||||
//
|
||||
// Note: The patch routine applies the differences to the source
|
||||
|
||||
@@ -43,33 +43,46 @@ int traceback_coroutine(lua_State *L) {
|
||||
firstpart = 0;
|
||||
continue;
|
||||
}
|
||||
lua_getinfo(L, "Snl", &ar);
|
||||
|
||||
lua_getinfo(L, "Snlf", &ar);
|
||||
|
||||
eng::string storedname;
|
||||
{
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, "funcnames");
|
||||
lua_pushvalue(L, -2);
|
||||
lua_rawget(L, -2);
|
||||
const char *fname = lua_tostring(L, -1);
|
||||
if (fname) storedname = fname;
|
||||
lua_pop(L, 3);
|
||||
}
|
||||
|
||||
if ((!any) && (*ar.what == 'C') && (ar.name != 0)) {
|
||||
if (strcmp(ar.name, "__newindex") == 0) continue;
|
||||
}
|
||||
if ((ar.currentline > 0) || (*ar.namewhat != 0) || (*ar.what != 'C')) {
|
||||
any = true;
|
||||
lua_pushliteral(L, "\n\t");
|
||||
if (strcmp(ar.short_src, "<console>")==0)
|
||||
{
|
||||
lua_pushstring(L, "in the console");
|
||||
|
||||
if (strcmp(ar.short_src, "<console>")==0) {
|
||||
lua_pushstring(L, "in the console in");
|
||||
} else if (strcmp(ar.short_src, "[C]")==0) {
|
||||
lua_pushstring(L, "in builtin C++ ");
|
||||
} else if (ar.currentline > 0) {
|
||||
lua_pushfstring(L, "in %s line %d in", ar.short_src, ar.currentline);
|
||||
} else {
|
||||
lua_pushfstring(L, "in %s in", ar.short_src);
|
||||
}
|
||||
else
|
||||
{
|
||||
lua_pushfstring(L, "in %s", ar.short_src);
|
||||
if (ar.currentline > 0)
|
||||
lua_pushfstring(L, " line %d", ar.currentline);
|
||||
}
|
||||
if (*ar.namewhat != '\0') /* is there a name? */
|
||||
lua_pushfstring(L, " in function " LUA_QS, ar.name);
|
||||
else {
|
||||
if (*ar.what == 'm') /* main? */
|
||||
lua_pushfstring(L, " in top-level expression ");
|
||||
else if (*ar.what == 'C' || *ar.what == 't')
|
||||
lua_pushliteral(L, " in unknown C function");
|
||||
else
|
||||
lua_pushfstring(L, " in function on line %d", ar.linedefined);
|
||||
|
||||
if (!storedname.empty()) {
|
||||
lua_pushfstring(L, " function " LUA_QS, storedname.c_str());
|
||||
} else if (*ar.namewhat != 0) {
|
||||
lua_pushfstring(L, " function " LUA_QS, ar.name);
|
||||
} else if (*ar.what == 'm') {
|
||||
lua_pushfstring(L, " top-level expression ");
|
||||
} else {
|
||||
lua_pushliteral(L, " unknown function");
|
||||
}
|
||||
|
||||
if (1 + lua_gettop(L) - top > 5) {
|
||||
lua_concat(L, 1 + lua_gettop(L) - top);
|
||||
}
|
||||
|
||||
@@ -368,26 +368,22 @@ LuaDefine(tangible_redirect, "actor1, actor2",
|
||||
"|client types his name and password, tangible.redirect is used "
|
||||
"|to tell the client to control the real actor."
|
||||
"|"
|
||||
"|Actor1 must be currently logged in. Actor2 must not be. This "
|
||||
"|function doesn't error check that the actors are valid: it just "
|
||||
"|queues the redirect, then the system error checks later. This "
|
||||
"|is because conditions can change. An invalid redirect will cause "
|
||||
"|a disconnection."
|
||||
"|Actor1 must be currently logged in. Actor2 must be a valid actor "
|
||||
"|tangible that is not curretly logged in."
|
||||
"|") {
|
||||
LuaArg lactor1, lactor2;
|
||||
LuaDefStack LS(L, lactor1, lactor2);
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
Tangible *actor1 = w->tangible_get(LS, lactor1, true);
|
||||
Tangible *actor2 = w->tangible_get(LS, lactor2, true);
|
||||
// if (!actor1->is_controlled_) {
|
||||
// luaL_error(L, "Actor1 is not a controlled actor.");
|
||||
// return 0;
|
||||
// }
|
||||
// if ((!actor2->can_be_controlled_) || (actor2->is_controlled_)) {
|
||||
// luaL_error(L, "Actor2 is not an uncontrolled actor.");
|
||||
// return 0;
|
||||
// }
|
||||
w->add_redirect(actor1->id(), actor2->id());
|
||||
int64_t client_id = w->connection_get_client(actor1->id());
|
||||
eng::string error = w->connection_redirect(client_id, actor2->id());
|
||||
if (!error.empty()) {
|
||||
eng::ostringstream oss;
|
||||
oss << "redirect from " << actor1->id() << " to " << actor2->id() << " failed: " << error;
|
||||
luaL_error(L, "%s", oss.str().c_str());
|
||||
return LS.result();
|
||||
}
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
|
||||
@@ -152,12 +152,20 @@ void Tangible::serialize(StreamBuffer *sb) {
|
||||
anim_queue_.serialize(sb);
|
||||
id_player_pool_.serialize(sb);
|
||||
print_buffer_.serialize(sb);
|
||||
sb->write_bool(can_be_controlled_);
|
||||
sb->write_bool(is_controlled_);
|
||||
sb->write_bool(force_disconnect_);
|
||||
sb->write_bool(delete_on_disconnect_);
|
||||
}
|
||||
|
||||
void Tangible::deserialize(StreamBuffer *sb) {
|
||||
anim_queue_.deserialize(sb);
|
||||
id_player_pool_.deserialize(sb);
|
||||
print_buffer_.deserialize(sb);
|
||||
can_be_controlled_ = sb->read_bool();
|
||||
is_controlled_ = sb->read_bool();
|
||||
force_disconnect_ = sb->read_bool();
|
||||
delete_on_disconnect_ = sb->read_bool();
|
||||
update_plane_item();
|
||||
}
|
||||
|
||||
@@ -294,13 +302,7 @@ void World::get_near(int64_t player_id, float radius, bool exclude_nowhere, bool
|
||||
get_near(scan, into);
|
||||
}
|
||||
|
||||
World::Redirects World::fetch_redirects() {
|
||||
World::Redirects result = std::move(redirects_);
|
||||
redirects_.clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
int64_t World::create_login_actor() {
|
||||
int64_t World::connection_create() {
|
||||
assert(stack_is_clear());
|
||||
int64_t id = id_global_pool_.get_one();
|
||||
{
|
||||
@@ -327,13 +329,18 @@ int64_t World::create_login_actor() {
|
||||
spawn(LS, id, id, func, 0, false);
|
||||
}
|
||||
}
|
||||
connections_.emplace(id, id);
|
||||
if (is_authoritative()) {
|
||||
run_scheduled_threads();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
void World::disconnected(int64_t actor_id) {
|
||||
eng::string World::connection_delete(int64_t client_id) {
|
||||
auto iter = connections_.find(client_id);
|
||||
if (iter == connections_.end()) return "no such client id";
|
||||
int64_t actor_id = iter->second;
|
||||
connections_.erase(iter);
|
||||
Tangible *tan = tangible_get(actor_id);
|
||||
assert(tan != nullptr);
|
||||
assert(tan->is_controlled_);
|
||||
@@ -343,16 +350,39 @@ void World::disconnected(int64_t actor_id) {
|
||||
util::dprintf("Deleted actor: %lld\n", actor_id);
|
||||
tangible_delete(actor_id);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool World::redirected(int64_t actor_id, int64_t new_id) {
|
||||
disconnected(actor_id);
|
||||
Tangible *tan = tangible_get(new_id);
|
||||
if (tan && (tan->can_be_controlled_) && (!tan->is_controlled_)) {
|
||||
tan->is_controlled_ = true;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
int64_t World::connection_get_actor(int64_t client_id) const {
|
||||
auto iter = connections_.find(client_id);
|
||||
if (iter == connections_.end()) return 0;
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
int64_t World::connection_get_client(int64_t actor_id) const {
|
||||
for (const auto &pair : connections_) {
|
||||
if (pair.second == actor_id) return pair.first;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
eng::string World::connection_redirect(int64_t client_id, int64_t new_id) {
|
||||
Tangible *newtan = tangible_get(new_id);
|
||||
if (newtan == nullptr) return "no such target tangible";
|
||||
if (newtan->is_controlled_) return "target tangible is already controlled";
|
||||
if (!newtan->can_be_controlled_) return "target tangible is not a potential actor";
|
||||
eng::string delresult = connection_delete(client_id);
|
||||
if (!delresult.empty()) return delresult;
|
||||
newtan->is_controlled_ = true;
|
||||
newtan->force_disconnect_ = false;
|
||||
connections_[client_id] = new_id;
|
||||
return "";
|
||||
}
|
||||
|
||||
void World::connection_close_all() {
|
||||
while (!connections_.empty()) {
|
||||
int64_t client_id = connections_.begin()->first;
|
||||
connection_delete(client_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -564,6 +594,7 @@ bool World::rebuild_sourcedb(int64_t actor_id) {
|
||||
if (actor_id != 0) lthread_prints_to_actor(actor_id);
|
||||
}
|
||||
}
|
||||
source_db_.rebuild_funcnames();
|
||||
lthread_prints_ << "Compiled " << successes << " modules successfully." << std::endl;
|
||||
if (failures > 0) {
|
||||
lthread_prints_ << "Compiled " << failures << " modules with errors." << std::endl;
|
||||
@@ -743,7 +774,13 @@ HttpServerResponse World::http_serve(const HttpParser &request) {
|
||||
return response;
|
||||
}
|
||||
|
||||
void World::invoke(const Invocation &inv) {
|
||||
void World::invoke(int64_t client_id, const Invocation &inv) {
|
||||
if (client_id != 0) {
|
||||
int64_t actor_id = connection_get_actor(client_id);
|
||||
if (actor_id == 0) return;
|
||||
if (inv.actor() != actor_id) return;
|
||||
if (!Invocation::is_valid_network_kind(inv.kind())) return;
|
||||
}
|
||||
switch (inv.kind()) {
|
||||
case AccessKind::INVOKE_LUA_CALL:
|
||||
invoke_lua_call(inv.actor(), inv.place(), inv.datapack());
|
||||
@@ -838,7 +875,7 @@ bool World::spawn(LuaCoreStack &LS0, int64_t actor_id, int64_t place_id, LuaSlot
|
||||
LS.rawset(thinfo, "isnew", true);
|
||||
LS.rawset(thinfo, "useppool", true);
|
||||
LS.rawset(thinfo, "print", print);
|
||||
|
||||
|
||||
// Store the thread into place's thread table.
|
||||
LS.rawget(threads, mt, "threads");
|
||||
if (!LS.istable(threads)) {
|
||||
@@ -1230,7 +1267,6 @@ const eng::string &World::get_global(const eng::string &gvar) {
|
||||
|
||||
void World::serialize(StreamBuffer *sb) {
|
||||
assert(stack_is_clear());
|
||||
assert(redirects_.empty());
|
||||
// int64_t wc0 = sb->total_writes();
|
||||
lua_snap_.serialize(sb);
|
||||
id_global_pool_.serialize(sb);
|
||||
@@ -1242,12 +1278,16 @@ void World::serialize(StreamBuffer *sb) {
|
||||
sb->write_int64(p.first);
|
||||
p.second->serialize(sb);
|
||||
}
|
||||
sb->write_uint32(connections_.size());
|
||||
for (const auto &p : connections_) {
|
||||
sb->write_int64(p.first);
|
||||
sb->write_int64(p.second);
|
||||
}
|
||||
assert(stack_is_clear());
|
||||
}
|
||||
|
||||
void World::deserialize(StreamBuffer *sb) {
|
||||
assert(stack_is_clear());
|
||||
redirects_.clear();
|
||||
lua_snap_.deserialize(sb);
|
||||
id_global_pool_.deserialize(sb);
|
||||
clock_ = sb->read_int64();
|
||||
@@ -1259,7 +1299,7 @@ void World::deserialize(StreamBuffer *sb) {
|
||||
}
|
||||
// Deserialize tangibles.
|
||||
size_t ntan = sb->read_uint32();
|
||||
for (size_t i = 0; i < ntan; i++) {
|
||||
for (uint32_t i = 0; i < ntan; i++) {
|
||||
int64_t id = sb->read_int64();
|
||||
UniqueTangible &t = tangibles_[id];
|
||||
if (t == nullptr) {
|
||||
@@ -1269,6 +1309,14 @@ void World::deserialize(StreamBuffer *sb) {
|
||||
}
|
||||
t->deserialize(sb);
|
||||
}
|
||||
// Deserialize connections.
|
||||
connections_.clear();
|
||||
uint32_t nconn = sb->read_uint32();
|
||||
for (uint32_t i = 0; i < nconn; i++) {
|
||||
int64_t client_id = sb->read_int64();
|
||||
int64_t actor_id = sb->read_int64();
|
||||
connections_.emplace(client_id, actor_id);
|
||||
}
|
||||
// Delete tangibles that didn't get deserialized.
|
||||
for (auto iter = tangibles_.begin(); iter != tangibles_.end(); ) {
|
||||
if (iter->second->plane_item_.id() == 0) {
|
||||
@@ -1277,8 +1325,6 @@ void World::deserialize(StreamBuffer *sb) {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
// After a save and load, http requests no longer should exist
|
||||
abort_all_http_requests(425, "http requests aborted by loading a save game");
|
||||
assert(stack_is_clear());
|
||||
}
|
||||
|
||||
|
||||
@@ -92,7 +92,8 @@ public:
|
||||
//
|
||||
// This flag is set to true when a client is controlling this player.
|
||||
// It gets set back to false when the client logs out or attaches
|
||||
// to a different player. This can only be set in master models.
|
||||
// to a different player. If this is set, then the connections_
|
||||
// table contains a map from a client ID to this actor.
|
||||
//
|
||||
bool is_controlled_;
|
||||
|
||||
@@ -129,7 +130,6 @@ class World : public eng::opnew {
|
||||
public:
|
||||
using IdVector = util::IdVector;
|
||||
using TanVector = eng::vector<const Tangible*>;
|
||||
using Redirects = eng::map<int64_t, int64_t>;
|
||||
const float RadiusVisibility = 1000.0;
|
||||
const float RadiusClose = 1000.0;
|
||||
|
||||
@@ -221,46 +221,7 @@ public:
|
||||
// If there's no such tangible, this is a no-op.
|
||||
//
|
||||
void tangible_delete(int64_t id);
|
||||
|
||||
// Create a login actor.
|
||||
//
|
||||
// Creates a tangible of class 'login' and returns its ID.
|
||||
// This is used to create a temporary actor which is used during
|
||||
// the login process.
|
||||
//
|
||||
// If this is a master model, The function 'login.init'
|
||||
// called. Then, the following login flags are set:
|
||||
// can_be_controlled, is_controlled, and delete_on_disconnect.
|
||||
//
|
||||
// In a client model, 'login.init' is not called,
|
||||
// and the login flags are not used in client models.
|
||||
//
|
||||
int64_t create_login_actor();
|
||||
|
||||
// Log out a connected player.
|
||||
//
|
||||
// This is to be called after a client disconnects.
|
||||
//
|
||||
void disconnected(int64_t actor_id);
|
||||
|
||||
// Notify the world that the front end has implemented a
|
||||
// redirect. This can return null to indicate a last-minute
|
||||
// failure, in which case the front end must disconnect.
|
||||
//
|
||||
bool redirected(int64_t actor_id, int64_t new_id);
|
||||
|
||||
// Add a redirect.
|
||||
//
|
||||
void add_redirect(int64_t a, int64_t b) { redirects_.emplace(a,b); }
|
||||
|
||||
// Fetch all redirects and clear the redirects table.
|
||||
//
|
||||
Redirects fetch_redirects();
|
||||
|
||||
// Return true if there are any redirects.
|
||||
//
|
||||
bool have_redirects() const { return !redirects_.empty(); }
|
||||
|
||||
// Probe an arbitrary lua expression.
|
||||
//
|
||||
// Any print-statements in the lua code are sent into
|
||||
@@ -285,7 +246,11 @@ public:
|
||||
// It is legal to mutate a world model without using 'Invoke', but
|
||||
// only in authoritative world models.
|
||||
//
|
||||
void invoke(const Invocation &inv);
|
||||
// If you pass in a nonzero client id, then that means the invocation was
|
||||
// sent by a connected client. In that case, this checks that the invocation
|
||||
// is legal for that client.
|
||||
//
|
||||
void invoke(int64_t client_id, const Invocation &inv);
|
||||
|
||||
// Get the PrintBuffer of the actor.
|
||||
//
|
||||
@@ -351,6 +316,11 @@ public:
|
||||
|
||||
// Serialize and deserialize.
|
||||
//
|
||||
// Caution: this will save all state, including state like current connections
|
||||
// and ongoing HTTP requests. It may be desirable, after saving and restoring,
|
||||
// to purge connections and HTTP requests. There are separate functions to do
|
||||
// these things.
|
||||
//
|
||||
void serialize(StreamBuffer *sb);
|
||||
void deserialize(StreamBuffer *sb);
|
||||
|
||||
@@ -395,19 +365,6 @@ public:
|
||||
//
|
||||
void lthread_prints_to_actor(int64_t actor_id);
|
||||
|
||||
// Set a lua global variable.
|
||||
//
|
||||
// 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(const eng::string &var, std::string_view value);
|
||||
|
||||
// Get a lua global variable.
|
||||
//
|
||||
const eng::string &get_global(const eng::string &var);
|
||||
|
||||
// Allocate a single ID.
|
||||
//
|
||||
// The rules are as follows:
|
||||
@@ -427,7 +384,75 @@ public:
|
||||
// Otherwise, return.
|
||||
void guard_nopredict(lua_State *L, const char *fn);
|
||||
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Global variables.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Set a lua global variable.
|
||||
//
|
||||
// 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(const eng::string &var, std::string_view value);
|
||||
|
||||
// Get a lua global variable.
|
||||
//
|
||||
const eng::string &get_global(const eng::string &var);
|
||||
|
||||
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Connection Management
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Create a connection.
|
||||
//
|
||||
// This creates a login actor, and also records the existence of
|
||||
// the connection. Returns the actor_id of the login actor, which
|
||||
// is also the client id.
|
||||
//
|
||||
int64_t connection_create();
|
||||
|
||||
// This is to be called after a client disconnects. This removes the
|
||||
// connection. On error, return an error message.
|
||||
//
|
||||
eng::string connection_delete(int64_t client_id);
|
||||
|
||||
// Get the current client_id for an actor_id.
|
||||
// Returns 0 if the actor is not a connected actor.
|
||||
//
|
||||
int64_t connection_get_client(int64_t actor_id) const;
|
||||
|
||||
// Get the current actor_id for a client_id.
|
||||
// Returns 0 if the client_id is not a connected client.
|
||||
//
|
||||
int64_t connection_get_actor(int64_t client_id) const;
|
||||
|
||||
// Add a redirect. On error, return an error message.
|
||||
//
|
||||
eng::string connection_redirect(int64_t client_id, int64_t actor_id);
|
||||
|
||||
// Close all connections.
|
||||
//
|
||||
// This is often useful after reloading a save game - the save contains
|
||||
// the connection state!
|
||||
//
|
||||
void connection_close_all();
|
||||
|
||||
private:
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Invocation and scheduling.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Add a thread to the scheduler queue.
|
||||
//
|
||||
void schedule(int64_t clk, int64_t thid, int64_t plid);
|
||||
@@ -533,14 +558,21 @@ public:
|
||||
public:
|
||||
///////////////////////////////////////////////////////////
|
||||
//
|
||||
// difference transmission internals related to table comparison
|
||||
//
|
||||
// These routines compare tables in the master lua to the corresponding
|
||||
// tables in the synchronous lua. This is a nonrecursive process, because
|
||||
// the recursion has already been done during the table enumeration process.
|
||||
// Difference transmission entry point.
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
int64_t patch(StreamBuffer *sb, DebugCollector *dbc);
|
||||
void diff(int64_t actor, bool full, World *master, StreamBuffer *sb);
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
//
|
||||
// Difference transmission internals
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
util::IdVector get_visible_union(int64_t actor_id, World *master);
|
||||
|
||||
void patch_numbered_tables(StreamBuffer *sb, DebugCollector *dbc);
|
||||
void diff_numbered_tables(lua_State *master, StreamBuffer *sb);
|
||||
|
||||
@@ -550,14 +582,6 @@ public:
|
||||
void patch_tangible_classes(StreamBuffer *sb, DebugCollector *dbc);
|
||||
void diff_tangible_classes(const IdVector &basis, lua_State *master, StreamBuffer *sb);
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
//
|
||||
// Difference transmission internals
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
util::IdVector get_visible_union(int64_t actor_id, World *master);
|
||||
|
||||
int64_t patch_actor(StreamBuffer *sb, DebugCollector *dbc);
|
||||
void diff_actor(int64_t actor_id, World *master, StreamBuffer *sb);
|
||||
|
||||
@@ -576,15 +600,6 @@ public:
|
||||
void patch_globals(StreamBuffer *sb, DebugCollector *dbc);
|
||||
void diff_globals(World *master, StreamBuffer *sb);
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
//
|
||||
// Difference transmission entry point.
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
int64_t patch(StreamBuffer *sb, DebugCollector *dbc);
|
||||
void diff(int64_t actor, bool full, World *master, StreamBuffer *sb);
|
||||
|
||||
public:
|
||||
///////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -640,6 +655,13 @@ public:
|
||||
void unnumber_lua_tables();
|
||||
|
||||
private:
|
||||
///////////////////////////////////////////////////////////
|
||||
//
|
||||
// Instance Variables
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// Type of model
|
||||
WorldType world_type_;
|
||||
|
||||
@@ -692,9 +714,9 @@ private:
|
||||
//
|
||||
StreamBuffer snapshot_;
|
||||
|
||||
// Redirects.
|
||||
// Connections. A Map from client_id to current actor ID.
|
||||
//
|
||||
Redirects redirects_;
|
||||
std::map<int64_t, int64_t> connections_;
|
||||
|
||||
// Storage for wrapper_get_tangibles_near and wrapper_get_animation_queues.
|
||||
// These hold results alive while the driver reads from the raw pointers.
|
||||
@@ -713,7 +735,6 @@ private:
|
||||
friend class Tangible;
|
||||
friend int lfn_tangible_animate(lua_State *L);
|
||||
friend int lfn_tangible_build(lua_State *L);
|
||||
friend int lfn_tangible_redirect(lua_State *L);
|
||||
friend int lfn_tangible_actor(lua_State *L);
|
||||
friend int lfn_tangible_place(lua_State *L);
|
||||
friend int lfn_tangible_nopredict(lua_State *L);
|
||||
|
||||
Reference in New Issue
Block a user