diff options
author | Star Rauchenberger <fefferburbia@gmail.com> | 2025-09-27 17:14:40 -0400 |
---|---|---|
committer | Star Rauchenberger <fefferburbia@gmail.com> | 2025-09-27 17:14:40 -0400 |
commit | b0f474bee1c8e1111f7542bf4985136d9aedf340 (patch) | |
tree | ef2aa34bad532ffb2a45d90893dbcd4c378a0dfb /apworld/context.py | |
parent | feb89a44ddf5f93bc476ca29cd02257aea47dc06 (diff) | |
download | lingo2-archipelago-b0f474bee1c8e1111f7542bf4985136d9aedf340.tar.gz lingo2-archipelago-b0f474bee1c8e1111f7542bf4985136d9aedf340.tar.bz2 lingo2-archipelago-b0f474bee1c8e1111f7542bf4985136d9aedf340.zip |
Treat local letters as items for tracker
Local letters are now synced with datastorage, so they transfer to other computers like regular items would, and the tracker also now waits until you collect local letters before showing what they give you in logic.
Diffstat (limited to 'apworld/context.py')
-rw-r--r-- | apworld/context.py | 388 |
1 files changed, 276 insertions, 112 deletions
diff --git a/apworld/context.py b/apworld/context.py index 0a058e5..bc3b1bf 100644 --- a/apworld/context.py +++ b/apworld/context.py | |||
@@ -15,35 +15,85 @@ from Utils import async_start | |||
15 | from . import Lingo2World | 15 | from . import Lingo2World |
16 | from .tracker import Tracker | 16 | from .tracker import Tracker |
17 | 17 | ||
18 | PORT = 43182 | 18 | ALL_LETTERS = "abcdefghijklmnopqrstuvwxyz" |
19 | MESSAGE_MAX_SIZE = 16*1024*1024 | 19 | MESSAGE_MAX_SIZE = 16*1024*1024 |
20 | PORT = 43182 | ||
21 | |||
22 | KEY_STORAGE_MAPPING = { | ||
23 | "a": (1, 0), "b": (1, 1), "c": (1, 2), "d": (1, 3), "e": (1, 4), "f": (1, 5), "g": (1, 6), "h": (1, 7), "i": (1, 8), | ||
24 | "j": (1, 9), "k": (1, 10), "l": (1, 11), "m": (1, 12), "n": (2, 0), "o": (2, 1), "p": (2, 2), "q": (2, 3), | ||
25 | "r": (2, 4), "s": (2, 5), "t": (2, 6), "u": (2, 7), "v": (2, 8), "w": (2, 9), "x": (2, 10), "y": (2, 11), | ||
26 | "z": (2, 12), | ||
27 | } | ||
28 | |||
29 | REVERSE_KEY_STORAGE_MAPPING = {t: k for k, t in KEY_STORAGE_MAPPING.items()} | ||
30 | |||
31 | |||
32 | class Lingo2Manager: | ||
33 | game_ctx: "Lingo2GameContext" | ||
34 | client_ctx: "Lingo2ClientContext" | ||
35 | tracker: Tracker | ||
36 | |||
37 | keyboard: dict[str, int] | ||
38 | |||
39 | def __init__(self, game_ctx: "Lingo2GameContext", client_ctx: "Lingo2ClientContext"): | ||
40 | self.game_ctx = game_ctx | ||
41 | self.game_ctx.manager = self | ||
42 | self.client_ctx = client_ctx | ||
43 | self.client_ctx.manager = self | ||
44 | self.tracker = Tracker(self) | ||
45 | self.keyboard = {} | ||
46 | |||
47 | self.reset() | ||
48 | |||
49 | def reset(self): | ||
50 | for k in ALL_LETTERS: | ||
51 | self.keyboard[k] = 0 | ||
52 | |||
53 | def update_keyboard(self, new_keyboard: dict[str, int]) -> dict[str, int]: | ||
54 | ret: dict[str, int] = {} | ||
55 | |||
56 | for k, v in new_keyboard.items(): | ||
57 | if v > self.keyboard.get(k, 0): | ||
58 | self.keyboard[k] = v | ||
59 | ret[k] = v | ||
60 | |||
61 | if len(ret) > 0: | ||
62 | self.tracker.refresh_state() | ||
63 | self.game_ctx.send_accessible_locations() | ||
64 | |||
65 | return ret | ||
20 | 66 | ||
21 | 67 | ||
22 | class Lingo2GameContext: | 68 | class Lingo2GameContext: |
23 | server: Endpoint | None | 69 | server: Endpoint | None |
24 | client: "Lingo2ClientContext" | 70 | manager: Lingo2Manager |
25 | tracker: Tracker | ||
26 | 71 | ||
27 | def __init__(self): | 72 | def __init__(self): |
28 | self.server = None | 73 | self.server = None |
29 | self.tracker = Tracker() | ||
30 | 74 | ||
31 | def send_connected(self): | 75 | def send_connected(self): |
76 | if self.server is None: | ||
77 | return | ||
78 | |||
32 | msg = { | 79 | msg = { |
33 | "cmd": "Connected", | 80 | "cmd": "Connected", |
34 | "user": self.client.username, | 81 | "user": self.manager.client_ctx.username, |
35 | "seed_name": self.client.seed_name, | 82 | "seed_name": self.manager.client_ctx.seed_name, |
36 | "version": self.client.server_version, | 83 | "version": self.manager.client_ctx.server_version, |
37 | "generator_version": self.client.generator_version, | 84 | "generator_version": self.manager.client_ctx.generator_version, |
38 | "team": self.client.team, | 85 | "team": self.manager.client_ctx.team, |
39 | "slot": self.client.slot, | 86 | "slot": self.manager.client_ctx.slot, |
40 | "checked_locations": self.client.checked_locations, | 87 | "checked_locations": self.manager.client_ctx.checked_locations, |
41 | "slot_data": self.client.slot_data, | 88 | "slot_data": self.manager.client_ctx.slot_data, |
42 | } | 89 | } |
43 | 90 | ||
44 | async_start(self.send_msgs([msg]), name="game Connected") | 91 | async_start(self.send_msgs([msg]), name="game Connected") |
45 | 92 | ||
46 | def send_item_sent_notification(self, item_name, receiver_name, item_flags): | 93 | def send_item_sent_notification(self, item_name, receiver_name, item_flags): |
94 | if self.server is None: | ||
95 | return | ||
96 | |||
47 | msg = { | 97 | msg = { |
48 | "cmd": "ItemSentNotif", | 98 | "cmd": "ItemSentNotif", |
49 | "item_name": item_name, | 99 | "item_name": item_name, |
@@ -54,6 +104,9 @@ class Lingo2GameContext: | |||
54 | async_start(self.send_msgs([msg]), name="item sent notif") | 104 | async_start(self.send_msgs([msg]), name="item sent notif") |
55 | 105 | ||
56 | def send_hint_received(self, item_name, location_name, receiver_name, item_flags, for_self): | 106 | def send_hint_received(self, item_name, location_name, receiver_name, item_flags, for_self): |
107 | if self.server is None: | ||
108 | return | ||
109 | |||
57 | msg = { | 110 | msg = { |
58 | "cmd": "HintReceived", | 111 | "cmd": "HintReceived", |
59 | "item_name": item_name, | 112 | "item_name": item_name, |
@@ -66,6 +119,9 @@ class Lingo2GameContext: | |||
66 | async_start(self.send_msgs([msg]), name="hint received notif") | 119 | async_start(self.send_msgs([msg]), name="hint received notif") |
67 | 120 | ||
68 | def send_item_received(self, items): | 121 | def send_item_received(self, items): |
122 | if self.server is None: | ||
123 | return | ||
124 | |||
69 | msg = { | 125 | msg = { |
70 | "cmd": "ItemReceived", | 126 | "cmd": "ItemReceived", |
71 | "items": items, | 127 | "items": items, |
@@ -74,6 +130,9 @@ class Lingo2GameContext: | |||
74 | async_start(self.send_msgs([msg]), name="item received") | 130 | async_start(self.send_msgs([msg]), name="item received") |
75 | 131 | ||
76 | def send_location_info(self, locations): | 132 | def send_location_info(self, locations): |
133 | if self.server is None: | ||
134 | return | ||
135 | |||
77 | msg = { | 136 | msg = { |
78 | "cmd": "LocationInfo", | 137 | "cmd": "LocationInfo", |
79 | "locations": locations, | 138 | "locations": locations, |
@@ -82,6 +141,9 @@ class Lingo2GameContext: | |||
82 | async_start(self.send_msgs([msg]), name="location info") | 141 | async_start(self.send_msgs([msg]), name="location info") |
83 | 142 | ||
84 | def send_text_message(self, parts): | 143 | def send_text_message(self, parts): |
144 | if self.server is None: | ||
145 | return | ||
146 | |||
85 | msg = { | 147 | msg = { |
86 | "cmd": "TextMessage", | 148 | "cmd": "TextMessage", |
87 | "data": parts, | 149 | "data": parts, |
@@ -90,14 +152,20 @@ class Lingo2GameContext: | |||
90 | async_start(self.send_msgs([msg]), name="notif") | 152 | async_start(self.send_msgs([msg]), name="notif") |
91 | 153 | ||
92 | def send_accessible_locations(self): | 154 | def send_accessible_locations(self): |
155 | if self.server is None: | ||
156 | return | ||
157 | |||
93 | msg = { | 158 | msg = { |
94 | "cmd": "AccessibleLocations", | 159 | "cmd": "AccessibleLocations", |
95 | "locations": list(self.tracker.accessible_locations), | 160 | "locations": list(self.manager.tracker.accessible_locations), |
96 | } | 161 | } |
97 | 162 | ||
98 | async_start(self.send_msgs([msg]), name="accessible locations") | 163 | async_start(self.send_msgs([msg]), name="accessible locations") |
99 | 164 | ||
100 | def send_update_locations(self, locations): | 165 | def send_update_locations(self, locations): |
166 | if self.server is None: | ||
167 | return | ||
168 | |||
101 | msg = { | 169 | msg = { |
102 | "cmd": "UpdateLocations", | 170 | "cmd": "UpdateLocations", |
103 | "locations": locations, | 171 | "locations": locations, |
@@ -105,6 +173,17 @@ class Lingo2GameContext: | |||
105 | 173 | ||
106 | async_start(self.send_msgs([msg]), name="update locations") | 174 | async_start(self.send_msgs([msg]), name="update locations") |
107 | 175 | ||
176 | def send_update_keyboard(self, updates): | ||
177 | if self.server is None: | ||
178 | return | ||
179 | |||
180 | msg = { | ||
181 | "cmd": "UpdateKeyboard", | ||
182 | "updates": updates, | ||
183 | } | ||
184 | |||
185 | async_start(self.send_msgs([msg]), name="update keyboard") | ||
186 | |||
108 | async def send_msgs(self, msgs: list[Any]) -> None: | 187 | async def send_msgs(self, msgs: list[Any]) -> None: |
109 | """ `msgs` JSON serializable """ | 188 | """ `msgs` JSON serializable """ |
110 | if not self.server or not self.server.socket.open or self.server.socket.closed: | 189 | if not self.server or not self.server.socket.open or self.server.socket.closed: |
@@ -113,7 +192,7 @@ class Lingo2GameContext: | |||
113 | 192 | ||
114 | 193 | ||
115 | class Lingo2ClientContext(CommonContext): | 194 | class Lingo2ClientContext(CommonContext): |
116 | game_ctx: Lingo2GameContext | 195 | manager: Lingo2Manager |
117 | 196 | ||
118 | game = "Lingo 2" | 197 | game = "Lingo 2" |
119 | items_handling = 0b111 | 198 | items_handling = 0b111 |
@@ -138,118 +217,201 @@ class Lingo2ClientContext(CommonContext): | |||
138 | elif cmd == "Connected": | 217 | elif cmd == "Connected": |
139 | self.slot_data = args.get("slot_data", None) | 218 | self.slot_data = args.get("slot_data", None) |
140 | 219 | ||
141 | if self.game_ctx.server is not None: | 220 | self.manager.reset() |
142 | self.game_ctx.send_connected() | 221 | |
143 | 222 | self.manager.game_ctx.send_connected() | |
144 | self.game_ctx.tracker.setup_slot(self.slot_data) | 223 | |
224 | self.manager.tracker.setup_slot(self.slot_data) | ||
225 | self.manager.tracker.set_checked_locations(self.checked_locations) | ||
226 | self.manager.game_ctx.send_accessible_locations() | ||
227 | |||
228 | self.set_notify(self.get_datastorage_key("keyboard1"), self.get_datastorage_key("keyboard2")) | ||
229 | async_start(self.send_msgs([{ | ||
230 | "cmd": "Set", | ||
231 | "key": self.get_datastorage_key("keyboard1"), | ||
232 | "default": 0, | ||
233 | "want_reply": True, | ||
234 | "operations": [{"operation": "default", "value": 0}] | ||
235 | }, { | ||
236 | "cmd": "Set", | ||
237 | "key": self.get_datastorage_key("keyboard2"), | ||
238 | "default": 0, | ||
239 | "want_reply": True, | ||
240 | "operations": [{"operation": "default", "value": 0}] | ||
241 | }]), name="default keys") | ||
145 | elif cmd == "RoomUpdate": | 242 | elif cmd == "RoomUpdate": |
146 | if self.game_ctx.server is not None: | 243 | self.manager.tracker.set_checked_locations(self.checked_locations) |
147 | self.game_ctx.send_update_locations(args["checked_locations"]) | 244 | self.manager.game_ctx.send_update_locations(args["checked_locations"]) |
148 | elif cmd == "ReceivedItems": | 245 | elif cmd == "ReceivedItems": |
149 | self.game_ctx.tracker.set_collected_items(self.items_received) | 246 | self.manager.tracker.set_collected_items(self.items_received) |
150 | 247 | ||
151 | if self.game_ctx.server is not None: | 248 | cur_index = 0 |
152 | cur_index = 0 | 249 | items = [] |
153 | items = [] | ||
154 | 250 | ||
155 | for item in args["items"]: | 251 | for item in args["items"]: |
156 | index = cur_index + args["index"] | 252 | index = cur_index + args["index"] |
157 | cur_index += 1 | 253 | cur_index += 1 |
158 | 254 | ||
159 | item_msg = { | 255 | item_msg = { |
160 | "id": item.item, | 256 | "id": item.item, |
161 | "index": index, | 257 | "index": index, |
162 | "flags": item.flags, | 258 | "flags": item.flags, |
163 | "text": self.item_names.lookup_in_slot(item.item, self.slot), | 259 | "text": self.item_names.lookup_in_slot(item.item, self.slot), |
164 | } | 260 | } |
165 | 261 | ||
166 | if item.player != self.slot: | 262 | if item.player != self.slot: |
167 | item_msg["sender"] = self.player_names.get(item.player) | 263 | item_msg["sender"] = self.player_names.get(item.player) |
168 | 264 | ||
169 | items.append(item_msg) | 265 | items.append(item_msg) |
170 | 266 | ||
171 | self.game_ctx.send_item_received(items) | 267 | self.manager.game_ctx.send_item_received(items) |
172 | 268 | ||
173 | if any(ItemClassification.progression in ItemClassification(item.flags) for item in args["items"]): | 269 | if any(ItemClassification.progression in ItemClassification(item.flags) for item in args["items"]): |
174 | self.game_ctx.send_accessible_locations() | 270 | self.manager.game_ctx.send_accessible_locations() |
175 | elif cmd == "PrintJSON": | 271 | elif cmd == "PrintJSON": |
176 | if self.game_ctx.server is not None: | 272 | if "receiving" in args and "item" in args and args["item"].player == self.slot: |
177 | if "receiving" in args and "item" in args and args["item"].player == self.slot: | 273 | item_name = self.item_names.lookup_in_slot(args["item"].item, args["receiving"]) |
178 | item_name = self.item_names.lookup_in_slot(args["item"].item, args["receiving"]) | 274 | location_name = self.location_names.lookup_in_slot(args["item"].location, args["item"].player) |
179 | location_name = self.location_names.lookup_in_slot(args["item"].location, args["item"].player) | 275 | receiver_name = self.player_names.get(args["receiving"]) |
180 | receiver_name = self.player_names.get(args["receiving"]) | 276 | |
181 | 277 | if args["type"] == "Hint" and not args.get("found", False): | |
182 | if args["type"] == "Hint" and not args.get("found", False): | 278 | self.manager.game_ctx.send_hint_received(item_name, location_name, receiver_name, args["item"].flags, |
183 | self.game_ctx.send_hint_received(item_name, location_name, receiver_name, args["item"].flags, | 279 | int(args["receiving"]) == self.slot) |
184 | int(args["receiving"]) == self.slot) | 280 | elif args["receiving"] != self.slot: |
185 | elif args["receiving"] != self.slot: | 281 | self.manager.game_ctx.send_item_sent_notification(item_name, receiver_name, args["item"].flags) |
186 | self.game_ctx.send_item_sent_notification(item_name, receiver_name, args["item"].flags) | 282 | |
187 | 283 | parts = [] | |
188 | parts = [] | 284 | for message_part in args["data"]: |
189 | for message_part in args["data"]: | 285 | if "type" not in message_part and "text" in message_part: |
190 | if "type" not in message_part and "text" in message_part: | 286 | parts.append({"type": "text", "text": message_part["text"]}) |
191 | parts.append({"type": "text", "text": message_part["text"]}) | 287 | elif message_part["type"] == "player_id": |
192 | elif message_part["type"] == "player_id": | 288 | parts.append({ |
193 | parts.append({ | 289 | "type": "player", |
194 | "type": "player", | 290 | "text": self.player_names.get(int(message_part["text"])), |
195 | "text": self.player_names.get(int(message_part["text"])), | 291 | "self": int(int(message_part["text"]) == self.slot), |
196 | "self": int(int(message_part["text"]) == self.slot), | ||
197 | }) | ||
198 | elif message_part["type"] == "item_id": | ||
199 | parts.append({ | ||
200 | "type": "item", | ||
201 | "text": self.item_names.lookup_in_slot(int(message_part["text"]), message_part["player"]), | ||
202 | "flags": message_part["flags"], | ||
203 | }) | ||
204 | elif message_part["type"] == "location_id": | ||
205 | parts.append({ | ||
206 | "type": "location", | ||
207 | "text": self.location_names.lookup_in_slot(int(message_part["text"]), | ||
208 | message_part["player"]) | ||
209 | }) | ||
210 | elif "text" in message_part: | ||
211 | parts.append({"type": "text", "text": message_part["text"]}) | ||
212 | |||
213 | self.game_ctx.send_text_message(parts) | ||
214 | elif cmd == "LocationInfo": | ||
215 | if self.game_ctx.server is not None: | ||
216 | locations = [] | ||
217 | |||
218 | for location in args["locations"]: | ||
219 | locations.append({ | ||
220 | "id": location.location, | ||
221 | "item": self.item_names.lookup_in_slot(location.item, location.player), | ||
222 | "player": self.player_names.get(location.player), | ||
223 | "flags": location.flags, | ||
224 | "self": int(location.player) == self.slot, | ||
225 | }) | 292 | }) |
293 | elif message_part["type"] == "item_id": | ||
294 | parts.append({ | ||
295 | "type": "item", | ||
296 | "text": self.item_names.lookup_in_slot(int(message_part["text"]), message_part["player"]), | ||
297 | "flags": message_part["flags"], | ||
298 | }) | ||
299 | elif message_part["type"] == "location_id": | ||
300 | parts.append({ | ||
301 | "type": "location", | ||
302 | "text": self.location_names.lookup_in_slot(int(message_part["text"]), | ||
303 | message_part["player"]) | ||
304 | }) | ||
305 | elif "text" in message_part: | ||
306 | parts.append({"type": "text", "text": message_part["text"]}) | ||
226 | 307 | ||
227 | self.game_ctx.send_location_info(locations) | 308 | self.manager.game_ctx.send_text_message(parts) |
228 | 309 | elif cmd == "LocationInfo": | |
229 | if cmd in ["Connected", "RoomUpdate"]: | 310 | locations = [] |
230 | self.game_ctx.tracker.set_checked_locations(self.checked_locations) | 311 | |
231 | 312 | for location in args["locations"]: | |
232 | 313 | locations.append({ | |
233 | async def pipe_loop(ctx: Lingo2GameContext): | 314 | "id": location.location, |
234 | while not ctx.client.exit_event.is_set(): | 315 | "item": self.item_names.lookup_in_slot(location.item, location.player), |
316 | "player": self.player_names.get(location.player), | ||
317 | "flags": location.flags, | ||
318 | "self": int(location.player) == self.slot, | ||
319 | }) | ||
320 | |||
321 | self.manager.game_ctx.send_location_info(locations) | ||
322 | elif cmd == "SetReply": | ||
323 | if args["key"] == self.get_datastorage_key("keyboard1"): | ||
324 | self.handle_keyboard_update(1, args) | ||
325 | elif args["key"] == self.get_datastorage_key("keyboard2"): | ||
326 | self.handle_keyboard_update(2, args) | ||
327 | |||
328 | def get_datastorage_key(self, name: str): | ||
329 | return f"Lingo2_{self.slot}_{name}" | ||
330 | |||
331 | async def update_keyboard(self, updates: dict[str, int]): | ||
332 | kb1 = 0 | ||
333 | kb2 = 0 | ||
334 | |||
335 | for k, v in updates.items(): | ||
336 | if v == 0: | ||
337 | continue | ||
338 | |||
339 | effect = 0 | ||
340 | if v >= 1: | ||
341 | effect |= 1 | ||
342 | if v == 2: | ||
343 | effect |= 2 | ||
344 | |||
345 | pos = KEY_STORAGE_MAPPING[k] | ||
346 | if pos[0] == 1: | ||
347 | kb1 |= (effect << pos[1] * 2) | ||
348 | else: | ||
349 | kb2 |= (effect << pos[1] * 2) | ||
350 | |||
351 | msgs = [] | ||
352 | |||
353 | if kb1 != 0: | ||
354 | msgs.append({ | ||
355 | "cmd": "Set", | ||
356 | "key": self.get_datastorage_key("keyboard1"), | ||
357 | "want_reply": True, | ||
358 | "operations": [{ | ||
359 | "operation": "or", | ||
360 | "value": kb1 | ||
361 | }] | ||
362 | }) | ||
363 | |||
364 | if kb2 != 0: | ||
365 | msgs.append({ | ||
366 | "cmd": "Set", | ||
367 | "key": self.get_datastorage_key("keyboard2"), | ||
368 | "want_reply": True, | ||
369 | "operations": [{ | ||
370 | "operation": "or", | ||
371 | "value": kb2 | ||
372 | }] | ||
373 | }) | ||
374 | |||
375 | if len(msgs) > 0: | ||
376 | print(updates) | ||
377 | print(msgs) | ||
378 | await self.send_msgs(msgs) | ||
379 | |||
380 | def handle_keyboard_update(self, field: int, args: dict[str, Any]): | ||
381 | keys = {} | ||
382 | value = args["value"] | ||
383 | |||
384 | for i in range(0, 13): | ||
385 | if (value & (1 << (i * 2))) != 0: | ||
386 | keys[REVERSE_KEY_STORAGE_MAPPING[(field, i)]] = 1 | ||
387 | if (value & (1 << (i * 2 + 1))) != 0: | ||
388 | keys[REVERSE_KEY_STORAGE_MAPPING[(field, i)]] = 2 | ||
389 | |||
390 | updates = self.manager.update_keyboard(keys) | ||
391 | if len(updates) > 0: | ||
392 | self.manager.game_ctx.send_update_keyboard(updates) | ||
393 | |||
394 | |||
395 | async def pipe_loop(manager: Lingo2Manager): | ||
396 | while not manager.client_ctx.exit_event.is_set(): | ||
235 | try: | 397 | try: |
236 | socket = await websockets.connect("ws://localhost", port=PORT, ping_timeout=None, ping_interval=None, | 398 | socket = await websockets.connect("ws://localhost", port=PORT, ping_timeout=None, ping_interval=None, |
237 | max_size=MESSAGE_MAX_SIZE) | 399 | max_size=MESSAGE_MAX_SIZE) |
238 | ctx.server = Endpoint(socket) | 400 | manager.game_ctx.server = Endpoint(socket) |
239 | logger.info("Connected to Lingo 2!") | 401 | logger.info("Connected to Lingo 2!") |
240 | if ctx.client.auth is not None: | 402 | if manager.client_ctx.auth is not None: |
241 | ctx.send_connected() | 403 | manager.game_ctx.send_connected() |
242 | ctx.send_accessible_locations() | 404 | manager.game_ctx.send_accessible_locations() |
243 | async for data in ctx.server.socket: | 405 | async for data in manager.game_ctx.server.socket: |
244 | for msg in decode(data): | 406 | for msg in decode(data): |
245 | await process_game_cmd(ctx, msg) | 407 | await process_game_cmd(manager, msg) |
246 | except ConnectionRefusedError: | 408 | except ConnectionRefusedError: |
247 | logger.info("Could not connect to Lingo 2.") | 409 | logger.info("Could not connect to Lingo 2.") |
248 | finally: | 410 | finally: |
249 | ctx.server = None | 411 | manager.game_ctx.server = None |
250 | 412 | ||
251 | 413 | ||
252 | async def process_game_cmd(ctx: Lingo2GameContext, args: dict): | 414 | async def process_game_cmd(manager: Lingo2Manager, args: dict): |
253 | cmd = args["cmd"] | 415 | cmd = args["cmd"] |
254 | 416 | ||
255 | if cmd == "Connect": | 417 | if cmd == "Connect": |
@@ -262,13 +424,17 @@ async def process_game_cmd(ctx: Lingo2GameContext, args: dict): | |||
262 | else: | 424 | else: |
263 | server_address = f"{player}:None@{server}" | 425 | server_address = f"{player}:None@{server}" |
264 | 426 | ||
265 | async_start(ctx.client.connect(server_address), name="client connect") | 427 | async_start(manager.client_ctx.connect(server_address), name="client connect") |
266 | elif cmd == "Disconnect": | 428 | elif cmd == "Disconnect": |
267 | async_start(ctx.client.disconnect(), name="client disconnect") | 429 | async_start(manager.client_ctx.disconnect(), name="client disconnect") |
268 | elif cmd in ["Sync", "LocationChecks", "Say", "StatusUpdate", "LocationScouts"]: | 430 | elif cmd in ["Sync", "LocationChecks", "Say", "StatusUpdate", "LocationScouts"]: |
269 | async_start(ctx.client.send_msgs([args]), name="client forward") | 431 | async_start(manager.client_ctx.send_msgs([args]), name="client forward") |
432 | elif cmd == "UpdateKeyboard": | ||
433 | updates = manager.update_keyboard(args["keyboard"]) | ||
434 | if len(updates) > 0: | ||
435 | async_start(manager.client_ctx.update_keyboard(updates), name="client update keyboard") | ||
270 | elif cmd == "Quit": | 436 | elif cmd == "Quit": |
271 | ctx.client.exit_event.set() | 437 | manager.client_ctx.exit_event.set() |
272 | 438 | ||
273 | 439 | ||
274 | async def run_game(): | 440 | async def run_game(): |
@@ -318,9 +484,7 @@ def client_main(*launch_args: str) -> None: | |||
318 | 484 | ||
319 | client_ctx = Lingo2ClientContext(args.connect, args.password) | 485 | client_ctx = Lingo2ClientContext(args.connect, args.password) |
320 | game_ctx = Lingo2GameContext() | 486 | game_ctx = Lingo2GameContext() |
321 | 487 | manager = Lingo2Manager(game_ctx, client_ctx) | |
322 | client_ctx.game_ctx = game_ctx | ||
323 | game_ctx.client = client_ctx | ||
324 | 488 | ||
325 | client_ctx.server_task = asyncio.create_task(server_loop(client_ctx), name="ServerLoop") | 489 | client_ctx.server_task = asyncio.create_task(server_loop(client_ctx), name="ServerLoop") |
326 | 490 | ||
@@ -328,7 +492,7 @@ def client_main(*launch_args: str) -> None: | |||
328 | client_ctx.run_gui() | 492 | client_ctx.run_gui() |
329 | client_ctx.run_cli() | 493 | client_ctx.run_cli() |
330 | 494 | ||
331 | pipe_task = asyncio.create_task(pipe_loop(game_ctx), name="GameWatcher") | 495 | pipe_task = asyncio.create_task(pipe_loop(manager), name="GameWatcher") |
332 | 496 | ||
333 | try: | 497 | try: |
334 | await pipe_task | 498 | await pipe_task |