blob: 18f318f7247d5b6cdfc8fa703ef5e8df0747fd3b (
plain) (
tree)
|
|
#include "ipc_state.h"
#define _WEBSOCKETPP_CPP11_STRICT_
#include <fmt/core.h>
#include <chrono>
#include <memory>
#include <mutex>
#include <nlohmann/json.hpp>
#include <optional>
#include <string>
#include <thread>
#include <tuple>
#include <wswrap.hpp>
#include "logger.h"
#include "tracker_frame.h"
namespace {
constexpr const char* kIpcAddress = "ws://127.0.0.1:41253";
struct IPCState {
std::mutex state_mutex;
TrackerFrame* tracker_frame = nullptr;
// Protected state
std::optional<std::string> status_message;
bool slot_matches = false;
std::string tracker_ap_server;
std::string tracker_ap_user;
std::string game_ap_server;
std::string game_ap_user;
std::optional<std::tuple<int, int>> player_position;
int backoff_amount = 0;
// Thread state
std::unique_ptr<wswrap::WS> ws;
bool connected = false;
void Start(TrackerFrame* frame) {
tracker_frame = frame;
std::thread([this]() { Thread(); }).detach();
}
std::optional<std::string> GetStatusMessage() {
std::lock_guard state_guard(state_mutex);
return status_message;
}
void SetTrackerSlot(std::string server, std::string user) {
std::lock_guard state_guard(state_mutex);
tracker_ap_server = std::move(server);
tracker_ap_user = std::move(user);
CheckIfSlotMatches();
backoff_amount = 0;
}
bool IsConnected() {
std::lock_guard state_guard(state_mutex);
return slot_matches;
}
std::optional<std::tuple<int, int>> GetPlayerPosition() {
std::lock_guard state_guard(state_mutex);
return player_position;
}
private:
void Thread() {
for (;;) {
player_position = std::nullopt;
TrackerLog("Looking for game over IPC...");
{
std::lock_guard state_guard(state_mutex);
backoff_amount = 0;
}
while (!TryConnect() || !connected) {
int backoff_limit;
{
std::lock_guard state_guard(state_mutex);
backoff_limit = (backoff_amount + 1) * 10;
}
for (int i = 0; i < backoff_limit && !connected; i++) {
ws->poll();
// Back off
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
{
std::lock_guard state_guard(state_mutex);
backoff_amount = std::min(9, backoff_amount + 1);
if (!connected) {
TrackerLog(fmt::format("Retrying IPC in {} second(s)...",
backoff_amount + 1));
}
}
}
while (connected) {
ws->poll();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
SetStatusMessage("Disconnected from game.");
}
}
bool TryConnect() {
try {
ws = std::make_unique<wswrap::WS>(
kIpcAddress, [this]() { OnConnect(); }, [this]() { OnClose(); },
[this](const std::string& s) { OnMessage(s); },
[this](const std::string& s) { OnError(s); });
return true;
} catch (const std::exception& ex) {
ws.reset();
return false;
}
}
void OnConnect() { connected = true; }
void OnClose() {
connected = false;
{
std::lock_guard state_guard(state_mutex);
slot_matches = false;
}
}
void OnMessage(const std::string& s) {
TrackerLog(s);
auto msg = nlohmann::json::parse(s);
if (msg["cmd"] == "Connect") {
std::lock_guard state_guard(state_mutex);
game_ap_server = msg["slot"]["server"];
game_ap_user = msg["slot"]["player"];
CheckIfSlotMatches();
} else if (msg["cmd"] == "UpdatePosition") {
std::lock_guard state_guard(state_mutex);
player_position =
std::make_tuple<int, int>(msg["position"]["x"], msg["position"]["z"]);
tracker_frame->RedrawPosition();
}
}
void OnError(const std::string& s) {}
void CheckIfSlotMatches() {
slot_matches = (tracker_ap_server == game_ap_server &&
tracker_ap_user == game_ap_user);
if (slot_matches) {
status_message = "Connected to game.";
Sync();
} else if (tracker_ap_server.empty()) {
status_message = std::nullopt;
} else if (connected) {
status_message = "Local game doesn't match AP slot.";
}
tracker_frame->UpdateStatusMessage();
}
void SetStatusMessage(std::optional<std::string> msg) {
{
std::lock_guard state_guard(state_mutex);
status_message = msg;
}
tracker_frame->UpdateStatusMessage();
}
void Sync() {
nlohmann::json msg;
msg["cmd"] = "Sync";
ws->send_text(msg.dump());
}
};
IPCState& GetState() {
static IPCState* instance = new IPCState();
return *instance;
}
} // namespace
void IPC_Start(TrackerFrame* tracker_frame) { GetState().Start(tracker_frame); }
std::optional<std::string> IPC_GetStatusMessage() {
return GetState().GetStatusMessage();
}
void IPC_SetTrackerSlot(std::string server, std::string user) {
GetState().SetTrackerSlot(server, user);
}
bool IPC_IsConnected() { return GetState().IsConnected(); }
std::optional<std::tuple<int, int>> IPC_GetPlayerPosition() {
return GetState().GetPlayerPosition();
}
|