diff options
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 |