diff options
Diffstat (limited to 'src/ap_state.cpp')
-rw-r--r-- | src/ap_state.cpp | 163 |
1 files changed, 118 insertions, 45 deletions
diff --git a/src/ap_state.cpp b/src/ap_state.cpp index 4fd241a..a7565cf 100644 --- a/src/ap_state.cpp +++ b/src/ap_state.cpp | |||
@@ -21,7 +21,6 @@ | |||
21 | #include <tuple> | 21 | #include <tuple> |
22 | 22 | ||
23 | #include "game_data.h" | 23 | #include "game_data.h" |
24 | #include "logger.h" | ||
25 | #include "tracker_frame.h" | 24 | #include "tracker_frame.h" |
26 | #include "tracker_state.h" | 25 | #include "tracker_state.h" |
27 | 26 | ||
@@ -72,11 +71,12 @@ struct APState { | |||
72 | bool sunwarp_shuffle = false; | 71 | bool sunwarp_shuffle = false; |
73 | 72 | ||
74 | std::map<std::string, std::string> painting_mapping; | 73 | std::map<std::string, std::string> painting_mapping; |
74 | std::set<std::string> painting_codomain; | ||
75 | std::map<int, SunwarpMapping> sunwarp_mapping; | 75 | std::map<int, SunwarpMapping> sunwarp_mapping; |
76 | 76 | ||
77 | void Connect(std::string server, std::string player, std::string password) { | 77 | void Connect(std::string server, std::string player, std::string password) { |
78 | if (!initialized) { | 78 | if (!initialized) { |
79 | TrackerLog("Initializing APState..."); | 79 | wxLogVerbose("Initializing APState..."); |
80 | 80 | ||
81 | std::thread([this]() { | 81 | std::thread([this]() { |
82 | for (;;) { | 82 | for (;;) { |
@@ -104,15 +104,16 @@ struct APState { | |||
104 | } | 104 | } |
105 | 105 | ||
106 | tracked_data_storage_keys.push_back("PlayerPos"); | 106 | tracked_data_storage_keys.push_back("PlayerPos"); |
107 | tracked_data_storage_keys.push_back("Paintings"); | ||
107 | 108 | ||
108 | initialized = true; | 109 | initialized = true; |
109 | } | 110 | } |
110 | 111 | ||
111 | tracker_frame->SetStatusMessage("Connecting to Archipelago server...."); | 112 | tracker_frame->SetStatusMessage("Connecting to Archipelago server...."); |
112 | TrackerLog("Connecting to Archipelago server (" + server + ")..."); | 113 | wxLogStatus("Connecting to Archipelago server (%s)...", server); |
113 | 114 | ||
114 | { | 115 | { |
115 | TrackerLog("Destroying old AP client..."); | 116 | wxLogVerbose("Destroying old AP client..."); |
116 | 117 | ||
117 | std::lock_guard client_guard(client_mutex); | 118 | std::lock_guard client_guard(client_mutex); |
118 | 119 | ||
@@ -139,6 +140,7 @@ struct APState { | |||
139 | color_shuffle = false; | 140 | color_shuffle = false; |
140 | painting_shuffle = false; | 141 | painting_shuffle = false; |
141 | painting_mapping.clear(); | 142 | painting_mapping.clear(); |
143 | painting_codomain.clear(); | ||
142 | mastery_requirement = 21; | 144 | mastery_requirement = 21; |
143 | level_2_requirement = 223; | 145 | level_2_requirement = 223; |
144 | location_checks = kNORMAL_LOCATIONS; | 146 | location_checks = kNORMAL_LOCATIONS; |
@@ -151,16 +153,17 @@ struct APState { | |||
151 | sunwarp_shuffle = false; | 153 | sunwarp_shuffle = false; |
152 | sunwarp_mapping.clear(); | 154 | sunwarp_mapping.clear(); |
153 | 155 | ||
156 | std::mutex connection_mutex; | ||
154 | connected = false; | 157 | connected = false; |
155 | has_connection_result = false; | 158 | has_connection_result = false; |
156 | 159 | ||
157 | apclient->set_room_info_handler([this, player, password]() { | 160 | apclient->set_room_info_handler([this, player, password]() { |
158 | inventory.clear(); | 161 | inventory.clear(); |
159 | 162 | ||
160 | TrackerLog("Connected to Archipelago server. Authenticating as " + | 163 | wxLogStatus("Connected to Archipelago server. Authenticating as %s %s", |
161 | player + | 164 | player, |
162 | (password.empty() ? " without password" | 165 | (password.empty() ? "without password" |
163 | : " with password " + password)); | 166 | : "with password " + password)); |
164 | tracker_frame->SetStatusMessage( | 167 | tracker_frame->SetStatusMessage( |
165 | "Connected to Archipelago server. Authenticating..."); | 168 | "Connected to Archipelago server. Authenticating..."); |
166 | 169 | ||
@@ -172,23 +175,23 @@ struct APState { | |||
172 | [this](const std::list<int64_t>& locations) { | 175 | [this](const std::list<int64_t>& locations) { |
173 | for (const int64_t location_id : locations) { | 176 | for (const int64_t location_id : locations) { |
174 | checked_locations.insert(location_id); | 177 | checked_locations.insert(location_id); |
175 | TrackerLog("Location: " + std::to_string(location_id)); | 178 | wxLogVerbose("Location: %lld", location_id); |
176 | } | 179 | } |
177 | 180 | ||
178 | RefreshTracker(); | 181 | RefreshTracker(false); |
179 | }); | 182 | }); |
180 | 183 | ||
181 | apclient->set_slot_disconnected_handler([this]() { | 184 | apclient->set_slot_disconnected_handler([this]() { |
182 | tracker_frame->SetStatusMessage( | 185 | tracker_frame->SetStatusMessage( |
183 | "Disconnected from Archipelago. Attempting to reconnect..."); | 186 | "Disconnected from Archipelago. Attempting to reconnect..."); |
184 | TrackerLog( | 187 | wxLogStatus( |
185 | "Slot disconnected from Archipelago. Attempting to reconnect..."); | 188 | "Slot disconnected from Archipelago. Attempting to reconnect..."); |
186 | }); | 189 | }); |
187 | 190 | ||
188 | apclient->set_socket_disconnected_handler([this]() { | 191 | apclient->set_socket_disconnected_handler([this]() { |
189 | tracker_frame->SetStatusMessage( | 192 | tracker_frame->SetStatusMessage( |
190 | "Disconnected from Archipelago. Attempting to reconnect..."); | 193 | "Disconnected from Archipelago. Attempting to reconnect..."); |
191 | TrackerLog( | 194 | wxLogStatus( |
192 | "Socket disconnected from Archipelago. Attempting to reconnect..."); | 195 | "Socket disconnected from Archipelago. Attempting to reconnect..."); |
193 | }); | 196 | }); |
194 | 197 | ||
@@ -196,10 +199,10 @@ struct APState { | |||
196 | [this](const std::list<APClient::NetworkItem>& items) { | 199 | [this](const std::list<APClient::NetworkItem>& items) { |
197 | for (const APClient::NetworkItem& item : items) { | 200 | for (const APClient::NetworkItem& item : items) { |
198 | inventory[item.item]++; | 201 | inventory[item.item]++; |
199 | TrackerLog("Item: " + std::to_string(item.item)); | 202 | wxLogVerbose("Item: %lld", item.item); |
200 | } | 203 | } |
201 | 204 | ||
202 | RefreshTracker(); | 205 | RefreshTracker(false); |
203 | }); | 206 | }); |
204 | 207 | ||
205 | apclient->set_retrieved_handler( | 208 | apclient->set_retrieved_handler( |
@@ -208,20 +211,20 @@ struct APState { | |||
208 | HandleDataStorage(key, value); | 211 | HandleDataStorage(key, value); |
209 | } | 212 | } |
210 | 213 | ||
211 | RefreshTracker(); | 214 | RefreshTracker(false); |
212 | }); | 215 | }); |
213 | 216 | ||
214 | apclient->set_set_reply_handler([this](const std::string& key, | 217 | apclient->set_set_reply_handler([this](const std::string& key, |
215 | const nlohmann::json& value, | 218 | const nlohmann::json& value, |
216 | const nlohmann::json&) { | 219 | const nlohmann::json&) { |
217 | HandleDataStorage(key, value); | 220 | HandleDataStorage(key, value); |
218 | RefreshTracker(); | 221 | RefreshTracker(false); |
219 | }); | 222 | }); |
220 | 223 | ||
221 | apclient->set_slot_connected_handler([this]( | 224 | apclient->set_slot_connected_handler([this, &connection_mutex]( |
222 | const nlohmann::json& slot_data) { | 225 | const nlohmann::json& slot_data) { |
223 | tracker_frame->SetStatusMessage("Connected to Archipelago!"); | 226 | tracker_frame->SetStatusMessage("Connected to Archipelago!"); |
224 | TrackerLog("Connected to Archipelago!"); | 227 | wxLogStatus("Connected to Archipelago!"); |
225 | 228 | ||
226 | data_storage_prefix = | 229 | data_storage_prefix = |
227 | "Lingo_" + std::to_string(apclient->get_player_number()) + "_"; | 230 | "Lingo_" + std::to_string(apclient->get_player_number()) + "_"; |
@@ -266,6 +269,7 @@ struct APState { | |||
266 | for (const auto& mapping_it : | 269 | for (const auto& mapping_it : |
267 | slot_data["painting_entrance_to_exit"].items()) { | 270 | slot_data["painting_entrance_to_exit"].items()) { |
268 | painting_mapping[mapping_it.key()] = mapping_it.value(); | 271 | painting_mapping[mapping_it.key()] = mapping_it.value(); |
272 | painting_codomain.insert(mapping_it.value()); | ||
269 | } | 273 | } |
270 | } | 274 | } |
271 | 275 | ||
@@ -281,11 +285,6 @@ struct APState { | |||
281 | } | 285 | } |
282 | } | 286 | } |
283 | 287 | ||
284 | connected = true; | ||
285 | has_connection_result = true; | ||
286 | |||
287 | RefreshTracker(); | ||
288 | |||
289 | std::list<std::string> corrected_keys; | 288 | std::list<std::string> corrected_keys; |
290 | for (const std::string& key : tracked_data_storage_keys) { | 289 | for (const std::string& key : tracked_data_storage_keys) { |
291 | corrected_keys.push_back(data_storage_prefix + key); | 290 | corrected_keys.push_back(data_storage_prefix + key); |
@@ -302,12 +301,23 @@ struct APState { | |||
302 | 301 | ||
303 | apclient->Get(corrected_keys); | 302 | apclient->Get(corrected_keys); |
304 | apclient->SetNotify(corrected_keys); | 303 | apclient->SetNotify(corrected_keys); |
304 | |||
305 | { | ||
306 | std::lock_guard connection_lock(connection_mutex); | ||
307 | if (!has_connection_result) { | ||
308 | connected = true; | ||
309 | has_connection_result = true; | ||
310 | } | ||
311 | } | ||
305 | }); | 312 | }); |
306 | 313 | ||
307 | apclient->set_slot_refused_handler( | 314 | apclient->set_slot_refused_handler( |
308 | [this](const std::list<std::string>& errors) { | 315 | [this, &connection_mutex](const std::list<std::string>& errors) { |
309 | connected = false; | 316 | { |
310 | has_connection_result = true; | 317 | std::lock_guard connection_lock(connection_mutex); |
318 | connected = false; | ||
319 | has_connection_result = true; | ||
320 | } | ||
311 | 321 | ||
312 | tracker_frame->SetStatusMessage("Disconnected from Archipelago."); | 322 | tracker_frame->SetStatusMessage("Disconnected from Archipelago."); |
313 | 323 | ||
@@ -336,7 +346,7 @@ struct APState { | |||
336 | } | 346 | } |
337 | 347 | ||
338 | std::string full_message = hatkirby::implode(error_messages, " "); | 348 | std::string full_message = hatkirby::implode(error_messages, " "); |
339 | TrackerLog(full_message); | 349 | wxLogError(wxString(full_message)); |
340 | 350 | ||
341 | wxMessageBox(full_message, "Connection failed", wxOK | wxICON_ERROR); | 351 | wxMessageBox(full_message, "Connection failed", wxOK | wxICON_ERROR); |
342 | }); | 352 | }); |
@@ -346,18 +356,29 @@ struct APState { | |||
346 | int timeout = 5000; // 5 seconds | 356 | int timeout = 5000; // 5 seconds |
347 | int interval = 100; | 357 | int interval = 100; |
348 | int remaining_loops = timeout / interval; | 358 | int remaining_loops = timeout / interval; |
349 | while (!has_connection_result) { | 359 | while (true) { |
350 | if (interval == 0) { | 360 | { |
351 | connected = false; | 361 | std::lock_guard connection_lock(connection_mutex); |
352 | has_connection_result = true; | 362 | if (has_connection_result) { |
363 | break; | ||
364 | } | ||
365 | } | ||
353 | 366 | ||
367 | if (interval == 0) { | ||
354 | DestroyClient(); | 368 | DestroyClient(); |
355 | 369 | ||
356 | tracker_frame->SetStatusMessage("Disconnected from Archipelago."); | 370 | tracker_frame->SetStatusMessage("Disconnected from Archipelago."); |
357 | 371 | wxLogStatus("Timeout while connecting to Archipelago server."); | |
358 | TrackerLog("Timeout while connecting to Archipelago server."); | ||
359 | wxMessageBox("Timeout while connecting to Archipelago server.", | 372 | wxMessageBox("Timeout while connecting to Archipelago server.", |
360 | "Connection failed", wxOK | wxICON_ERROR); | 373 | "Connection failed", wxOK | wxICON_ERROR); |
374 | |||
375 | { | ||
376 | std::lock_guard connection_lock(connection_mutex); | ||
377 | connected = false; | ||
378 | has_connection_result = true; | ||
379 | } | ||
380 | |||
381 | break; | ||
361 | } | 382 | } |
362 | 383 | ||
363 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); | 384 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); |
@@ -366,7 +387,8 @@ struct APState { | |||
366 | } | 387 | } |
367 | 388 | ||
368 | if (connected) { | 389 | if (connected) { |
369 | RefreshTracker(); | 390 | ResetReachabilityRequirements(); |
391 | RefreshTracker(true); | ||
370 | } else { | 392 | } else { |
371 | client_active = false; | 393 | client_active = false; |
372 | } | 394 | } |
@@ -375,12 +397,11 @@ struct APState { | |||
375 | void HandleDataStorage(const std::string& key, const nlohmann::json& value) { | 397 | void HandleDataStorage(const std::string& key, const nlohmann::json& value) { |
376 | if (value.is_boolean()) { | 398 | if (value.is_boolean()) { |
377 | data_storage[key] = value.get<bool>(); | 399 | data_storage[key] = value.get<bool>(); |
378 | TrackerLog("Data storage " + key + " retrieved as " + | 400 | wxLogVerbose("Data storage %s retrieved as %s", key, |
379 | (value.get<bool>() ? "true" : "false")); | 401 | (value.get<bool>() ? "true" : "false")); |
380 | } else if (value.is_number()) { | 402 | } else if (value.is_number()) { |
381 | data_storage[key] = value.get<int>(); | 403 | data_storage[key] = value.get<int>(); |
382 | TrackerLog("Data storage " + key + " retrieved as " + | 404 | wxLogVerbose("Data storage %s retrieved as %d", key, value.get<int>()); |
383 | std::to_string(value.get<int>())); | ||
384 | } else if (value.is_object()) { | 405 | } else if (value.is_object()) { |
385 | if (key.ends_with("PlayerPos")) { | 406 | if (key.ends_with("PlayerPos")) { |
386 | auto map_value = value.get<std::map<std::string, int>>(); | 407 | auto map_value = value.get<std::map<std::string, int>>(); |
@@ -389,7 +410,7 @@ struct APState { | |||
389 | data_storage[key] = value.get<std::map<std::string, int>>(); | 410 | data_storage[key] = value.get<std::map<std::string, int>>(); |
390 | } | 411 | } |
391 | 412 | ||
392 | TrackerLog("Data storage " + key + " retrieved as dictionary"); | 413 | wxLogVerbose("Data storage %s retrieved as dictionary", key); |
393 | } else if (value.is_null()) { | 414 | } else if (value.is_null()) { |
394 | if (key.ends_with("PlayerPos")) { | 415 | if (key.ends_with("PlayerPos")) { |
395 | player_pos = std::nullopt; | 416 | player_pos = std::nullopt; |
@@ -397,7 +418,19 @@ struct APState { | |||
397 | data_storage.erase(key); | 418 | data_storage.erase(key); |
398 | } | 419 | } |
399 | 420 | ||
400 | TrackerLog("Data storage " + key + " retrieved as null"); | 421 | wxLogVerbose("Data storage %s retrieved as null", key); |
422 | } else if (value.is_array()) { | ||
423 | auto list_value = value.get<std::vector<std::string>>(); | ||
424 | |||
425 | if (key.ends_with("Paintings")) { | ||
426 | data_storage[key] = | ||
427 | std::set<std::string>(list_value.begin(), list_value.end()); | ||
428 | } else { | ||
429 | data_storage[key] = list_value; | ||
430 | } | ||
431 | |||
432 | wxLogVerbose("Data storage %s retrieved as list: [%s]", key, | ||
433 | hatkirby::implode(list_value, ", ")); | ||
401 | } | 434 | } |
402 | } | 435 | } |
403 | 436 | ||
@@ -420,22 +453,46 @@ struct APState { | |||
420 | return data_storage.count(key) && std::any_cast<bool>(data_storage.at(key)); | 453 | return data_storage.count(key) && std::any_cast<bool>(data_storage.at(key)); |
421 | } | 454 | } |
422 | 455 | ||
423 | void RefreshTracker() { | 456 | const std::set<std::string>& GetCheckedPaintings() { |
424 | TrackerLog("Refreshing display..."); | 457 | std::string key = data_storage_prefix + "Paintings"; |
458 | if (!data_storage.count(key)) { | ||
459 | data_storage[key] = std::set<std::string>(); | ||
460 | } | ||
461 | |||
462 | return std::any_cast<const std::set<std::string>&>(data_storage.at(key)); | ||
463 | } | ||
464 | |||
465 | bool IsPaintingChecked(const std::string& painting_id) { | ||
466 | const auto& checked_paintings = GetCheckedPaintings(); | ||
467 | |||
468 | return checked_paintings.count(painting_id) || | ||
469 | (painting_mapping.count(painting_id) && | ||
470 | checked_paintings.count(painting_mapping.at(painting_id))); | ||
471 | } | ||
472 | |||
473 | void RefreshTracker(bool reset) { | ||
474 | wxLogVerbose("Refreshing display..."); | ||
425 | 475 | ||
426 | RecalculateReachability(); | 476 | RecalculateReachability(); |
427 | tracker_frame->UpdateIndicators(); | 477 | |
478 | if (reset) { | ||
479 | tracker_frame->ResetIndicators(); | ||
480 | } else { | ||
481 | tracker_frame->UpdateIndicators(); | ||
482 | } | ||
428 | } | 483 | } |
429 | 484 | ||
430 | int64_t GetItemId(const std::string& item_name) { | 485 | int64_t GetItemId(const std::string& item_name) { |
431 | int64_t ap_id = apclient->get_item_id(item_name); | 486 | int64_t ap_id = apclient->get_item_id(item_name); |
432 | if (ap_id == APClient::INVALID_NAME_ID) { | 487 | if (ap_id == APClient::INVALID_NAME_ID) { |
433 | TrackerLog("Could not find AP item ID for " + item_name); | 488 | wxLogError("Could not find AP item ID for %s", item_name); |
434 | } | 489 | } |
435 | 490 | ||
436 | return ap_id; | 491 | return ap_id; |
437 | } | 492 | } |
438 | 493 | ||
494 | std::string GetItemName(int id) { return apclient->get_item_name(id); } | ||
495 | |||
439 | bool HasReachedGoal() { | 496 | bool HasReachedGoal() { |
440 | return data_storage.count(victory_data_storage_key) && | 497 | return data_storage.count(victory_data_storage_key) && |
441 | std::any_cast<int>(data_storage.at(victory_data_storage_key)) == | 498 | std::any_cast<int>(data_storage.at(victory_data_storage_key)) == |
@@ -474,6 +531,10 @@ bool AP_HasItem(int item_id, int quantity) { | |||
474 | return GetState().HasItem(item_id, quantity); | 531 | return GetState().HasItem(item_id, quantity); |
475 | } | 532 | } |
476 | 533 | ||
534 | std::string AP_GetItemName(int item_id) { | ||
535 | return GetState().GetItemName(item_id); | ||
536 | } | ||
537 | |||
477 | DoorShuffleMode AP_GetDoorShuffleMode() { return GetState().door_shuffle_mode; } | 538 | DoorShuffleMode AP_GetDoorShuffleMode() { return GetState().door_shuffle_mode; } |
478 | 539 | ||
479 | bool AP_AreDoorsGrouped() { return GetState().group_doors; } | 540 | bool AP_AreDoorsGrouped() { return GetState().group_doors; } |
@@ -482,10 +543,22 @@ bool AP_IsColorShuffle() { return GetState().color_shuffle; } | |||
482 | 543 | ||
483 | bool AP_IsPaintingShuffle() { return GetState().painting_shuffle; } | 544 | bool AP_IsPaintingShuffle() { return GetState().painting_shuffle; } |
484 | 545 | ||
485 | const std::map<std::string, std::string> AP_GetPaintingMapping() { | 546 | const std::map<std::string, std::string>& AP_GetPaintingMapping() { |
486 | return GetState().painting_mapping; | 547 | return GetState().painting_mapping; |
487 | } | 548 | } |
488 | 549 | ||
550 | bool AP_IsPaintingMappedTo(const std::string& painting_id) { | ||
551 | return GetState().painting_codomain.count(painting_id); | ||
552 | } | ||
553 | |||
554 | const std::set<std::string>& AP_GetCheckedPaintings() { | ||
555 | return GetState().GetCheckedPaintings(); | ||
556 | } | ||
557 | |||
558 | bool AP_IsPaintingChecked(const std::string& painting_id) { | ||
559 | return GetState().IsPaintingChecked(painting_id); | ||
560 | } | ||
561 | |||
489 | int AP_GetMasteryRequirement() { return GetState().mastery_requirement; } | 562 | int AP_GetMasteryRequirement() { return GetState().mastery_requirement; } |
490 | 563 | ||
491 | int AP_GetLevel2Requirement() { return GetState().level_2_requirement; } | 564 | int AP_GetLevel2Requirement() { return GetState().level_2_requirement; } |