More work on redirect
This commit is contained in:
@@ -8,11 +8,16 @@
|
|||||||
* Object-Oriented Lua Support
|
* Object-Oriented Lua Support
|
||||||
|
|
||||||
* Investigate whether "delayed_invocations_" in lpxclient/lpxserver is really necessary.
|
* Investigate whether "delayed_invocations_" in lpxclient/lpxserver is really necessary.
|
||||||
Is it just a performance optimization, or does it need to be that way?
|
Is it just a performance optimization, or does it need to be that way? In particular,
|
||||||
|
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?
|
||||||
|
|
||||||
|
* Replace engio with 'invoke.' and 'probe.'
|
||||||
|
|
||||||
Secret / semi-secret variables
|
Secret / semi-secret variables
|
||||||
|
|
||||||
Secret functions
|
Secret functions
|
||||||
@@ -29,7 +34,6 @@ The Spectra game
|
|||||||
|
|
||||||
Heroes of Rock,Paper,Scissors
|
Heroes of Rock,Paper,Scissors
|
||||||
|
|
||||||
Come up with a plan for 'redirect'.
|
|
||||||
|
|
||||||
|
|
||||||
probes should never modify the state of the world. Currently, there is no protection to guarantee this, except for something in lpxclient where it does a snapshot and rollback before and after the probe. We should come up with a more comprehensive mechanism.
|
probes should never modify the state of the world. Currently, there is no protection to guarantee this, except for something in lpxclient where it does a snapshot and rollback before and after the probe. We should come up with a more comprehensive mechanism.
|
||||||
|
|||||||
@@ -45,7 +45,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();
|
actor_id_ = world_->connection_create();
|
||||||
|
|
||||||
// Clear the unack command queue.
|
// Clear the unack command queue.
|
||||||
unack_.clear();
|
unack_.clear();
|
||||||
@@ -70,7 +70,7 @@ public:
|
|||||||
channel_.reset();
|
channel_.reset();
|
||||||
|
|
||||||
// Create the standalone actor.
|
// Create the standalone actor.
|
||||||
actor_id_ = world_->create_login_actor();
|
actor_id_ = world_->connection_create();
|
||||||
|
|
||||||
// Clear the unack command queue.
|
// Clear the unack command queue.
|
||||||
unack_.clear();
|
unack_.clear();
|
||||||
@@ -102,7 +102,7 @@ public:
|
|||||||
if (world_->snapshot_empty()) {
|
if (world_->snapshot_empty()) {
|
||||||
world_->snapshot();
|
world_->snapshot();
|
||||||
for (const Invocation &inv : unack_) {
|
for (const Invocation &inv : unack_) {
|
||||||
world_->invoke(inv);
|
world_->invoke(0, inv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,10 +117,10 @@ public:
|
|||||||
|
|
||||||
void send_invocation(const Invocation &inv) {
|
void send_invocation(const Invocation &inv) {
|
||||||
if (channel_ == nullptr) {
|
if (channel_ == nullptr) {
|
||||||
world_->invoke(inv);
|
world_->invoke(0, inv);
|
||||||
} else {
|
} else {
|
||||||
world_to_asynchronous();
|
world_to_asynchronous();
|
||||||
world_->invoke(inv);
|
world_->invoke(0, inv);
|
||||||
unack_.push_back(inv);
|
unack_.push_back(inv);
|
||||||
StreamBuffer *sb = channel_->out();
|
StreamBuffer *sb = channel_->out();
|
||||||
sb->write_uint8(util::MSG_INVOKE);
|
sb->write_uint8(util::MSG_INVOKE);
|
||||||
@@ -134,12 +134,6 @@ public:
|
|||||||
// set_initial_state_connect(util::ss("nocert:", hostname, ":8085"));
|
// 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) {
|
void receive_ack_from_server(StreamBuffer *sb) {
|
||||||
// An ack is just a single byte, so there's nothing left to read.
|
// An ack is just a single byte, so there's nothing left to read.
|
||||||
if (unack_.empty()) {
|
if (unack_.empty()) {
|
||||||
@@ -148,7 +142,7 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
world_to_synchronous();
|
world_to_synchronous();
|
||||||
world_->invoke(unack_.front());
|
world_->invoke(0, unack_.front());
|
||||||
unack_.pop_front();
|
unack_.pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +151,11 @@ public:
|
|||||||
try {
|
try {
|
||||||
DebugCollector dbc("");
|
DebugCollector dbc("");
|
||||||
int64_t nactor = world_->patch(sb, &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(...);
|
// dbc.dump(...);
|
||||||
} catch (const StreamException &sexcept) {
|
} catch (const StreamException &sexcept) {
|
||||||
abandon_server();
|
abandon_server();
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
class Client : public eng::opnew {
|
class Client : public eng::opnew {
|
||||||
public:
|
public:
|
||||||
|
int64_t client_id_;
|
||||||
int64_t actor_id_;
|
int64_t actor_id_;
|
||||||
SharedChannel channel_;
|
SharedChannel channel_;
|
||||||
UniqueWorld sync_;
|
UniqueWorld sync_;
|
||||||
@@ -44,7 +45,7 @@ public:
|
|||||||
world_->expose_world_to_driver(wrapper_);
|
world_->expose_world_to_driver(wrapper_);
|
||||||
|
|
||||||
// Create the admin actor. Note: there isn't any 'init' function yet.
|
// 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.
|
// Print out admin ID for debugging purposes.
|
||||||
util::dprint("Admin actor id = ", actor_id_);
|
util::dprint("Admin actor id = ", actor_id_);
|
||||||
@@ -73,12 +74,13 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void delete_client(UniqueClient &client) {
|
void delete_client(UniqueClient &client) {
|
||||||
util::dprint("Client closed: actor id=", client->actor_id_);
|
util::dprint("Client closed: client_id=", client->client_id_, " actor_id=", client->actor_id_);
|
||||||
world_->disconnected(client->actor_id_);
|
world_->connection_delete(client->client_id_);
|
||||||
client.reset();
|
client.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_diffs(UniqueClient &client, bool full) {
|
void send_diffs(UniqueClient &client, bool full) {
|
||||||
|
client->actor_id_ = world_->connection_get_actor(client->client_id_);
|
||||||
StreamBuffer *sb = client->channel_->out();
|
StreamBuffer *sb = client->channel_->out();
|
||||||
sb->write_uint8(util::MSG_DIFF);
|
sb->write_uint8(util::MSG_DIFF);
|
||||||
sb->write_uint32(0);
|
sb->write_uint32(0);
|
||||||
@@ -89,27 +91,6 @@ public:
|
|||||||
sb->overwrite_int32(tw_1, tw_2 - tw_1);
|
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) {
|
bool handle_invocation(UniqueClient &client) {
|
||||||
if (client == nullptr) return false;
|
if (client == nullptr) return false;
|
||||||
StreamBuffer *sb = client->channel_->in();
|
StreamBuffer *sb = client->channel_->in();
|
||||||
@@ -131,18 +112,12 @@ public:
|
|||||||
return false;
|
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.
|
// Acknowledge the invocation.
|
||||||
client->channel_->out()->write_uint8(util::MSG_ACK);
|
client->channel_->out()->write_uint8(util::MSG_ACK);
|
||||||
client->channel_->out()->write_uint32(0);
|
client->channel_->out()->write_uint32(0);
|
||||||
|
|
||||||
// Execute the invocation with the sync model.
|
// Execute the invocation with the sync model.
|
||||||
client->sync_->invoke(inv);
|
client->sync_->invoke(0, inv);
|
||||||
client->async_diff_ = true;
|
client->async_diff_ = true;
|
||||||
|
|
||||||
// Process the invocation in the master model.
|
// Process the invocation in the master model.
|
||||||
@@ -152,8 +127,7 @@ public:
|
|||||||
// who may not know their new actor_id yet.
|
// who may not know their new actor_id yet.
|
||||||
//
|
//
|
||||||
if (inv.actor() == client->actor_id_) {
|
if (inv.actor() == client->actor_id_) {
|
||||||
world_->invoke(inv);
|
world_->invoke(client->client_id_, inv);
|
||||||
process_redirects();
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -210,7 +184,7 @@ public:
|
|||||||
// come from the local command line. We just feed
|
// come from the local command line. We just feed
|
||||||
// these directly into the master model.
|
// these directly into the master model.
|
||||||
for (const Invocation &inv : delayed_invocations_) {
|
for (const Invocation &inv : delayed_invocations_) {
|
||||||
world_->invoke(inv);
|
world_->invoke(0, inv);
|
||||||
}
|
}
|
||||||
delayed_invocations_.clear();
|
delayed_invocations_.clear();
|
||||||
|
|
||||||
@@ -220,7 +194,8 @@ 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_ = world_->create_login_actor();
|
client->actor_id_ = world_->connection_create();
|
||||||
|
client->client_id_ = client->actor_id_;
|
||||||
// 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;
|
||||||
@@ -229,7 +204,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();
|
client->sync_->connection_create();
|
||||||
clients_.emplace_back(client);
|
clients_.emplace_back(client);
|
||||||
util::dprint("New client: actor id=", client->actor_id_);
|
util::dprint("New client: actor id=", client->actor_id_);
|
||||||
} else if (chan->port() == 8080) {
|
} else if (chan->port() == 8080) {
|
||||||
@@ -241,7 +216,7 @@ public:
|
|||||||
|
|
||||||
// If the clock has advanced far enough, tick the master model.
|
// If the clock has advanced far enough, tick the master model.
|
||||||
if (clock >= next_tick_) {
|
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_) {
|
for (UniqueClient &client : clients_) {
|
||||||
client->async_diff_ = true;
|
client->async_diff_ = true;
|
||||||
}
|
}
|
||||||
@@ -249,11 +224,6 @@ public:
|
|||||||
if (next_tick_ < clock + 0.3) next_tick_ = clock + 0.3;
|
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.
|
// Traverse all existing channels, process any communication.
|
||||||
for (UniqueClient &client : clients_) {
|
for (UniqueClient &client : clients_) {
|
||||||
if (client->channel_->closed()) {
|
if (client->channel_->closed()) {
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ lua_State *LuaCoreStack::newstate (lua_Alloc allocf) {
|
|||||||
LS.rawset(LuaRegistry, "tangibles", LuaNewTable);
|
LS.rawset(LuaRegistry, "tangibles", LuaNewTable);
|
||||||
LS.rawset(LuaRegistry, "persist", LuaNewTable);
|
LS.rawset(LuaRegistry, "persist", LuaNewTable);
|
||||||
LS.rawset(LuaRegistry, "unpersist", LuaNewTable);
|
LS.rawset(LuaRegistry, "unpersist", LuaNewTable);
|
||||||
|
LS.rawset(LuaRegistry, "funcnames", LuaNewTable);
|
||||||
|
|
||||||
// Tag the registry and global environment with their tabletypes.
|
// Tag the registry and global environment with their tabletypes.
|
||||||
LS.settabletype(LuaRegistry, LUA_TT_REGISTRY);
|
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.
|
// Clear all the classes in the registry classes table.
|
||||||
//
|
//
|
||||||
static void source_clear_globals(lua_State *L) {
|
static void source_clear_globals(lua_State *L) {
|
||||||
LuaVar classname, classtab, key, globtab, classes;
|
LuaVar classname, classtab, key, globtab, classes, funcnames;
|
||||||
LuaExtStack LS(L, classname, classtab, key, globtab, classes);
|
LuaExtStack LS(L, classname, classtab, key, globtab, classes, funcnames);
|
||||||
|
|
||||||
LS.getglobaltable(globtab);
|
LS.getglobaltable(globtab);
|
||||||
LS.cleartable(globtab, true);
|
LS.cleartable(globtab, true);
|
||||||
@@ -306,8 +306,45 @@ static void source_clear_globals(lua_State *L) {
|
|||||||
assert(LS.istable(classtab));
|
assert(LS.istable(classtab));
|
||||||
LS.cleartable(classtab, true);
|
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.
|
// Load all the 'LuaDefine' C functions into the lua state.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -172,6 +172,13 @@ public:
|
|||||||
//
|
//
|
||||||
void rebuild_core();
|
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.
|
// Difference transmission.
|
||||||
//
|
//
|
||||||
// Note: The patch routine applies the differences to the source
|
// Note: The patch routine applies the differences to the source
|
||||||
|
|||||||
@@ -43,33 +43,46 @@ int traceback_coroutine(lua_State *L) {
|
|||||||
firstpart = 0;
|
firstpart = 0;
|
||||||
continue;
|
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 ((!any) && (*ar.what == 'C') && (ar.name != 0)) {
|
||||||
if (strcmp(ar.name, "__newindex") == 0) continue;
|
if (strcmp(ar.name, "__newindex") == 0) continue;
|
||||||
}
|
}
|
||||||
if ((ar.currentline > 0) || (*ar.namewhat != 0) || (*ar.what != 'C')) {
|
if ((ar.currentline > 0) || (*ar.namewhat != 0) || (*ar.what != 'C')) {
|
||||||
any = true;
|
any = true;
|
||||||
lua_pushliteral(L, "\n\t");
|
lua_pushliteral(L, "\n\t");
|
||||||
if (strcmp(ar.short_src, "<console>")==0)
|
|
||||||
{
|
if (strcmp(ar.short_src, "<console>")==0) {
|
||||||
lua_pushstring(L, "in the console");
|
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
|
|
||||||
{
|
if (!storedname.empty()) {
|
||||||
lua_pushfstring(L, "in %s", ar.short_src);
|
lua_pushfstring(L, " function " LUA_QS, storedname.c_str());
|
||||||
if (ar.currentline > 0)
|
} else if (*ar.namewhat != 0) {
|
||||||
lua_pushfstring(L, " line %d", ar.currentline);
|
lua_pushfstring(L, " function " LUA_QS, ar.name);
|
||||||
}
|
} else if (*ar.what == 'm') {
|
||||||
if (*ar.namewhat != '\0') /* is there a name? */
|
lua_pushfstring(L, " top-level expression ");
|
||||||
lua_pushfstring(L, " in function " LUA_QS, ar.name);
|
} else {
|
||||||
else {
|
lua_pushliteral(L, " unknown function");
|
||||||
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 (1 + lua_gettop(L) - top > 5) {
|
if (1 + lua_gettop(L) - top > 5) {
|
||||||
lua_concat(L, 1 + lua_gettop(L) - top);
|
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 "
|
"|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 not be. This "
|
"|Actor1 must be currently logged in. Actor2 must be a valid actor "
|
||||||
"|function doesn't error check that the actors are valid: it just "
|
"|tangible that is not curretly logged in."
|
||||||
"|queues the redirect, then the system error checks later. This "
|
|
||||||
"|is because conditions can change. An invalid redirect will cause "
|
|
||||||
"|a disconnection."
|
|
||||||
"|") {
|
"|") {
|
||||||
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);
|
||||||
// if (!actor1->is_controlled_) {
|
int64_t client_id = w->connection_get_client(actor1->id());
|
||||||
// luaL_error(L, "Actor1 is not a controlled actor.");
|
eng::string error = w->connection_redirect(client_id, actor2->id());
|
||||||
// return 0;
|
if (!error.empty()) {
|
||||||
// }
|
eng::ostringstream oss;
|
||||||
// if ((!actor2->can_be_controlled_) || (actor2->is_controlled_)) {
|
oss << "redirect from " << actor1->id() << " to " << actor2->id() << " failed: " << error;
|
||||||
// luaL_error(L, "Actor2 is not an uncontrolled actor.");
|
luaL_error(L, "%s", oss.str().c_str());
|
||||||
// return 0;
|
return LS.result();
|
||||||
// }
|
}
|
||||||
w->add_redirect(actor1->id(), actor2->id());
|
|
||||||
return LS.result();
|
return LS.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -152,12 +152,20 @@ 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(force_disconnect_);
|
||||||
|
sb->write_bool(delete_on_disconnect_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tangible::deserialize(StreamBuffer *sb) {
|
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();
|
||||||
|
force_disconnect_ = sb->read_bool();
|
||||||
|
delete_on_disconnect_ = sb->read_bool();
|
||||||
update_plane_item();
|
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);
|
get_near(scan, into);
|
||||||
}
|
}
|
||||||
|
|
||||||
World::Redirects World::fetch_redirects() {
|
int64_t World::connection_create() {
|
||||||
World::Redirects result = std::move(redirects_);
|
|
||||||
redirects_.clear();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
||||||
{
|
{
|
||||||
@@ -327,13 +329,18 @@ int64_t World::create_login_actor() {
|
|||||||
spawn(LS, id, id, func, 0, false);
|
spawn(LS, id, id, func, 0, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
connections_.emplace(id, id);
|
||||||
if (is_authoritative()) {
|
if (is_authoritative()) {
|
||||||
run_scheduled_threads();
|
run_scheduled_threads();
|
||||||
}
|
}
|
||||||
return id;
|
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);
|
Tangible *tan = tangible_get(actor_id);
|
||||||
assert(tan != nullptr);
|
assert(tan != nullptr);
|
||||||
assert(tan->is_controlled_);
|
assert(tan->is_controlled_);
|
||||||
@@ -343,16 +350,39 @@ void World::disconnected(int64_t actor_id) {
|
|||||||
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 "";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool World::redirected(int64_t actor_id, int64_t new_id) {
|
int64_t World::connection_get_actor(int64_t client_id) const {
|
||||||
disconnected(actor_id);
|
auto iter = connections_.find(client_id);
|
||||||
Tangible *tan = tangible_get(new_id);
|
if (iter == connections_.end()) return 0;
|
||||||
if (tan && (tan->can_be_controlled_) && (!tan->is_controlled_)) {
|
return iter->second;
|
||||||
tan->is_controlled_ = true;
|
}
|
||||||
return true;
|
|
||||||
} else {
|
int64_t World::connection_get_client(int64_t actor_id) const {
|
||||||
return false;
|
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);
|
if (actor_id != 0) lthread_prints_to_actor(actor_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
source_db_.rebuild_funcnames();
|
||||||
lthread_prints_ << "Compiled " << successes << " modules successfully." << std::endl;
|
lthread_prints_ << "Compiled " << successes << " modules successfully." << std::endl;
|
||||||
if (failures > 0) {
|
if (failures > 0) {
|
||||||
lthread_prints_ << "Compiled " << failures << " modules with errors." << std::endl;
|
lthread_prints_ << "Compiled " << failures << " modules with errors." << std::endl;
|
||||||
@@ -743,7 +774,13 @@ HttpServerResponse World::http_serve(const HttpParser &request) {
|
|||||||
return response;
|
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()) {
|
switch (inv.kind()) {
|
||||||
case AccessKind::INVOKE_LUA_CALL:
|
case AccessKind::INVOKE_LUA_CALL:
|
||||||
invoke_lua_call(inv.actor(), inv.place(), inv.datapack());
|
invoke_lua_call(inv.actor(), inv.place(), inv.datapack());
|
||||||
@@ -1230,7 +1267,6 @@ const eng::string &World::get_global(const eng::string &gvar) {
|
|||||||
|
|
||||||
void World::serialize(StreamBuffer *sb) {
|
void World::serialize(StreamBuffer *sb) {
|
||||||
assert(stack_is_clear());
|
assert(stack_is_clear());
|
||||||
assert(redirects_.empty());
|
|
||||||
// int64_t wc0 = sb->total_writes();
|
// int64_t wc0 = sb->total_writes();
|
||||||
lua_snap_.serialize(sb);
|
lua_snap_.serialize(sb);
|
||||||
id_global_pool_.serialize(sb);
|
id_global_pool_.serialize(sb);
|
||||||
@@ -1242,12 +1278,16 @@ void World::serialize(StreamBuffer *sb) {
|
|||||||
sb->write_int64(p.first);
|
sb->write_int64(p.first);
|
||||||
p.second->serialize(sb);
|
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());
|
assert(stack_is_clear());
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::deserialize(StreamBuffer *sb) {
|
void World::deserialize(StreamBuffer *sb) {
|
||||||
assert(stack_is_clear());
|
assert(stack_is_clear());
|
||||||
redirects_.clear();
|
|
||||||
lua_snap_.deserialize(sb);
|
lua_snap_.deserialize(sb);
|
||||||
id_global_pool_.deserialize(sb);
|
id_global_pool_.deserialize(sb);
|
||||||
clock_ = sb->read_int64();
|
clock_ = sb->read_int64();
|
||||||
@@ -1259,7 +1299,7 @@ void World::deserialize(StreamBuffer *sb) {
|
|||||||
}
|
}
|
||||||
// Deserialize tangibles.
|
// Deserialize tangibles.
|
||||||
size_t ntan = sb->read_uint32();
|
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();
|
int64_t id = sb->read_int64();
|
||||||
UniqueTangible &t = tangibles_[id];
|
UniqueTangible &t = tangibles_[id];
|
||||||
if (t == nullptr) {
|
if (t == nullptr) {
|
||||||
@@ -1269,6 +1309,14 @@ void World::deserialize(StreamBuffer *sb) {
|
|||||||
}
|
}
|
||||||
t->deserialize(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.
|
// Delete tangibles that didn't get deserialized.
|
||||||
for (auto iter = tangibles_.begin(); iter != tangibles_.end(); ) {
|
for (auto iter = tangibles_.begin(); iter != tangibles_.end(); ) {
|
||||||
if (iter->second->plane_item_.id() == 0) {
|
if (iter->second->plane_item_.id() == 0) {
|
||||||
@@ -1277,8 +1325,6 @@ void World::deserialize(StreamBuffer *sb) {
|
|||||||
++iter;
|
++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());
|
assert(stack_is_clear());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,8 @@ public:
|
|||||||
//
|
//
|
||||||
// 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.
|
||||||
// It gets set back to false when the client logs out or attaches
|
// 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_;
|
bool is_controlled_;
|
||||||
|
|
||||||
@@ -129,7 +130,6 @@ class World : public eng::opnew {
|
|||||||
public:
|
public:
|
||||||
using IdVector = util::IdVector;
|
using IdVector = util::IdVector;
|
||||||
using TanVector = eng::vector<const Tangible*>;
|
using TanVector = eng::vector<const Tangible*>;
|
||||||
using Redirects = eng::map<int64_t, int64_t>;
|
|
||||||
const float RadiusVisibility = 1000.0;
|
const float RadiusVisibility = 1000.0;
|
||||||
const float RadiusClose = 1000.0;
|
const float RadiusClose = 1000.0;
|
||||||
|
|
||||||
@@ -222,45 +222,6 @@ public:
|
|||||||
//
|
//
|
||||||
void tangible_delete(int64_t id);
|
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.
|
// Probe an arbitrary lua expression.
|
||||||
//
|
//
|
||||||
// Any print-statements in the lua code are sent into
|
// 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
|
// It is legal to mutate a world model without using 'Invoke', but
|
||||||
// only in authoritative world models.
|
// 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.
|
// Get the PrintBuffer of the actor.
|
||||||
//
|
//
|
||||||
@@ -351,6 +316,11 @@ public:
|
|||||||
|
|
||||||
// Serialize and deserialize.
|
// 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 serialize(StreamBuffer *sb);
|
||||||
void deserialize(StreamBuffer *sb);
|
void deserialize(StreamBuffer *sb);
|
||||||
|
|
||||||
@@ -395,19 +365,6 @@ public:
|
|||||||
//
|
//
|
||||||
void lthread_prints_to_actor(int64_t actor_id);
|
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.
|
// Allocate a single ID.
|
||||||
//
|
//
|
||||||
// The rules are as follows:
|
// The rules are as follows:
|
||||||
@@ -427,7 +384,75 @@ public:
|
|||||||
// Otherwise, return.
|
// Otherwise, return.
|
||||||
void guard_nopredict(lua_State *L, const char *fn);
|
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:
|
private:
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Invocation and scheduling.
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Add a thread to the scheduler queue.
|
// Add a thread to the scheduler queue.
|
||||||
//
|
//
|
||||||
void schedule(int64_t clk, int64_t thid, int64_t plid);
|
void schedule(int64_t clk, int64_t thid, int64_t plid);
|
||||||
@@ -533,14 +558,21 @@ public:
|
|||||||
public:
|
public:
|
||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// difference transmission internals related to table comparison
|
// Difference transmission entry point.
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
//
|
||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
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 patch_numbered_tables(StreamBuffer *sb, DebugCollector *dbc);
|
||||||
void diff_numbered_tables(lua_State *master, StreamBuffer *sb);
|
void diff_numbered_tables(lua_State *master, StreamBuffer *sb);
|
||||||
|
|
||||||
@@ -550,14 +582,6 @@ public:
|
|||||||
void patch_tangible_classes(StreamBuffer *sb, DebugCollector *dbc);
|
void patch_tangible_classes(StreamBuffer *sb, DebugCollector *dbc);
|
||||||
void diff_tangible_classes(const IdVector &basis, lua_State *master, StreamBuffer *sb);
|
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);
|
int64_t patch_actor(StreamBuffer *sb, DebugCollector *dbc);
|
||||||
void diff_actor(int64_t actor_id, World *master, StreamBuffer *sb);
|
void diff_actor(int64_t actor_id, World *master, StreamBuffer *sb);
|
||||||
|
|
||||||
@@ -576,15 +600,6 @@ public:
|
|||||||
void patch_globals(StreamBuffer *sb, DebugCollector *dbc);
|
void patch_globals(StreamBuffer *sb, DebugCollector *dbc);
|
||||||
void diff_globals(World *master, StreamBuffer *sb);
|
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:
|
public:
|
||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
@@ -640,6 +655,13 @@ public:
|
|||||||
void unnumber_lua_tables();
|
void unnumber_lua_tables();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Instance Variables
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
// Type of model
|
// Type of model
|
||||||
WorldType world_type_;
|
WorldType world_type_;
|
||||||
|
|
||||||
@@ -692,9 +714,9 @@ private:
|
|||||||
//
|
//
|
||||||
StreamBuffer snapshot_;
|
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.
|
// Storage for wrapper_get_tangibles_near and wrapper_get_animation_queues.
|
||||||
// These hold results alive while the driver reads from the raw pointers.
|
// These hold results alive while the driver reads from the raw pointers.
|
||||||
@@ -713,7 +735,6 @@ private:
|
|||||||
friend class Tangible;
|
friend class Tangible;
|
||||||
friend int lfn_tangible_animate(lua_State *L);
|
friend int lfn_tangible_animate(lua_State *L);
|
||||||
friend int lfn_tangible_build(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_actor(lua_State *L);
|
||||||
friend int lfn_tangible_place(lua_State *L);
|
friend int lfn_tangible_place(lua_State *L);
|
||||||
friend int lfn_tangible_nopredict(lua_State *L);
|
friend int lfn_tangible_nopredict(lua_State *L);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ function login.init()
|
|||||||
global.set("nextplayer", player + 1)
|
global.set("nextplayer", player + 1)
|
||||||
dprint("login.init initializing player ", player)
|
dprint("login.init initializing player ", player)
|
||||||
actor.player = player
|
actor.player = player
|
||||||
|
tangible.keepactor(actor) -- do not delete this login when the client disconnects
|
||||||
tangible.animinit{tan=actor, anim={bp="character", mesh="manny", plane="earth", xyz={player * 100, 0, 90}}}
|
tangible.animinit{tan=actor, anim={bp="character", mesh="manny", plane="earth", xyz={player * 100, 0, 90}}}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -24,7 +25,6 @@ end
|
|||||||
|
|
||||||
function engio.move(action, xyz, facing)
|
function engio.move(action, xyz, facing)
|
||||||
-- todo: sanity check the parameters.
|
-- todo: sanity check the parameters.
|
||||||
dprint("engio.move ", action, " ", xyz[1], " ", xyz[2], " ", xyz[3])
|
|
||||||
tangible.animate{tan=actor, anim={action=action, interactive=true, xyz=xyz, facing=facing}}
|
tangible.animate{tan=actor, anim={action=action, interactive=true, xyz=xyz, facing=facing}}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -33,6 +33,10 @@ function moveto(x, y)
|
|||||||
tangible.animate{tan=actor, anim={action="moveto", xyz={x, y, z}, facing=math.auto}}
|
tangible.animate{tan=actor, anim={action="moveto", xyz={x, y, z}, facing=math.auto}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function login.lookmenu(add)
|
||||||
|
add("Redirect", function() tangible.redirect(actor, place) end)
|
||||||
|
end
|
||||||
|
|
||||||
function cube.lookmenu(add)
|
function cube.lookmenu(add)
|
||||||
add("Cube A", function () dprint("Doing Cube A") end)
|
add("Cube A", function () dprint("Doing Cube A") end)
|
||||||
add("Cube B", function () dprint("Doing Cube B") end)
|
add("Cube B", function () dprint("Doing Cube B") end)
|
||||||
|
|||||||
@@ -55,14 +55,14 @@ function engio.pressmenu(label)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local result = nil
|
local menuclosure = nil
|
||||||
local add = function(lbl, closure)
|
local add = function(lbl, closure)
|
||||||
if (lbl == label) then
|
if (lbl == label) then
|
||||||
result = closure
|
menuclosure = closure
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
class.lookmenu(add)
|
class.lookmenu(add)
|
||||||
if result ~= nil then
|
if menuclosure ~= nil then
|
||||||
result()
|
menuclosure()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user