2021-02-09 17:15:54 -05:00
|
|
|
|
|
|
|
|
#include "luasnap.hpp"
|
2021-03-01 17:29:32 -05:00
|
|
|
#include "luastack.hpp"
|
2021-02-09 17:15:54 -05:00
|
|
|
#include <iostream>
|
|
|
|
|
#include <cassert>
|
2021-03-01 17:29:32 -05:00
|
|
|
#include <sstream>
|
2021-02-09 17:15:54 -05:00
|
|
|
|
|
|
|
|
|
2021-03-01 17:29:32 -05:00
|
|
|
LuaSnap::LuaSnap() {
|
|
|
|
|
state_ = luaL_newstate();
|
|
|
|
|
LuaStack LS(state_);
|
2021-03-01 18:10:40 -05:00
|
|
|
|
|
|
|
|
// Create the persist table and the unpersist table.
|
|
|
|
|
//
|
|
|
|
|
// These tables need to contain all C functions. Whenever
|
|
|
|
|
// the source module inserts a C function into the lua environment,
|
|
|
|
|
// it also needs to register the C function in these tables.
|
|
|
|
|
//
|
|
|
|
|
// I don't think anything else needs to go in these tables.
|
|
|
|
|
//
|
2021-03-01 17:29:32 -05:00
|
|
|
LS.rawset(LuaRegistry, "persist", LuaNewTable);
|
|
|
|
|
LS.rawset(LuaRegistry, "unpersist", LuaNewTable);
|
2021-02-09 17:15:54 -05:00
|
|
|
}
|
|
|
|
|
|
2021-03-01 17:29:32 -05:00
|
|
|
LuaSnap::~LuaSnap() {
|
|
|
|
|
std::cerr << "LuaSnap destructor not implemented yet" << std::endl;
|
2021-02-09 17:15:54 -05:00
|
|
|
}
|
|
|
|
|
|
2021-03-02 18:58:50 -05:00
|
|
|
void LuaSnap::serialize(Packer *pk) {
|
|
|
|
|
// Lua stack should be empty.
|
2021-03-01 17:29:32 -05:00
|
|
|
assert(lua_gettop(state_) == 0);
|
|
|
|
|
|
|
|
|
|
// lua variables that we'll need.
|
|
|
|
|
LuaVar key, value;
|
|
|
|
|
LuaRet permstable, regcopy;
|
|
|
|
|
LuaStack LS(state_, permstable, regcopy, key, value);
|
|
|
|
|
|
|
|
|
|
// 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-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
|
|
|
|
2021-03-01 17:29:32 -05:00
|
|
|
// When we call 'LS.result', this should leave the
|
|
|
|
|
// permstable and the regcopy on the stack.
|
|
|
|
|
LS.result();
|
|
|
|
|
assert(lua_gettop(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.
|
|
|
|
|
pk->write_int64(0);
|
|
|
|
|
size_t tell = pk->tellp();
|
|
|
|
|
eris_dump(state_, pk->lua_writer, pk->lua_writer_ud());
|
|
|
|
|
pk->overwrite_int64(tell, pk->tellp() - tell);
|
2021-03-01 18:10:40 -05:00
|
|
|
lua_settop(state_, 0);
|
2021-03-02 18:58:50 -05:00
|
|
|
std::cerr << "Eris dump is " << (pk->tellp() - tell) << " bytes." << std::endl;
|
2021-02-09 17:15:54 -05:00
|
|
|
}
|
|
|
|
|
|
2021-03-02 18:58:50 -05:00
|
|
|
void LuaSnap::deserialize(Unpacker *unpk) {
|
|
|
|
|
// Lua stack should be empty.
|
2021-03-01 18:10:40 -05:00
|
|
|
assert(lua_gettop(state_) == 0);
|
|
|
|
|
|
2021-03-02 18:58:50 -05:00
|
|
|
// Get a reader subsection containing the eris data.
|
|
|
|
|
size_t len = unpk->read_int64();
|
|
|
|
|
Unpacker subsec = unpk->read_section(len);
|
|
|
|
|
|
2021-03-01 18:10:40 -05:00
|
|
|
// Call eris with the permanents table and passing the snapshot as a lua_Reader.
|
|
|
|
|
lua_getfield(state_, LUA_REGISTRYINDEX, "unpersist");
|
2021-03-02 18:58:50 -05:00
|
|
|
eris_undump(state_, subsec.lua_reader, subsec.lua_reader_ud());
|
2021-03-01 18:10:40 -05:00
|
|
|
assert(lua_gettop(state_) == 2);
|
|
|
|
|
|
|
|
|
|
// Set up a stack frame.
|
|
|
|
|
LuaArg permstable, regcopy;
|
|
|
|
|
LuaVar key, value;
|
|
|
|
|
LuaStack LS(state_, permstable, regcopy, key, value);
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
LS.result();
|
|
|
|
|
assert(lua_gettop(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.
|
|
|
|
|
|
|
|
|
|
bool LuaSnap::have_snapshot() const {
|
|
|
|
|
return !snapshot_.empty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LuaSnap::snapshot() {
|
|
|
|
|
assert(snapshot_.empty());
|
|
|
|
|
std::ostringstream oss;
|
|
|
|
|
Packer pk(&oss);
|
|
|
|
|
serialize(&pk);
|
|
|
|
|
snapshot_ = oss.str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LuaSnap::rollback() {
|
|
|
|
|
assert(!snapshot_.empty());
|
|
|
|
|
Unpacker unpk(snapshot_.c_str(), snapshot_.size());
|
|
|
|
|
deserialize(&unpk);
|
2021-03-01 18:10:40 -05:00
|
|
|
snapshot_.clear();
|
2021-02-09 17:15:54 -05:00
|
|
|
}
|
2021-03-01 17:29:32 -05:00
|
|
|
|
2021-03-02 18:58:50 -05:00
|
|
|
|
|
|
|
|
|