Implement autodeletion of players on logout

This commit is contained in:
2024-03-04 16:33:23 -05:00
parent c2a94b5332
commit 044bb89edf
4 changed files with 84 additions and 11 deletions

View File

@@ -34,7 +34,7 @@ public:
// the invariant that there's always an actor. When the first difference // the invariant that there's always an actor. When the first difference
// transmission arrives, this actor may be deleted, or it may just be // transmission arrives, this actor may be deleted, or it may just be
// ignored, at the server's discretion. // ignored, at the server's discretion.
actor_id_ = world_->create_login_actor(false); actor_id_ = world_->create_login_actor();
// Clear the unack command queue. // Clear the unack command queue.
unack_.clear(); unack_.clear();
@@ -57,7 +57,7 @@ public:
channel_.reset(); channel_.reset();
// Create the standalone actor. // Create the standalone actor.
actor_id_ = world_->create_login_actor(true); actor_id_ = world_->create_login_actor();
// TODO: initialize the standalone actor. // TODO: initialize the standalone actor.

View File

@@ -43,7 +43,7 @@ public:
master_->update_source(srcpk); master_->update_source(srcpk);
// Create an actor for administrative commands. // Create an actor for administrative commands.
admin_id_ = master_->create_login_actor(true); admin_id_ = master_->create_login_actor();
// TODO: initialize the admin actor. // TODO: initialize the admin actor.
@@ -132,6 +132,7 @@ public:
void delete_client(UniqueClient &client) { void delete_client(UniqueClient &client) {
stdostream() << "Client closed: actor id=" << client->actor_id_ << std::endl; stdostream() << "Client closed: actor id=" << client->actor_id_ << std::endl;
master_->disconnected(client->actor_id_);
client.reset(); client.reset();
} }
@@ -209,7 +210,7 @@ public:
if (chan == nullptr) break; if (chan == nullptr) break;
if (chan->port() == 8085) { if (chan->port() == 8085) {
Client *client = new Client; 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. // TODO: initialize the login actor on the master.
client->channel_ = std::move(chan); client->channel_ = std::move(chan);
client->async_diff_ = true; client->async_diff_ = true;
@@ -217,7 +218,7 @@ public:
client->sync_.reset(new World(WORLD_TYPE_PREDICTIVE)); client->sync_.reset(new World(WORLD_TYPE_PREDICTIVE));
// This login actor is never used, it is just to preserve the invariant that // This login actor is never used, it is just to preserve the invariant that
// the client model and the server synchronous model are identical. // 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); clients_.emplace_back(client);
stdostream() << "New client: actor id=" << client->actor_id_ << std::endl; stdostream() << "New client: actor id=" << client->actor_id_ << std::endl;
} else if (chan->port() == 8080) { } else if (chan->port() == 8080) {

View File

@@ -150,6 +150,12 @@ Tangible *World::tangible_make(const LuaCoreStack &LS0, LuaSlot database, int64_
assert (t == nullptr); assert (t == nullptr);
t.reset(new Tangible(this, id)); 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. // AnimQueue initializes itself to a valid default state.
AnimState state; AnimState state;
state.add_defaults(nullptr); state.add_defaults(nullptr);
@@ -246,28 +252,51 @@ World::Redirects World::fetch_redirects() {
return result; return result;
} }
int64_t World::create_login_actor(bool initialize) { int64_t World::create_login_actor() {
assert(stack_is_clear()); assert(stack_is_clear());
int64_t id = id_global_pool_.get_one(); int64_t id = id_global_pool_.get_one();
{ {
LuaVar database, classtab, mt, func; LuaVar database, classtab, mt, func;
LuaExtStack LS(state(), database, classtab, mt, func); LuaExtStack LS(state(), database, classtab, mt, func);
Tangible *tan = tangible_make(LS, database, id); 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.makeclass(classtab, "login");
LS.getmetatable(mt, database); LS.getmetatable(mt, database);
LS.rawset(mt, "__index", classtab); LS.rawset(mt, "__index", classtab);
tan->configure_id_pool_for_actor(); tan->configure_id_pool_for_actor();
tan->print_buffer_.clear(); tan->print_buffer_.clear();
if (initialize) { if (is_authoritative()) {
LS.rawget(func, classtab, "initialize"); LS.rawget(func, classtab, "initialize");
spawn(LS, id, id, func, true, 0, false); spawn(LS, id, id, func, true, 0, false);
} }
} }
run_scheduled_threads(); if (is_authoritative()) {
run_scheduled_threads();
}
return id; 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) { eng::string World::probe_lua(int64_t actor_id, std::string_view lua) {
assert(stack_is_clear()); assert(stack_is_clear());
lua_State *L = state(); lua_State *L = state();

View File

@@ -76,6 +76,39 @@ public:
// //
PrintBuffer print_buffer_; 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. // constructor.
// //
Tangible(World *w, int64_t id); Tangible(World *w, int64_t id);
@@ -184,10 +217,20 @@ public:
// This is used to create a temporary actor which is used during // This is used to create a temporary actor which is used during
// the login process. // the login process.
// //
// If initialize is true, then the function 'login.initialize' // If this is a master model, The function 'login.initialize'
// will be executed. // called. Then, the following login flags are set:
// can_be_controlled, is_controlled, and delete_on_disconnect.
// //
int64_t create_login_actor(bool initialize); // In a client model, 'login.initialize' 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);
// Fetch all redirects and clear the redirects table. // Fetch all redirects and clear the redirects table.
// //