Working on redirect/connect/etc
This commit is contained in:
@@ -14,8 +14,6 @@ I'm thinking lpxserver can do without.
|
|||||||
* Consider targeted difference transmission for just one tangible,
|
* Consider targeted difference transmission for just one tangible,
|
||||||
meant to be used for rapid update after the player invokes a thread on that one tangible.
|
meant to be used for rapid update after the player invokes a thread on that one tangible.
|
||||||
|
|
||||||
* Do we really need can_be_controlled?
|
|
||||||
|
|
||||||
* Think about terrain / backgrounds
|
* Think about terrain / backgrounds
|
||||||
|
|
||||||
* Think about downloading assets
|
* Think about downloading assets
|
||||||
|
|||||||
@@ -121,14 +121,7 @@ public:
|
|||||||
client->async_diff_ = true;
|
client->async_diff_ = true;
|
||||||
|
|
||||||
// Process the invocation in the master model.
|
// Process the invocation in the master model.
|
||||||
//
|
world_->invoke(client->client_id_, inv);
|
||||||
// An invoke with the wrong actor_id is quietly a noop. This is
|
|
||||||
// to make leeway for clients who have recently been redirected, and
|
|
||||||
// who may not know their new actor_id yet.
|
|
||||||
//
|
|
||||||
if (inv.actor() == client->actor_id_) {
|
|
||||||
world_->invoke(client->client_id_, inv);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,6 +228,13 @@ public:
|
|||||||
while (handle_invocation(client));
|
while (handle_invocation(client));
|
||||||
if (client == nullptr) continue;
|
if (client == nullptr) continue;
|
||||||
|
|
||||||
|
// Check for force disconnect.
|
||||||
|
int64_t actor_id = world_->connection_get_actor(client->client_id_);
|
||||||
|
if (actor_id == 0) {
|
||||||
|
delete_client(client);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Possibly send a diff.
|
// Possibly send a diff.
|
||||||
// Currently, it's configured to send about
|
// Currently, it's configured to send about
|
||||||
// ten mini-diffs per second, and two full diffs
|
// ten mini-diffs per second, and two full diffs
|
||||||
|
|||||||
@@ -293,36 +293,9 @@ LuaDefine(tangible_delete, "tan",
|
|||||||
luaL_error(L, "Not a tangible.");
|
luaL_error(L, "Not a tangible.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (tan->can_be_controlled_) {
|
|
||||||
luaL_error(L, "Cannot delete a player using tangible.delete, use tangible.deleteactor instead.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
w->tangible_delete(tan->id());
|
|
||||||
return LS.result();
|
|
||||||
}
|
|
||||||
|
|
||||||
LuaDefine(tangible_deleteactor, "tan",
|
|
||||||
"|Delete an actor tangible, or mark it for deletion on logout."
|
|
||||||
"|"
|
|
||||||
"|This function is used to delete an actor tangible."
|
|
||||||
"|"
|
|
||||||
"|If the actor is not currently logged in, then the tangible is"
|
|
||||||
"|immediately deleted. If the actor is logged in, then the tangible"
|
|
||||||
"|is marked as delete_on_disconnect, and the force_disconnect flag"
|
|
||||||
"|is set. This will cause the actor to cleanly disconnect, and then"
|
|
||||||
"|the deletion will take place."
|
|
||||||
"|") {
|
|
||||||
LuaArg tanobj;
|
|
||||||
LuaDefStack LS(L, tanobj);
|
|
||||||
World *w = World::fetch_global_pointer(L);
|
|
||||||
Tangible *tan = w->tangible_get(LS, tanobj, true);
|
|
||||||
if ((tan == nullptr) || (!tan->can_be_controlled_)) {
|
|
||||||
luaL_error(L, "Tangible is not an actor.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (tan->is_controlled_) {
|
if (tan->is_controlled_) {
|
||||||
tan->delete_on_disconnect_ = true;
|
tan->delete_on_disconnect_ = true;
|
||||||
tan->force_disconnect_ = true;
|
w->connection_redirect(tan, nullptr);
|
||||||
} else {
|
} else {
|
||||||
w->tangible_delete(tan->id());
|
w->tangible_delete(tan->id());
|
||||||
}
|
}
|
||||||
@@ -351,7 +324,7 @@ LuaDefine(tangible_keepactor, "tan",
|
|||||||
LuaDefStack LS(L, tanobj);
|
LuaDefStack LS(L, tanobj);
|
||||||
World *w = World::fetch_global_pointer(L);
|
World *w = World::fetch_global_pointer(L);
|
||||||
Tangible *tan = w->tangible_get(LS, tanobj, true);
|
Tangible *tan = w->tangible_get(LS, tanobj, true);
|
||||||
if ((tan == nullptr) || (!tan->can_be_controlled_)) {
|
if ((tan == nullptr) || (!tan->is_controlled_)) {
|
||||||
luaL_error(L, "Tangible is not an actor.");
|
luaL_error(L, "Tangible is not an actor.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -368,16 +341,18 @@ LuaDefine(tangible_redirect, "actor1, actor2",
|
|||||||
"|client types his name and password, tangible.redirect is used "
|
"|client types his name and password, tangible.redirect is used "
|
||||||
"|to tell the client to control the real actor."
|
"|to tell the client to control the real actor."
|
||||||
"|"
|
"|"
|
||||||
"|Actor1 must be currently logged in. Actor2 must be a valid actor "
|
"|If actor1 is not logged in, an error will be reported."
|
||||||
"|tangible that is not curretly logged in."
|
"|"
|
||||||
|
"|If actor2 is already logged in, they will be booted in order to "
|
||||||
|
"|enable actor1 to take control."
|
||||||
|
"|"
|
||||||
"|") {
|
"|") {
|
||||||
LuaArg lactor1, lactor2;
|
LuaArg lactor1, lactor2;
|
||||||
LuaDefStack LS(L, lactor1, lactor2);
|
LuaDefStack LS(L, lactor1, lactor2);
|
||||||
World *w = World::fetch_global_pointer(L);
|
World *w = World::fetch_global_pointer(L);
|
||||||
Tangible *actor1 = w->tangible_get(LS, lactor1, true);
|
Tangible *actor1 = w->tangible_get(LS, lactor1, true);
|
||||||
Tangible *actor2 = w->tangible_get(LS, lactor2, true);
|
Tangible *actor2 = w->tangible_get(LS, lactor2, true);
|
||||||
int64_t client_id = w->connection_get_client(actor1->id());
|
eng::string error = w->connection_redirect(actor1, actor2);
|
||||||
eng::string error = w->connection_redirect(client_id, actor2->id());
|
|
||||||
if (!error.empty()) {
|
if (!error.empty()) {
|
||||||
eng::ostringstream oss;
|
eng::ostringstream oss;
|
||||||
oss << "redirect from " << actor1->id() << " to " << actor2->id() << " failed: " << error;
|
oss << "redirect from " << actor1->id() << " to " << actor2->id() << " failed: " << error;
|
||||||
@@ -387,6 +362,29 @@ LuaDefine(tangible_redirect, "actor1, actor2",
|
|||||||
return LS.result();
|
return LS.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LuaDefine(tangible_forcedisconnect, "actor",
|
||||||
|
"|Cause the client controlling actor to be disconnected"
|
||||||
|
"|"
|
||||||
|
"|This is a no-op if nobody is logged in to the specified actor."
|
||||||
|
"|") {
|
||||||
|
LuaArg lactor1, lactor2;
|
||||||
|
LuaDefStack LS(L, lactor1, lactor2);
|
||||||
|
World *w = World::fetch_global_pointer(L);
|
||||||
|
Tangible *actor = w->tangible_get(LS, lactor1, true);
|
||||||
|
if (actor->is_controlled_) {
|
||||||
|
eng::string error = w->connection_redirect(actor, nullptr);
|
||||||
|
if (!error.empty()) {
|
||||||
|
eng::ostringstream oss;
|
||||||
|
oss << "forcedisconnect of " << actor->id() << " failed: " << error;
|
||||||
|
luaL_error(L, "%s", oss.str().c_str());
|
||||||
|
return LS.result();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return LS.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
LuaDefine(tangible_build, "config",
|
LuaDefine(tangible_build, "config",
|
||||||
"|Build a new tangible object."
|
"|Build a new tangible object."
|
||||||
"|"
|
"|"
|
||||||
|
|||||||
@@ -152,9 +152,7 @@ void Tangible::serialize(StreamBuffer *sb) {
|
|||||||
anim_queue_.serialize(sb);
|
anim_queue_.serialize(sb);
|
||||||
id_player_pool_.serialize(sb);
|
id_player_pool_.serialize(sb);
|
||||||
print_buffer_.serialize(sb);
|
print_buffer_.serialize(sb);
|
||||||
sb->write_bool(can_be_controlled_);
|
|
||||||
sb->write_bool(is_controlled_);
|
sb->write_bool(is_controlled_);
|
||||||
sb->write_bool(force_disconnect_);
|
|
||||||
sb->write_bool(delete_on_disconnect_);
|
sb->write_bool(delete_on_disconnect_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,9 +160,7 @@ void Tangible::deserialize(StreamBuffer *sb) {
|
|||||||
anim_queue_.deserialize(sb);
|
anim_queue_.deserialize(sb);
|
||||||
id_player_pool_.deserialize(sb);
|
id_player_pool_.deserialize(sb);
|
||||||
print_buffer_.deserialize(sb);
|
print_buffer_.deserialize(sb);
|
||||||
can_be_controlled_ = sb->read_bool();
|
|
||||||
is_controlled_ = sb->read_bool();
|
is_controlled_ = sb->read_bool();
|
||||||
force_disconnect_ = sb->read_bool();
|
|
||||||
delete_on_disconnect_ = sb->read_bool();
|
delete_on_disconnect_ = sb->read_bool();
|
||||||
update_plane_item();
|
update_plane_item();
|
||||||
}
|
}
|
||||||
@@ -220,9 +216,7 @@ Tangible *World::tangible_make(const LuaCoreStack &LS0, LuaSlot database, int64_
|
|||||||
t.reset(new Tangible(this, id));
|
t.reset(new Tangible(this, id));
|
||||||
|
|
||||||
// Set the login flags.
|
// Set the login flags.
|
||||||
t->can_be_controlled_ = false;
|
|
||||||
t->is_controlled_ = false;
|
t->is_controlled_ = false;
|
||||||
t->force_disconnect_ = false;
|
|
||||||
t->delete_on_disconnect_ = false;
|
t->delete_on_disconnect_ = false;
|
||||||
|
|
||||||
// AnimQueue initializes itself to a valid default state.
|
// AnimQueue initializes itself to a valid default state.
|
||||||
@@ -302,6 +296,12 @@ void World::get_near(int64_t player_id, float radius, bool exclude_nowhere, bool
|
|||||||
get_near(scan, into);
|
get_near(scan, into);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void World::connection_prepare(Tangible *tan) {
|
||||||
|
tan->configure_id_pool_for_actor();
|
||||||
|
tan->print_buffer_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int64_t World::connection_create() {
|
int64_t World::connection_create() {
|
||||||
assert(stack_is_clear());
|
assert(stack_is_clear());
|
||||||
int64_t id = id_global_pool_.get_one();
|
int64_t id = id_global_pool_.get_one();
|
||||||
@@ -312,17 +312,14 @@ int64_t World::connection_create() {
|
|||||||
|
|
||||||
// Set the login flags.
|
// Set the login flags.
|
||||||
if (is_authoritative()) {
|
if (is_authoritative()) {
|
||||||
tan->can_be_controlled_ = true;
|
|
||||||
tan->is_controlled_ = true;
|
tan->is_controlled_ = true;
|
||||||
tan->force_disconnect_ = false;
|
|
||||||
tan->delete_on_disconnect_ = true;
|
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();
|
connection_prepare(tan);
|
||||||
tan->print_buffer_.clear();
|
|
||||||
|
|
||||||
if (is_authoritative()) {
|
if (is_authoritative()) {
|
||||||
LS.rawget(func, classtab, "init");
|
LS.rawget(func, classtab, "init");
|
||||||
@@ -336,46 +333,63 @@ int64_t World::connection_create() {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
eng::string World::connection_delete(int64_t client_id) {
|
void World::connection_delete(int64_t client_id) {
|
||||||
auto iter = connections_.find(client_id);
|
auto iter = connections_.find(client_id);
|
||||||
if (iter == connections_.end()) return "no such client id";
|
if (iter == connections_.end()) return;
|
||||||
int64_t actor_id = iter->second;
|
int64_t actor_id = iter->second;
|
||||||
connections_.erase(iter);
|
connections_.erase(iter);
|
||||||
Tangible *tan = tangible_get(actor_id);
|
Tangible *tan = tangible_get(actor_id);
|
||||||
assert(tan != nullptr);
|
assert(tan != nullptr);
|
||||||
assert(tan->is_controlled_);
|
assert(tan->is_controlled_);
|
||||||
tan->is_controlled_ = false;
|
tan->is_controlled_ = false;
|
||||||
tan->force_disconnect_ = false;
|
|
||||||
if (tan->delete_on_disconnect_) {
|
if (tan->delete_on_disconnect_) {
|
||||||
util::dprintf("Deleted actor: %lld\n", actor_id);
|
util::dprintf("Deleted actor: %lld\n", actor_id);
|
||||||
tangible_delete(actor_id);
|
tangible_delete(actor_id);
|
||||||
}
|
}
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t World::connection_get_actor(int64_t client_id) const {
|
int64_t World::connection_get_actor(int64_t client_id) const {
|
||||||
auto iter = connections_.find(client_id);
|
auto iter = connections_.find(client_id);
|
||||||
if (iter == connections_.end()) return 0;
|
if (iter != connections_.end()) return iter->second;
|
||||||
return iter->second;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t World::connection_get_client(int64_t actor_id) const {
|
int64_t World::connection_get_client(Tangible *actor) const {
|
||||||
|
if (!actor->is_controlled_) return 0;
|
||||||
for (const auto &pair : connections_) {
|
for (const auto &pair : connections_) {
|
||||||
if (pair.second == actor_id) return pair.first;
|
if (pair.second == actor->id()) return pair.first;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
eng::string World::connection_redirect(int64_t client_id, int64_t new_id) {
|
eng::string World::connection_redirect(Tangible *actor1, Tangible *actor2) {
|
||||||
Tangible *newtan = tangible_get(new_id);
|
if (actor1 == actor2) return "";
|
||||||
if (newtan == nullptr) return "no such target tangible";
|
|
||||||
if (newtan->is_controlled_) return "target tangible is already controlled";
|
if (!actor1->is_controlled_) {
|
||||||
if (!newtan->can_be_controlled_) return "target tangible is not a potential actor";
|
return "actor1 is not logged in, cannot redirect";
|
||||||
eng::string delresult = connection_delete(client_id);
|
}
|
||||||
if (!delresult.empty()) return delresult;
|
|
||||||
newtan->is_controlled_ = true;
|
// If neccessary, bump actor2. Do not implement delete-on-disconnect,
|
||||||
newtan->force_disconnect_ = false;
|
// because actor2 isn't really "disconnected" yet.
|
||||||
connections_[client_id] = new_id;
|
if ((actor2 != nullptr) && (actor2->is_controlled_)) {
|
||||||
|
int64_t client_id2 = connection_get_client(actor2);
|
||||||
|
actor2->is_controlled_ = false;
|
||||||
|
connections_.erase(client_id2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t client_id1 = connection_get_client(actor1);
|
||||||
|
actor1->is_controlled_ = false;
|
||||||
|
connections_.erase(client_id1);
|
||||||
|
|
||||||
|
if (actor1->delete_on_disconnect_) {
|
||||||
|
tangible_delete(actor1->id());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actor2 != nullptr) {
|
||||||
|
connection_prepare(actor2);
|
||||||
|
actor2->is_controlled_ = true;
|
||||||
|
connections_[client_id1] = actor2->id();
|
||||||
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -781,8 +795,9 @@ HttpServerResponse World::http_serve(const HttpParser &request) {
|
|||||||
|
|
||||||
void World::invoke(int64_t client_id, const Invocation &inv) {
|
void World::invoke(int64_t client_id, const Invocation &inv) {
|
||||||
if (client_id != 0) {
|
if (client_id != 0) {
|
||||||
int64_t actor_id = connection_get_actor(client_id);
|
auto iter = connections_.find(client_id);
|
||||||
if (actor_id == 0) return;
|
if (iter == connections_.end()) return;
|
||||||
|
int64_t actor_id = iter->second;
|
||||||
if (inv.actor() != actor_id) return;
|
if (inv.actor() != actor_id) return;
|
||||||
if (!Invocation::is_valid_network_kind(inv.kind())) return;
|
if (!Invocation::is_valid_network_kind(inv.kind())) return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,16 +78,6 @@ 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.
|
// Is Controlled Flag.
|
||||||
//
|
//
|
||||||
// This flag is set to true when a client is controlling this player.
|
// This flag is set to true when a client is controlling this player.
|
||||||
@@ -97,13 +87,6 @@ public:
|
|||||||
//
|
//
|
||||||
bool is_controlled_;
|
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.
|
// Delete on Logout Flag.
|
||||||
//
|
//
|
||||||
// This flag can be set on a controlled player. When the player
|
// This flag can be set on a controlled player. When the player
|
||||||
@@ -411,33 +394,50 @@ public:
|
|||||||
// Connection Management
|
// Connection Management
|
||||||
//
|
//
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Prepare a tangible to be controlled.
|
||||||
|
//
|
||||||
|
// This does whatever initialization is necessary to turn an ordinary
|
||||||
|
// tangible into a player-controlled tangible.
|
||||||
|
//
|
||||||
|
void connection_prepare(Tangible *tan);
|
||||||
|
|
||||||
// Create a connection.
|
// Create a connection.
|
||||||
//
|
//
|
||||||
// This creates a login actor, and also records the existence of
|
// This is used by the DrivenEngine to create the login
|
||||||
// the connection. Returns the actor_id of the login actor, which
|
// actor. It also records the existence of the
|
||||||
// is also the client id.
|
// connection. Returns the actor_id of the login actor,
|
||||||
|
// which is also the client id.
|
||||||
//
|
//
|
||||||
int64_t connection_create();
|
int64_t connection_create();
|
||||||
|
|
||||||
// This is to be called after a client disconnects. This removes the
|
// Delete a connection.
|
||||||
// connection. On error, return an error message.
|
//
|
||||||
|
// This is used by the DrivenEngine to report that the specified
|
||||||
|
// client has been disconnected.
|
||||||
//
|
//
|
||||||
eng::string connection_delete(int64_t client_id);
|
void connection_delete(int64_t client_id);
|
||||||
|
|
||||||
// Get the current client_id for an actor_id.
|
// Find out what actor the given client is controlling.
|
||||||
// Returns 0 if the actor is not a connected actor.
|
//
|
||||||
//
|
// This is used by the DrivenEngine to check if the given client has
|
||||||
int64_t connection_get_client(int64_t actor_id) const;
|
// changed actor, or if it has been forced to disconnect.
|
||||||
|
|
||||||
// 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;
|
int64_t connection_get_actor(int64_t client_id) const;
|
||||||
|
|
||||||
// Add a redirect. On error, return an error message.
|
// Connection get client.
|
||||||
//
|
//
|
||||||
eng::string connection_redirect(int64_t client_id, int64_t actor_id);
|
// Get the ID of the client that is controlling the specified actor.
|
||||||
|
//
|
||||||
|
int64_t connection_get_client(Tangible *tan) const;
|
||||||
|
|
||||||
|
// Connection redirection.
|
||||||
|
//
|
||||||
|
// This is used by lua code to force changes in connection status.
|
||||||
|
// It can be used to redirect a client, or disconnect a client
|
||||||
|
// if actor2 == nullptr.
|
||||||
|
//
|
||||||
|
eng::string connection_redirect(Tangible *actor1, Tangible *actor2);
|
||||||
|
|
||||||
// Close all connections.
|
// Close all connections.
|
||||||
//
|
//
|
||||||
|
|||||||
Reference in New Issue
Block a user