From 044bb89edfef87002625b63faa689d7176a8487b Mon Sep 17 00:00:00 2001 From: jyelon Date: Mon, 4 Mar 2024 16:33:23 -0500 Subject: [PATCH] Implement autodeletion of players on logout --- luprex/cpp/core/lpxclient.cpp | 4 +-- luprex/cpp/core/lpxserver.cpp | 7 ++--- luprex/cpp/core/world-core.cpp | 35 +++++++++++++++++++++--- luprex/cpp/core/world.hpp | 49 +++++++++++++++++++++++++++++++--- 4 files changed, 84 insertions(+), 11 deletions(-) diff --git a/luprex/cpp/core/lpxclient.cpp b/luprex/cpp/core/lpxclient.cpp index 04187c88..77c7580a 100644 --- a/luprex/cpp/core/lpxclient.cpp +++ b/luprex/cpp/core/lpxclient.cpp @@ -34,7 +34,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(false); + actor_id_ = world_->create_login_actor(); // Clear the unack command queue. unack_.clear(); @@ -57,7 +57,7 @@ public: channel_.reset(); // Create the standalone actor. - actor_id_ = world_->create_login_actor(true); + actor_id_ = world_->create_login_actor(); // TODO: initialize the standalone actor. diff --git a/luprex/cpp/core/lpxserver.cpp b/luprex/cpp/core/lpxserver.cpp index 9fbf3f8b..5e91aae5 100644 --- a/luprex/cpp/core/lpxserver.cpp +++ b/luprex/cpp/core/lpxserver.cpp @@ -43,7 +43,7 @@ public: master_->update_source(srcpk); // Create an actor for administrative commands. - admin_id_ = master_->create_login_actor(true); + admin_id_ = master_->create_login_actor(); // TODO: initialize the admin actor. @@ -132,6 +132,7 @@ public: void delete_client(UniqueClient &client) { stdostream() << "Client closed: actor id=" << client->actor_id_ << std::endl; + master_->disconnected(client->actor_id_); client.reset(); } @@ -209,7 +210,7 @@ public: if (chan == nullptr) break; if (chan->port() == 8085) { Client *client = new Client; - client->actor_id_ = master_->create_login_actor(true); + client->actor_id_ = master_->create_login_actor(); // TODO: initialize the login actor on the master. client->channel_ = std::move(chan); client->async_diff_ = true; @@ -217,7 +218,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(false); + client->sync_->create_login_actor(); clients_.emplace_back(client); stdostream() << "New client: actor id=" << client->actor_id_ << std::endl; } else if (chan->port() == 8080) { diff --git a/luprex/cpp/core/world-core.cpp b/luprex/cpp/core/world-core.cpp index 64afc0ad..05d78de4 100644 --- a/luprex/cpp/core/world-core.cpp +++ b/luprex/cpp/core/world-core.cpp @@ -150,6 +150,12 @@ Tangible *World::tangible_make(const LuaCoreStack &LS0, LuaSlot database, int64_ assert (t == nullptr); t.reset(new Tangible(this, id)); + // Set the login flags. + t->can_be_controlled_ = false; + t->is_controlled_ = false; + t->force_disconnect_ = false; + t->delete_on_disconnect_ = false; + // AnimQueue initializes itself to a valid default state. AnimState state; state.add_defaults(nullptr); @@ -246,28 +252,51 @@ World::Redirects World::fetch_redirects() { return result; } -int64_t World::create_login_actor(bool initialize) { +int64_t World::create_login_actor() { assert(stack_is_clear()); int64_t id = id_global_pool_.get_one(); { LuaVar database, classtab, mt, func; LuaExtStack LS(state(), database, classtab, mt, func); Tangible *tan = tangible_make(LS, database, id); + + // Set the login flags. + if (is_authoritative()) { + tan->can_be_controlled_ = true; + tan->is_controlled_ = true; + tan->force_disconnect_ = false; + tan->delete_on_disconnect_ = true; + } + LS.makeclass(classtab, "login"); LS.getmetatable(mt, database); LS.rawset(mt, "__index", classtab); tan->configure_id_pool_for_actor(); tan->print_buffer_.clear(); - if (initialize) { + if (is_authoritative()) { LS.rawget(func, classtab, "initialize"); spawn(LS, id, id, func, true, 0, false); } } - run_scheduled_threads(); + if (is_authoritative()) { + run_scheduled_threads(); + } return id; } +void World::disconnected(int64_t actor_id) { + Tangible *tan = tangible_get(actor_id); + assert(tan != nullptr); + assert(tan->is_controlled_); + tan->is_controlled_ = false; + tan->force_disconnect_ = false; + if (tan->delete_on_disconnect_) { + util::dprintf("Deleted actor: %lld\n", actor_id); + tangible_delete(actor_id); + } +} + eng::string World::probe_lua(int64_t actor_id, std::string_view lua) { assert(stack_is_clear()); lua_State *L = state(); diff --git a/luprex/cpp/core/world.hpp b/luprex/cpp/core/world.hpp index a7efda42..5e8167ce 100644 --- a/luprex/cpp/core/world.hpp +++ b/luprex/cpp/core/world.hpp @@ -76,6 +76,39 @@ public: // PrintBuffer print_buffer_; + // Can-Be-Controlled flag. + // + // This flag indicates whether the tangible can be controlled + // by a client. Clients will not be allowed to attach to tangibles + // who don't have this flag. If this flag is true, the + // tangible cannot be deleted using a mere 'tangible.delete', instead, + // you have to use 'tangible.deleteplayer'. + // + bool can_be_controlled_; + + // Is Controlled Flag. + // + // 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. + // + bool is_controlled_; + + // Force disconnect flag. + // + // This flag is used to force the client to log out ASAP. This flag + // can only be set in master models. + // + bool force_disconnect_; + + // Delete on Logout Flag. + // + // This flag can be set on a controlled player. When the player + // disconnects, their character will be deleted. This flag can only + // be set if the is_controlled_ flag is true. + // + bool delete_on_disconnect_; + // constructor. // Tangible(World *w, int64_t id); @@ -184,10 +217,20 @@ public: // This is used to create a temporary actor which is used during // the login process. // - // If initialize is true, then the function 'login.initialize' - // will be executed. + // If this is a master model, The function 'login.initialize' + // called. Then, the following login flags are set: + // can_be_controlled, is_controlled, and delete_on_disconnect. + // + // In a client model, 'login.initialize' is not called, + // and the login flags are not used in client models. // - int64_t create_login_actor(bool initialize); + 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); // Fetch all redirects and clear the redirects table. //