about summary refs log tree commit diff stats
path: root/src/ap_state.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ap_state.cpp')
-rw-r--r--src/ap_state.cpp172
1 files changed, 123 insertions, 49 deletions
diff --git a/src/ap_state.cpp b/src/ap_state.cpp index b0b4f0b..876fdd8 100644 --- a/src/ap_state.cpp +++ b/src/ap_state.cpp
@@ -4,6 +4,7 @@
4#define _WEBSOCKETPP_CPP11_STRICT_ 4#define _WEBSOCKETPP_CPP11_STRICT_
5#pragma comment(lib, "crypt32") 5#pragma comment(lib, "crypt32")
6 6
7#include <fmt/core.h>
7#include <hkutil/string.h> 8#include <hkutil/string.h>
8 9
9#include <any> 10#include <any>
@@ -71,6 +72,7 @@ struct APState {
71 bool sunwarp_shuffle = false; 72 bool sunwarp_shuffle = false;
72 73
73 std::map<std::string, std::string> painting_mapping; 74 std::map<std::string, std::string> painting_mapping;
75 std::set<std::string> painting_codomain;
74 std::map<int, SunwarpMapping> sunwarp_mapping; 76 std::map<int, SunwarpMapping> sunwarp_mapping;
75 77
76 void Connect(std::string server, std::string player, std::string password) { 78 void Connect(std::string server, std::string player, std::string password) {
@@ -91,24 +93,25 @@ struct APState {
91 }).detach(); 93 }).detach();
92 94
93 for (int panel_id : GD_GetAchievementPanels()) { 95 for (int panel_id : GD_GetAchievementPanels()) {
94 tracked_data_storage_keys.push_back( 96 tracked_data_storage_keys.push_back(fmt::format(
95 "Achievement|" + GD_GetPanel(panel_id).achievement_name); 97 "Achievement|{}", GD_GetPanel(panel_id).achievement_name));
96 } 98 }
97 99
98 for (const MapArea& map_area : GD_GetMapAreas()) { 100 for (const MapArea& map_area : GD_GetMapAreas()) {
99 for (const Location& location : map_area.locations) { 101 for (const Location& location : map_area.locations) {
100 tracked_data_storage_keys.push_back( 102 tracked_data_storage_keys.push_back(
101 "Hunt|" + std::to_string(location.ap_location_id)); 103 fmt::format("Hunt|{}", location.ap_location_id));
102 } 104 }
103 } 105 }
104 106
105 tracked_data_storage_keys.push_back("PlayerPos"); 107 tracked_data_storage_keys.push_back("PlayerPos");
108 tracked_data_storage_keys.push_back("Paintings");
106 109
107 initialized = true; 110 initialized = true;
108 } 111 }
109 112
110 tracker_frame->SetStatusMessage("Connecting to Archipelago server...."); 113 tracker_frame->SetStatusMessage("Connecting to Archipelago server....");
111 TrackerLog("Connecting to Archipelago server (" + server + ")..."); 114 TrackerLog(fmt::format("Connecting to Archipelago server ({})...", server));
112 115
113 { 116 {
114 TrackerLog("Destroying old AP client..."); 117 TrackerLog("Destroying old AP client...");
@@ -137,6 +140,7 @@ struct APState {
137 color_shuffle = false; 140 color_shuffle = false;
138 painting_shuffle = false; 141 painting_shuffle = false;
139 painting_mapping.clear(); 142 painting_mapping.clear();
143 painting_codomain.clear();
140 mastery_requirement = 21; 144 mastery_requirement = 21;
141 level_2_requirement = 223; 145 level_2_requirement = 223;
142 location_checks = kNORMAL_LOCATIONS; 146 location_checks = kNORMAL_LOCATIONS;
@@ -149,16 +153,17 @@ struct APState {
149 sunwarp_shuffle = false; 153 sunwarp_shuffle = false;
150 sunwarp_mapping.clear(); 154 sunwarp_mapping.clear();
151 155
156 std::mutex connection_mutex;
152 connected = false; 157 connected = false;
153 has_connection_result = false; 158 has_connection_result = false;
154 159
155 apclient->set_room_info_handler([this, player, password]() { 160 apclient->set_room_info_handler([this, player, password]() {
156 inventory.clear(); 161 inventory.clear();
157 162
158 TrackerLog("Connected to Archipelago server. Authenticating as " + 163 TrackerLog(fmt::format(
159 player + 164 "Connected to Archipelago server. Authenticating as {} {}", player,
160 (password.empty() ? " without password" 165 (password.empty() ? "without password"
161 : " with password " + password)); 166 : "with password " + password)));
162 tracker_frame->SetStatusMessage( 167 tracker_frame->SetStatusMessage(
163 "Connected to Archipelago server. Authenticating..."); 168 "Connected to Archipelago server. Authenticating...");
164 169
@@ -170,10 +175,10 @@ struct APState {
170 [this](const std::list<int64_t>& locations) { 175 [this](const std::list<int64_t>& locations) {
171 for (const int64_t location_id : locations) { 176 for (const int64_t location_id : locations) {
172 checked_locations.insert(location_id); 177 checked_locations.insert(location_id);
173 TrackerLog("Location: " + std::to_string(location_id)); 178 TrackerLog(fmt::format("Location: {}", location_id));
174 } 179 }
175 180
176 RefreshTracker(); 181 RefreshTracker(false);
177 }); 182 });
178 183
179 apclient->set_slot_disconnected_handler([this]() { 184 apclient->set_slot_disconnected_handler([this]() {
@@ -194,10 +199,10 @@ struct APState {
194 [this](const std::list<APClient::NetworkItem>& items) { 199 [this](const std::list<APClient::NetworkItem>& items) {
195 for (const APClient::NetworkItem& item : items) { 200 for (const APClient::NetworkItem& item : items) {
196 inventory[item.item]++; 201 inventory[item.item]++;
197 TrackerLog("Item: " + std::to_string(item.item)); 202 TrackerLog(fmt::format("Item: {}", item.item));
198 } 203 }
199 204
200 RefreshTracker(); 205 RefreshTracker(false);
201 }); 206 });
202 207
203 apclient->set_retrieved_handler( 208 apclient->set_retrieved_handler(
@@ -206,23 +211,23 @@ struct APState {
206 HandleDataStorage(key, value); 211 HandleDataStorage(key, value);
207 } 212 }
208 213
209 RefreshTracker(); 214 RefreshTracker(false);
210 }); 215 });
211 216
212 apclient->set_set_reply_handler([this](const std::string& key, 217 apclient->set_set_reply_handler([this](const std::string& key,
213 const nlohmann::json& value, 218 const nlohmann::json& value,
214 const nlohmann::json&) { 219 const nlohmann::json&) {
215 HandleDataStorage(key, value); 220 HandleDataStorage(key, value);
216 RefreshTracker(); 221 RefreshTracker(false);
217 }); 222 });
218 223
219 apclient->set_slot_connected_handler([this]( 224 apclient->set_slot_connected_handler([this, &connection_mutex](
220 const nlohmann::json& slot_data) { 225 const nlohmann::json& slot_data) {
221 tracker_frame->SetStatusMessage("Connected to Archipelago!"); 226 tracker_frame->SetStatusMessage("Connected to Archipelago!");
222 TrackerLog("Connected to Archipelago!"); 227 TrackerLog("Connected to Archipelago!");
223 228
224 data_storage_prefix = 229 data_storage_prefix =
225 "Lingo_" + std::to_string(apclient->get_player_number()) + "_"; 230 fmt::format("Lingo_{}_", apclient->get_player_number());
226 door_shuffle_mode = slot_data["shuffle_doors"].get<DoorShuffleMode>(); 231 door_shuffle_mode = slot_data["shuffle_doors"].get<DoorShuffleMode>();
227 color_shuffle = slot_data["shuffle_colors"].get<int>() == 1; 232 color_shuffle = slot_data["shuffle_colors"].get<int>() == 1;
228 painting_shuffle = slot_data["shuffle_paintings"].get<int>() == 1; 233 painting_shuffle = slot_data["shuffle_paintings"].get<int>() == 1;
@@ -253,6 +258,7 @@ struct APState {
253 for (const auto& mapping_it : 258 for (const auto& mapping_it :
254 slot_data["painting_entrance_to_exit"].items()) { 259 slot_data["painting_entrance_to_exit"].items()) {
255 painting_mapping[mapping_it.key()] = mapping_it.value(); 260 painting_mapping[mapping_it.key()] = mapping_it.value();
261 painting_codomain.insert(mapping_it.value());
256 } 262 }
257 } 263 }
258 264
@@ -268,33 +274,39 @@ struct APState {
268 } 274 }
269 } 275 }
270 276
271 connected = true;
272 has_connection_result = true;
273
274 RefreshTracker();
275
276 std::list<std::string> corrected_keys; 277 std::list<std::string> corrected_keys;
277 for (const std::string& key : tracked_data_storage_keys) { 278 for (const std::string& key : tracked_data_storage_keys) {
278 corrected_keys.push_back(data_storage_prefix + key); 279 corrected_keys.push_back(data_storage_prefix + key);
279 } 280 }
280 281
281 { 282 victory_data_storage_key =
282 std::ostringstream vdsks; 283 fmt::format("_read_client_status_{}_{}", apclient->get_team_number(),
283 vdsks << "_read_client_status_" << apclient->get_team_number() << "_" 284 apclient->get_player_number());
284 << apclient->get_player_number();
285 victory_data_storage_key = vdsks.str();
286 }
287 285
288 corrected_keys.push_back(victory_data_storage_key); 286 corrected_keys.push_back(victory_data_storage_key);
289 287
290 apclient->Get(corrected_keys); 288 apclient->Get(corrected_keys);
291 apclient->SetNotify(corrected_keys); 289 apclient->SetNotify(corrected_keys);
290
291 ResetReachabilityRequirements();
292 RefreshTracker(true);
293
294 {
295 std::lock_guard connection_lock(connection_mutex);
296 if (!has_connection_result) {
297 connected = true;
298 has_connection_result = true;
299 }
300 }
292 }); 301 });
293 302
294 apclient->set_slot_refused_handler( 303 apclient->set_slot_refused_handler(
295 [this](const std::list<std::string>& errors) { 304 [this, &connection_mutex](const std::list<std::string>& errors) {
296 connected = false; 305 {
297 has_connection_result = true; 306 std::lock_guard connection_lock(connection_mutex);
307 connected = false;
308 has_connection_result = true;
309 }
298 310
299 tracker_frame->SetStatusMessage("Disconnected from Archipelago."); 311 tracker_frame->SetStatusMessage("Disconnected from Archipelago.");
300 312
@@ -333,18 +345,29 @@ struct APState {
333 int timeout = 5000; // 5 seconds 345 int timeout = 5000; // 5 seconds
334 int interval = 100; 346 int interval = 100;
335 int remaining_loops = timeout / interval; 347 int remaining_loops = timeout / interval;
336 while (!has_connection_result) { 348 while (true) {
337 if (interval == 0) { 349 {
338 connected = false; 350 std::lock_guard connection_lock(connection_mutex);
339 has_connection_result = true; 351 if (has_connection_result) {
352 break;
353 }
354 }
340 355
356 if (interval == 0) {
341 DestroyClient(); 357 DestroyClient();
342 358
343 tracker_frame->SetStatusMessage("Disconnected from Archipelago."); 359 tracker_frame->SetStatusMessage("Disconnected from Archipelago.");
344
345 TrackerLog("Timeout while connecting to Archipelago server."); 360 TrackerLog("Timeout while connecting to Archipelago server.");
346 wxMessageBox("Timeout while connecting to Archipelago server.", 361 wxMessageBox("Timeout while connecting to Archipelago server.",
347 "Connection failed", wxOK | wxICON_ERROR); 362 "Connection failed", wxOK | wxICON_ERROR);
363
364 {
365 std::lock_guard connection_lock(connection_mutex);
366 connected = false;
367 has_connection_result = true;
368 }
369
370 break;
348 } 371 }
349 372
350 std::this_thread::sleep_for(std::chrono::milliseconds(100)); 373 std::this_thread::sleep_for(std::chrono::milliseconds(100));
@@ -353,8 +376,6 @@ struct APState {
353 } 376 }
354 377
355 if (connected) { 378 if (connected) {
356 RefreshTracker();
357 } else {
358 client_active = false; 379 client_active = false;
359 } 380 }
360 } 381 }
@@ -362,12 +383,12 @@ struct APState {
362 void HandleDataStorage(const std::string& key, const nlohmann::json& value) { 383 void HandleDataStorage(const std::string& key, const nlohmann::json& value) {
363 if (value.is_boolean()) { 384 if (value.is_boolean()) {
364 data_storage[key] = value.get<bool>(); 385 data_storage[key] = value.get<bool>();
365 TrackerLog("Data storage " + key + " retrieved as " + 386 TrackerLog(fmt::format("Data storage {} retrieved as {}", key,
366 (value.get<bool>() ? "true" : "false")); 387 (value.get<bool>() ? "true" : "false")));
367 } else if (value.is_number()) { 388 } else if (value.is_number()) {
368 data_storage[key] = value.get<int>(); 389 data_storage[key] = value.get<int>();
369 TrackerLog("Data storage " + key + " retrieved as " + 390 TrackerLog(fmt::format("Data storage {} retrieved as {}", key,
370 std::to_string(value.get<int>())); 391 value.get<int>()));
371 } else if (value.is_object()) { 392 } else if (value.is_object()) {
372 if (key.ends_with("PlayerPos")) { 393 if (key.ends_with("PlayerPos")) {
373 auto map_value = value.get<std::map<std::string, int>>(); 394 auto map_value = value.get<std::map<std::string, int>>();
@@ -376,7 +397,7 @@ struct APState {
376 data_storage[key] = value.get<std::map<std::string, int>>(); 397 data_storage[key] = value.get<std::map<std::string, int>>();
377 } 398 }
378 399
379 TrackerLog("Data storage " + key + " retrieved as dictionary"); 400 TrackerLog(fmt::format("Data storage {} retrieved as dictionary", key));
380 } else if (value.is_null()) { 401 } else if (value.is_null()) {
381 if (key.ends_with("PlayerPos")) { 402 if (key.ends_with("PlayerPos")) {
382 player_pos = std::nullopt; 403 player_pos = std::nullopt;
@@ -384,7 +405,19 @@ struct APState {
384 data_storage.erase(key); 405 data_storage.erase(key);
385 } 406 }
386 407
387 TrackerLog("Data storage " + key + " retrieved as null"); 408 TrackerLog(fmt::format("Data storage {} retrieved as null", key));
409 } else if (value.is_array()) {
410 auto list_value = value.get<std::vector<std::string>>();
411
412 if (key.ends_with("Paintings")) {
413 data_storage[key] =
414 std::set<std::string>(list_value.begin(), list_value.end());
415 } else {
416 data_storage[key] = list_value;
417 }
418
419 TrackerLog(fmt::format("Data storage {} retrieved as list: [{}]", key,
420 hatkirby::implode(list_value, ", ")));
388 } 421 }
389 } 422 }
390 423
@@ -394,7 +427,7 @@ struct APState {
394 427
395 bool HasCheckedHuntPanel(int location_id) { 428 bool HasCheckedHuntPanel(int location_id) {
396 std::string key = 429 std::string key =
397 data_storage_prefix + "Hunt|" + std::to_string(location_id); 430 fmt::format("{}Hunt|{}", data_storage_prefix, location_id);
398 return data_storage.count(key) && std::any_cast<bool>(data_storage.at(key)); 431 return data_storage.count(key) && std::any_cast<bool>(data_storage.at(key));
399 } 432 }
400 433
@@ -403,26 +436,51 @@ struct APState {
403 } 436 }
404 437
405 bool HasAchievement(const std::string& name) { 438 bool HasAchievement(const std::string& name) {
406 std::string key = data_storage_prefix + "Achievement|" + name; 439 std::string key =
440 fmt::format("{}Achievement|{}", data_storage_prefix, name);
407 return data_storage.count(key) && std::any_cast<bool>(data_storage.at(key)); 441 return data_storage.count(key) && std::any_cast<bool>(data_storage.at(key));
408 } 442 }
409 443
410 void RefreshTracker() { 444 const std::set<std::string>& GetCheckedPaintings() {
445 std::string key = fmt::format("{}Paintings", data_storage_prefix);
446 if (!data_storage.count(key)) {
447 data_storage[key] = std::set<std::string>();
448 }
449
450 return std::any_cast<const std::set<std::string>&>(data_storage.at(key));
451 }
452
453 bool IsPaintingChecked(const std::string& painting_id) {
454 const auto& checked_paintings = GetCheckedPaintings();
455
456 return checked_paintings.count(painting_id) ||
457 (painting_mapping.count(painting_id) &&
458 checked_paintings.count(painting_mapping.at(painting_id)));
459 }
460
461 void RefreshTracker(bool reset) {
411 TrackerLog("Refreshing display..."); 462 TrackerLog("Refreshing display...");
412 463
413 RecalculateReachability(); 464 RecalculateReachability();
414 tracker_frame->UpdateIndicators(); 465
466 if (reset) {
467 tracker_frame->ResetIndicators();
468 } else {
469 tracker_frame->UpdateIndicators();
470 }
415 } 471 }
416 472
417 int64_t GetItemId(const std::string& item_name) { 473 int64_t GetItemId(const std::string& item_name) {
418 int64_t ap_id = apclient->get_item_id(item_name); 474 int64_t ap_id = apclient->get_item_id(item_name);
419 if (ap_id == APClient::INVALID_NAME_ID) { 475 if (ap_id == APClient::INVALID_NAME_ID) {
420 TrackerLog("Could not find AP item ID for " + item_name); 476 TrackerLog(fmt::format("Could not find AP item ID for {}", item_name));
421 } 477 }
422 478
423 return ap_id; 479 return ap_id;
424 } 480 }
425 481
482 std::string GetItemName(int id) { return apclient->get_item_name(id); }
483
426 bool HasReachedGoal() { 484 bool HasReachedGoal() {
427 return data_storage.count(victory_data_storage_key) && 485 return data_storage.count(victory_data_storage_key) &&
428 std::any_cast<int>(data_storage.at(victory_data_storage_key)) == 486 std::any_cast<int>(data_storage.at(victory_data_storage_key)) ==
@@ -461,16 +519,32 @@ bool AP_HasItem(int item_id, int quantity) {
461 return GetState().HasItem(item_id, quantity); 519 return GetState().HasItem(item_id, quantity);
462} 520}
463 521
522std::string AP_GetItemName(int item_id) {
523 return GetState().GetItemName(item_id);
524}
525
464DoorShuffleMode AP_GetDoorShuffleMode() { return GetState().door_shuffle_mode; } 526DoorShuffleMode AP_GetDoorShuffleMode() { return GetState().door_shuffle_mode; }
465 527
466bool AP_IsColorShuffle() { return GetState().color_shuffle; } 528bool AP_IsColorShuffle() { return GetState().color_shuffle; }
467 529
468bool AP_IsPaintingShuffle() { return GetState().painting_shuffle; } 530bool AP_IsPaintingShuffle() { return GetState().painting_shuffle; }
469 531
470const std::map<std::string, std::string> AP_GetPaintingMapping() { 532const std::map<std::string, std::string>& AP_GetPaintingMapping() {
471 return GetState().painting_mapping; 533 return GetState().painting_mapping;
472} 534}
473 535
536bool AP_IsPaintingMappedTo(const std::string& painting_id) {
537 return GetState().painting_codomain.count(painting_id);
538}
539
540const std::set<std::string>& AP_GetCheckedPaintings() {
541 return GetState().GetCheckedPaintings();
542}
543
544bool AP_IsPaintingChecked(const std::string& painting_id) {
545 return GetState().IsPaintingChecked(painting_id);
546}
547
474int AP_GetMasteryRequirement() { return GetState().mastery_requirement; } 548int AP_GetMasteryRequirement() { return GetState().mastery_requirement; }
475 549
476int AP_GetLevel2Requirement() { return GetState().level_2_requirement; } 550int AP_GetLevel2Requirement() { return GetState().level_2_requirement; }