Windows support: portable local server launch + clean quit, %APPDATA% data dir, Win32 libs, MinGW/MSVC build docs

This commit is contained in:
Gabe
2026-10-02 15:44:12 -05:00
parent 238cd9c6c3
commit 34fa21139f
8 changed files with 326 additions and 53 deletions

View File

@@ -9,18 +9,13 @@
#include <algorithm>
#include <chrono>
#include <cmath>
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <filesystem>
#include <fstream>
#include <random>
#include <thread>
#include <spawn.h>
#include <sys/wait.h>
#include <unistd.h>
extern char** environ;
namespace bs {
@@ -29,15 +24,6 @@ namespace ui = hd2d::ui;
namespace {
fs::path dataDir() {
const char* xdg = std::getenv("XDG_DATA_HOME");
fs::path base = xdg && *xdg ? fs::path(xdg) : fs::path(std::getenv("HOME") ? std::getenv("HOME") : ".") / ".local/share";
fs::path d = base / "brandling-safari";
std::error_code ec;
fs::create_directories(d, ec);
return d;
}
std::string randomToken() {
std::random_device rd;
static const char* hex = "0123456789abcdef";
@@ -106,7 +92,7 @@ int keyCode(const std::string& k) {
// ------------------------------------------------------------------ identity
void App::loadIdentity() {
fs::path p = dataDir() / (opt_.profile.empty() ? "client.json" : "client-" + opt_.profile + ".json");
fs::path p = userDataDir() / (opt_.profile.empty() ? "client.json" : "client-" + opt_.profile + ".json");
std::ifstream f(p);
if (f) {
try {
@@ -126,7 +112,7 @@ void App::loadIdentity() {
}
void App::saveIdentity() {
fs::path p = dataDir() / (opt_.profile.empty() ? "client.json" : "client-" + opt_.profile + ".json");
fs::path p = userDataDir() / (opt_.profile.empty() ? "client.json" : "client-" + opt_.profile + ".json");
nlohmann::json j = {{"token", token_}, {"lastHost", lastHost_}, {"lastPort", lastPort_}, {"name", savedName_}};
std::ofstream(p) << j.dump(2);
}
@@ -283,38 +269,13 @@ bool App::scripted(const char* key) const { return pressed(keyCode(key)); }
// ------------------------------------------------------------------ local server
bool App::startLocalServer() {
if (serverPid_ > 0) return true;
std::error_code ec;
fs::path exe = fs::read_symlink("/proc/self/exe", ec);
fs::path server = exe.parent_path() / "safari_server";
if (ec || !fs::exists(server)) {
message_ = "safari_server was not found next to the game";
return false;
}
std::string port = std::to_string(opt_.port);
std::string name = std::string(nameBuf_) + "'s Safari";
std::vector<std::string> args = {server.string(), "--port", port, "--name", name};
std::vector<char*> argv;
for (std::string& a : args) argv.push_back(a.data());
argv.push_back(nullptr);
pid_t pid = 0;
if (posix_spawn(&pid, server.c_str(), nullptr, nullptr, argv.data(), environ) != 0) {
message_ = "could not start the local server";
return false;
}
serverPid_ = int(pid);
HD2D_LOG("started local server (pid %d) on port %s", serverPid_, port.c_str());
usleep(250000); // let it bind before we connect (the client retries anyway)
if (localServer_.running()) return true;
if (!localServer_.start(opt_.port, std::string(nameBuf_) + "'s Safari", message_)) return false;
std::this_thread::sleep_for(std::chrono::milliseconds(250)); // let it bind (the client retries anyway)
return true;
}
void App::stopLocalServer() {
if (serverPid_ <= 0) return;
kill(pid_t(serverPid_), SIGTERM);
int status = 0;
for (int i = 0; i < 50 && waitpid(pid_t(serverPid_), &status, WNOHANG) == 0; ++i) usleep(100000);
serverPid_ = -1;
}
void App::stopLocalServer() { localServer_.stop(); }
// ------------------------------------------------------------------ flow
@@ -345,7 +306,7 @@ void App::handleNet() {
NetClient::State st = net_.state();
if (st == NetClient::State::Failed && screen_ != Screen::Title && screen_ != Screen::Join) {
// a freshly started local server may not be listening yet: retry for a few seconds
if (screen_ == Screen::Connecting && serverPid_ > 0 && connectTimer_ < 6.0f) {
if (screen_ == Screen::Connecting && localServer_.running() && connectTimer_ < 6.0f) {
net_.connect(lastHost_, lastPort_, token_);
return;
}