diff options
Diffstat (limited to 'ap_state.cpp')
-rw-r--r-- | ap_state.cpp | 245 |
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 | ||
19 | constexpr int AP_MAJOR = 0; | 24 | constexpr int AP_MAJOR = 0; |
@@ -22,29 +27,81 @@ constexpr int AP_REVISION = 0; | |||
22 | 27 | ||
23 | constexpr int ITEM_HANDLING = 7; // <- all | 28 | constexpr int ITEM_HANDLING = 7; // <- all |
24 | 29 | ||
25 | static APClient* apclient = nullptr; | 30 | namespace { |
26 | 31 | ||
27 | APState::APState() { | 32 | APClient* apclient = nullptr; |
28 | std::thread([this]() { | 33 | |
29 | for (;;) { | 34 | bool initialized = false; |
30 | { | 35 | |
31 | std::lock_guard client_guard(client_mutex_); | 36 | TrackerFrame* tracker_frame; |
32 | if (apclient) { | 37 | |
33 | apclient->poll(); | 38 | bool client_active = false; |
39 | std::mutex client_mutex; | ||
40 | |||
41 | bool connected = false; | ||
42 | bool has_connection_result = false; | ||
43 | |||
44 | std::map<int64_t, int> inventory; | ||
45 | std::set<int64_t> checked_locations; | ||
46 | |||
47 | std::map<std::tuple<int, int>, int64_t> ap_id_by_location_id; | ||
48 | std::map<std::string, int64_t> ap_id_by_item_name; | ||
49 | std::map<LingoColor, int64_t> ap_id_by_color; | ||
50 | std::map<int64_t, std::string> progressive_item_by_ap_id; | ||
51 | |||
52 | DoorShuffleMode door_shuffle_mode = kNO_DOORS; | ||
53 | bool color_shuffle = false; | ||
54 | bool painting_shuffle = false; | ||
55 | |||
56 | std::map<std::string, std::string> painting_mapping; | ||
57 | |||
58 | void RefreshTracker() { | ||
59 | GetTrackerState().CalculateState(); | ||
60 | tracker_frame->UpdateIndicators(); | ||
61 | } | ||
62 | |||
63 | int64_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 | |||
72 | void DestroyClient() { | ||
73 | client_active = false; | ||
74 | apclient->reset(); | ||
75 | delete apclient; | ||
76 | apclient = nullptr; | ||
77 | } | ||
78 | |||
79 | } // namespace | ||
80 | |||
81 | void AP_SetTrackerFrame(TrackerFrame* arg) { tracker_frame = arg; } | ||
82 | |||
83 | void 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 | ||
42 | void 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 | ||
231 | bool APState::HasCheckedGameLocation(int area_id, int section_id) const { | 288 | bool 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 | ||
241 | bool APState::HasColorItem(LingoColor color) const { | 298 | bool 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 | ||
249 | bool APState::HasItem(const std::string& item, int quantity) const { | 306 | bool 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 | ||
258 | void APState::RefreshTracker() { | 315 | DoorShuffleMode AP_GetDoorShuffleMode() { return door_shuffle_mode; } |
259 | GetTrackerState().CalculateState(); | ||
260 | tracker_frame_->UpdateIndicators(); | ||
261 | } | ||
262 | |||
263 | int64_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; | 317 | bool AP_IsColorShuffle() { return color_shuffle; } |
270 | } | ||
271 | 318 | ||
272 | void APState::DestroyClient() { | 319 | bool AP_IsPaintingShuffle() { return painting_shuffle; } |
273 | client_active_ = false; | ||
274 | apclient->reset(); | ||
275 | delete apclient; | ||
276 | apclient = nullptr; | ||
277 | } | ||
278 | 320 | ||
279 | APState& GetAPState() { | 321 | const std::map<std::string, std::string> AP_GetPaintingMapping() { |
280 | static APState* instance = new APState(); | 322 | return painting_mapping; |
281 | return *instance; | ||
282 | } | 323 | } |