2022-02-23 23:08:28 -05:00
|
|
|
#include "wrap-string.hpp"
|
|
|
|
|
#include "wrap-vector.hpp"
|
2022-04-16 02:26:32 -04:00
|
|
|
#include "util.hpp"
|
|
|
|
|
#include "drivenengine.hpp"
|
2022-02-23 23:08:28 -05:00
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
#include <string_view>
|
2022-02-25 19:57:23 -05:00
|
|
|
#include <utility>
|
2022-02-17 20:02:08 -05:00
|
|
|
#include <iostream>
|
|
|
|
|
#include <cstring>
|
2023-02-14 13:14:18 -05:00
|
|
|
#include <cstdio>
|
|
|
|
|
#include <fstream>
|
|
|
|
|
#include <cassert>
|
2021-10-04 17:45:18 -04:00
|
|
|
|
2022-02-25 19:57:23 -05:00
|
|
|
DrivenEngineReg *DrivenEngineReg::All;
|
2022-02-17 20:02:08 -05:00
|
|
|
|
2022-02-25 19:57:23 -05:00
|
|
|
DrivenEngineReg::DrivenEngineReg(const char *n, DrivenEngineMaker fn) {
|
|
|
|
|
name = n;
|
|
|
|
|
maker = fn;
|
|
|
|
|
next = All;
|
|
|
|
|
All = this;
|
2022-02-17 20:02:08 -05:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
DrivenEngineInitializer DrivenEngineInitializerReg::func;
|
|
|
|
|
|
|
|
|
|
DrivenEngineInitializerReg::DrivenEngineInitializerReg(DrivenEngineInitializer fn) {
|
|
|
|
|
assert(func == nullptr);
|
|
|
|
|
func = fn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// DrivenEngine private methods
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
int DrivenEngine::find_unused_chid() {
|
|
|
|
|
// Note: channel ID zero is special, it is never reused.
|
|
|
|
|
for (int i = 0; i < DRV_MAX_CHAN; i++) {
|
|
|
|
|
int id = next_unused_chid_++;
|
|
|
|
|
if (next_unused_chid_ == DRV_MAX_CHAN) next_unused_chid_ = 1;
|
|
|
|
|
if (channels_[id] == nullptr) return id;
|
2022-02-17 20:02:08 -05:00
|
|
|
}
|
2023-02-14 13:14:18 -05:00
|
|
|
assert(false);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Channel *DrivenEngine::get_chid(int chid) const {
|
|
|
|
|
assert(unsigned(chid) < DRV_MAX_CHAN);
|
|
|
|
|
assert(channels_[chid].get() != nullptr);
|
|
|
|
|
return channels_[chid].get();
|
2022-02-17 20:02:08 -05:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
static DrivenEngine *make_engine(std::string_view kind) {
|
2022-02-25 19:57:23 -05:00
|
|
|
for (auto reg = DrivenEngineReg::All; reg != nullptr; reg=reg->next) {
|
2023-02-14 13:14:18 -05:00
|
|
|
if (kind == reg->name) {
|
2022-03-11 18:35:51 -05:00
|
|
|
UniqueDrivenEngine result = reg->maker();
|
2023-02-14 13:14:18 -05:00
|
|
|
return result.release();
|
2022-02-17 20:02:08 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2021-10-04 17:45:18 -04:00
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Class Channel
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
2022-02-24 02:17:41 -05:00
|
|
|
Channel::Channel(DrivenEngine *de, int chid, int port, const eng::string &target, bool stop) {
|
2021-10-04 17:45:18 -04:00
|
|
|
chid_ = chid;
|
|
|
|
|
port_ = port;
|
|
|
|
|
closed_ = false;
|
|
|
|
|
target_ = target;
|
2021-10-12 12:46:11 -04:00
|
|
|
readline_lastc_ = 0;
|
2021-11-04 14:03:05 -04:00
|
|
|
desired_prompt_ = "";
|
2022-01-05 12:50:16 -05:00
|
|
|
stop_driver_ = stop;
|
2022-02-25 19:57:23 -05:00
|
|
|
sb_in_ = eng::make_shared<StreamBuffer>();
|
|
|
|
|
sb_out_ = eng::make_shared<StreamBuffer>();
|
2022-01-06 17:24:32 -05:00
|
|
|
sb_drvout_ = sb_out_;
|
2021-10-07 14:58:20 -04:00
|
|
|
}
|
|
|
|
|
|
2022-01-06 17:24:32 -05:00
|
|
|
void Channel::erase_command() {
|
|
|
|
|
int ccsize = current_prompt_.size() + current_command_.size();
|
|
|
|
|
if (ccsize > 0) {
|
|
|
|
|
sb_drvout_->write_bytes(util::repeat_string("\b \b", ccsize));
|
2021-11-04 14:00:35 -04:00
|
|
|
current_prompt_ = "";
|
|
|
|
|
current_command_ = "";
|
|
|
|
|
}
|
2022-01-06 17:24:32 -05:00
|
|
|
}
|
2021-11-04 14:00:35 -04:00
|
|
|
|
2022-01-06 17:24:32 -05:00
|
|
|
void Channel::echo_command() {
|
2021-11-04 13:40:42 -04:00
|
|
|
// If the prompt has changed, erase everything and start over.
|
|
|
|
|
if (desired_prompt_ != current_prompt_) {
|
|
|
|
|
int ccsize = current_prompt_.size() + current_command_.size();
|
2022-01-06 17:24:32 -05:00
|
|
|
sb_drvout_->write_bytes(util::repeat_string("\b \b", ccsize));
|
|
|
|
|
sb_drvout_->write_bytes(desired_prompt_);
|
2021-11-04 13:40:42 -04:00
|
|
|
current_command_ = "";
|
|
|
|
|
current_prompt_ = desired_prompt_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find out how much of the command matches.
|
2022-04-16 02:26:32 -04:00
|
|
|
int match = sv::common_prefix_length(current_command_, desired_command_);
|
2021-11-04 13:40:42 -04:00
|
|
|
|
|
|
|
|
// Echo backspaces to remove the non-matching part.
|
|
|
|
|
int remove = current_command_.size() - match;
|
|
|
|
|
if (remove > 0) {
|
2022-01-06 17:24:32 -05:00
|
|
|
sb_drvout_->write_bytes(util::repeat_string("\b \b", remove));
|
2021-11-04 13:40:42 -04:00
|
|
|
current_command_ = current_command_.substr(0, match);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Echo the new part.
|
2022-02-24 02:17:41 -05:00
|
|
|
eng::string newpart = desired_command_.substr(current_command_.size());
|
2021-11-04 13:40:42 -04:00
|
|
|
if (newpart != "") {
|
2022-01-06 17:24:32 -05:00
|
|
|
sb_drvout_->write_bytes(newpart);
|
2021-11-04 13:40:42 -04:00
|
|
|
current_command_ = desired_command_;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-24 02:17:41 -05:00
|
|
|
void Channel::set_prompt(const eng::string &p) {
|
2021-11-04 13:40:42 -04:00
|
|
|
desired_prompt_ = p;
|
|
|
|
|
}
|
2021-10-12 12:46:11 -04:00
|
|
|
|
2022-03-04 16:45:47 -05:00
|
|
|
void Channel::feed_readline(std::string_view data) {
|
|
|
|
|
int nbytes = data.size();
|
|
|
|
|
const char *bytes = data.data();
|
2021-10-12 12:46:11 -04:00
|
|
|
for (int i = 0; i < nbytes; i++) {
|
|
|
|
|
char c = bytes[i];
|
|
|
|
|
if ((c == '\n') && (readline_lastc_ == '\r')) {
|
|
|
|
|
// Ignore newline immediately after carriage return.
|
|
|
|
|
// Otherwise, crlf produces two newlines.
|
|
|
|
|
} else if ((c == '\r') || (c == '\n')) {
|
2022-01-06 17:24:32 -05:00
|
|
|
echo_command();
|
2022-02-24 02:17:41 -05:00
|
|
|
sb_drvout_->write_bytes(eng::string(" \n"));
|
2021-11-04 13:40:42 -04:00
|
|
|
sb_in_->write_bytes(desired_command_);
|
|
|
|
|
sb_in_->write_uint8('\n');
|
|
|
|
|
desired_command_ = "";
|
|
|
|
|
current_prompt_ = "";
|
|
|
|
|
current_command_ = "";
|
2021-12-08 13:28:09 -05:00
|
|
|
} else if ((c == '\b') || (c == 127)) {
|
2021-11-04 13:40:42 -04:00
|
|
|
int len = desired_command_.size();
|
|
|
|
|
if (len > 0) {
|
|
|
|
|
desired_command_ = desired_command_.substr(0, len-1);
|
2021-10-12 12:46:11 -04:00
|
|
|
}
|
|
|
|
|
} else if (c >= 32) {
|
2021-11-04 13:40:42 -04:00
|
|
|
int len = desired_command_.size();
|
|
|
|
|
if (len < READLINE_MAX) {
|
|
|
|
|
desired_command_ = desired_command_ + c;
|
2021-10-12 12:46:11 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
readline_lastc_ = c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-04 16:45:47 -05:00
|
|
|
std::string_view Channel::peek_outgoing() const {
|
|
|
|
|
return sb_drvout_->view();
|
2022-02-18 03:59:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Channel::pump_readline() {
|
|
|
|
|
if (sb_drvout_ != sb_out_) {
|
2022-01-06 17:24:32 -05:00
|
|
|
if (!sb_out_->empty()) {
|
|
|
|
|
erase_command();
|
|
|
|
|
sb_out_->transfer_into(sb_drvout_.get());
|
|
|
|
|
}
|
|
|
|
|
echo_command();
|
2021-11-04 13:40:42 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Channel::sent_outgoing(int nbytes) {
|
2022-01-06 17:24:32 -05:00
|
|
|
sb_drvout_->read_bytes(nbytes);
|
2021-11-04 13:40:42 -04:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// DrivenEngine Client-Side API
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
2021-10-04 17:45:18 -04:00
|
|
|
|
2021-10-08 21:03:52 -04:00
|
|
|
void DrivenEngine::listen_port(int port) {
|
2023-02-14 13:14:18 -05:00
|
|
|
assert(listen_ports_.size() < DRV_MAX_LISTEN_PORTS);
|
2022-02-17 20:02:08 -05:00
|
|
|
listen_ports_.push_back(port);
|
2021-10-08 21:03:52 -04:00
|
|
|
}
|
|
|
|
|
|
2021-10-04 17:45:18 -04:00
|
|
|
double DrivenEngine::get_clock() {
|
|
|
|
|
return clock_;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-24 02:17:41 -05:00
|
|
|
SharedChannel DrivenEngine::new_outgoing_channel(const eng::string &target) {
|
2021-10-07 14:58:20 -04:00
|
|
|
int chid = find_unused_chid();
|
2022-02-17 20:02:08 -05:00
|
|
|
new_outgoing_.push_back(chid);
|
2022-02-25 19:57:23 -05:00
|
|
|
SharedChannel result = eng::make_shared<Channel>(this, chid, 0, target, stop_driver_);
|
2022-01-05 12:50:16 -05:00
|
|
|
channels_[chid] = result;
|
|
|
|
|
return result;
|
2021-10-04 17:45:18 -04:00
|
|
|
}
|
|
|
|
|
|
2022-01-05 12:50:16 -05:00
|
|
|
SharedChannel DrivenEngine::new_incoming_channel() {
|
2021-10-04 17:45:18 -04:00
|
|
|
if (accepted_channels_.empty()) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
} else {
|
2022-01-05 12:50:16 -05:00
|
|
|
SharedChannel result = std::move(accepted_channels_.back());
|
2021-10-04 17:45:18 -04:00
|
|
|
accepted_channels_.pop_back();
|
2021-11-23 14:38:08 -05:00
|
|
|
return result;
|
2021-10-04 17:45:18 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-05 12:50:16 -05:00
|
|
|
SharedChannel DrivenEngine::get_stdio_channel() {
|
|
|
|
|
return stdio_channel_;
|
2021-10-04 17:45:18 -04:00
|
|
|
}
|
|
|
|
|
|
2021-10-05 12:54:37 -04:00
|
|
|
util::LuaSourcePtr DrivenEngine::get_lua_source() {
|
2021-10-04 17:45:18 -04:00
|
|
|
return std::move(lua_source_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrivenEngine::rescan_lua_source() {
|
|
|
|
|
rescan_lua_source_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrivenEngine::stop_driver() {
|
|
|
|
|
stop_driver_ = true;
|
2023-02-14 13:14:18 -05:00
|
|
|
for (int i = 0; i < DRV_MAX_CHAN; i++) {
|
2022-01-05 12:50:16 -05:00
|
|
|
if (channels_[i] != nullptr) {
|
|
|
|
|
channels_[i]->stop_driver_ = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-10-04 17:45:18 -04:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
DrivenEngine::DrivenEngine() {
|
|
|
|
|
next_unused_chid_ = 1;
|
|
|
|
|
stdio_channel_ = eng::make_shared<Channel>(this, 0, 0, "", false);
|
|
|
|
|
stdio_channel_->sb_drvout_ = eng::make_shared<StreamBuffer>();
|
|
|
|
|
channels_[0] = stdio_channel_;
|
|
|
|
|
rescan_lua_source_ = false;
|
|
|
|
|
clock_ = 0.0;
|
|
|
|
|
stop_driver_ = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DrivenEngine::~DrivenEngine() {}
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// LOGFILE EVENT IDS.
|
|
|
|
|
//
|
|
|
|
|
// There's one event ID for each mutator, plus one for 'release'.
|
|
|
|
|
//
|
|
|
|
|
// There are no event IDs for getters, these aren't considered loggable events.
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
enum DrvAction {
|
|
|
|
|
PLAY_INITIALIZE,
|
|
|
|
|
PLAY_CLEAR_NEW_OUTGOING,
|
|
|
|
|
PLAY_SENT_OUTGOING,
|
|
|
|
|
PLAY_RECV_INCOMING,
|
|
|
|
|
PLAY_NOTIFY_CLOSE,
|
|
|
|
|
PLAY_NOTIFY_ACCEPT,
|
|
|
|
|
PLAY_INVOKE_EVENT_UPDATE,
|
|
|
|
|
PLAY_SET_LUA_SOURCE,
|
|
|
|
|
PLAY_RELEASE,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
inline static const char *action_string(DrvAction act) {
|
|
|
|
|
switch(act) {
|
|
|
|
|
case PLAY_INITIALIZE: return "PLAY_INITIALIZE";
|
|
|
|
|
case PLAY_CLEAR_NEW_OUTGOING: return "PLAY_CLEAR_NEW_OUTGOING";
|
|
|
|
|
case PLAY_SENT_OUTGOING: return "PLAY_SENT_OUTGOING";
|
|
|
|
|
case PLAY_RECV_INCOMING: return "PLAY_RECV_INCOMING";
|
|
|
|
|
case PLAY_NOTIFY_CLOSE: return "PLAY_NOTIFY_CLOSE";
|
|
|
|
|
case PLAY_NOTIFY_ACCEPT: return "PLAY_NOTIFY_ACCEPT";
|
|
|
|
|
case PLAY_SET_LUA_SOURCE: return "PLAY_SET_LUA_SOURCE";
|
|
|
|
|
case PLAY_INVOKE_EVENT_UPDATE: return "PLAY_INVOKE_EVENT_UPDATE";
|
|
|
|
|
case PLAY_RELEASE: return "PLAY_RELEASE";
|
|
|
|
|
default: return "unknown";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// RLOG and WLOG, functions to read and write binary data to logfiles.
|
|
|
|
|
//
|
|
|
|
|
// After doing an rlog operation, you should check the stream
|
|
|
|
|
// for "good" to find out if there was any error.
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
class PlayLogfile : public std::ofstream { using std::ofstream::ofstream; };
|
|
|
|
|
class ReplayLogfile : public std::ifstream { using std::ifstream::ifstream; };
|
|
|
|
|
|
|
|
|
|
static uint8_t rlog_uint8(EngineWrapper *w) {
|
|
|
|
|
uint8_t result;
|
|
|
|
|
w->rlog->read((char *)&result, 1);
|
|
|
|
|
if (!w->rlog->good()) return 0;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint32_t rlog_uint32(EngineWrapper *w) {
|
|
|
|
|
uint32_t result;
|
|
|
|
|
w->rlog->read((char *)&result, 4);
|
|
|
|
|
if (!w->rlog->good()) return 0;
|
|
|
|
|
return result;
|
2021-10-08 21:03:52 -04:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
static uint64_t rlog_uint64(EngineWrapper *w) {
|
|
|
|
|
uint64_t result;
|
|
|
|
|
w->rlog->read((char *)&result, 8);
|
|
|
|
|
if (!w->rlog->good()) return 0;
|
|
|
|
|
return result;
|
2022-02-17 20:02:08 -05:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
static double rlog_double(EngineWrapper *w) {
|
|
|
|
|
double result;
|
|
|
|
|
w->rlog->read((char *)&result, 8);
|
|
|
|
|
if (!w->rlog->good()) return 0.0;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string_view rlog_short_string(EngineWrapper *w) {
|
|
|
|
|
uint32_t len = rlog_uint8(w);
|
|
|
|
|
if (len == 255) {
|
|
|
|
|
len = rlog_uint32(w);
|
|
|
|
|
}
|
|
|
|
|
assert (len <= DRV_SHORTSTRING_SIZE);
|
|
|
|
|
if (len > 0) w->rlog->read(w->databuffer, len);
|
|
|
|
|
if (!w->rlog->good()) return std::string_view();
|
|
|
|
|
return std::string_view(w->databuffer, len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string rlog_string(EngineWrapper *w) {
|
|
|
|
|
uint32_t len = rlog_uint8(w);
|
|
|
|
|
if (len == 255) {
|
|
|
|
|
len = rlog_uint32(w);
|
|
|
|
|
}
|
|
|
|
|
std::string result(len, ' ');
|
|
|
|
|
if (len > 0) w->rlog->read(&result[0], len);
|
|
|
|
|
if (!w->rlog->good()) return "";
|
|
|
|
|
return result;
|
2021-10-04 17:45:18 -04:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
static void wlog_uint8(EngineWrapper *w, uint8_t v) {
|
|
|
|
|
w->wlog->put((char)v);
|
2021-10-04 17:45:18 -04:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
static void wlog_uint32(EngineWrapper *w, uint32_t v) {
|
|
|
|
|
w->wlog->write((const char *)&v, 4);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void wlog_uint64(EngineWrapper *w, uint64_t v) {
|
|
|
|
|
w->wlog->write((const char *)&v, 8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void wlog_double(EngineWrapper *w, double v) {
|
|
|
|
|
w->wlog->write((const char *)&v, 8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void wlog_short_string(EngineWrapper *w, std::string_view v) {
|
|
|
|
|
assert (v.size() <= DRV_SHORTSTRING_SIZE);
|
|
|
|
|
if (v.size() >= 255) {
|
|
|
|
|
wlog_uint8(w, 0xFF);
|
|
|
|
|
wlog_uint32(w, v.size());
|
|
|
|
|
} else {
|
|
|
|
|
wlog_uint8(w, v.size());
|
|
|
|
|
}
|
|
|
|
|
w->wlog->write(v.data(), v.size());
|
2021-10-07 14:58:20 -04:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
static void wlog_string(EngineWrapper *w, std::string_view v) {
|
|
|
|
|
if (v.size() >= 255) {
|
|
|
|
|
wlog_uint8(w, 0xFF);
|
|
|
|
|
wlog_uint32(w, v.size());
|
|
|
|
|
} else {
|
|
|
|
|
wlog_uint8(w, v.size());
|
|
|
|
|
}
|
|
|
|
|
w->wlog->write(v.data(), v.size());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void wlog_cmd_hash(EngineWrapper *w, DrvAction act, uint32_t hash) {
|
|
|
|
|
wlog_uint8(w, act);
|
|
|
|
|
wlog_uint32(w, hash);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// reset_wrapper
|
|
|
|
|
//
|
|
|
|
|
// Shut down a EngineWrapper, store an optional error message.
|
|
|
|
|
//
|
|
|
|
|
// release
|
|
|
|
|
//
|
|
|
|
|
// Shut down an EngineWrapper cleanly, with no error message, and
|
|
|
|
|
// log the step if the logfile is open.
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
static void reset_wrapper(EngineWrapper *w, const char *format, ...) {
|
|
|
|
|
va_list argp;
|
|
|
|
|
va_start(argp, format);
|
|
|
|
|
memset(w->error, 0, DRV_ERRMSG_SIZE);
|
|
|
|
|
vsnprintf(w->error, DRV_ERRMSG_SIZE, format, argp);
|
|
|
|
|
w->error[DRV_ERRMSG_SIZE - 1] = 0;
|
|
|
|
|
|
|
|
|
|
if (w->wlog != nullptr) {
|
|
|
|
|
w->wlog->close();
|
|
|
|
|
delete w->wlog;
|
|
|
|
|
w->wlog = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (w->rlog != nullptr) {
|
|
|
|
|
w->rlog->close();
|
|
|
|
|
delete w->rlog;
|
|
|
|
|
w->rlog = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (w->engine != nullptr) {
|
|
|
|
|
delete w->engine;
|
|
|
|
|
w->engine = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void release(EngineWrapper *w) {
|
|
|
|
|
if (w->wlog != nullptr) {
|
|
|
|
|
wlog_cmd_hash(w, PLAY_RELEASE, eng::memhash());
|
|
|
|
|
}
|
|
|
|
|
reset_wrapper(w, "");
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// DRIVER Methods: Getters
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void DrivenEngine::drv_get_listen_ports(uint32_t *nports, const uint32_t **ports) const {
|
|
|
|
|
*nports = listen_ports_.size();
|
|
|
|
|
*ports = &listen_ports_[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrivenEngine::drv_get_new_outgoing(uint32_t *nchids, const uint32_t **chids) const {
|
|
|
|
|
*nchids = new_outgoing_.size();
|
|
|
|
|
*chids = &new_outgoing_[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *DrivenEngine::drv_get_target(uint32_t chid) const {
|
|
|
|
|
return get_chid(chid)->target_.c_str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DrivenEngine::drv_get_channel_released(uint32_t chid) const {
|
2022-01-05 12:50:16 -05:00
|
|
|
return channels_[chid].use_count() == 1;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
void DrivenEngine::drv_get_outgoing(uint32_t chid, uint32_t *len, const char **data) const {
|
|
|
|
|
std::string_view v = get_chid(chid)->peek_outgoing();
|
|
|
|
|
*len = v.size();
|
|
|
|
|
*data = v.data();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DrivenEngine::drv_get_outgoing_empty(uint32_t chid) const {
|
|
|
|
|
std::string_view v = get_chid(chid)->peek_outgoing();
|
|
|
|
|
return (v.size() == 0);
|
2021-10-04 17:45:18 -04:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
double DrivenEngine::drv_get_clock() const {
|
|
|
|
|
return clock_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DrivenEngine::drv_get_rescan_lua_source() const {
|
|
|
|
|
return rescan_lua_source_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool DrivenEngine::drv_get_stop_driver() const {
|
|
|
|
|
return stop_driver_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// DRIVER Methods: Mutators
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void DrivenEngine::drv_initialize(uint32_t srcpklen, const char *srcpk, int argc, char **argv) {
|
|
|
|
|
drv_set_lua_source(srcpklen, srcpk);
|
|
|
|
|
event_init(argc, argv);
|
|
|
|
|
stdio_channel_->pump_readline();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrivenEngine::drv_clear_new_outgoing() {
|
|
|
|
|
new_outgoing_.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrivenEngine::drv_sent_outgoing(uint32_t chid, uint32_t nbytes) {
|
2021-11-04 13:40:42 -04:00
|
|
|
return get_chid(chid)->sent_outgoing(nbytes);
|
2021-10-04 17:45:18 -04:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
void DrivenEngine::drv_recv_incoming(uint32_t chid, uint32_t nbytes, const char *bytes) {
|
2023-02-24 17:47:13 -05:00
|
|
|
std::string_view sbytes(bytes, nbytes);
|
2023-02-14 13:14:18 -05:00
|
|
|
if (nbytes > 0) {
|
2021-10-12 12:46:11 -04:00
|
|
|
Channel *ch = get_chid(chid);
|
2022-02-18 03:59:21 -05:00
|
|
|
if (ch->sb_drvout_ != ch->sb_out_) {
|
2023-02-24 17:47:13 -05:00
|
|
|
ch->feed_readline(sbytes);
|
2021-10-12 12:46:11 -04:00
|
|
|
} else {
|
2023-02-24 17:47:13 -05:00
|
|
|
ch->sb_in_->write_bytes(sbytes);
|
2021-10-12 12:46:11 -04:00
|
|
|
}
|
2021-10-05 12:54:37 -04:00
|
|
|
}
|
2021-10-04 17:45:18 -04:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
void DrivenEngine::drv_notify_close(uint32_t chid, uint32_t len, const char *data) {
|
2021-10-12 13:54:08 -04:00
|
|
|
Channel *ch = get_chid(chid);
|
|
|
|
|
ch->closed_ = true;
|
2023-02-14 13:14:18 -05:00
|
|
|
ch->error_ = std::string(data, len);
|
2022-01-05 12:50:16 -05:00
|
|
|
channels_[chid].reset();
|
2021-10-04 17:45:18 -04:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
uint32_t DrivenEngine::drv_notify_accept(uint32_t port) {
|
2021-10-07 14:58:20 -04:00
|
|
|
int chid = find_unused_chid();
|
2022-02-25 19:57:23 -05:00
|
|
|
channels_[chid] = eng::make_shared<Channel>(this, chid, port, "", stop_driver_);
|
2022-01-05 12:50:16 -05:00
|
|
|
accepted_channels_.push_back(channels_[chid]);
|
2021-10-04 17:45:18 -04:00
|
|
|
return chid;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
void DrivenEngine::drv_invoke_event_update(double clock) {
|
|
|
|
|
clock_ = clock;
|
|
|
|
|
event_update();
|
|
|
|
|
stdio_channel_->pump_readline();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrivenEngine::drv_set_lua_source(uint32_t srcpklen, const char *srcpk) {
|
|
|
|
|
StreamBuffer sb(srcpk, srcpklen);
|
|
|
|
|
uint32_t nfiles = sb.read_uint32();
|
|
|
|
|
lua_source_.reset(new util::LuaSourceVec);
|
|
|
|
|
lua_source_->resize(nfiles);
|
|
|
|
|
for (uint32_t i = 0; i < nfiles; i++) {
|
|
|
|
|
(*lua_source_)[i].first = sb.read_string();
|
|
|
|
|
}
|
|
|
|
|
for (uint32_t i = 0; i < nfiles; i++) {
|
|
|
|
|
(*lua_source_)[i].second = sb.read_string();
|
|
|
|
|
}
|
2022-02-17 20:02:08 -05:00
|
|
|
rescan_lua_source_ = false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// C Wrappers: Getters
|
|
|
|
|
//
|
|
|
|
|
// These wrappers make it possible to call the drv_get routines using C
|
|
|
|
|
// functions instead of methods. This is important if the engine is compiled
|
|
|
|
|
// with one C++ compiler, but the driver is compiled with a different C++
|
|
|
|
|
// compiler.
|
|
|
|
|
//
|
|
|
|
|
// Some of these take parameter 'EngineWrapper', some take 'EngineWrapper',
|
|
|
|
|
// and some come in two versions. This all depends on whether they are used
|
|
|
|
|
// during play, during replay, or both.
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
static void drv_get_listen_ports(EngineWrapper *w, uint32_t *nports, const uint32_t **ports) {
|
|
|
|
|
return w->engine->drv_get_listen_ports(nports, ports);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void drv_get_new_outgoing(EngineWrapper *w, uint32_t *nchanids, const uint32_t **chanids) {
|
|
|
|
|
return w->engine->drv_get_new_outgoing(nchanids, chanids);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *drv_get_target(EngineWrapper *w, uint32_t chid) {
|
|
|
|
|
return w->engine->drv_get_target(chid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool drv_get_channel_released(EngineWrapper *w, uint32_t chid) {
|
|
|
|
|
return w->engine->drv_get_channel_released(chid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void drv_get_outgoing(EngineWrapper *w, uint32_t chid, uint32_t *len, const char **data) {
|
|
|
|
|
return w->engine->drv_get_outgoing(chid, len, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool drv_get_outgoing_empty(EngineWrapper *w, uint32_t chid) {
|
|
|
|
|
return w->engine->drv_get_outgoing_empty(chid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static double drv_get_clock(EngineWrapper *w) {
|
|
|
|
|
return w->engine->drv_get_clock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool drv_get_rescan_lua_source(EngineWrapper *w) {
|
|
|
|
|
return w->engine->drv_get_rescan_lua_source();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool drv_get_stop_driver(EngineWrapper *w) {
|
|
|
|
|
return w->engine->drv_get_stop_driver();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// C Wrappers: Mutators
|
|
|
|
|
//
|
|
|
|
|
// The wrapper for a mutator consists of two parts: the wrapper which is used at
|
|
|
|
|
// 'play' time, and the wrapper which is used at 'replay' time.
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void play_initialize(EngineWrapper *w, uint32_t argc, char **argv, uint32_t srcpklen, const char *srcpk, const char *logfn) {
|
|
|
|
|
if (w->engine != nullptr) {
|
|
|
|
|
return reset_wrapper(w, "Cannot initialize wrapper, it's already initialized.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear the error message.
|
|
|
|
|
memset(w->error, 0, DRV_ERRMSG_SIZE);
|
|
|
|
|
|
|
|
|
|
// Open the logfile, if any is specified.
|
|
|
|
|
if ((logfn != nullptr) && (logfn[0] != 0)) {
|
|
|
|
|
w->wlog = new PlayLogfile(logfn, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
|
|
|
|
|
if (!w->wlog->good()) {
|
|
|
|
|
return reset_wrapper(w, "Could not open replay log for writing: %s", logfn);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
w->wlog = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we have a logfile, then log this initialization.
|
|
|
|
|
if (w->wlog != nullptr) {
|
|
|
|
|
wlog_cmd_hash(w, PLAY_INITIALIZE, eng::memhash());
|
|
|
|
|
wlog_uint32(w, argc);
|
|
|
|
|
for (uint32_t i = 0; i < argc; i++) {
|
|
|
|
|
wlog_string(w, argv[i]);
|
|
|
|
|
}
|
|
|
|
|
wlog_string(w, std::string_view(srcpk, srcpklen));
|
|
|
|
|
w->wlog->flush();
|
2022-02-17 20:02:08 -05:00
|
|
|
}
|
2023-02-14 13:14:18 -05:00
|
|
|
|
|
|
|
|
// Create the engine of the appropriate type.
|
|
|
|
|
if (argc < 1) {
|
|
|
|
|
std::ostringstream oss;
|
|
|
|
|
oss << "Must pass an engine type on the command line. Known types:\n";
|
|
|
|
|
for (auto reg = DrivenEngineReg::All; reg != nullptr; reg=reg->next) {
|
|
|
|
|
oss << " " << reg->name << std::endl;
|
|
|
|
|
}
|
|
|
|
|
std::string err = oss.str();
|
|
|
|
|
return reset_wrapper(w, err.c_str());
|
|
|
|
|
}
|
|
|
|
|
w->engine = make_engine(argv[0]);
|
|
|
|
|
if (w->engine == nullptr) {
|
|
|
|
|
return reset_wrapper(w, "No such driven engine type: %s", argv[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Call the engine initialization sequence.
|
|
|
|
|
w->engine->drv_initialize(srcpklen, srcpk, argc - 1, argv + 1);
|
2022-02-17 20:02:08 -05:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
|
|
|
|
|
static void replay_initialize(EngineWrapper *w) {
|
|
|
|
|
assert(w->rlog != nullptr);
|
|
|
|
|
std::vector<std::string> argvstr;
|
|
|
|
|
uint32_t argc = rlog_uint32(w);
|
|
|
|
|
for (uint32_t i = 0; i < argc; i++) {
|
|
|
|
|
argvstr.push_back(rlog_string(w));
|
|
|
|
|
}
|
|
|
|
|
std::string srcpk = rlog_string(w);
|
|
|
|
|
|
|
|
|
|
if (!w->rlog->good()) {
|
|
|
|
|
return reset_wrapper(w, "replay log corrupt in replay_initialize");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We need to convert the argument vector from an array
|
|
|
|
|
// of C++ strings into the canonical argc, argv format.
|
|
|
|
|
std::vector<char *> argvec;
|
|
|
|
|
for (uint32_t i = 0; i < argc; i++) {
|
|
|
|
|
argvec.push_back(&argvstr[i][0]);
|
|
|
|
|
}
|
|
|
|
|
char **argv = &argvec[0];
|
|
|
|
|
|
|
|
|
|
// Create the engine.
|
|
|
|
|
w->engine = make_engine(argv[0]);
|
|
|
|
|
if (w->engine == nullptr) {
|
|
|
|
|
return reset_wrapper(w, "No such driven engine type: %s", argvstr[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
w->engine->drv_initialize(srcpk.size(), srcpk.c_str(), argc - 1, argv + 1);
|
2021-10-08 16:38:10 -04:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
|
|
|
|
|
////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void play_clear_new_outgoing(EngineWrapper *w) {
|
|
|
|
|
assert(w->rlog == nullptr);
|
|
|
|
|
if (w->wlog != nullptr) {
|
|
|
|
|
wlog_cmd_hash(w, PLAY_CLEAR_NEW_OUTGOING, eng::memhash());
|
|
|
|
|
w->wlog->flush();
|
|
|
|
|
}
|
|
|
|
|
w->engine->drv_clear_new_outgoing();
|
2021-10-05 12:54:37 -04:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
static void replay_clear_new_outgoing(EngineWrapper *w) {
|
|
|
|
|
w->engine->drv_clear_new_outgoing();
|
2021-10-04 17:45:18 -04:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
|
|
|
|
|
////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void play_sent_outgoing(EngineWrapper *w, uint32_t chid, uint32_t nbytes) {
|
|
|
|
|
assert(w->rlog == nullptr);
|
|
|
|
|
if (w->wlog != nullptr) {
|
|
|
|
|
uint32_t ndata; const char *data;
|
|
|
|
|
w->engine->drv_get_outgoing(chid, &ndata, &data);
|
|
|
|
|
assert(nbytes <= ndata);
|
|
|
|
|
wlog_cmd_hash(w, PLAY_SENT_OUTGOING, eng::memhash());
|
|
|
|
|
wlog_uint32(w, chid);
|
|
|
|
|
wlog_uint32(w, nbytes);
|
|
|
|
|
wlog_uint64(w, SpookyHash::QkHash64(data, nbytes));
|
|
|
|
|
w->wlog->flush();
|
|
|
|
|
}
|
|
|
|
|
w->engine->drv_sent_outgoing(chid, nbytes);
|
2021-10-04 17:45:18 -04:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
static void replay_sent_outgoing(EngineWrapper *w) {
|
|
|
|
|
uint32_t chid = rlog_uint32(w);
|
|
|
|
|
uint32_t nbytes = rlog_uint32(w);
|
|
|
|
|
uint64_t hash = rlog_uint64(w);
|
|
|
|
|
|
|
|
|
|
if (!w->rlog->good()) {
|
|
|
|
|
return reset_wrapper(w, "replay log corrupt in replay_sent_outgoing");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t ndata; const char *data;
|
|
|
|
|
w->engine->drv_get_outgoing(chid, &ndata, &data);
|
|
|
|
|
if ((nbytes > ndata) || (hash != SpookyHash::QkHash64(data, nbytes))) {
|
|
|
|
|
return reset_wrapper(w, "nondeterministic in replay_sent_outgoing");
|
|
|
|
|
}
|
2023-02-20 16:02:18 -05:00
|
|
|
if (w->replay_cb_sent_outgoing != nullptr) {
|
|
|
|
|
w->replay_cb_sent_outgoing(w->replay_cb_vp, chid, ndata, data);
|
|
|
|
|
}
|
2023-02-14 13:14:18 -05:00
|
|
|
w->engine->drv_sent_outgoing(chid, nbytes);
|
2021-10-04 17:45:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void play_recv_incoming(EngineWrapper *w, uint32_t chid, uint32_t len, const char *data) {
|
|
|
|
|
assert(w->rlog == nullptr);
|
|
|
|
|
if (w->wlog != nullptr) {
|
|
|
|
|
wlog_cmd_hash(w, PLAY_RECV_INCOMING, eng::memhash());
|
|
|
|
|
wlog_uint32(w, chid);
|
|
|
|
|
wlog_short_string(w, std::string_view(data, len));
|
|
|
|
|
w->wlog->flush();
|
|
|
|
|
}
|
|
|
|
|
w->engine->drv_recv_incoming(chid, len, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void replay_recv_incoming(EngineWrapper *w) {
|
|
|
|
|
uint32_t chid = rlog_uint32(w);
|
|
|
|
|
std::string_view data = rlog_short_string(w);
|
|
|
|
|
|
|
|
|
|
if (!w->rlog->good()) {
|
|
|
|
|
return reset_wrapper(w, "replay log corrupt in replay_recv_incoming");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w->engine->drv_recv_incoming(chid, data.size(), data.data());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////
|
|
|
|
|
|
2021-10-04 17:45:18 -04:00
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
static void play_notify_close(EngineWrapper *w, uint32_t chid, uint32_t len, const char *data) {
|
|
|
|
|
assert(w->rlog == nullptr);
|
|
|
|
|
if (w->wlog != nullptr) {
|
|
|
|
|
wlog_cmd_hash(w, PLAY_NOTIFY_CLOSE, eng::memhash());
|
|
|
|
|
wlog_uint32(w, chid);
|
|
|
|
|
wlog_string(w, std::string_view(data, len));
|
|
|
|
|
w->wlog->flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w->engine->drv_notify_close(chid, len, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void replay_notify_close(EngineWrapper *w) {
|
|
|
|
|
uint32_t chid = rlog_uint32(w);
|
|
|
|
|
std::string message = rlog_string(w);
|
|
|
|
|
|
|
|
|
|
if (!w->rlog->good()) {
|
|
|
|
|
return reset_wrapper(w, "replay log corrupt in replay_notify_close");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w->engine->drv_notify_close(chid, message.size(), message.c_str());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static uint32_t play_notify_accept(EngineWrapper *w, uint32_t port) {
|
|
|
|
|
assert(w->rlog == nullptr);
|
|
|
|
|
if (w->wlog != nullptr) {
|
|
|
|
|
wlog_cmd_hash(w, PLAY_NOTIFY_ACCEPT, eng::memhash());
|
|
|
|
|
wlog_uint32(w, port);
|
|
|
|
|
w->wlog->flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return w->engine->drv_notify_accept(port);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void replay_notify_accept(EngineWrapper *w) {
|
|
|
|
|
uint32_t port = rlog_uint32(w);
|
|
|
|
|
|
|
|
|
|
if (!w->rlog->good()) {
|
|
|
|
|
return reset_wrapper(w, "replay log corrupt in replay_notify_accept");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w->engine->drv_notify_accept(port);
|
2021-10-04 17:45:18 -04:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
|
|
|
|
|
////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void play_invoke_event_update(EngineWrapper *w, double clock) {
|
|
|
|
|
assert(w->rlog == nullptr);
|
|
|
|
|
if (w->wlog != nullptr) {
|
|
|
|
|
wlog_cmd_hash(w, PLAY_INVOKE_EVENT_UPDATE, eng::memhash());
|
|
|
|
|
wlog_double(w, clock);
|
|
|
|
|
w->wlog->flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w->engine->drv_invoke_event_update(clock);
|
2021-10-04 17:45:18 -04:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 13:14:18 -05:00
|
|
|
static void replay_invoke_event_update(EngineWrapper *w) {
|
|
|
|
|
double clock = rlog_double(w);
|
|
|
|
|
|
|
|
|
|
if (!w->rlog->good()) {
|
|
|
|
|
return reset_wrapper(w, "replay log corrupt in replay_event_update");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w->engine->drv_invoke_event_update(clock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void play_set_lua_source(EngineWrapper *w, uint32_t srcpklen, const char *srcpk) {
|
|
|
|
|
assert(w->rlog == nullptr);
|
|
|
|
|
if (w->wlog != nullptr) {
|
|
|
|
|
wlog_cmd_hash(w, PLAY_SET_LUA_SOURCE, eng::memhash());
|
|
|
|
|
wlog_string(w, std::string_view(srcpk, srcpklen));
|
|
|
|
|
w->wlog->flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w->engine->drv_set_lua_source(srcpklen, srcpk);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void replay_set_lua_source(EngineWrapper *w) {
|
|
|
|
|
std::string srcpack = rlog_string(w);
|
|
|
|
|
|
|
|
|
|
if (!w->rlog->good()) {
|
|
|
|
|
return reset_wrapper(w, "replay log corrupt in replay_set_lua_source");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w->engine->drv_set_lua_source(srcpack.size(), srcpack.c_str());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Replay Core
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void replaycore_initialize(EngineWrapper *w, const char *logfn) {
|
|
|
|
|
if (w->engine != nullptr) {
|
|
|
|
|
return reset_wrapper(w, "Cannot initialize wrapper, it's already initialized.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear the error message.
|
|
|
|
|
memset(w->error, 0, DRV_ERRMSG_SIZE);
|
|
|
|
|
|
|
|
|
|
// Open the logfile.
|
|
|
|
|
w->rlog = new ReplayLogfile(logfn, std::ios_base::in | std::ios_base::binary);
|
|
|
|
|
if (!w->rlog->good()) {
|
|
|
|
|
return reset_wrapper(w, "Could not open replay log for reading: %s", logfn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read one step from the logfile, and make sure it's an initialize step.
|
|
|
|
|
uint8_t code = rlog_uint8(w);
|
|
|
|
|
int hash = rlog_uint32(w);
|
|
|
|
|
if (!w->rlog->good()) {
|
2023-02-20 16:02:18 -05:00
|
|
|
return reset_wrapper(w, "logfile corrupt in initial step");
|
2023-02-14 13:14:18 -05:00
|
|
|
}
|
|
|
|
|
if (hash != eng::memhash()) {
|
|
|
|
|
return reset_wrapper(w, "nondeterminism detected in initial step");
|
|
|
|
|
}
|
|
|
|
|
if (code != PLAY_INITIALIZE) {
|
|
|
|
|
return reset_wrapper(w, "replay log doesn't begin with initialize step");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Replay the initialize step from the logfile.
|
|
|
|
|
// Doing this immediately, rather than waiting for the driver
|
|
|
|
|
// to call 'step', enforces the invariant that after calling
|
|
|
|
|
// initialize, there's an engine.
|
|
|
|
|
replay_initialize(w);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void replaycore_step(EngineWrapper *w) {
|
|
|
|
|
if (w->rlog == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t code = rlog_uint8(w);
|
2023-02-20 16:02:18 -05:00
|
|
|
if (w->rlog->eof()) {
|
|
|
|
|
return reset_wrapper(w, "logfile terminated abruptly");
|
|
|
|
|
}
|
2023-02-14 13:14:18 -05:00
|
|
|
int hash = rlog_uint32(w);
|
|
|
|
|
if (!w->rlog->good()) {
|
2023-02-20 16:02:18 -05:00
|
|
|
return reset_wrapper(w, "logfile corrupt in replay step");
|
2023-02-14 13:14:18 -05:00
|
|
|
}
|
|
|
|
|
if (hash != eng::memhash()) {
|
|
|
|
|
return reset_wrapper(w, "nondeterminism detected");
|
|
|
|
|
}
|
|
|
|
|
switch (code) {
|
|
|
|
|
case PLAY_CLEAR_NEW_OUTGOING: replay_clear_new_outgoing(w); return;
|
|
|
|
|
case PLAY_SENT_OUTGOING: replay_sent_outgoing(w); return;
|
|
|
|
|
case PLAY_RECV_INCOMING: replay_recv_incoming(w); return;
|
|
|
|
|
case PLAY_NOTIFY_CLOSE: replay_notify_close(w); return;
|
|
|
|
|
case PLAY_NOTIFY_ACCEPT: replay_notify_accept(w); return;
|
|
|
|
|
case PLAY_SET_LUA_SOURCE: replay_set_lua_source(w); return;
|
|
|
|
|
case PLAY_INVOKE_EVENT_UPDATE: replay_invoke_event_update(w); return;
|
|
|
|
|
case PLAY_RELEASE: release(w); return;
|
|
|
|
|
default: return reset_wrapper(w, "Replay log corrupt in command dispatcher");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// General Mutators
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Wrapper Initialization
|
|
|
|
|
//
|
|
|
|
|
// To access the engine across a DLL boundary, you first use
|
|
|
|
|
// GetProcAddress or dlsym to fetch the addresses of 'init_play_engine'
|
|
|
|
|
// and 'init_replay_engine'. Then, you use those two functions to
|
|
|
|
|
// initialize a EngineWrapper or a EngineWrapper, which contain the addresses
|
|
|
|
|
// of all the other functions you need. These are the only two functions
|
|
|
|
|
// marked 'DLLEXPORT', all other functions are exported from the DLL
|
|
|
|
|
// indirectly.
|
|
|
|
|
//
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
#if defined(__linux__)
|
|
|
|
|
#define DLLEXPORT __attribute__((visibility("default")))
|
|
|
|
|
#elif defined(_WIN32)
|
|
|
|
|
#define DLLEXPORT __declspec(dllexport)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static void init_engine_wrapper_helper(EngineWrapper *w) {
|
|
|
|
|
static bool called_initializer;
|
|
|
|
|
assert(DrivenEngineInitializerReg::func != nullptr);
|
|
|
|
|
if (!called_initializer) {
|
|
|
|
|
DrivenEngineInitializerReg::func();
|
|
|
|
|
called_initializer = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(w, 0, sizeof(EngineWrapper));
|
|
|
|
|
|
|
|
|
|
w->get_listen_ports = drv_get_listen_ports;
|
|
|
|
|
w->get_new_outgoing = drv_get_new_outgoing;
|
|
|
|
|
w->get_target = drv_get_target;
|
|
|
|
|
w->get_channel_released = drv_get_channel_released;
|
|
|
|
|
w->get_outgoing = drv_get_outgoing;
|
|
|
|
|
w->get_outgoing_empty = drv_get_outgoing_empty;
|
|
|
|
|
w->get_clock = drv_get_clock;
|
|
|
|
|
w->get_rescan_lua_source = drv_get_rescan_lua_source;
|
|
|
|
|
w->get_stop_driver = drv_get_stop_driver;
|
|
|
|
|
|
|
|
|
|
w->play_initialize = play_initialize;
|
|
|
|
|
w->play_clear_new_outgoing = play_clear_new_outgoing;
|
|
|
|
|
w->play_sent_outgoing = play_sent_outgoing;
|
|
|
|
|
w->play_recv_incoming = play_recv_incoming;
|
|
|
|
|
w->play_notify_close = play_notify_close;
|
|
|
|
|
w->play_notify_accept = play_notify_accept;
|
|
|
|
|
w->play_invoke_event_update = play_invoke_event_update;
|
|
|
|
|
w->play_set_lua_source = play_set_lua_source;
|
|
|
|
|
|
|
|
|
|
w->replay_initialize = replaycore_initialize;
|
|
|
|
|
w->replay_step = replaycore_step;
|
|
|
|
|
|
2023-04-05 18:41:03 -04:00
|
|
|
w->hook_dprint = util::hook_dprint;
|
2023-02-14 13:14:18 -05:00
|
|
|
w->release = release;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
DLLEXPORT void init_engine_wrapper(EngineWrapper *w) {
|
|
|
|
|
init_engine_wrapper_helper(w);
|
|
|
|
|
}
|
|
|
|
|
}
|