diff options
Diffstat (limited to 'src/ap_state.cpp')
-rw-r--r-- | src/ap_state.cpp | 172 |
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 | ||
522 | std::string AP_GetItemName(int item_id) { | ||
523 | return GetState().GetItemName(item_id); | ||
524 | } | ||
525 | |||
464 | DoorShuffleMode AP_GetDoorShuffleMode() { return GetState().door_shuffle_mode; } | 526 | DoorShuffleMode AP_GetDoorShuffleMode() { return GetState().door_shuffle_mode; } |
465 | 527 | ||
466 | bool AP_IsColorShuffle() { return GetState().color_shuffle; } | 528 | bool AP_IsColorShuffle() { return GetState().color_shuffle; } |
467 | 529 | ||
468 | bool AP_IsPaintingShuffle() { return GetState().painting_shuffle; } | 530 | bool AP_IsPaintingShuffle() { return GetState().painting_shuffle; } |
469 | 531 | ||
470 | const std::map<std::string, std::string> AP_GetPaintingMapping() { | 532 | const std::map<std::string, std::string>& AP_GetPaintingMapping() { |
471 | return GetState().painting_mapping; | 533 | return GetState().painting_mapping; |
472 | } | 534 | } |
473 | 535 | ||
536 | bool AP_IsPaintingMappedTo(const std::string& painting_id) { | ||
537 | return GetState().painting_codomain.count(painting_id); | ||
538 | } | ||
539 | |||
540 | const std::set<std::string>& AP_GetCheckedPaintings() { | ||
541 | return GetState().GetCheckedPaintings(); | ||
542 | } | ||
543 | |||
544 | bool AP_IsPaintingChecked(const std::string& painting_id) { | ||
545 | return GetState().IsPaintingChecked(painting_id); | ||
546 | } | ||
547 | |||
474 | int AP_GetMasteryRequirement() { return GetState().mastery_requirement; } | 548 | int AP_GetMasteryRequirement() { return GetState().mastery_requirement; } |
475 | 549 | ||
476 | int AP_GetLevel2Requirement() { return GetState().level_2_requirement; } | 550 | int AP_GetLevel2Requirement() { return GetState().level_2_requirement; } |