Builder is now using the FSM I wrote. Still not as clean as I like but big improvement.
This commit is contained in:
parent
dcf1a4020d
commit
a7c5de6ac3
4 changed files with 176 additions and 129 deletions
226
builder.cpp
226
builder.cpp
|
@ -1,12 +1,6 @@
|
||||||
#include "builder.hpp"
|
#include "builder.hpp"
|
||||||
#include "dbc.hpp"
|
#include "dbc.hpp"
|
||||||
#include "watcher.hpp"
|
|
||||||
#include "game_engine.hpp"
|
|
||||||
#include <chrono> // for milliseconds
|
#include <chrono> // for milliseconds
|
||||||
#include <efsw/efsw.hpp>
|
|
||||||
#include <fmt/chrono.h>
|
|
||||||
#include <fmt/color.h>
|
|
||||||
#include <fmt/core.h>
|
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <git2.h>
|
#include <git2.h>
|
||||||
|
@ -82,122 +76,116 @@ MatchResult Builder::parse_line(const string &line) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum BuildState {
|
|
||||||
WAITING, BUILDING, DONE, STARTING, READING
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void Builder::run() {
|
void Builder::run() {
|
||||||
git_repository* repo = nullptr;
|
int rc = gui.main_loop(game, [&] {
|
||||||
|
event(GO);
|
||||||
try {
|
|
||||||
gui.output(format("Using build command: {}", build_cmd));
|
|
||||||
efsw::FileWatcher* fileWatcher = new efsw::FileWatcher();
|
|
||||||
dbc::check(fileWatcher != nullptr, "Failed to create filewatcher.");
|
|
||||||
|
|
||||||
git_libgit2_init();
|
|
||||||
|
|
||||||
int err = git_repository_open(&repo, git_path.c_str());
|
|
||||||
dbc::check(err == 0, git_error_last()->message);
|
|
||||||
|
|
||||||
UpdateListener* listener = new UpdateListener(repo);
|
|
||||||
dbc::check(listener != nullptr, "Failed to create listener.");
|
|
||||||
|
|
||||||
gui.output(format("Watching directory {} for changes...", git_path));
|
|
||||||
efsw::WatchID wid = fileWatcher->addWatch(git_path, listener, true);
|
|
||||||
|
|
||||||
FILE *build_out = NULL;
|
|
||||||
bool build_done = false;
|
|
||||||
string line = "";
|
|
||||||
BuildState state = WAITING;
|
|
||||||
std::future<FILE *> build_fut;
|
|
||||||
|
|
||||||
fileWatcher->watch();
|
|
||||||
|
|
||||||
int rc = gui.main_loop(game, [&] {
|
|
||||||
switch(state) {
|
|
||||||
case WAITING:
|
|
||||||
if(listener->changes) {
|
|
||||||
game.start_round();
|
|
||||||
gui.building();
|
|
||||||
gui.output(format("CHANGES! Running build {}", build_cmd));
|
|
||||||
build_fut = std::async([&]() {
|
|
||||||
return start_command(build_cmd);
|
|
||||||
});
|
|
||||||
state = STARTING;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STARTING: {
|
|
||||||
println(">>> STARTING");
|
|
||||||
std::future_status status = build_fut.wait_for(0ms);
|
|
||||||
|
|
||||||
if(status == std::future_status::ready) {
|
|
||||||
build_out = build_fut.get();
|
|
||||||
state = READING;
|
|
||||||
} else {
|
|
||||||
state = STARTING;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case READING: {
|
|
||||||
line = read_line(build_out, build_done);
|
|
||||||
state = BUILDING;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BUILDING: {
|
|
||||||
println(">>> BUILDING");
|
|
||||||
// check if there's output
|
|
||||||
if(build_done) {
|
|
||||||
bool good = end_build(build_out, gui, build_cmd);
|
|
||||||
|
|
||||||
if(good) {
|
|
||||||
gui.build_works();
|
|
||||||
} else {
|
|
||||||
// BUG: make it not play two sounds
|
|
||||||
gui.build_failed(true, build_cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
build_out = NULL;
|
|
||||||
state = DONE;
|
|
||||||
} else {
|
|
||||||
auto m = parse_line(line);
|
|
||||||
|
|
||||||
if(m.match) {
|
|
||||||
gui.output(format("HIT WITH {} @ {}:{}:{} {}", m.type, m.file_name, m.lnumber, m.col, m.message));
|
|
||||||
game.hit(m.type);
|
|
||||||
}
|
|
||||||
state = READING;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DONE: {
|
|
||||||
game.end_round();
|
|
||||||
|
|
||||||
if(game.is_dead()) {
|
|
||||||
gui.you_died();
|
|
||||||
game.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
listener->reset_state();
|
|
||||||
gui.output("^^^^^^^^^^^ END ^^^^^^^^^^^");
|
|
||||||
state = WAITING;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
dbc::check(rc == 0, "Invalid return from main_loop.");
|
if(rc != 0) println("ERROR IN GUI");
|
||||||
|
|
||||||
fileWatcher->removeWatch(wid);
|
event(QUIT);
|
||||||
git_libgit2_shutdown();
|
}
|
||||||
} catch(dbc::Error &err) {
|
|
||||||
if(repo != nullptr) git_repository_free(repo);
|
void Builder::building(BuildEvent ev) {
|
||||||
git_libgit2_shutdown();
|
println(">>> BUILDING");
|
||||||
throw err;
|
// check if there's output
|
||||||
|
if(build_done) {
|
||||||
|
bool good = end_build(build_out, gui, build_cmd);
|
||||||
|
|
||||||
|
if(good) {
|
||||||
|
gui.build_works();
|
||||||
|
} else {
|
||||||
|
// BUG: make it not play two sounds
|
||||||
|
gui.build_failed(true, build_cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
build_out = NULL;
|
||||||
|
state(DONE);
|
||||||
|
} else {
|
||||||
|
auto m = parse_line(line);
|
||||||
|
|
||||||
|
if(m.match) {
|
||||||
|
gui.output(format("HIT WITH {} @ {}:{}:{} {}", m.type, m.file_name, m.lnumber, m.col, m.message));
|
||||||
|
game.hit(m.type);
|
||||||
|
}
|
||||||
|
state(READING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::start(BuildEvent ev) {
|
||||||
|
gui.output(format("Using build command: {}", build_cmd));
|
||||||
|
fileWatcher = new efsw::FileWatcher();
|
||||||
|
dbc::check(fileWatcher != nullptr, "Failed to create filewatcher.");
|
||||||
|
|
||||||
|
git_libgit2_init();
|
||||||
|
|
||||||
|
int err = git_repository_open(&repo, git_path.c_str());
|
||||||
|
dbc::check(err == 0, git_error_last()->message);
|
||||||
|
|
||||||
|
listener = new UpdateListener(repo);
|
||||||
|
dbc::check(listener != nullptr, "Failed to create listener.");
|
||||||
|
|
||||||
|
gui.output(format("Watching directory {} for changes...", git_path));
|
||||||
|
wid = fileWatcher->addWatch(git_path, listener, true);
|
||||||
|
fileWatcher->watch();
|
||||||
|
|
||||||
|
state(WAITING);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::waiting(BuildEvent ev) {
|
||||||
|
if(listener->changes) {
|
||||||
|
game.start_round();
|
||||||
|
gui.building();
|
||||||
|
gui.output(format("CHANGES! Running build {}", build_cmd));
|
||||||
|
build_fut = std::async([&]() {
|
||||||
|
return start_command(build_cmd);
|
||||||
|
});
|
||||||
|
state(STARTING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::starting(BuildEvent ev) {
|
||||||
|
std::future_status status = build_fut.wait_for(0ms);
|
||||||
|
|
||||||
|
if(status == std::future_status::ready) {
|
||||||
|
build_out = build_fut.get();
|
||||||
|
state(READING);
|
||||||
|
} else {
|
||||||
|
state(STARTING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::reading(BuildEvent ev) {
|
||||||
|
line = read_line(build_out, build_done);
|
||||||
|
state(BUILDING);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::done(BuildEvent ev) {
|
||||||
|
game.end_round();
|
||||||
|
|
||||||
|
if(game.is_dead()) {
|
||||||
|
gui.you_died();
|
||||||
|
game.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
listener->reset_state();
|
||||||
|
gui.output("^^^^^^^^^^^ END ^^^^^^^^^^^");
|
||||||
|
state(WAITING);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::exit(BuildEvent ev) {
|
||||||
|
if(ev == QUIT) {
|
||||||
|
fileWatcher->removeWatch(wid);
|
||||||
|
git_libgit2_shutdown();
|
||||||
|
state(EXIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::error(BuildEvent ev) {
|
||||||
|
// how to avoid doing this more than once?
|
||||||
|
if(ev == CRASH) {
|
||||||
|
if(repo != nullptr) git_repository_free(repo);
|
||||||
|
git_libgit2_shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
54
builder.hpp
54
builder.hpp
|
@ -2,6 +2,11 @@
|
||||||
#include "gui.hpp"
|
#include "gui.hpp"
|
||||||
#include "game_engine.hpp"
|
#include "game_engine.hpp"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include "fsm.hpp"
|
||||||
|
#include <efsw/efsw.hpp>
|
||||||
|
#include <future>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "watcher.hpp"
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
|
|
||||||
|
@ -14,11 +19,28 @@ struct MatchResult {
|
||||||
string message = "";
|
string message = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
class Builder {
|
enum BuildState {
|
||||||
|
START, WAITING, BUILDING, DONE, STARTING, READING,
|
||||||
|
EXIT, ERROR
|
||||||
|
};
|
||||||
|
|
||||||
|
enum BuildEvent {
|
||||||
|
GO, QUIT, CRASH
|
||||||
|
};
|
||||||
|
|
||||||
|
class Builder : DeadSimpleFSM<BuildState, BuildEvent> {
|
||||||
GUI gui;
|
GUI gui;
|
||||||
GameEngine game;
|
GameEngine game;
|
||||||
string git_path = "NOT SET";
|
string git_path = "NOT SET";
|
||||||
string build_cmd = "NOT SET";
|
string build_cmd = "NOT SET";
|
||||||
|
efsw::FileWatcher* fileWatcher = NULL;
|
||||||
|
UpdateListener* listener = NULL;
|
||||||
|
efsw::WatchID wid;
|
||||||
|
FILE *build_out = NULL;
|
||||||
|
bool build_done = false;
|
||||||
|
string line = "";
|
||||||
|
std::future<FILE *> build_fut;
|
||||||
|
git_repository* repo = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -27,4 +49,34 @@ class Builder {
|
||||||
MatchResult parse_line(const string &line);
|
MatchResult parse_line(const string &line);
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
|
void event(BuildEvent ev) override {
|
||||||
|
try {
|
||||||
|
if(ev == QUIT) {
|
||||||
|
exit(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(_state) {
|
||||||
|
FSM_STATE(BUILDING, building, ev);
|
||||||
|
FSM_STATE(START, start, ev);
|
||||||
|
FSM_STATE(WAITING, waiting, ev);
|
||||||
|
FSM_STATE(DONE, done, ev);
|
||||||
|
FSM_STATE(STARTING, starting, ev);
|
||||||
|
FSM_STATE(READING, reading, ev);
|
||||||
|
FSM_STATE(EXIT, exit, ev);
|
||||||
|
FSM_STATE(ERROR, exit, ev);
|
||||||
|
}
|
||||||
|
} catch(...) {
|
||||||
|
error(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void building(BuildEvent ev);
|
||||||
|
void start(BuildEvent ev);
|
||||||
|
void waiting(BuildEvent ev);
|
||||||
|
void done(BuildEvent ev);
|
||||||
|
void starting(BuildEvent ev);
|
||||||
|
void reading(BuildEvent ev);
|
||||||
|
void error(BuildEvent ev);
|
||||||
|
void exit(BuildEvent ev);
|
||||||
};
|
};
|
||||||
|
|
11
fsm.hpp
11
fsm.hpp
|
@ -1,5 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
|
#define FSM_EV(S, F) case S: F(); break
|
||||||
|
#define FSM_STATE(S, F, E) case S: fmt::println(">>> " #S ":" #F ":{}", int(E)); F(E); break
|
||||||
|
|
||||||
template<typename S, typename E>
|
template<typename S, typename E>
|
||||||
class DeadSimpleFSM {
|
class DeadSimpleFSM {
|
||||||
protected:
|
protected:
|
||||||
|
@ -11,6 +16,8 @@ public:
|
||||||
void state(S next_state) {
|
void state(S next_state) {
|
||||||
_state = next_state;
|
_state = next_state;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
#define FSM_T(S, F) case S: F(); break
|
bool in_state(S state) {
|
||||||
|
return _state == state;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
14
fsmtest.cpp
14
fsmtest.cpp
|
@ -14,24 +14,24 @@ enum MyEvent {
|
||||||
class MyFSM : DeadSimpleFSM<MyState, MyEvent> {
|
class MyFSM : DeadSimpleFSM<MyState, MyEvent> {
|
||||||
public:
|
public:
|
||||||
void event(MyEvent ev) override {
|
void event(MyEvent ev) override {
|
||||||
switch(ev) {
|
switch(_state) {
|
||||||
FSM_T(STARTED, start);
|
FSM_STATE(START, start, ev);
|
||||||
FSM_T(PUSH, push);
|
FSM_STATE(RUNNING, push, ev);
|
||||||
FSM_T(QUIT, quit);
|
FSM_STATE(END, quit, ev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void start() {
|
void start(MyEvent ev) {
|
||||||
println("<<< START");
|
println("<<< START");
|
||||||
state(RUNNING);
|
state(RUNNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
void push() {
|
void push(MyEvent ev) {
|
||||||
println("<<< RUN");
|
println("<<< RUN");
|
||||||
state(RUNNING);
|
state(RUNNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
void quit() {
|
void quit(MyEvent ev) {
|
||||||
println("<<< STOP");
|
println("<<< STOP");
|
||||||
state(END);
|
state(END);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue