Builder is now using the FSM I wrote. Still not as clean as I like but big improvement.

This commit is contained in:
Zed A. Shaw 2024-09-10 01:56:22 -04:00
parent dcf1a4020d
commit a7c5de6ac3
4 changed files with 176 additions and 129 deletions

View file

@ -1,12 +1,6 @@
#include "builder.hpp"
#include "dbc.hpp"
#include "watcher.hpp"
#include "game_engine.hpp"
#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 <fstream>
#include <git2.h>
@ -82,122 +76,116 @@ MatchResult Builder::parse_line(const string &line) {
}
}
enum BuildState {
WAITING, BUILDING, DONE, STARTING, READING
};
void Builder::run() {
git_repository* repo = nullptr;
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;
}
int rc = gui.main_loop(game, [&] {
event(GO);
return 0;
});
});
dbc::check(rc == 0, "Invalid return from main_loop.");
if(rc != 0) println("ERROR IN GUI");
fileWatcher->removeWatch(wid);
git_libgit2_shutdown();
} catch(dbc::Error &err) {
if(repo != nullptr) git_repository_free(repo);
git_libgit2_shutdown();
throw err;
event(QUIT);
}
void Builder::building(BuildEvent ev) {
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);
}
}
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();
}
}