Added luasnap checkpointing mechanism
This commit is contained in:
@@ -11,6 +11,7 @@ CPP_FILES=\
|
|||||||
cpp/globaldb.cpp\
|
cpp/globaldb.cpp\
|
||||||
cpp/table.cpp\
|
cpp/table.cpp\
|
||||||
cpp/gui.cpp\
|
cpp/gui.cpp\
|
||||||
|
cpp/luasnap.cpp\
|
||||||
cpp/animqueue.cpp\
|
cpp/animqueue.cpp\
|
||||||
cpp/source.cpp\
|
cpp/source.cpp\
|
||||||
cpp/world.cpp\
|
cpp/world.cpp\
|
||||||
|
|||||||
195
luprex/cpp/luasnap.cpp
Normal file
195
luprex/cpp/luasnap.cpp
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
|
||||||
|
#include "luasnap.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
void *lj_alloc_create();
|
||||||
|
void *lj_alloc_f(void *, void *, size_t, size_t);
|
||||||
|
lua_State *lj_state_newstate(lua_Alloc f, void *ud);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *lsnap_create() {
|
||||||
|
return lj_alloc_create();
|
||||||
|
}
|
||||||
|
static lua_State *lsnap_newstate(lua_Alloc f, void *ud) {
|
||||||
|
return lj_state_newstate(f, ud);
|
||||||
|
}
|
||||||
|
static void *lsnap_malloc(void *mptr, size_t nsize) {
|
||||||
|
return lj_alloc_f(mptr, 0, 0, nsize);
|
||||||
|
}
|
||||||
|
static void *lsnap_realloc(void *mptr, void *ptr, size_t osize, size_t nsize) {
|
||||||
|
return lj_alloc_f(mptr, ptr, osize, nsize);
|
||||||
|
}
|
||||||
|
static void *lsnap_free(void *mptr, size_t osize, void *ptr) {
|
||||||
|
return lj_alloc_f(mptr, ptr, osize, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BlockHeader {
|
||||||
|
BlockHeader *prev_;
|
||||||
|
BlockHeader *next_;
|
||||||
|
int32_t sanity_; // 0x12345678 only when linked.
|
||||||
|
size_t size_;
|
||||||
|
bool in_use_;
|
||||||
|
void *snapshot_;
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t blocksize(size_t base) {
|
||||||
|
return base + sizeof(BlockHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockHeader *pointer_to_header(void *ptr) {
|
||||||
|
return (BlockHeader*)(((char *)ptr) - sizeof(BlockHeader));
|
||||||
|
}
|
||||||
|
|
||||||
|
void *header_to_pointer(BlockHeader *blk) {
|
||||||
|
return (void *)(((char *)blk) + sizeof(BlockHeader));
|
||||||
|
}
|
||||||
|
|
||||||
|
class LuaSnapData {
|
||||||
|
public:
|
||||||
|
lua_State *state_;
|
||||||
|
bool have_snapshot_;
|
||||||
|
void *lj_mstate_;
|
||||||
|
BlockHeader sentinel_;
|
||||||
|
|
||||||
|
LuaSnapData();
|
||||||
|
~LuaSnapData();
|
||||||
|
lua_State *state() const { return state_; }
|
||||||
|
bool have_snapshot() const { return have_snapshot_; }
|
||||||
|
void snapshot();
|
||||||
|
void rollback();
|
||||||
|
void link(BlockHeader *blk);
|
||||||
|
void unlink(BlockHeader *blk);
|
||||||
|
void *allocf(void *ptr, size_t osize, size_t nsize);
|
||||||
|
};
|
||||||
|
|
||||||
|
void *lsd_alloc_f(void *lsd, void *ptr, size_t osize, size_t nsize) {
|
||||||
|
return ((LuaSnapData*)lsd)->allocf(ptr, osize, nsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
LuaSnapData::LuaSnapData() {
|
||||||
|
sentinel_.prev_ = &sentinel_;
|
||||||
|
sentinel_.next_ = &sentinel_;
|
||||||
|
sentinel_.size_ = 0;
|
||||||
|
sentinel_.in_use_ = false;
|
||||||
|
sentinel_.snapshot_ = 0;
|
||||||
|
lj_mstate_ = lsnap_create();
|
||||||
|
have_snapshot_ = false;
|
||||||
|
state_ = lsnap_newstate(lsd_alloc_f, (void*)this);
|
||||||
|
}
|
||||||
|
|
||||||
|
LuaSnapData::~LuaSnapData() {
|
||||||
|
std::cerr << "LuaSnapData destructor not implemented." << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaSnapData::snapshot() {
|
||||||
|
assert(!have_snapshot_);
|
||||||
|
for (BlockHeader *blk = sentinel_.next_; blk != &sentinel_; blk = blk->next_) {
|
||||||
|
assert(blk->in_use_);
|
||||||
|
blk->snapshot_ = malloc(blk->size_);
|
||||||
|
memcpy(blk->snapshot_, header_to_pointer(blk), blk->size_);
|
||||||
|
}
|
||||||
|
have_snapshot_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaSnapData::rollback() {
|
||||||
|
assert(have_snapshot_);
|
||||||
|
for (BlockHeader *blk = sentinel_.next_; blk != &sentinel_; ) {
|
||||||
|
BlockHeader *next = blk->next_;
|
||||||
|
if (blk->snapshot_) {
|
||||||
|
memcpy(header_to_pointer(blk), blk->snapshot_, blk->size_);
|
||||||
|
free(blk->snapshot_);
|
||||||
|
blk->snapshot_ = nullptr;
|
||||||
|
blk->in_use_ = true;
|
||||||
|
} else {
|
||||||
|
assert(blk->in_use_);
|
||||||
|
unlink(blk);
|
||||||
|
lsnap_free(lj_mstate_, blocksize(blk->size_), blk);
|
||||||
|
}
|
||||||
|
blk = next;
|
||||||
|
}
|
||||||
|
have_snapshot_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaSnapData::link(BlockHeader *blk) {
|
||||||
|
blk->prev_ = sentinel_.prev_;
|
||||||
|
blk->next_ = &sentinel_;
|
||||||
|
blk->prev_->next_ = blk;
|
||||||
|
blk->next_->prev_ = blk;
|
||||||
|
blk->sanity_ = 0x12345678;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaSnapData::unlink(BlockHeader *blk) {
|
||||||
|
blk->prev_->next_ = blk->next_;
|
||||||
|
blk->next_->prev_ = blk->prev_;
|
||||||
|
blk->prev_ = nullptr;
|
||||||
|
blk->next_ = nullptr;
|
||||||
|
blk->sanity_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *LuaSnapData::allocf(void *ptr, size_t osize, size_t nsize) {
|
||||||
|
if (nsize == 0) {
|
||||||
|
if (ptr == 0) return nullptr;
|
||||||
|
BlockHeader *blk = pointer_to_header(ptr);
|
||||||
|
assert(blk->sanity_ = 0x12345678);
|
||||||
|
assert(blk->in_use_);
|
||||||
|
if (blk->snapshot_ == nullptr) {
|
||||||
|
unlink(blk);
|
||||||
|
lsnap_free(lj_mstate_, blocksize(blk->size_), blk);
|
||||||
|
} else {
|
||||||
|
blk->in_use_ = false;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
} else if (ptr == NULL) {
|
||||||
|
BlockHeader *blk = (BlockHeader*)lsnap_malloc(lj_mstate_, blocksize(nsize));
|
||||||
|
link(blk);
|
||||||
|
blk->size_ = nsize;
|
||||||
|
blk->in_use_ = true;
|
||||||
|
blk->snapshot_ = nullptr;
|
||||||
|
return header_to_pointer(blk);
|
||||||
|
} else {
|
||||||
|
BlockHeader *blk = pointer_to_header(ptr);
|
||||||
|
assert(blk->sanity_ = 0x12345678);
|
||||||
|
assert(blk->in_use_);
|
||||||
|
if (blk->snapshot_ == nullptr) {
|
||||||
|
unlink(blk);
|
||||||
|
blk = (BlockHeader*)lsnap_realloc(lj_mstate_, blk, blocksize(blk->size_), blocksize(nsize));
|
||||||
|
blk->size_ = nsize;
|
||||||
|
link(blk);
|
||||||
|
return header_to_pointer(blk);
|
||||||
|
} else {
|
||||||
|
BlockHeader *nblk = (BlockHeader*)lsnap_malloc(lj_mstate_, blocksize(nsize));
|
||||||
|
memcpy(nblk, blk, (blk->size_ < nsize) ? blk->size_ : nsize);
|
||||||
|
blk->in_use_ = false;
|
||||||
|
link(nblk);
|
||||||
|
nblk->size_ = nsize;
|
||||||
|
nblk->in_use_ = true;
|
||||||
|
nblk->snapshot_ = nullptr;
|
||||||
|
return header_to_pointer(nblk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LuaSnap::LuaSnap() {
|
||||||
|
data_ = new LuaSnapData;
|
||||||
|
state_ = data_->state();
|
||||||
|
}
|
||||||
|
|
||||||
|
LuaSnap::~LuaSnap() {
|
||||||
|
delete data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LuaSnap::have_snapshot() const {
|
||||||
|
return data_->have_snapshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaSnap::snapshot() {
|
||||||
|
data_->snapshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaSnap::rollback() {
|
||||||
|
data_->rollback();
|
||||||
|
}
|
||||||
60
luprex/cpp/luasnap.hpp
Normal file
60
luprex/cpp/luasnap.hpp
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// LUASNAP
|
||||||
|
//
|
||||||
|
// A lua interpreter that can be checkpointed (snapshotted).
|
||||||
|
// This makes it possible to roll the entire interpreter back
|
||||||
|
// to a previously-snapshotted state.
|
||||||
|
//
|
||||||
|
// To accomplish this, we take advantage of the 'allocf' parameter
|
||||||
|
// to lua_newstate. This lets us hook the lua allocator, which
|
||||||
|
// in turn lets us find every block of memory currently in use by
|
||||||
|
// lua. To snapshot, we 'memcpy' every block of memory
|
||||||
|
// that lua is using into a buffer. To rollback, we just 'memcpy'
|
||||||
|
// the data back.
|
||||||
|
//
|
||||||
|
// The current implementation is a proof-of-concept. It's quite
|
||||||
|
// wasteful of memory, roughly doubling the amount of RAM that
|
||||||
|
// LUA uses. But it does demonstrate that this method of snapshot
|
||||||
|
// and rollback is feasible.
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef LUASNAP_HPP
|
||||||
|
#define LUASNAP_HPP
|
||||||
|
|
||||||
|
#include "luastack.hpp"
|
||||||
|
|
||||||
|
class LuaSnapData;
|
||||||
|
class LuaSnap {
|
||||||
|
private:
|
||||||
|
lua_State *state_;
|
||||||
|
LuaSnapData *data_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
LuaSnap();
|
||||||
|
~LuaSnap();
|
||||||
|
|
||||||
|
// Get the lua intepreter.
|
||||||
|
//
|
||||||
|
lua_State *state() const { return state_; }
|
||||||
|
|
||||||
|
// Return true if there's a saved snapshot.
|
||||||
|
//
|
||||||
|
bool have_snapshot() const;
|
||||||
|
|
||||||
|
// snapshot the state of the lua interpreter.
|
||||||
|
//
|
||||||
|
// If there is already a snapshot, this panics.
|
||||||
|
//
|
||||||
|
void snapshot();
|
||||||
|
|
||||||
|
// Rollback the lua intepreter to the snapshotted state.
|
||||||
|
//
|
||||||
|
// If there is no snapshot, this panics.
|
||||||
|
//
|
||||||
|
void rollback();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // LUASNAP_HPP
|
||||||
@@ -38,17 +38,20 @@ int LuaStack::collect_tagged_pointer(lua_State *L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LuaStack::register_all_userdata(lua_State *L) {
|
void LuaStack::register_all_userdata(lua_State *L) {
|
||||||
LuaVar tab, lud;
|
LuaVar tab, lud, classtab, classname;
|
||||||
LuaStack LS(L, tab, lud);
|
LuaStack LS(L, tab, lud, classtab, classname);
|
||||||
auto regs = LuaFunctionReg::all();
|
auto regs = LuaFunctionReg::all();
|
||||||
for (const LuaFunctionReg *r : regs) {
|
for (const LuaFunctionReg *r : regs) {
|
||||||
const std::string &name = util::tolower(r->get_name());
|
const std::string &name = util::tolower(r->get_name());
|
||||||
lua_CFunction tag = r->get_func();
|
lua_CFunction tag = r->get_func();
|
||||||
std::string mode = r->get_mode();
|
std::string mode = r->get_mode();
|
||||||
|
LS.set(classname, name);
|
||||||
if (mode.find('t') != std::string::npos) { // Register type
|
if (mode.find('t') != std::string::npos) { // Register type
|
||||||
LS.newtable(tab);
|
LS.newtable(tab);
|
||||||
LS.setfield(tab, "type", name);
|
LS.setfield(tab, "type", name);
|
||||||
LS.setfield(tab, "__gc", collect_tagged_pointer);
|
LS.setfield(tab, "__gc", collect_tagged_pointer);
|
||||||
|
LS.makeclass(classtab, classname);
|
||||||
|
LS.setfield(tab, "__index", classtab);
|
||||||
LS.setlightuserdata(lud, (void *)tag);
|
LS.setlightuserdata(lud, (void *)tag);
|
||||||
LS.rawset(LuaRegistry, lud, tab);
|
LS.rawset(LuaRegistry, lud, tab);
|
||||||
}
|
}
|
||||||
@@ -202,6 +205,37 @@ void LuaStack::newtable(LuaSlot target) const {
|
|||||||
lua_replace(L_, target);
|
lua_replace(L_, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LuaStack::makeclass(LuaSlot classtab, LuaSlot classname) const {
|
||||||
|
int top = lua_gettop(L_);
|
||||||
|
checkstring(classname);
|
||||||
|
|
||||||
|
// Special case: if the classname is _G, return global env.
|
||||||
|
lua_pushstring(L_, "_G");
|
||||||
|
int eqlg = lua_equal(L_, -1, classname.index());
|
||||||
|
lua_settop(L_, top);
|
||||||
|
if (eqlg) {
|
||||||
|
set(classtab, LuaGlobals);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the classtab from the global environment.
|
||||||
|
// Create it if it doesn't exist.
|
||||||
|
rawget(classtab, LuaGlobals, classname);
|
||||||
|
if (isnil(classtab)) {
|
||||||
|
newtable(classtab);
|
||||||
|
rawset(LuaGlobals, classname, classtab);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the name isn't bound to a table, abort.
|
||||||
|
if (!istable(classtab)) {
|
||||||
|
luaL_error(L_, "%s is not a class", ckstring(classname).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repair the special fields.
|
||||||
|
setfield(classtab, "__index", classtab);
|
||||||
|
setfield(classtab, "__class", classname);
|
||||||
|
}
|
||||||
|
|
||||||
void LuaStack::setlightuserdata(LuaSlot target, void *p) const {
|
void LuaStack::setlightuserdata(LuaSlot target, void *p) const {
|
||||||
lua_pushlightuserdata(L_, p);
|
lua_pushlightuserdata(L_, p);
|
||||||
lua_replace(L_, target);
|
lua_replace(L_, target);
|
||||||
|
|||||||
@@ -425,6 +425,9 @@ public:
|
|||||||
void checknometa(LuaSlot index) const;
|
void checknometa(LuaSlot index) const;
|
||||||
|
|
||||||
void newtable(LuaSlot target) const;
|
void newtable(LuaSlot target) const;
|
||||||
|
|
||||||
|
void makeclass(LuaSlot tab, LuaSlot name) const;
|
||||||
|
|
||||||
void setlightuserdata(LuaSlot target, void *p) const;
|
void setlightuserdata(LuaSlot target, void *p) const;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|||||||
@@ -34,42 +34,9 @@ util::stringvec read_control_lst(const std::string &path) {
|
|||||||
|
|
||||||
LuaDefine(source_makeclass, "f") {
|
LuaDefine(source_makeclass, "f") {
|
||||||
LuaArg classname;
|
LuaArg classname;
|
||||||
LuaVar action, gname;
|
|
||||||
LuaRet classtab;
|
LuaRet classtab;
|
||||||
LuaStack LS(L, classname, classtab, action, gname);
|
LuaStack LS(L, classname, classtab);
|
||||||
|
LS.makeclass(classtab, classname);
|
||||||
LS.checkstring(classname);
|
|
||||||
|
|
||||||
// Special case: if the classname is _G, return global env.
|
|
||||||
LS.set(gname, "_G");
|
|
||||||
if (LS.equal(classname, gname)) {
|
|
||||||
LS.set(classtab, LuaGlobals);
|
|
||||||
return LS.result();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the classtab from the global environment.
|
|
||||||
// Create it if it doesn't exist.
|
|
||||||
LS.rawget(classtab, LuaGlobals, classname);
|
|
||||||
if (LS.isnil(classtab)) {
|
|
||||||
LS.newtable(classtab);
|
|
||||||
LS.rawset(LuaGlobals, classname, classtab);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the name isn't bound to a table, abort.
|
|
||||||
if (!LS.istable(classtab)) {
|
|
||||||
luaL_error(L, "%s is not a class", LS.ckstring(classname).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repair the special fields.
|
|
||||||
LS.setfield(classtab, "__index", classtab);
|
|
||||||
LS.setfield(classtab, "__class", classname);
|
|
||||||
|
|
||||||
// Repair the action table.
|
|
||||||
LS.getfield(action, classtab, "action");
|
|
||||||
if (!LS.istable(action)) {
|
|
||||||
LS.setfield(classtab, "action", LuaNewTable);
|
|
||||||
}
|
|
||||||
|
|
||||||
return LS.result();
|
return LS.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,26 +167,17 @@ void SourceDB::update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete everything from the global environment except
|
// Delete everything from the global environment except
|
||||||
// the class tables and the class action tables.
|
// the class tables.
|
||||||
//
|
//
|
||||||
static void source_clear_globals(lua_State *L) {
|
static void source_clear_globals(lua_State *L) {
|
||||||
LuaVar classname, classtab, action, key;
|
LuaVar classname, classtab, key;
|
||||||
LuaStack LS(L, classname, classtab, action, key);
|
LuaStack LS(L, classname, classtab, key);
|
||||||
|
|
||||||
LS.setfield(LuaGlobals, "_G", LuaNil);
|
LS.setfield(LuaGlobals, "_G", LuaNil);
|
||||||
LS.set(classname, LuaNil);
|
LS.set(classname, LuaNil);
|
||||||
while (LS.next(LuaGlobals, classname, classtab) != 0) {
|
while (LS.next(LuaGlobals, classname, classtab) != 0) {
|
||||||
if (LS.istable(classtab)) {
|
if (LS.istable(classtab)) {
|
||||||
bool keep_action = false;
|
|
||||||
LS.getfield(action, classtab, "action");
|
|
||||||
if (LS.istable(action)) {
|
|
||||||
LS.call(table_clear, action);
|
|
||||||
keep_action = true;
|
|
||||||
}
|
|
||||||
LS.call(table_clear, classtab);
|
LS.call(table_clear, classtab);
|
||||||
if (keep_action) {
|
|
||||||
LS.setfield(classtab, "action", action);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
LS.rawset(LuaGlobals, classname, LuaNil);
|
LS.rawset(LuaGlobals, classname, LuaNil);
|
||||||
}
|
}
|
||||||
@@ -239,7 +197,7 @@ static void source_restore_builtins(lua_State *L) {
|
|||||||
LS.set(key, LuaNil);
|
LS.set(key, LuaNil);
|
||||||
while (LS.next(snapshot, key, value) != 0) {
|
while (LS.next(snapshot, key, value) != 0) {
|
||||||
LS.checktable(value);
|
LS.checktable(value);
|
||||||
LS.call(subglobal, source_makeclass, key);
|
LS.makeclass(subglobal, key);
|
||||||
LS.set(skey, LuaNil);
|
LS.set(skey, LuaNil);
|
||||||
while (LS.next(value, skey, svalue) != 0) {
|
while (LS.next(value, skey, svalue) != 0) {
|
||||||
LS.rawset(subglobal, skey, svalue);
|
LS.rawset(subglobal, skey, svalue);
|
||||||
|
|||||||
@@ -43,9 +43,8 @@ static void l_message(const char *msg)
|
|||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TextGame::do_lua(const std::string &exp) {
|
void TextGame::do_lua(const std::string &exp) {
|
||||||
lua_State *L = viewer_.get_lua_state();
|
lua_State *L = viewer_.state();
|
||||||
int status = luaL_loadbuffer(L, exp.c_str(), exp.size(), "=stdin");
|
int status = luaL_loadbuffer(L, exp.c_str(), exp.size(), "=stdin");
|
||||||
assert(status == LUA_OK);
|
assert(status == LUA_OK);
|
||||||
globalL = L;
|
globalL = L;
|
||||||
@@ -119,6 +118,22 @@ void TextGame::do_choose_command(const StringVec &cmd) {
|
|||||||
std::cerr << "Choose command (index " << index << ") not implemented yet." << std::endl;
|
std::cerr << "Choose command (index " << index << ") not implemented yet." << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextGame::do_snapshot_command(const StringVec &cmd) {
|
||||||
|
if (cmd.size() != 1) {
|
||||||
|
std::cerr << "s command (snapshot) takes no arguments" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
viewer_.snapshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextGame::do_rollback_command(const StringVec &cmd) {
|
||||||
|
if (cmd.size() != 1) {
|
||||||
|
std::cerr << "r command (rollback) takes no arguments" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
viewer_.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
void TextGame::do_quit_command(const StringVec &cmd) {
|
void TextGame::do_quit_command(const StringVec &cmd) {
|
||||||
if (cmd.size() != 1) {
|
if (cmd.size() != 1) {
|
||||||
std::cerr << "q command (quit) takes no arguments" << std::endl;
|
std::cerr << "q command (quit) takes no arguments" << std::endl;
|
||||||
@@ -133,6 +148,8 @@ void TextGame::do_command(const StringVec &words) {
|
|||||||
case 'm': do_menu_command(words); break;
|
case 'm': do_menu_command(words); break;
|
||||||
case 'c': do_choose_command(words); break;
|
case 'c': do_choose_command(words); break;
|
||||||
case 'q': do_quit_command(words); break;
|
case 'q': do_quit_command(words); break;
|
||||||
|
case 's': do_snapshot_command(words); break;
|
||||||
|
case 'r': do_rollback_command(words); break;
|
||||||
default:
|
default:
|
||||||
std::cerr << "Unknown command: " << words[0] << std::endl;
|
std::cerr << "Unknown command: " << words[0] << std::endl;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ private:
|
|||||||
void do_menu_command(const StringVec &cmd);
|
void do_menu_command(const StringVec &cmd);
|
||||||
void do_choose_command(const StringVec &cmd);
|
void do_choose_command(const StringVec &cmd);
|
||||||
void do_quit_command(const StringVec &cmd);
|
void do_quit_command(const StringVec &cmd);
|
||||||
|
void do_snapshot_command(const StringVec &cmd);
|
||||||
|
void do_rollback_command(const StringVec &cmd);
|
||||||
|
|
||||||
void do_lua(const std::string &exp);
|
void do_lua(const std::string &exp);
|
||||||
void do_command(const StringVec &exp);
|
void do_command(const StringVec &exp);
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -16,9 +16,13 @@ public:
|
|||||||
Viewer();
|
Viewer();
|
||||||
~Viewer();
|
~Viewer();
|
||||||
|
|
||||||
|
// Snapshot/rollback the lua state (temporary hack)
|
||||||
|
void snapshot() { world_->lua_snap_.snapshot(); }
|
||||||
|
void rollback() { world_->lua_snap_.rollback(); }
|
||||||
|
|
||||||
// Get the lua state for interaction.
|
// Get the lua state for interaction.
|
||||||
//
|
//
|
||||||
lua_State *get_lua_state() { return world_->get_lua_state(); }
|
lua_State *state() { return world_->state(); }
|
||||||
|
|
||||||
// Get the player ID of the current player.
|
// Get the player ID of the current player.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -15,22 +15,15 @@ World::~World() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
World::World() {
|
World::World() {
|
||||||
// Create the lua state.
|
|
||||||
lua_state_ = lua_open();
|
|
||||||
if (lua_state_ == nullptr) {
|
|
||||||
std::cerr << "Cannot create lua state." << std::endl;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the userdata metatables.
|
// Initialize the userdata metatables.
|
||||||
LuaStack::register_all_userdata(lua_state_);
|
LuaStack::register_all_userdata(state());
|
||||||
|
|
||||||
// Initialize the ID allocator in master mode.
|
// Initialize the ID allocator in master mode.
|
||||||
id_global_pool_.init_master(10);
|
id_global_pool_.init_master(10);
|
||||||
|
|
||||||
// Prepare to manipulate the lua state.
|
// Prepare to manipulate the lua state.
|
||||||
LuaVar world;
|
LuaVar world;
|
||||||
LuaStack LS(lua_state_, world);
|
LuaStack LS(state(), world);
|
||||||
|
|
||||||
// Put the world pointer into the lua registry.
|
// Put the world pointer into the lua registry.
|
||||||
LS.newpointer(world, this, false);
|
LS.newpointer(world, this, false);
|
||||||
@@ -40,11 +33,11 @@ World::World() {
|
|||||||
LS.setfield(LuaRegistry, "tangibles", LuaNewTable);
|
LS.setfield(LuaRegistry, "tangibles", LuaNewTable);
|
||||||
|
|
||||||
// Initialize the SourceDB
|
// Initialize the SourceDB
|
||||||
source_db_.initialize(lua_state_);
|
source_db_.initialize(state());
|
||||||
source_db_.rebuild();
|
source_db_.rebuild();
|
||||||
|
|
||||||
LS.result();
|
LS.result();
|
||||||
assert (lua_gettop(lua_state_) == 0);
|
assert (lua_gettop(state()) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tangible::be_a_player() {
|
void Tangible::be_a_player() {
|
||||||
@@ -53,6 +46,16 @@ void Tangible::be_a_player() {
|
|||||||
|
|
||||||
anim_queue_.add(world_->id_global_pool_.get_one(), "");
|
anim_queue_.add(world_->id_global_pool_.get_one(), "");
|
||||||
anim_queue_.set_graphic("player");
|
anim_queue_.set_graphic("player");
|
||||||
|
|
||||||
|
LuaVar classtab, mt, place, tangibles;
|
||||||
|
LuaStack LS(world_->state(), classtab, mt, place, tangibles);
|
||||||
|
|
||||||
|
LS.call(classtab, source_makeclass, "player");
|
||||||
|
LS.getfield(tangibles, LuaRegistry, "tangibles");
|
||||||
|
LS.rawget(place, tangibles, anim_queue_.get_id());
|
||||||
|
LS.getmetatable(mt, place);
|
||||||
|
LS.setfield(mt, "__index", classtab);
|
||||||
|
LS.result();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +68,7 @@ void World::init_standalone() {
|
|||||||
source_db_.run_unittests();
|
source_db_.run_unittests();
|
||||||
|
|
||||||
// Create the player tangible.
|
// Create the player tangible.
|
||||||
Tangible *player = tangible_make(lua_state_, 1, false);
|
Tangible *player = tangible_make(state(), 1, false);
|
||||||
player->be_a_player();
|
player->be_a_player();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +138,7 @@ World *World::fetch(lua_State *L) {
|
|||||||
|
|
||||||
void World::update_gui(int64_t actor_id, int64_t place_id, Gui *gui) {
|
void World::update_gui(int64_t actor_id, int64_t place_id, Gui *gui) {
|
||||||
gui->clear();
|
gui->clear();
|
||||||
lua_State *L = get_lua_state();
|
lua_State *L = state();
|
||||||
|
|
||||||
LuaVar actor, place, ugui, func, tangibles;
|
LuaVar actor, place, ugui, func, tangibles;
|
||||||
LuaStack LS(L, actor, place, ugui, func, tangibles);
|
LuaStack LS(L, actor, place, ugui, func, tangibles);
|
||||||
@@ -179,8 +182,6 @@ void World::update_gui(int64_t actor_id, int64_t place_id, Gui *gui) {
|
|||||||
LS.result();
|
LS.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
LuaDefine(tangible_get, "c") {
|
LuaDefine(tangible_get, "c") {
|
||||||
LuaArg id;
|
LuaArg id;
|
||||||
LuaRet database;
|
LuaRet database;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "animqueue.hpp"
|
#include "animqueue.hpp"
|
||||||
#include "source.hpp"
|
#include "source.hpp"
|
||||||
#include "gui.hpp"
|
#include "gui.hpp"
|
||||||
|
#include "luasnap.hpp"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
@@ -43,10 +44,10 @@ public:
|
|||||||
|
|
||||||
class World {
|
class World {
|
||||||
public:
|
public:
|
||||||
// A pointer to the lua State.
|
// A lua intepreter with snapshot function.
|
||||||
//
|
//
|
||||||
lua_State *lua_state_;
|
LuaSnap lua_snap_;
|
||||||
|
|
||||||
// The Global ID Pool.
|
// The Global ID Pool.
|
||||||
//
|
//
|
||||||
IdGlobalPool id_global_pool_;
|
IdGlobalPool id_global_pool_;
|
||||||
@@ -81,7 +82,7 @@ public:
|
|||||||
//
|
//
|
||||||
// Get the lua interpreter associated with this world model.
|
// Get the lua interpreter associated with this world model.
|
||||||
//
|
//
|
||||||
lua_State *get_lua_state() { return lua_state_; }
|
lua_State *state() { return lua_snap_.state(); }
|
||||||
|
|
||||||
// get_near
|
// get_near
|
||||||
//
|
//
|
||||||
|
|||||||
Reference in New Issue
Block a user