diff options
author | Star Rauchenberger <fefferburbia@gmail.com> | 2024-12-17 15:40:19 -0500 |
---|---|---|
committer | Star Rauchenberger <fefferburbia@gmail.com> | 2024-12-17 15:40:19 -0500 |
commit | ad7c3e616fdc6f13812ec52675d226a19351494a (patch) | |
tree | 4cdd8b27c6df4e5f3475fe1b3f1815689b387fd7 /src/ipc_state.cpp | |
parent | 5ea44a418ec5db446dd28daf5ed95f46fea504f8 (diff) | |
download | lingo-ap-tracker-ad7c3e616fdc6f13812ec52675d226a19351494a.tar.gz lingo-ap-tracker-ad7c3e616fdc6f13812ec52675d226a19351494a.tar.bz2 lingo-ap-tracker-ad7c3e616fdc6f13812ec52675d226a19351494a.zip |
Added getting player position from IPC
Diffstat (limited to 'src/ipc_state.cpp')
-rw-r--r-- | src/ipc_state.cpp | 231 |
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 | |||
20 | namespace { | ||
21 | |||
22 | constexpr const char* kIpcAddress = "ws://127.0.0.1:41253"; | ||
23 | |||
24 | struct 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 | |||
210 | IPCState& GetState() { | ||
211 | static IPCState* instance = new IPCState(); | ||
212 | return *instance; | ||
213 | } | ||
214 | |||
215 | } // namespace | ||
216 | |||
217 | void IPC_Start(TrackerFrame* tracker_frame) { GetState().Start(tracker_frame); } | ||
218 | |||
219 | std::optional<std::string> IPC_GetStatusMessage() { | ||
220 | return GetState().GetStatusMessage(); | ||
221 | } | ||
222 | |||
223 | void IPC_SetTrackerSlot(std::string server, std::string user) { | ||
224 | GetState().SetTrackerSlot(server, user); | ||
225 | } | ||
226 | |||
227 | bool IPC_IsConnected() { return GetState().IsConnected(); } | ||
228 | |||
229 | std::optional<std::tuple<int, int>> IPC_GetPlayerPosition() { | ||
230 | return GetState().GetPlayerPosition(); | ||
231 | } | ||