Inverted control flow, engine as library
This commit is contained in:
@@ -1 +1,175 @@
|
||||
#include "channel.hpp"
|
||||
#include "drivenengine.hpp"
|
||||
|
||||
|
||||
Channel::Channel(DrivenEngine *de, int chid, int port, const std::string &target) {
|
||||
driven_ = de;
|
||||
chid_ = chid;
|
||||
sb_in_.reset(new StreamBuffer);
|
||||
sb_out_.reset(new StreamBuffer);
|
||||
port_ = port;
|
||||
closed_ = false;
|
||||
target_ = target;
|
||||
assert(driven_->channels_[chid_] == nullptr);
|
||||
driven_->channels_[chid_] = this;
|
||||
}
|
||||
|
||||
Channel::~Channel() {
|
||||
assert(driven_->channels_[chid_] == this);
|
||||
driven_->channels_.erase(chid_);
|
||||
if (driven_->recent_channel_ == this) {
|
||||
driven_->recent_channel_ = driven_->stdio_channel_.get();
|
||||
}
|
||||
}
|
||||
|
||||
double DrivenEngine::get_clock() {
|
||||
return clock_;
|
||||
}
|
||||
|
||||
std::unique_ptr<Channel> DrivenEngine::new_outgoing_channel(const std::string &target) {
|
||||
return std::unique_ptr<Channel>(new Channel(this, next_channel_id_++, 0, target));
|
||||
}
|
||||
|
||||
std::unique_ptr<Channel> DrivenEngine::new_incoming_channel() {
|
||||
if (accepted_channels_.empty()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
std::unique_ptr<Channel> result = std::move(accepted_channels_.back());
|
||||
accepted_channels_.pop_back();
|
||||
return std::move(result);
|
||||
}
|
||||
}
|
||||
|
||||
Channel *DrivenEngine::get_stdio_channel() {
|
||||
return stdio_channel_.get();
|
||||
}
|
||||
|
||||
std::unique_ptr<util::LuaSource> DrivenEngine::get_lua_source() {
|
||||
return std::move(lua_source_);
|
||||
}
|
||||
|
||||
void DrivenEngine::rescan_lua_source() {
|
||||
rescan_lua_source_ = true;
|
||||
}
|
||||
|
||||
void DrivenEngine::stop_driver() {
|
||||
stop_driver_ = true;
|
||||
}
|
||||
|
||||
void DrivenEngine::drv_logmode_write(const std::string &filename, int64_t maxsize) {
|
||||
// NOT IMPLEMENTED YET, but it's okay as a stub.
|
||||
}
|
||||
|
||||
void DrivenEngine::drv_logmode_replay(const std::string &filename) {
|
||||
// NOT IMPLEMENTED YET.
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void DrivenEngine::drv_logmode_none() {
|
||||
// NOT IMPLEMENTED YET, but it's okay as a stub.
|
||||
}
|
||||
|
||||
void DrivenEngine::drv_invoke_engine_init() {
|
||||
init();
|
||||
}
|
||||
|
||||
void DrivenEngine::drv_invoke_engine_update() {
|
||||
update();
|
||||
}
|
||||
|
||||
Channel *DrivenEngine::get_chid(int chid) {
|
||||
// We cache the most recently used channel.
|
||||
if (recent_channel_->chid_ != chid) {
|
||||
auto iter = channels_.find(chid);
|
||||
assert(iter != channels_.end());
|
||||
recent_channel_ = iter->second;
|
||||
}
|
||||
return recent_channel_;
|
||||
}
|
||||
|
||||
void DrivenEngine::drv_list_channels(std::vector<int> &channels) {
|
||||
channels.clear();
|
||||
for (const auto &p : channels_) {
|
||||
if (!p.second->closed_) {
|
||||
channels.push_back(p.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::string &DrivenEngine::drv_get_target(int chid) {
|
||||
return get_chid(chid)->target_;
|
||||
}
|
||||
|
||||
void DrivenEngine::drv_peek_outgoing(int chid, int *nbytes, const char **bytes) {
|
||||
Channel *ch = get_chid(chid);
|
||||
*nbytes = ch->sb_out_->fill();
|
||||
*bytes = ch->sb_out_->data();
|
||||
}
|
||||
|
||||
void DrivenEngine::drv_sent_outgoing(int chid, int nbytes) {
|
||||
get_chid(chid)->sb_out_->read_bytes(nbytes);
|
||||
}
|
||||
|
||||
void DrivenEngine::drv_recv_incoming(int chid, int nbytes, char *bytes) {
|
||||
get_chid(chid)->sb_in_->write_bytes(bytes, nbytes);
|
||||
}
|
||||
|
||||
void DrivenEngine::drv_notify_close(int chid) {
|
||||
get_chid(chid)->closed_ = true;
|
||||
}
|
||||
|
||||
int DrivenEngine::drv_notify_accept(int port) {
|
||||
int chid = next_channel_id_++;
|
||||
accepted_channels_.emplace_back(new Channel(this, chid, port, ""));
|
||||
return chid;
|
||||
}
|
||||
|
||||
void DrivenEngine::drv_set_clock(double t) {
|
||||
clock_ = t;
|
||||
}
|
||||
|
||||
void DrivenEngine::drv_set_lua_source(const util::LuaSource &source) {
|
||||
lua_source_.reset(new util::LuaSource(source));
|
||||
rescan_lua_source_ = false;
|
||||
}
|
||||
|
||||
bool DrivenEngine::drv_get_rescan_lua_source() {
|
||||
return rescan_lua_source_;
|
||||
}
|
||||
|
||||
bool DrivenEngine::drv_get_stop_driver() {
|
||||
return stop_driver_;
|
||||
}
|
||||
|
||||
bool DrivenEngine::drv_step_logfile() {
|
||||
// NOT IMPLEMENTED
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
DrivenEngine::DrivenEngine() {
|
||||
next_channel_id_ = 1;
|
||||
stdio_channel_.reset(new Channel(this, 0, 0, ""));
|
||||
recent_channel_ = stdio_channel_.get();
|
||||
rescan_lua_source_ = true;
|
||||
clock_ = 0.0;
|
||||
stop_driver_ = false;
|
||||
}
|
||||
|
||||
DrivenEngine::~DrivenEngine() {
|
||||
// Delete the channels that we own.
|
||||
stdio_channel_.reset();
|
||||
accepted_channels_.clear();
|
||||
// At this point, all channels should be gone.
|
||||
assert(channels_.empty());
|
||||
}
|
||||
|
||||
static DrivenEngine *engine_;
|
||||
|
||||
void DrivenEngine::set(DrivenEngine *de) {
|
||||
engine_ = de;
|
||||
}
|
||||
|
||||
DrivenEngine *DrivenEngine::get() {
|
||||
return engine_;
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,9 @@
|
||||
//
|
||||
// * Repeat the following steps over and over:
|
||||
//
|
||||
// - If the engine asked that the lua source be refreshed, read the source
|
||||
// from disk and call 'drv_set_lua_source'.
|
||||
//
|
||||
// - List all existing channels using drv_list_channels.
|
||||
//
|
||||
// - If there are any new channels in the channel list, use
|
||||
@@ -101,8 +104,7 @@
|
||||
// drv_peek_outgoing, drv_sent_outgoing, and drv_recv_incoming in the
|
||||
// same manner as you would for a socket.
|
||||
//
|
||||
// - Use 'drv_invoke_engine_update' to invoke the engine's update
|
||||
// callback.
|
||||
// - Use 'drv_invoke_engine_update' to invoke the engine's update callback.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -111,25 +113,19 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include "streambuffer.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
class DrivenEngine;
|
||||
|
||||
class Channel {
|
||||
private:
|
||||
int chid_;
|
||||
std::unique_ptr<StreamBuffer> sb_in_;
|
||||
std::unique_ptr<StreamBuffer> sb_out_;
|
||||
int port_;
|
||||
bool remote_closed_;
|
||||
std::string target_;
|
||||
|
||||
public:
|
||||
// Get the buffers associated with this channel.
|
||||
//
|
||||
StreamBuffer *out();
|
||||
StreamBuffer *in();
|
||||
const StreamBuffer *out() const;
|
||||
const StreamBuffer *in() const;
|
||||
StreamBuffer *out() { return sb_out_.get(); }
|
||||
StreamBuffer *in() { return sb_in_.get(); }
|
||||
|
||||
// If this is a socket connection, the receiver's port number.
|
||||
//
|
||||
@@ -140,30 +136,32 @@ public:
|
||||
|
||||
// True if the remote closed the connection, or a failure occurred.
|
||||
//
|
||||
bool remote_closed() const { return remote_closed_; }
|
||||
bool closed() const { return closed_; }
|
||||
|
||||
// If communications were closed by the remote, there may
|
||||
// be an error message.
|
||||
// You may delete any channel except for stdio. This closes
|
||||
// the channel.
|
||||
//
|
||||
std::string errmsg() const { return errmsg_; }
|
||||
~Channel();
|
||||
|
||||
private:
|
||||
// Constructor is deliberately private. Use
|
||||
// DrivenEngine::new_outgoing_channel to create outgoing socket channels.
|
||||
//
|
||||
Channel(DrivenEngine *de, int chid, int port, const std::string &target);
|
||||
|
||||
private:
|
||||
DrivenEngine *driven_;
|
||||
int chid_;
|
||||
std::unique_ptr<StreamBuffer> sb_in_;
|
||||
std::unique_ptr<StreamBuffer> sb_out_;
|
||||
int port_;
|
||||
bool closed_;
|
||||
std::string target_;
|
||||
friend class DrivenEngine;
|
||||
};
|
||||
|
||||
class DrivenEngine {
|
||||
public:
|
||||
// Constructor.
|
||||
//
|
||||
// Most initialization is achieved by 'drv_xxx' functions, so
|
||||
// this constructor takes no arguments.
|
||||
//
|
||||
DrivenEngine();
|
||||
|
||||
// Destructor.
|
||||
//
|
||||
// It is necessary to delete all channels before deleting the
|
||||
// DrivenEngine. The destructor will verify that this has been done.
|
||||
//
|
||||
~DrivenEngine();
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
//
|
||||
// The following methods are the 'engine' side of the pipe.
|
||||
@@ -174,7 +172,7 @@ public:
|
||||
// This will be called to initialize the logic engine, shortly after the lua
|
||||
// source is loaded.
|
||||
//
|
||||
virtual void init() { }
|
||||
virtual void init() {}
|
||||
|
||||
// The update function. You should override this in a subclass. This will
|
||||
// be called to give the engine a chance to respond to new data.
|
||||
@@ -195,7 +193,7 @@ public:
|
||||
// actually opening the connection and relaying data into the channel using
|
||||
// drv_get_target, drv_peek_outgoing, drv_sent_outgoing, drv_recv_incoming.
|
||||
//
|
||||
std::unique_ptr<Channel> new_outgoing_channel(const std::string &target)
|
||||
std::unique_ptr<Channel> new_outgoing_channel(const std::string &target);
|
||||
|
||||
// Create a new channel from any pending incoming connection. If there is no
|
||||
// incoming connection, returns nullptr.
|
||||
@@ -234,7 +232,7 @@ public:
|
||||
// periodically poll to see if the engine has called rescan_lua_source,
|
||||
// using drv_get_rescan
|
||||
//
|
||||
std::unique_ptr<util::StringMap> get_lua_source();
|
||||
std::unique_ptr<util::LuaSource> get_lua_source();
|
||||
|
||||
// Rescan the lua source directory. The lua source directory is read once,
|
||||
// automatically, at engine creation time. If you want to read it again,
|
||||
@@ -245,6 +243,11 @@ public:
|
||||
//
|
||||
void rescan_lua_source();
|
||||
|
||||
// Stop the driver. The engine should call this when it's done
|
||||
// and there's nothing left to do.
|
||||
//
|
||||
void stop_driver();
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
//
|
||||
// The following methods are the 'driver' side of the pipe.
|
||||
@@ -293,7 +296,7 @@ public:
|
||||
// is supposed to be talking to. Non-socket channels and incoming channels
|
||||
// have empty targets.
|
||||
//
|
||||
std::string drv_get_target(int chid);
|
||||
const std::string &drv_get_target(int chid);
|
||||
|
||||
// Get a pointer to the bytes in the outgoing buffer. The pointer returned
|
||||
// here is naturally only valid until the buffer is changed. This function
|
||||
@@ -338,7 +341,7 @@ public:
|
||||
// Set the lua source code. The driver is expected to read the lua source
|
||||
// code and store it (using this function) once before invoking
|
||||
//
|
||||
void drv_set_lua_source(const util::StringMap &source);
|
||||
void drv_set_lua_source(const util::LuaSource &source);
|
||||
|
||||
// Check the 'rescan_lua_source' flag. If this flag is set, it means
|
||||
// that the engine wants the driver to rescan the lua source code.
|
||||
@@ -347,19 +350,59 @@ public:
|
||||
//
|
||||
bool drv_get_rescan_lua_source();
|
||||
|
||||
// If true, the engine is done. Stop the driver.
|
||||
//
|
||||
bool drv_get_stop_driver();
|
||||
|
||||
// In replay mode, perform a single step of the logfile. Returns true
|
||||
// if the logfile was not empty.
|
||||
//
|
||||
bool drv_step_logfile();
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Creation and Destruction.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
// Constructor.
|
||||
//
|
||||
// Most initialization is achieved by 'drv_xxx' functions, so
|
||||
// this constructor takes no arguments.
|
||||
//
|
||||
DrivenEngine();
|
||||
|
||||
// Destructor.
|
||||
//
|
||||
// It is necessary to delete all channels before deleting the
|
||||
// DrivenEngine. The destructor will verify that this has been done.
|
||||
//
|
||||
virtual ~DrivenEngine();
|
||||
|
||||
// Set/Get Global Pointer.
|
||||
//
|
||||
// Normally, there is a single global "DrivenEngine" instance.
|
||||
// We provide a global pointer to store this instance. This is
|
||||
// a raw pointer, you must manually delete the DrivenEngine.
|
||||
//
|
||||
static void set(DrivenEngine *de);
|
||||
static DrivenEngine *get();
|
||||
|
||||
private:
|
||||
Channel *stdio_channel_;
|
||||
// Get a channel by channel ID.
|
||||
Channel *get_chid(int chid);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Channel> stdio_channel_;
|
||||
int next_channel_id_;
|
||||
std::map<int, Channel *> channels_;
|
||||
Channel *recent_channel_;
|
||||
std::vector<Channel*> accepted_channels_;
|
||||
std::vector<std::unique_ptr<Channel>> accepted_channels_;
|
||||
bool rescan_lua_source_;
|
||||
std::unique_ptr<util::StringMap> lua_source_;
|
||||
std::unique_ptr<util::LuaSource> lua_source_;
|
||||
double clock_;
|
||||
bool stop_driver_;
|
||||
friend class Channel;
|
||||
};
|
||||
|
||||
#endif // DRIVENENGINE_HPP
|
||||
|
||||
32
luprex/core/cpp/driver.cpp
Normal file
32
luprex/core/cpp/driver.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
#include "driver.hpp"
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
void driver_drive(DrivenEngine *de) {
|
||||
const int MAXINPUT = 1000;
|
||||
char buf[MAXINPUT];
|
||||
int nbytes; const char *bytes;
|
||||
DrivenEngine::set(de);
|
||||
de->drv_logmode_none();
|
||||
de->drv_set_lua_source(util::read_lua_source("lua"));
|
||||
de->drv_invoke_engine_init();
|
||||
while (!de->drv_get_stop_driver()) {
|
||||
if (de->drv_get_rescan_lua_source()) {
|
||||
de->drv_set_lua_source(util::read_lua_source("lua"));
|
||||
}
|
||||
de->drv_peek_outgoing(0, &nbytes, &bytes);
|
||||
if (nbytes > 0) {
|
||||
fwrite(bytes, 1, nbytes, stdout);
|
||||
}
|
||||
if (fgets(buf, MAXINPUT, stdin)) {
|
||||
de->drv_recv_incoming(0, strlen(buf), buf);
|
||||
}
|
||||
de->drv_invoke_engine_update();
|
||||
}
|
||||
DrivenEngine::set(nullptr);
|
||||
}
|
||||
|
||||
8
luprex/core/cpp/driver.hpp
Normal file
8
luprex/core/cpp/driver.hpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef DRIVER_HPP
|
||||
#define DRIVER_HPP
|
||||
|
||||
#include "drivenengine.hpp"
|
||||
|
||||
void driver_drive(DrivenEngine *de);
|
||||
|
||||
#endif // DRIVER_HPP
|
||||
@@ -1,8 +1,9 @@
|
||||
|
||||
#include "textgame.hpp"
|
||||
#include "driver.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
TextGame tg;
|
||||
tg.run();
|
||||
driver_drive(&tg);
|
||||
}
|
||||
|
||||
@@ -15,24 +15,6 @@
|
||||
#include "source.hpp"
|
||||
#include "luasnap.hpp"
|
||||
|
||||
// Read control.lst
|
||||
//
|
||||
// - trim all lines
|
||||
// - remove blank lines
|
||||
// - remove comment lines
|
||||
//
|
||||
util::StringVec read_control_lst(const std::string &path) {
|
||||
util::StringVec lines = util::get_file_lines(path);
|
||||
util::StringVec result;
|
||||
for (int i = 0; i < int(lines.size()); i++) {
|
||||
std::string trimmed = util::trim(lines[i]);
|
||||
if ((trimmed.size() > 0) && (trimmed[0] != '#')) {
|
||||
result.push_back(trimmed);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
LuaDefine(source_makeclass, "f") {
|
||||
LuaArg classname;
|
||||
LuaRet classtab;
|
||||
@@ -155,17 +137,7 @@ static void source_updatefile(LuaStack &LS0, LuaSlot source, LuaSlot fn, LuaSlot
|
||||
old_fingerprint = LS.ckstring(fingerprint);
|
||||
}
|
||||
// std::cerr << "Probing " << cfn << std::endl;
|
||||
std::string new_fingerprint = util::get_file_fingerprint("lua/" + cfn);
|
||||
if ((old_fingerprint == "") || (old_fingerprint != new_fingerprint)) {
|
||||
std::cerr << "Rereading " << cfn << std::endl;
|
||||
std::string code;
|
||||
if (new_fingerprint != "") code = util::get_file_contents("lua/" + cfn);
|
||||
LS.rawset(info, "name", fn);
|
||||
LS.rawset(info, "fingerprint", new_fingerprint);
|
||||
LS.rawset(info, "code", code);
|
||||
LS.rawset(info, "hash", util::hash_to_hex(util::hash_string(code)));
|
||||
calculate_loadresult(LS, info, cfn, code);
|
||||
}
|
||||
|
||||
LS.result();
|
||||
}
|
||||
|
||||
@@ -295,39 +267,31 @@ std::string SourceDB::get(const std::string &fn) {
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
void SourceDB::update() {
|
||||
void SourceDB::update(const util::LuaSource &source) {
|
||||
lua_State *L = lua_state_;
|
||||
LuaVar sourcedb, newdb, info, fn, seq;
|
||||
LuaStack LS(L, newdb, sourcedb, info, fn, seq);
|
||||
LuaVar sourcedb, info;
|
||||
LuaStack LS(L, sourcedb, info);
|
||||
|
||||
// Get the (old) source database.
|
||||
// Get and clear the source database.
|
||||
LS.rawget(sourcedb, LuaRegistry, "sourcedb");
|
||||
if (!LS.istable(sourcedb)) {
|
||||
LS.newtable(sourcedb);
|
||||
LS.rawset(LuaRegistry, "sourcedb", sourcedb);
|
||||
}
|
||||
LS.cleartable(sourcedb);
|
||||
|
||||
// Read the list of filenames.
|
||||
util::StringVec filenames = read_control_lst("lua/control.lst");
|
||||
if (filenames.empty()) {
|
||||
luaL_error(L, "cannot read source database control.lst");
|
||||
for (int i = 0; i < int(source.size()); i++) {
|
||||
const std::string &file = source[i].first;
|
||||
const std::string &code = source[i].second;
|
||||
std::cerr << "Compiling " << file << std::endl;
|
||||
LS.newtable(info);
|
||||
LS.rawset(info, "name", file);
|
||||
LS.rawset(info, "code", code);
|
||||
LS.rawset(info, "hash", util::hash_to_hex(util::hash_string(code)));
|
||||
LS.rawset(info, "sequence", i + 1);
|
||||
calculate_loadresult(LS, info, file, code);
|
||||
LS.rawset(sourcedb, file, info);
|
||||
}
|
||||
|
||||
// Process the files one by one.
|
||||
LS.newtable(newdb);
|
||||
for (int i = 0; i < int(filenames.size()); i++) {
|
||||
LS.set(fn, filenames[i]);
|
||||
|
||||
// Call source_updatefile to get the updated info for one file.
|
||||
source_updatefile(LS, sourcedb, fn, info);
|
||||
|
||||
// Insert the sequence number and put finalized info into the new database.
|
||||
LS.set(seq, i + 1);
|
||||
LS.rawset(info, "sequence", seq);
|
||||
LS.rawset(newdb, fn, info);
|
||||
}
|
||||
|
||||
// Store the new source db.
|
||||
LS.rawset(LuaRegistry, "sourcedb", newdb);
|
||||
LS.result();
|
||||
}
|
||||
|
||||
|
||||
@@ -132,12 +132,10 @@ public:
|
||||
|
||||
// Update
|
||||
//
|
||||
// Read all the lua source files from disk and store them in the
|
||||
// source database. Also compiles these files using lua's "load"
|
||||
// function. Efficient: if a source file is already in the database
|
||||
// and hasn't been modified, it is not reloaded.
|
||||
// Update the database using the specified lua source code.
|
||||
// Compiles these files using lua's "load" function.
|
||||
//
|
||||
void update();
|
||||
void update(const util::LuaSource &source);
|
||||
|
||||
// Rebuild
|
||||
//
|
||||
|
||||
@@ -111,6 +111,19 @@ void StreamBuffer::clear() {
|
||||
lua_reader_size_ = 0;
|
||||
}
|
||||
|
||||
std::string StreamBuffer::readline() {
|
||||
char *p = read_cursor_;
|
||||
while ((p < write_cursor_) && (*p != '\n')) p++;
|
||||
if (p == write_cursor_) {
|
||||
return "";
|
||||
} else {
|
||||
p++;
|
||||
std::string result(read_cursor_, p - read_cursor_);
|
||||
read_cursor_ = p;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// These routines return true if you can losslessly cast the
|
||||
// specified value to the specified type.
|
||||
|
||||
|
||||
@@ -266,6 +266,11 @@ public:
|
||||
// Frees up as much space as possible.
|
||||
void clear();
|
||||
|
||||
// Attempt to do a "readline". If there is no newline in
|
||||
// the buffer, returns empty string. If there is a newline,
|
||||
// returns a block of text that ends in newline.
|
||||
std::string readline();
|
||||
|
||||
// Write block of bytes into the buffer.
|
||||
//
|
||||
// Caution: this function doesn't write the length!
|
||||
|
||||
@@ -144,6 +144,7 @@ void TextGame::do_quit_command(const StringVec &cmd) {
|
||||
return;
|
||||
}
|
||||
actor_id_ = 0;
|
||||
stop_driver();
|
||||
}
|
||||
|
||||
void TextGame::do_command(const StringVec &words) {
|
||||
@@ -169,18 +170,28 @@ void TextGame::check_redirects() {
|
||||
}
|
||||
}
|
||||
|
||||
void TextGame::run()
|
||||
void TextGame::init()
|
||||
{
|
||||
world_.reset(new World(util::WORLD_TYPE_STANDALONE));
|
||||
world_->update_source();
|
||||
std::unique_ptr<util::LuaSource> lsource = get_lua_source();
|
||||
world_->update_source(*lsource);
|
||||
world_->run_unittests();
|
||||
actor_id_ = world_->create_login_actor();
|
||||
std::cerr << "Login actor ID: " << actor_id_ << std::endl;
|
||||
console_.clear();
|
||||
}
|
||||
|
||||
void TextGame::update() {
|
||||
check_redirects();
|
||||
if (actor_id_ == 0) {
|
||||
stop_driver();
|
||||
return;
|
||||
}
|
||||
// Process lines from stdin.
|
||||
while (true) {
|
||||
check_redirects();
|
||||
if (actor_id_ == 0) break;
|
||||
console_.add_stdin();
|
||||
std::string line = get_stdio_channel()->in()->readline();
|
||||
if (line == "") break;
|
||||
console_.add(line);
|
||||
int action = console_.action();
|
||||
if (action == LuaConsole::DO_LUA) {
|
||||
do_lua(console_.lua_expression());
|
||||
@@ -190,5 +201,8 @@ void TextGame::run()
|
||||
std::cerr << console_.syntax() << std::endl;
|
||||
}
|
||||
}
|
||||
// Process lua source if available.
|
||||
std::unique_ptr<util::LuaSource> source = get_lua_source();
|
||||
if (source != nullptr) world_->update_source(*source);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
|
||||
#include "luaconsole.hpp"
|
||||
#include "world.hpp"
|
||||
#include "drivenengine.hpp"
|
||||
#include <memory>
|
||||
|
||||
class TextGame {
|
||||
class TextGame : public DrivenEngine {
|
||||
private:
|
||||
using StringVec = LuaConsole::StringVec;
|
||||
std::unique_ptr<World> world_;
|
||||
@@ -27,7 +28,8 @@ private:
|
||||
|
||||
void check_redirects();
|
||||
public:
|
||||
void run();
|
||||
virtual void init();
|
||||
virtual void update();
|
||||
};
|
||||
|
||||
#endif // TEXTGAME_HPP
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <cassert>
|
||||
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
@@ -206,36 +207,34 @@ double distance_squared(double x1, double y1, double x2, double y2) {
|
||||
return dx*dx + dy*dy;
|
||||
}
|
||||
|
||||
std::string get_file_contents(const std::string &fn) {
|
||||
static std::string get_file_contents(const std::string &fn) {
|
||||
std::ifstream fs(fn);
|
||||
std::stringstream buffer;
|
||||
buffer << fs.rdbuf();
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
StringVec get_file_lines(const std::string &path) {
|
||||
StringVec result;
|
||||
std::ifstream f;
|
||||
f.open(path);
|
||||
if (f) {
|
||||
std::string line;
|
||||
while (std::getline(f, line)) {
|
||||
result.push_back(line);
|
||||
static StringVec read_control_lst(const std::string &path) {
|
||||
StringVec lines = split(get_file_contents(path), '\n');
|
||||
util::StringVec result;
|
||||
for (int i = 0; i < int(lines.size()); i++) {
|
||||
std::string trimmed = trim(lines[i]);
|
||||
if ((trimmed.size() > 0) && (trimmed[0] != '#')) {
|
||||
result.push_back(trimmed);
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string get_file_fingerprint(const std::string &fn) {
|
||||
struct stat result;
|
||||
if(stat(fn.c_str(), &result)==0)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << result.st_mtime;
|
||||
return ss.str();
|
||||
LuaSource read_lua_source(const std::string &dir) {
|
||||
StringVec files = read_control_lst(dir + "/control.lst");
|
||||
assert (!files.empty());
|
||||
LuaSource result;
|
||||
for (const std::string &file : files) {
|
||||
std::string data = get_file_contents(dir + "/" + file);
|
||||
result.emplace_back(file, data);
|
||||
}
|
||||
return "";
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string XYZ::debug_string() const {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <ostream>
|
||||
@@ -21,7 +22,8 @@ enum WorldType {
|
||||
};
|
||||
|
||||
using StringVec = std::vector<std::string>;
|
||||
using StringMap = std::map<std::string, std::string>;
|
||||
using StringPair = std::pair<std::string, std::string>;
|
||||
using LuaSource = std::vector<StringPair>;
|
||||
using HashValue = std::pair<uint64_t, uint64_t>;
|
||||
using IdVector = std::vector<int64_t>;
|
||||
|
||||
@@ -73,14 +75,8 @@ std::string trim(std::string s);
|
||||
// Calculate distance between two points
|
||||
double distance_squared(double x1, double y1, double x2, double y2);
|
||||
|
||||
// Read a file as one big string.
|
||||
std::string get_file_contents(const std::string &path);
|
||||
|
||||
// Read a file as a vector of lines.
|
||||
StringVec get_file_lines(const std::string &path);
|
||||
|
||||
// Get a file's fingerprint - ie, size and modification time.
|
||||
std::string get_file_fingerprint(const std::string &path);
|
||||
// Read the lua source code from the specified directory.
|
||||
LuaSource read_lua_source(const std::string &directory);
|
||||
|
||||
// An XYZ coordinate, general purpose.
|
||||
struct XYZ {
|
||||
|
||||
@@ -174,7 +174,7 @@ public:
|
||||
|
||||
// Update the source database from disk.
|
||||
//
|
||||
void update_source() { source_db_.update(); source_db_.rebuild(true); }
|
||||
void update_source(const util::LuaSource &source) { source_db_.update(source); source_db_.rebuild(true); }
|
||||
|
||||
// Run all unit tests.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user