Inverted control flow, engine as library

This commit is contained in:
2021-10-04 17:45:18 -04:00
parent e0fdb2d42f
commit bc22dc89af
15 changed files with 387 additions and 136 deletions

View File

@@ -4,6 +4,8 @@ CXX=g++ -std=c++17 -Wall -g -Iinc -Icpp
CPP_FILES=\ CPP_FILES=\
cpp/invocation.cpp\ cpp/invocation.cpp\
cpp/spookyv2.cpp\ cpp/spookyv2.cpp\
cpp/drivenengine.cpp\
cpp/driver.cpp\
cpp/util.cpp\ cpp/util.cpp\
cpp/luastack.cpp\ cpp/luastack.cpp\
cpp/traceback.cpp\ cpp/traceback.cpp\

View File

@@ -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_;
}

View File

@@ -73,6 +73,9 @@
// //
// * Repeat the following steps over and over: // * 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. // - List all existing channels using drv_list_channels.
// //
// - If there are any new channels in the channel list, use // - 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 // drv_peek_outgoing, drv_sent_outgoing, and drv_recv_incoming in the
// same manner as you would for a socket. // same manner as you would for a socket.
// //
// - Use 'drv_invoke_engine_update' to invoke the engine's update // - Use 'drv_invoke_engine_update' to invoke the engine's update callback.
// callback.
// //
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
@@ -111,25 +113,19 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector>
#include <map>
#include "streambuffer.hpp" #include "streambuffer.hpp"
#include "util.hpp" #include "util.hpp"
class DrivenEngine;
class Channel { 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: public:
// Get the buffers associated with this channel. // Get the buffers associated with this channel.
// //
StreamBuffer *out(); StreamBuffer *out() { return sb_out_.get(); }
StreamBuffer *in(); StreamBuffer *in() { return sb_in_.get(); }
const StreamBuffer *out() const;
const StreamBuffer *in() const;
// If this is a socket connection, the receiver's port number. // 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. // 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 // You may delete any channel except for stdio. This closes
// be an error message. // 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 { class DrivenEngine {
public: 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. // 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 // This will be called to initialize the logic engine, shortly after the lua
// source is loaded. // source is loaded.
// //
virtual void init() { } virtual void init() {}
// The update function. You should override this in a subclass. This will // 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. // 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 // actually opening the connection and relaying data into the channel using
// drv_get_target, drv_peek_outgoing, drv_sent_outgoing, drv_recv_incoming. // 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 // Create a new channel from any pending incoming connection. If there is no
// incoming connection, returns nullptr. // incoming connection, returns nullptr.
@@ -234,7 +232,7 @@ public:
// periodically poll to see if the engine has called rescan_lua_source, // periodically poll to see if the engine has called rescan_lua_source,
// using drv_get_rescan // 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, // Rescan the lua source directory. The lua source directory is read once,
// automatically, at engine creation time. If you want to read it again, // automatically, at engine creation time. If you want to read it again,
@@ -245,6 +243,11 @@ public:
// //
void rescan_lua_source(); 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. // 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 // is supposed to be talking to. Non-socket channels and incoming channels
// have empty targets. // 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 // 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 // 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 // Set the lua source code. The driver is expected to read the lua source
// code and store it (using this function) once before invoking // 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 // 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. // that the engine wants the driver to rescan the lua source code.
@@ -347,19 +350,59 @@ public:
// //
bool drv_get_rescan_lua_source(); 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 // In replay mode, perform a single step of the logfile. Returns true
// if the logfile was not empty. // if the logfile was not empty.
// //
bool drv_step_logfile(); 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: 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_; int next_channel_id_;
std::map<int, Channel *> channels_; std::map<int, Channel *> channels_;
Channel *recent_channel_; Channel *recent_channel_;
std::vector<Channel*> accepted_channels_; std::vector<std::unique_ptr<Channel>> accepted_channels_;
bool rescan_lua_source_; 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 #endif // DRIVENENGINE_HPP

View 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);
}

View File

