2021-02-09 17:15:54 -05:00
|
|
|
|
2022-02-23 23:08:28 -05:00
|
|
|
#include "wrap-sstream.hpp"
|
|
|
|
|
#include "wrap-string.hpp"
|
|
|
|
|
|
2021-02-09 17:15:54 -05:00
|
|
|
#include "luasnap.hpp"
|
2021-03-01 17:29:32 -05:00
|
|
|
#include "luastack.hpp"
|
2022-02-23 23:08:28 -05:00
|
|
|
#include "streambuffer.hpp"
|
|
|
|
|
|
2021-02-09 17:15:54 -05:00
|
|
|
#include <cassert>
|
|
|
|
|
|
2026-02-24 23:44:10 -05:00
|
|
|
class LuaByteReader {
|
|
|
|
|
private:
|
|
|
|
|
const char *data_;
|
|
|
|
|
int64_t size_;
|
|
|
|
|
public:
|
|
|
|
|
LuaByteReader(const char *d, int64_t s) : data_(d), size_(s) {}
|
|
|
|
|
static const char *lua_reader(lua_State *L, void *ud, size_t *size) {
|
|
|
|
|
LuaByteReader *reader = (LuaByteReader*)ud;
|
|
|
|
|
*size = reader->size_;
|
|
|
|
|
const char *data = reader->data_;
|
|
|
|
|
reader->data_ = 0;
|
|
|
|
|
reader->size_ = 0;
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
};
|
2021-02-09 17:15:54 -05:00
|
|
|
|
2021-03-01 17:29:32 -05:00
|
|
|
LuaSnap::LuaSnap() {
|
2023-04-07 14:20:45 -04:00
|
|
|
state_ = LuaCoreStack::newstate(eng::l_alloc);
|
|
|
|
|
LuaExtStack LS(state_);
|
2021-02-09 17:15:54 -05:00
|
|
|
}
|
|
|
|
|
|
2021-03-01 17:29:32 -05:00
|
|
|
LuaSnap::~LuaSnap() {
|
2021-03-30 19:30:58 -04:00
|
|
|
lua_close(state_);
|
2021-02-09 17:15:54 -05:00
|
|
|
}
|
|
|
|
|
|
2021-03-05 14:20:21 -05:00
|
|
|
void LuaSnap::serialize(StreamBuffer *sb) {
|
2021-03-02 18:58:50 -05:00
|
|
|
// Lua stack should be empty.
|
2021-03-01 17:29:32 -05:00
|
|
|
assert(lua_gettop(state_) == 0);
|
|
|
|
|
|
2023-04-13 13:26:45 -04:00
|
|
|
// We'll allocate stack slots manually, to ensure
|
|
|
|
|
// that the thread serializes as we expect it to.
|
|
|
|
|
lua_pushnil(state_); // permstable
|
|
|
|
|
lua_pushnil(state_); // regcopy
|
|
|
|
|
lua_pushnil(state_); // key
|
|
|
|
|
lua_pushnil(state_); // value
|
|
|
|
|
LuaSpecial permstable(1);
|
|
|
|
|
LuaSpecial regcopy(2);
|
|
|
|
|
LuaSpecial key(3);
|
|
|
|
|
LuaSpecial value(4);
|
|
|
|
|
LuaCoreStack LS(state_);
|
2021-03-01 17:29:32 -05:00
|
|
|
|
|
|
|
|
// Construct a copy of the registry table.
|
|
|
|
|
LS.set(regcopy, LuaNewTable);
|
|
|
|
|
LS.set(key, LuaNil);
|
|
|
|
|
while (LS.next(LuaRegistry, key, value) != 0) {
|
|
|
|
|
LS.rawset(regcopy, key, value);
|
2021-02-09 17:15:54 -05:00
|
|
|
}
|
|
|
|
|
|
2021-03-01 18:10:40 -05:00
|
|
|
// Remove certain things from the copy. These items
|
|
|
|
|
// don't get included in the snapshot.
|
|
|
|
|
//
|
|
|
|
|
// From a modularity perspective, this is bad.
|
|
|
|
|
//
|
2021-03-01 17:29:32 -05:00
|
|
|
LS.rawset(regcopy, LUA_RIDX_MAINTHREAD, LuaNil);
|
|
|
|
|
LS.rawset(regcopy, "persist", LuaNil);
|
|
|
|
|
LS.rawset(regcopy, "unpersist", LuaNil);
|
|
|
|
|
LS.rawset(regcopy, "world", LuaNil);
|
|
|
|
|
LS.rawset(regcopy, "gui", LuaNil);
|
2021-08-13 17:02:35 -04:00
|
|
|
LS.rawset(regcopy, "tnmap", LuaNil);
|
|
|
|
|
LS.rawset(regcopy, "ntmap", LuaNil);
|
2021-02-09 17:15:54 -05:00
|
|
|
|
2021-03-01 18:10:40 -05:00
|
|
|
// Get the eris permanents table from the registry.
|
2021-03-01 17:29:32 -05:00
|
|
|
LS.rawget(permstable, LuaRegistry, "persist");
|
2021-03-01 18:10:40 -05:00
|
|
|
assert(LS.istable(permstable));
|
2021-02-09 17:15:54 -05:00
|
|
|
|
2023-04-13 13:26:45 -04:00
|
|
|
// Remove key and value from stack, leaving permstable and regcopy.
|
|
|
|
|
lua_settop(state_, 2);
|
2021-02-09 17:15:54 -05:00
|
|
|
|
2021-03-02 18:58:50 -05:00
|
|
|
// Write dummy length, use eris to write data, then overwrite length.
|
2021-03-05 14:20:21 -05:00
|
|
|
sb->write_int64(0);
|
2021-07-20 14:48:53 -04:00
|
|
|
int64_t pos1 = sb->total_writes();
|
2023-10-18 14:03:05 -04:00
|
|
|
|
|
|
|
|
eris_dump(state_, lua_writer_into_streambuffer, sb);
|
2021-07-20 14:48:53 -04:00
|
|
|
int64_t pos2 = sb->total_writes();
|
2021-03-05 14:20:21 -05:00
|
|
|
sb->overwrite_int64(pos1, pos2 - pos1);
|
2021-03-01 18:10:40 -05:00
|
|
|
lua_settop(state_, 0);
|
2021-02-09 17:15:54 -05:00
|
|
|
}
|
|
|
|
|
|
2021-03-05 14:20:21 -05:00
|
|
|
void LuaSnap::deserialize(StreamBuffer *sb) {
|
2021-03-02 18:58:50 -05:00
|
|
|
// Lua stack should be empty.
|
2021-03-01 18:10:40 -05:00
|
|
|
assert(lua_gettop(state_) == 0);
|
|
|
|
|
|
2026-02-24 23:44:10 -05:00
|
|
|
// Get the eris data and convert it to a byte reader.
|
2023-10-18 14:03:05 -04:00
|
|
|
int64_t eris_len = sb->read_int64();
|
|
|
|
|
const char *eris_bytes = sb->read_bytes(eris_len);
|
|
|
|
|
LuaByteReader bytereader(eris_bytes, eris_len);
|
|
|
|
|
|
2021-03-01 18:10:40 -05:00
|
|
|
// Call eris with the permanents table and passing the snapshot as a lua_Reader.
|
2023-03-01 16:07:13 -05:00
|
|
|
lua_pushstring(state_, "unpersist");
|
|
|
|
|
lua_rawget(state_, LUA_REGISTRYINDEX);
|
2026-02-24 23:44:10 -05:00
|
|
|
eris_undump(state_, bytereader.lua_reader, &bytereader);
|
2021-03-01 18:10:40 -05:00
|
|
|
|
2023-04-13 13:26:45 -04:00
|
|
|
// Set up a stack frame manually. Eris deserialization
|
|
|
|
|
// should have left permstable and regcopy on the stack.
|
|
|
|
|
assert(lua_gettop(state_) == 2);
|
|
|
|
|
LuaSpecial permstable(1);
|
|
|
|
|
LuaSpecial regcopy(2);
|
|
|
|
|
lua_pushnil(state_);
|
|
|
|
|
lua_pushnil(state_);
|
|
|
|
|
LuaSpecial key(3);
|
|
|
|
|
LuaSpecial value(4);
|
|
|
|
|
LuaCoreStack LS(state_);
|
2021-03-01 18:10:40 -05:00
|
|
|
assert(LS.istable(regcopy));
|
|
|
|
|
|
|
|
|
|
// Copy the contents of the snapshot over to the registry.
|
|
|
|
|
LS.set(key, LuaNil);
|
|
|
|
|
while (LS.next(regcopy, key, value) != 0) {
|
|
|
|
|
LS.rawset(LuaRegistry, key, value);
|
|
|
|
|
}
|
2023-04-13 13:26:45 -04:00
|
|
|
lua_settop(state_, 0);
|
2021-03-02 18:58:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Snapshot and rollback can trivially be implemented on top of serialize and
|
|
|
|
|
// deserialize. However, it's also possible to implement snapshot and rollback
|
|
|
|
|
// using an alternative technique:
|
|
|
|
|
//
|
|
|
|
|
// 1. When constructing the lua interpreter, use a custom memory allocator that
|
|
|
|
|
// keeps track of all the memory blocks used by lua.
|
|
|
|
|
//
|
|
|
|
|
// 2. Snapshot simply copies all the memory blocks used by lua into a buffer.
|
|
|
|
|
//
|
|
|
|
|
// 3. Rollback restores lua's memory blocks back to their previous state. This
|
|
|
|
|
// has the effect of restoring lua's state.
|
|
|
|
|
//
|
|
|
|
|
// A proof-of-concept implementation of the memory-snapshotting design was
|
|
|
|
|
// created, and it worked. It is probably faster than using serialize and
|
|
|
|
|
// deserialize.
|
|
|
|
|
//
|
|
|
|
|
// Note: even if we implement this alternative design, we still need to keep
|
|
|
|
|
// serialize and deserialize around in order to implement the save-game
|
|
|
|
|
// functionality. So for now, we're sticking with this design, which doesn't
|
|
|
|
|
// require us to maintain any additional code.
|
|
|
|
|
|
|
|
|
|
|