about summary refs log tree commit diff stats
path: root/src/ipc_state.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ipc_state.cpp')
-rw-r--r--src/ipc_state.cpp231
1 files changed, 231 insertions, 0 deletions
diff --git a/src/ipc_state.cpp b/src/ipc_state.cpp new file mode 100644 index 0000000..18f318f --- /dev/null +++ b/src/ipc_state.cpp
@@ -0,0 +1,231 @@
1#include "ipc_state.h"
2
3#define _WEBSOCKETPP_CPP11_STRICT_
4
5#include <fmt/core.h>
6
7#include <chrono>
8#include <memory>
9#include <mutex>
10#include <nlohmann/json.hpp>
11#include <optional>
12#include <string>
13#include <thread>
14#include <tuple>
15#include <wswrap.hpp>
16
17#include "logger.h"
18#include "tracker_frame.h"
19
20namespace {
21
22constexpr const char* kIpcAddress = "ws://127.0.0.1:41253";
23
24struct IPCState {
25 std::mutex state_mutex;
26 TrackerFrame* tracker_frame = nullptr;
27
28 // Protected state
29 std::optional<std::string> status_message;
30
31 bool slot_matches = false;
32 std::string tracker_ap_server;
33 std::string tracker_ap_user;
34 std::string game_ap_server;
35 std::string game_ap_user;
36
37 std::optional<std::tuple<int, int>> player_position;
38
39 int backoff_amount = 0;
40
41 // Thread state
42 std::unique_ptr<wswrap::WS> ws;
43 bool connected = false;
44
45 void Start(TrackerFrame* frame) {
46 tracker_frame = frame;
47
48 std::thread([this]() { Thread(); }).detach();
49 }
50
51 std::optional<std::string> GetStatusMessage() {
52 std::lock_guard state_guard(state_mutex);
53
54 return status_message;
55 }
56
57 void SetTrackerSlot(std::string server, std::string user) {
58 std::lock_guard state_guard(state_mutex);
59
60 tracker_ap_server = std::move(server);
61 tracker_ap_user = std::move(user);
62
63 CheckIfSlotMatches();
64
65 backoff_amount = 0;
66 }
67
68 bool IsConnected() {
69 std::lock_guard state_guard(state_mutex);
70
71 return slot_matches;
72 }
73
74 std::optional<std::tuple<int, int>> GetPlayerPosition() {
75 std::lock_guard state_guard(state_mutex);
76
77 return player_position;
78 }
79
80 private:
81 void Thread() {
82 for (;;) {
83 player_position = std::nullopt;
84
85 TrackerLog("Looking for game over IPC...");
86
87 {
88 std::lock_guard state_guard(state_mutex);
89 backoff_amount = 0;
90 }
91
92 while (!TryConnect() || !connected) {
93 int backoff_limit;
94 {
95 std::lock_guard state_guard(state_mutex);
96 backoff_limit = (backoff_amount + 1) * 10;
97 }
98
99 for (int i = 0; i < backoff_limit && !connected; i++) {
100 ws->poll();
101
102 // Back off
103 std::this_thread::sleep_for(std::chrono::milliseconds(100));
104 }
105
106 {
107 std::lock_guard state_guard(state_mutex);
108 backoff_amount = std::min(9, backoff_amount + 1);
109
110 if (!connected) {
111 TrackerLog(fmt::format("Retrying IPC in {} second(s)...",
112 backoff_amount + 1));
113 }
114 }
115 }
116
117 while (connected) {
118 ws->poll();
119
120 std::this_thread::sleep_for(std::chrono::milliseconds(100));
121 }
122
123 SetStatusMessage("Disconnected from game.");
124 }
125 }
126
127 bool TryConnect() {
128 try {
129 ws = std::make_unique<wswrap::WS>(
130 kIpcAddress, [this]() { OnConnect(); }, [this]() { OnClose(); },
131 [this](const std::string& s) { OnMessage(s); },
132 [this](const std::string& s) { OnError(s); });
133 return true;
134 } catch (const std::exception& ex) {
135 ws.reset();
136 return false;
137 }
138 }
139
140 void OnConnect() { connected = true; }
141
142 void OnClose() {
143 connected = false;
144
145 {
146 std::lock_guard state_guard(state_mutex);
147
148 slot_matches = false;
149 }
150 }
151
152 void OnMessage(const std::string& s) {
153 TrackerLog(s);
154
155 auto msg = nlohmann::json::parse(s);
156
157 if (msg["cmd"] == "Connect") {
158 std::lock_guard state_guard(state_mutex);
159
160 game_ap_server = msg["slot"]["server"];
161 game_ap_user = msg["slot"]["player"];
162
163 CheckIfSlotMatches();
164 } else if (msg["cmd"] == "UpdatePosition") {
165 std::lock_guard state_guard(state_mutex);
166
167 player_position =
168 std::make_tuple<int, int>(msg["position"]["x"], msg["position"]["z"]);
169
170 tracker_frame->RedrawPosition();
171 }
172 }
173
174 void OnError(const std::string& s) {}
175
176 void CheckIfSlotMatches() {
177 slot_matches = (tracker_ap_server == game_ap_server &&
178 tracker_ap_user == game_ap_user);
179
180 if (slot_matches) {
181 status_message = "Connected to game.";
182
183 Sync();
184 } else if (tracker_ap_server.empty()) {
185 status_message = std::nullopt;
186 } else if (connected) {
187 status_message = "Local game doesn't match AP slot.";
188 }
189
190 tracker_frame->UpdateStatusMessage();
191 }
192
193 void SetStatusMessage(std::optional<std::string> msg) {
194 {
195 std::lock_guard state_guard(state_mutex);
196 status_message = msg;
197 }
198
199 tracker_frame->UpdateStatusMessage();
200 }
201
202 void Sync() {
203 nlohmann::json msg;
204 msg["cmd"] = "Sync";
205
206 ws->send_text(msg.dump());
207 }
208};
209
210IPCState& GetState() {
211 static IPCState* instance = new IPCState();
212 return *instance;
213}
214
215} // namespace
216
217void IPC_Start(TrackerFrame* tracker_frame) { GetState().Start(tracker_frame); }
218
219std::optional<std::string> IPC_GetStatusMessage() {
220 return GetState().GetStatusMessage();
221}
222
223void IPC_SetTrackerSlot(std::string server, std::string user) {
224 GetState().SetTrackerSlot(server, user);
225}
226
227bool IPC_IsConnected() { return GetState().IsConnected(); }
228
229std::optional<std::tuple<int, int>> IPC_GetPlayerPosition() {
230 return GetState().GetPlayerPosition();
231}