about summary refs log tree commit diff stats
path: root/ap_state.cpp
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2023-05-03 18:27:37 -0400
committerStar Rauchenberger <fefferburbia@gmail.com>2023-05-03 18:27:37 -0400
commit325ea53e7bb873870b46d3e38b2b314493a22f79 (patch)
tree53968efbfd78a5960f68147cd6c671513f04938d /ap_state.cpp
parent08ffb400114029569b4043b4f4c5a3f2af9b37b8 (diff)
downloadlingo-ap-tracker-325ea53e7bb873870b46d3e38b2b314493a22f79.tar.gz
lingo-ap-tracker-325ea53e7bb873870b46d3e38b2b314493a22f79.tar.bz2
lingo-ap-tracker-325ea53e7bb873870b46d3e38b2b314493a22f79.zip
Refactored APState
It is no longer a class, because it didn't need to be.
Diffstat (limited to 'ap_state.cpp')
-rw-r--r--ap_state.cpp245
1 files changed, 143 insertions, 102 deletions
diff --git a/ap_state.cpp b/ap_state.cpp index 8b7339a..c13b87c 100644 --- a/ap_state.cpp +++ b/ap_state.cpp
@@ -11,9 +11,14 @@
11#include <chrono> 11#include <chrono>
12#include <exception> 12#include <exception>
13#include <list> 13#include <list>
14#include <memory>
15#include <mutex>
16#include <set>
14#include <thread> 17#include <thread>
18#include <tuple>
15 19
16#include "game_data.h" 20#include "game_data.h"
21#include "tracker_frame.h"
17#include "tracker_state.h" 22#include "tracker_state.h"
18 23
19constexpr int AP_MAJOR = 0; 24constexpr int AP_MAJOR = 0;
@@ -22,29 +27,81 @@ constexpr int AP_REVISION = 0;
22 27
23constexpr int ITEM_HANDLING = 7; // <- all 28constexpr int ITEM_HANDLING = 7; // <- all
24 29
25static APClient* apclient = nullptr; 30namespace {
26 31
27APState::APState() { 32APClient* apclient = nullptr;
28 std::thread([this]() { 33
29 for (;;) { 34bool initialized = false;
30 { 35
31 std::lock_guard client_guard(client_mutex_); 36TrackerFrame* tracker_frame;
32 if (apclient) { 37
33 apclient->poll(); 38bool client_active = false;
39std::mutex client_mutex;
40
41bool connected = false;
42bool has_connection_result = false;
43
44std::map<int64_t, int> inventory;
45std::set<int64_t> checked_locations;
46
47std::map<std::tuple<int, int>, int64_t> ap_id_by_location_id;
48std::map<std::string, int64_t> ap_id_by_item_name;
49std::map<LingoColor, int64_t> ap_id_by_color;
50std::map<int64_t, std::string> progressive_item_by_ap_id;
51
52DoorShuffleMode door_shuffle_mode = kNO_DOORS;
53bool color_shuffle = false;
54bool painting_shuffle = false;
55
56std::map<std::string, std::string> painting_mapping;
57
58void RefreshTracker() {
59 GetTrackerState().CalculateState();
60 tracker_frame->UpdateIndicators();
61}
62
63int64_t GetItemId(const std::string& item_name) {
64 int64_t ap_id = apclient->get_item_id(item_name);
65 if (ap_id == APClient::INVALID_NAME_ID) {
66 std::cout << "Could not find AP item ID for " << item_name << std::endl;
67 }
68
69 return ap_id;
70}
71
72void DestroyClient() {
73 client_active = false;
74 apclient->reset();
75 delete apclient;
76 apclient = nullptr;
77}
78
79} // namespace
80
81void AP_SetTrackerFrame(TrackerFrame* arg) { tracker_frame = arg; }
82
83void AP_Connect(std::string server, std::string player, std::string password) {
84 if (!initialized) {
85 std::thread([]() {
86 for (;;) {
87 {
88 std::lock_guard client_guard(client_mutex);
89 if (apclient) {
90 apclient->poll();
91 }
34 } 92 }
93
94 std::this_thread::sleep_for(std::chrono::milliseconds(100));
35 } 95 }
96 }).detach();
36 97
37 std::this_thread::sleep_for(std::chrono::milliseconds(100)); 98 initialized = true;
38 } 99 }
39 }).detach();
40}
41 100
42void APState::Connect(std::string server, std::string player, 101 tracker_frame->SetStatusMessage("Connecting to Archipelago server....");
43 std::string password) {
44 tracker_frame_->SetStatusMessage("Connecting to Archipelago server....");
45 102
46 { 103 {
47 std::lock_guard client_guard(client_mutex_); 104 std::lock_guard client_guard(client_mutex);
48 105
49 if (apclient) { 106 if (apclient) {
50 DestroyClient(); 107 DestroyClient();
@@ -53,20 +110,20 @@ void APState::Connect(std::string server, std::string player,
53 apclient = new APClient(ap_get_uuid(""), "Lingo", server); 110 apclient = new APClient(ap_get_uuid(""), "Lingo", server);
54 } 111 }
55 112
56 inventory_.clear(); 113 inventory.clear();
57 checked_locations_.clear(); 114 checked_locations.clear();
58 door_shuffle_mode_ = kNO_DOORS; 115 door_shuffle_mode = kNO_DOORS;
59 color_shuffle_ = false; 116 color_shuffle = false;
60 painting_shuffle_ = false; 117 painting_shuffle = false;
61 painting_mapping_.clear(); 118 painting_mapping.clear();
62 119
63 connected_ = false; 120 connected = false;
64 has_connection_result_ = false; 121 has_connection_result = false;
65 122
66 apclient->set_room_info_handler([this, player, password]() { 123 apclient->set_room_info_handler([player, password]() {
67 inventory_.clear(); 124 inventory.clear();
68 125
69 tracker_frame_->SetStatusMessage( 126 tracker_frame->SetStatusMessage(
70 "Connected to Archipelago server. Authenticating..."); 127 "Connected to Archipelago server. Authenticating...");
71 128
72 apclient->ConnectSlot(player, password, ITEM_HANDLING, {"Tracker"}, 129 apclient->ConnectSlot(player, password, ITEM_HANDLING, {"Tracker"},
@@ -74,61 +131,60 @@ void APState::Connect(std::string server, std::string player,
74 }); 131 });
75 132
76 apclient->set_location_checked_handler( 133 apclient->set_location_checked_handler(
77 [this](const std::list<int64_t>& locations) { 134 [](const std::list<int64_t>& locations) {
78 for (const int64_t location_id : locations) { 135 for (const int64_t location_id : locations) {
79 checked_locations_.insert(location_id); 136 checked_locations.insert(location_id);
80 std::cout << "Location: " << location_id << std::endl; 137 std::cout << "Location: " << location_id << std::endl;
81 } 138 }
82 139
83 RefreshTracker(); 140 RefreshTracker();
84 }); 141 });
85 142
86 apclient->set_slot_disconnected_handler([this]() { 143 apclient->set_slot_disconnected_handler([]() {
87 tracker_frame_->SetStatusMessage( 144 tracker_frame->SetStatusMessage(
88 "Disconnected from Archipelago. Attempting to reconnect..."); 145 "Disconnected from Archipelago. Attempting to reconnect...");
89 }); 146 });
90 147
91 apclient->set_socket_disconnected_handler([this]() { 148 apclient->set_socket_disconnected_handler([]() {
92 tracker_frame_->SetStatusMessage( 149 tracker_frame->SetStatusMessage(
93 "Disconnected from Archipelago. Attempting to reconnect..."); 150 "Disconnected from Archipelago. Attempting to reconnect...");
94 }); 151 });
95 152
96 apclient->set_items_received_handler( 153 apclient->set_items_received_handler(
97 [this](const std::list<APClient::NetworkItem>& items) { 154 [](const std::list<APClient::NetworkItem>& items) {
98 for (const APClient::NetworkItem& item : items) { 155 for (const APClient::NetworkItem& item : items) {
99 inventory_[item.item]++; 156 inventory[item.item]++;
100 std::cout << "Item: " << item.item << std::endl; 157 std::cout << "Item: " << item.item << std::endl;
101 } 158 }
102 159
103 RefreshTracker(); 160 RefreshTracker();
104 }); 161 });
105 162
106 apclient->set_slot_connected_handler([this](const nlohmann::json& slot_data) { 163 apclient->set_slot_connected_handler([](const nlohmann::json& slot_data) {
107 tracker_frame_->SetStatusMessage("Connected to Archipelago!"); 164 tracker_frame->SetStatusMessage("Connected to Archipelago!");
108 165
109 door_shuffle_mode_ = slot_data["shuffle_doors"].get<DoorShuffleMode>(); 166 door_shuffle_mode = slot_data["shuffle_doors"].get<DoorShuffleMode>();
110 color_shuffle_ = slot_data["shuffle_colors"].get<bool>(); 167 color_shuffle = slot_data["shuffle_colors"].get<bool>();
111 painting_shuffle_ = slot_data["shuffle_paintings"].get<bool>(); 168 painting_shuffle = slot_data["shuffle_paintings"].get<bool>();
112 169
113 if (painting_shuffle_ && slot_data.contains("painting_entrance_to_exit")) { 170 if (painting_shuffle && slot_data.contains("painting_entrance_to_exit")) {
114 painting_mapping_.clear(); 171 painting_mapping.clear();
115 172
116 for (const auto& mapping_it : 173 for (const auto& mapping_it :
117 slot_data["painting_entrance_to_exit"].items()) { 174 slot_data["painting_entrance_to_exit"].items()) {
118 painting_mapping_[mapping_it.key()] = mapping_it.value(); 175 painting_mapping[mapping_it.key()] = mapping_it.value();
119 } 176 }
120 } 177 }
121 178
122 connected_ = true; 179 connected = true;
123 has_connection_result_ = true; 180 has_connection_result = true;
124 }); 181 });
125 182
126 apclient->set_slot_refused_handler([this]( 183 apclient->set_slot_refused_handler([](const std::list<std::string>& errors) {
127 const std::list<std::string>& errors) { 184 connected = false;
128 connected_ = false; 185 has_connection_result = true;
129 has_connection_result_ = true;
130 186
131 tracker_frame_->SetStatusMessage("Disconnected from Archipelago."); 187 tracker_frame->SetStatusMessage("Disconnected from Archipelago.");
132 188
133 std::vector<std::string> error_messages; 189 std::vector<std::string> error_messages;
134 error_messages.push_back("Could not connect to Archipelago."); 190 error_messages.push_back("Could not connect to Archipelago.");
@@ -158,19 +214,19 @@ void APState::Connect(std::string server, std::string player,
158 wxMessageBox(full_message, "Connection failed", wxOK | wxICON_ERROR); 214 wxMessageBox(full_message, "Connection failed", wxOK | wxICON_ERROR);
159 }); 215 });
160 216
161 client_active_ = true; 217 client_active = true;
162 218
163 int timeout = 5000; // 5 seconds 219 int timeout = 5000; // 5 seconds
164 int interval = 100; 220 int interval = 100;
165 int remaining_loops = timeout / interval; 221 int remaining_loops = timeout / interval;
166 while (!has_connection_result_) { 222 while (!has_connection_result) {
167 if (interval == 0) { 223 if (interval == 0) {
168 connected_ = false; 224 connected = false;
169 has_connection_result_ = true; 225 has_connection_result = true;
170 226
171 DestroyClient(); 227 DestroyClient();
172 228
173 tracker_frame_->SetStatusMessage("Disconnected from Archipelago."); 229 tracker_frame->SetStatusMessage("Disconnected from Archipelago.");
174 230
175 wxMessageBox("Timeout while connecting to Archipelago server.", 231 wxMessageBox("Timeout while connecting to Archipelago server.",
176 "Connection failed", wxOK | wxICON_ERROR); 232 "Connection failed", wxOK | wxICON_ERROR);
@@ -181,7 +237,7 @@ void APState::Connect(std::string server, std::string player,
181 interval--; 237 interval--;
182 } 238 }
183 239
184 if (connected_) { 240 if (connected) {
185 for (const MapArea& map_area : GetGameData().GetMapAreas()) { 241 for (const MapArea& map_area : GetGameData().GetMapAreas()) {
186 for (int section_id = 0; section_id < map_area.locations.size(); 242 for (int section_id = 0; section_id < map_area.locations.size();
187 section_id++) { 243 section_id++) {
@@ -192,91 +248,76 @@ void APState::Connect(std::string server, std::string player,
192 std::cout << "Could not find AP location ID for " 248 std::cout << "Could not find AP location ID for "
193 << location.ap_location_name << std::endl; 249 << location.ap_location_name << std::endl;
194 } else { 250 } else {
195 ap_id_by_location_id_[{map_area.id, section_id}] = ap_id; 251 ap_id_by_location_id[{map_area.id, section_id}] = ap_id;
196 } 252 }
197 } 253 }
198 } 254 }
199 255
200 for (const Door& door : GetGameData().GetDoors()) { 256 for (const Door& door : GetGameData().GetDoors()) {
201 if (!door.skip_item) { 257 if (!door.skip_item) {
202 ap_id_by_item_name_[door.item_name] = GetItemId(door.item_name); 258 ap_id_by_item_name[door.item_name] = GetItemId(door.item_name);
203 259
204 if (!door.group_name.empty() && 260 if (!door.group_name.empty() &&
205 !ap_id_by_item_name_.count(door.group_name)) { 261 !ap_id_by_item_name.count(door.group_name)) {
206 ap_id_by_item_name_[door.group_name] = GetItemId(door.group_name); 262 ap_id_by_item_name[door.group_name] = GetItemId(door.group_name);
207 } 263 }
208 264
209 for (const ProgressiveRequirement& prog_req : door.progressives) { 265 for (const ProgressiveRequirement& prog_req : door.progressives) {
210 ap_id_by_item_name_[prog_req.item_name] = GetItemId(prog_req.item_name); 266 ap_id_by_item_name[prog_req.item_name] =
267 GetItemId(prog_req.item_name);
211 } 268 }
212 } 269 }
213 } 270 }
214 271
215 ap_id_by_color_[LingoColor::kBlack] = GetItemId("Black"); 272 ap_id_by_color[LingoColor::kBlack] = GetItemId("Black");
216 ap_id_by_color_[LingoColor::kRed] = GetItemId("Red"); 273 ap_id_by_color[LingoColor::kRed] = GetItemId("Red");
217 ap_id_by_color_[LingoColor::kBlue] = GetItemId("Blue"); 274 ap_id_by_color[LingoColor::kBlue] = GetItemId("Blue");
218 ap_id_by_color_[LingoColor::kYellow] = GetItemId("Yellow"); 275 ap_id_by_color[LingoColor::kYellow] = GetItemId("Yellow");
219 ap_id_by_color_[LingoColor::kPurple] = GetItemId("Purple"); 276 ap_id_by_color[LingoColor::kPurple] = GetItemId("Purple");
220 ap_id_by_color_[LingoColor::kOrange] = GetItemId("Orange"); 277 ap_id_by_color[LingoColor::kOrange] = GetItemId("Orange");
221 ap_id_by_color_[LingoColor::kGreen] = GetItemId("Green"); 278 ap_id_by_color[LingoColor::kGreen] = GetItemId("Green");
222 ap_id_by_color_[LingoColor::kBrown] = GetItemId("Brown"); 279 ap_id_by_color[LingoColor::kBrown] = GetItemId("Brown");
223 ap_id_by_color_[LingoColor::kGray] = GetItemId("Gray"); 280 ap_id_by_color[LingoColor::kGray] = GetItemId("Gray");
224 281
225 RefreshTracker(); 282 RefreshTracker();
226 } else { 283 } else {
227 client_active_ = false; 284 client_active = false;
228 } 285 }
229} 286}
230 287
231bool APState::HasCheckedGameLocation(int area_id, int section_id) const { 288bool AP_HasCheckedGameLocation(int area_id, int section_id) {
232 std::tuple<int, int> location_key = {area_id, section_id}; 289 std::tuple<int, int> location_key = {area_id, section_id};
233 290
234 if (ap_id_by_location_id_.count(location_key)) { 291 if (ap_id_by_location_id.count(location_key)) {
235 return checked_locations_.count(ap_id_by_location_id_.at(location_key)); 292 return checked_locations.count(ap_id_by_location_id.at(location_key));
236 } else { 293 } else {
237 return false; 294 return false;
238 } 295 }
239} 296}
240 297
241bool APState::HasColorItem(LingoColor color) const { 298bool AP_HasColorItem(LingoColor color) {
242 if (ap_id_by_color_.count(color)) { 299 if (ap_id_by_color.count(color)) {
243 return inventory_.count(ap_id_by_color_.at(color)); 300 return inventory.count(ap_id_by_color.at(color));
244 } else { 301 } else {
245 return false; 302 return false;
246 } 303 }
247} 304}
248 305
249bool APState::HasItem(const std::string& item, int quantity) const { 306bool AP_HasItem(const std::string& item, int quantity) {
250 if (ap_id_by_item_name_.count(item)) { 307 if (ap_id_by_item_name.count(item)) {
251 int64_t ap_id = ap_id_by_item_name_.at(item); 308 int64_t ap_id = ap_id_by_item_name.at(item);
252 return inventory_.count(ap_id) && inventory_.at(ap_id) >= quantity; 309 return inventory.count(ap_id) && inventory.at(ap_id) >= quantity;
253 } else { 310 } else {
254 return false; 311 return false;
255 } 312 }
256} 313}
257 314
258void APState::RefreshTracker() { 315DoorShuffleMode AP_GetDoorShuffleMode() { return door_shuffle_mode; }
259 GetTrackerState().CalculateState();
260 tracker_frame_->UpdateIndicators();
261}
262
263int64_t APState::GetItemId(const std::string& item_name) {
264 int64_t ap_id = apclient->get_item_id(item_name);
265 if (ap_id == APClient::INVALID_NAME_ID) {
266 std::cout << "Could not find AP item ID for " << item_name << std::endl;
267 }
268 316
269 return ap_id; 317bool AP_IsColorShuffle() { return color_shuffle; }
270}
271 318
272void APState::DestroyClient() { 319bool AP_IsPaintingShuffle() { return painting_shuffle; }
273 client_active_ = false;
274 apclient->reset();
275 delete apclient;
276 apclient = nullptr;
277}
278 320
279APState& GetAPState() { 321const std::map<std::string, std::string> AP_GetPaintingMapping() {
280 static APState* instance = new APState(); 322 return painting_mapping;
281 return *instance;
282} 323}