@@ -0,0 +1,8 @@
#ifndef DRIVER_HPP
#define DRIVER_HPP
#include "drivenengine.hpp"
void driver_drive(DrivenEngine *de);
#endif // DRIVER_HPP

View File

@@ -1,8 +1,9 @@
#include "textgame.hpp" #include "textgame.hpp"
#include "driver.hpp"
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
TextGame tg; TextGame tg;
tg.run(); driver_drive(&tg);
} }

View File

@@ -15,24 +15,6 @@
#include "source.hpp" #include "source.hpp"
#include "luasnap.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") { LuaDefine(source_makeclass, "f") {
LuaArg classname; LuaArg classname;
LuaRet classtab; LuaRet classtab;
@@ -155,17 +137,7 @@ static void source_updatefile(LuaStack &LS0, LuaSlot source, LuaSlot fn, LuaSlot
old_fingerprint = LS.ckstring(fingerprint); old_fingerprint = LS.ckstring(fingerprint);
} }
// std::cerr << "Probing " << cfn << std::endl; // 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(); LS.result();
} }
@@ -295,39 +267,31 @@ std::string SourceDB::get(const std::string &fn) {
return oss.str(); return oss.str();
} }
void SourceDB::update() { void SourceDB::update(const util::LuaSource &source) {
lua_State *L = lua_state_; lua_State *L = lua_state_;
LuaVar sourcedb, newdb, info, fn, seq; LuaVar sourcedb, info;
LuaStack LS(L, newdb, sourcedb, info, fn, seq); LuaStack LS(L, sourcedb, info);
// Get the (old) source database. // Get and clear the source database.
LS.rawget(sourcedb, LuaRegistry, "sourcedb"); LS.rawget(sourcedb, LuaRegistry, "sourcedb");
if (!LS.istable(sourcedb)) { if (!LS.istable(sourcedb)) {
LS.newtable(sourcedb); LS.newtable(sourcedb);
LS.rawset(LuaRegistry, "sourcedb", sourcedb);
} }
LS.cleartable(sourcedb);
// Read the list of filenames. for (int i = 0; i < int(source.size()); i++) {
util::StringVec filenames = read_control_lst("lua/control.lst"); const std::string &file = source[i].first;
if (filenames.empty()) { const std::string &code = source[i].second;
luaL_error(L, "cannot read source database control.lst"); 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(); LS.result();
} }

View File

@@ -132,12 +132,10 @@ public:
// Update // Update
// //
// Read all the lua source files from disk and store them in the // Update the database using the specified lua source code.
// source database. Also compiles these files using lua's "load" // Compiles these files using lua's "load" function.
// function. Efficient: if a source file is already in the database
// and hasn't been modified, it is not reloaded.
// //
void update(); void update(const util::LuaSource &source);
// Rebuild // Rebuild
// //

View File

@@ -111,6 +111,19 @@ void StreamBuffer::clear() {
lua_reader_size_ = 0; 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 // These routines return true if you can losslessly cast the
// specified value to the specified type. // specified value to the specified type.

View File

@@ -266,6 +266,11 @@ public:
// Frees up as much space as possible. // Frees up as much space as possible.
void clear(); 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. // Write block of bytes into the buffer.
// //
// Caution: this function doesn't write the length! // Caution: this function doesn't write the length!

View File

@@ -144,6 +144,7 @@ void TextGame::do_quit_command(const StringVec &cmd) {
return; return;
} }
actor_id_ = 0; actor_id_ = 0;
stop_driver();
} }
void TextGame::do_command(const StringVec &words) { 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_.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(); world_->run_unittests();
actor_id_ = world_->create_login_actor(); actor_id_ = world_->create_login_actor();
std::cerr << "Login actor ID: " << actor_id_ << std::endl; std::cerr << "Login actor ID: " << actor_id_ << std::endl;
console_.clear(); console_.clear();
}
void TextGame::update() {
check_redirects();
if (actor_id_ == 0) {
stop_driver();
return;
}
// Process lines from stdin.
while (true) { while (true) {
check_redirects(); std::string line = get_stdio_channel()->in()->readline();
if (actor_id_ == 0) break; if (line == "") break;
console_.add_stdin(); console_.add(line);
int action = console_.action(); int action = console_.action();
if (action == LuaConsole::DO_LUA) { if (action == LuaConsole::DO_LUA) {
do_lua(console_.lua_expression()); do_lua(console_.lua_expression());
@@ -190,5 +201,8 @@ void TextGame::run()
std::cerr << console_.syntax() << std::endl; 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);
} }

View File

@@ -4,9 +4,10 @@
#include "luaconsole.hpp" #include "luaconsole.hpp"
#include "world.hpp" #include "world.hpp"
#include "drivenengine.hpp"
#include <memory> #include <memory>
class TextGame { class TextGame : public DrivenEngine {
private: private:
using StringVec = LuaConsole::StringVec; using StringVec = LuaConsole::StringVec;
std::unique_ptr<World> world_; std::unique_ptr<World> world_;
@@ -27,7 +28,8 @@ private:
void check_redirects(); void check_redirects();
public: public:
void run(); virtual void init();
virtual void update();
}; };
#endif // TEXTGAME_HPP #endif // TEXTGAME_HPP

View File

@@ -8,6 +8,7 @@
#include <cmath> #include <cmath>
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include <cassert>
#ifndef WIN32 #ifndef WIN32
#include <unistd.h> #include <unistd.h>
@@ -206,36 +207,34 @@ double distance_squared(double x1, double y1, double x2, double y2) {
return dx*dx + dy*dy; 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::ifstream fs(fn);
std::stringstream buffer; std::stringstream buffer;
buffer << fs.rdbuf(); buffer << fs.rdbuf();
return buffer.str(); return buffer.str();
} }
StringVec get_file_lines(const std::string &path) { static StringVec read_control_lst(const std::string &path) {
StringVec result; StringVec lines = split(get_file_contents(path), '\n');
std::ifstream f; util::StringVec result;
f.open(path); for (int i = 0; i < int(lines.size()); i++) {
if (f) { std::string trimmed = trim(lines[i]);
std::string line; if ((trimmed.size() > 0) && (trimmed[0] != '#')) {
while (std::getline(f, line)) { result.push_back(trimmed);
result.push_back(line);
} }
f.close();
} }
return result; return result;
} }
std::string get_file_fingerprint(const std::string &fn) { LuaSource read_lua_source(const std::string &dir) {
struct stat result; StringVec files = read_control_lst(dir + "/control.lst");
if(stat(fn.c_str(), &result)==0) assert (!files.empty());
{ LuaSource result;
std::stringstream ss; for (const std::string &file : files) {
ss << result.st_mtime; std::string data = get_file_contents(dir + "/" + file);
return ss.str(); result.emplace_back(file, data);
} }
return ""; return result;
} }
std::string XYZ::debug_string() const { std::string XYZ::debug_string() const {

View File

@@ -3,6 +3,7 @@
#include <string> #include <string>
#include <set> #include <set>
#include <map>
#include <algorithm> #include <algorithm>
#include <sstream> #include <sstream>
#include <ostream> #include <ostream>
@@ -21,7 +22,8 @@ enum WorldType {
}; };
using StringVec = std::vector<std::string>; 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 HashValue = std::pair<uint64_t, uint64_t>;
using IdVector = std::vector<int64_t>; using IdVector = std::vector<int64_t>;
@@ -73,14 +75,8 @@ std::string trim(std::string s);
// Calculate distance between two points // Calculate distance between two points
double distance_squared(double x1, double y1, double x2, double y2); double distance_squared(double x1, double y1, double x2, double y2);
// Read a file as one big string. // Read the lua source code from the specified directory.
std::string get_file_contents(const std::string &path); LuaSource read_lua_source(const std::string &directory);
// 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);
// An XYZ coordinate, general purpose. // An XYZ coordinate, general purpose.
struct XYZ { struct XYZ {

View File

@@ -174,7 +174,7 @@ public:
// Update the source database from disk. // 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. // Run all unit tests.
// //