diff options
Diffstat (limited to 'apworld/context.py')
| -rw-r--r-- | apworld/context.py | 83 |
1 files changed, 80 insertions, 3 deletions
| diff --git a/apworld/context.py b/apworld/context.py index 52b04ae..86392f9 100644 --- a/apworld/context.py +++ b/apworld/context.py | |||
| @@ -49,6 +49,7 @@ class Lingo2Manager: | |||
| 49 | worldports: set[int] | 49 | worldports: set[int] |
| 50 | goaled: bool | 50 | goaled: bool |
| 51 | latches: set[int] | 51 | latches: set[int] |
| 52 | hinted_locations: set[int] | ||
| 52 | 53 | ||
| 53 | def __init__(self, game_ctx: "Lingo2GameContext", client_ctx: "Lingo2ClientContext"): | 54 | def __init__(self, game_ctx: "Lingo2GameContext", client_ctx: "Lingo2ClientContext"): |
| 54 | self.game_ctx = game_ctx | 55 | self.game_ctx = game_ctx |
| @@ -57,8 +58,6 @@ class Lingo2Manager: | |||
| 57 | self.client_ctx.manager = self | 58 | self.client_ctx.manager = self |
| 58 | self.tracker = Tracker(self) | 59 | self.tracker = Tracker(self) |
| 59 | self.keyboard = {} | 60 | self.keyboard = {} |
| 60 | self.worldports = set() | ||
| 61 | self.latches = set() | ||
| 62 | 61 | ||
| 63 | self.reset() | 62 | self.reset() |
| 64 | 63 | ||
| @@ -69,6 +68,7 @@ class Lingo2Manager: | |||
| 69 | self.worldports = set() | 68 | self.worldports = set() |
| 70 | self.goaled = False | 69 | self.goaled = False |
| 71 | self.latches = set() | 70 | self.latches = set() |
| 71 | self.hinted_locations = set() | ||
| 72 | 72 | ||
| 73 | def update_keyboard(self, new_keyboard: dict[str, int]) -> dict[str, int]: | 73 | def update_keyboard(self, new_keyboard: dict[str, int]) -> dict[str, int]: |
| 74 | ret: dict[str, int] = {} | 74 | ret: dict[str, int] = {} |
| @@ -101,6 +101,12 @@ class Lingo2Manager: | |||
| 101 | 101 | ||
| 102 | return ret | 102 | return ret |
| 103 | 103 | ||
| 104 | def update_hinted_locations(self, new_locs: set[int]) -> set[int]: | ||
| 105 | ret = new_locs.difference(self.hinted_locations) | ||
| 106 | self.hinted_locations.update(new_locs) | ||
| 107 | |||
| 108 | return ret | ||
| 109 | |||
| 104 | 110 | ||
| 105 | class Lingo2GameContext: | 111 | class Lingo2GameContext: |
| 106 | server: Endpoint | None | 112 | server: Endpoint | None |
| @@ -276,6 +282,28 @@ class Lingo2GameContext: | |||
| 276 | 282 | ||
| 277 | async_start(self.send_msgs([msg]), name="update latches") | 283 | async_start(self.send_msgs([msg]), name="update latches") |
| 278 | 284 | ||
| 285 | def send_ignored_locations(self, ignored_locations): | ||
| 286 | if self.server is None: | ||
| 287 | return | ||
| 288 | |||
| 289 | msg = { | ||
| 290 | "cmd": "SetIgnoredLocations", | ||
| 291 | "locations": ignored_locations, | ||
| 292 | } | ||
| 293 | |||
| 294 | async_start(self.send_msgs([msg]), name="set ignored locations") | ||
| 295 | |||
| 296 | def send_update_hinted_locations(self, hinted_locations): | ||
| 297 | if self.server is None: | ||
| 298 | return | ||
| 299 | |||
| 300 | msg = { | ||
| 301 | "cmd": "UpdateHintedLocations", | ||
| 302 | "locations": hinted_locations, | ||
| 303 | } | ||
| 304 | |||
| 305 | async_start(self.send_msgs([msg]), name="update hinted locations") | ||
| 306 | |||
| 279 | async def send_msgs(self, msgs: list[Any]) -> None: | 307 | async def send_msgs(self, msgs: list[Any]) -> None: |
| 280 | """ `msgs` JSON serializable """ | 308 | """ `msgs` JSON serializable """ |
| 281 | if not self.server or not self.server.socket.open or self.server.socket.closed: | 309 | if not self.server or not self.server.socket.open or self.server.socket.closed: |
| @@ -290,6 +318,7 @@ class Lingo2ClientContext(CommonContext): | |||
| 290 | items_handling = 0b111 | 318 | items_handling = 0b111 |
| 291 | 319 | ||
| 292 | slot_data: dict[str, Any] | None | 320 | slot_data: dict[str, Any] | None |
| 321 | hints_data_storage_key: str | ||
| 293 | victory_data_storage_key: str | 322 | victory_data_storage_key: str |
| 294 | 323 | ||
| 295 | def __init__(self, server_address: str | None = None, password: str | None = None): | 324 | def __init__(self, server_address: str | None = None, password: str | None = None): |
| @@ -327,10 +356,12 @@ class Lingo2ClientContext(CommonContext): | |||
| 327 | self.manager.tracker.set_checked_locations(self.checked_locations) | 356 | self.manager.tracker.set_checked_locations(self.checked_locations) |
| 328 | self.manager.game_ctx.send_accessible_locations() | 357 | self.manager.game_ctx.send_accessible_locations() |
| 329 | 358 | ||
| 359 | self.hints_data_storage_key = f"_read_hints_{self.team}_{self.slot}" | ||
| 330 | self.victory_data_storage_key = f"_read_client_status_{self.team}_{self.slot}" | 360 | self.victory_data_storage_key = f"_read_client_status_{self.team}_{self.slot}" |
| 331 | 361 | ||
| 332 | self.set_notify(self.get_datastorage_key("keyboard1"), self.get_datastorage_key("keyboard2"), | 362 | self.set_notify(self.get_datastorage_key("keyboard1"), self.get_datastorage_key("keyboard2"), |
| 333 | self.victory_data_storage_key, self.get_datastorage_key("latches")) | 363 | self.victory_data_storage_key, self.get_datastorage_key("latches"), |
| 364 | self.get_datastorage_key("ignored_locations")) | ||
| 334 | msg_batch = [{ | 365 | msg_batch = [{ |
| 335 | "cmd": "Set", | 366 | "cmd": "Set", |
| 336 | "key": self.get_datastorage_key("keyboard1"), | 367 | "key": self.get_datastorage_key("keyboard1"), |
| @@ -349,6 +380,12 @@ class Lingo2ClientContext(CommonContext): | |||
| 349 | "default": [], | 380 | "default": [], |
| 350 | "want_reply": True, | 381 | "want_reply": True, |
| 351 | "operations": [{"operation": "default", "value": []}] | 382 | "operations": [{"operation": "default", "value": []}] |
| 383 | }, { | ||
| 384 | "cmd": "Set", | ||
| 385 | "key": self.get_datastorage_key("ignored_locations"), | ||
| 386 | "default": [], | ||
| 387 | "want_reply": True, | ||
| 388 | "operations": [{"operation": "default", "value": []}] | ||
| 352 | }] | 389 | }] |
| 353 | 390 | ||
| 354 | if self.slot_data.get("shuffle_worldports", False): | 391 | if self.slot_data.get("shuffle_worldports", False): |
| @@ -447,6 +484,8 @@ class Lingo2ClientContext(CommonContext): | |||
| 447 | for k, v in args["keys"].items(): | 484 | for k, v in args["keys"].items(): |
| 448 | if k == self.victory_data_storage_key: | 485 | if k == self.victory_data_storage_key: |
| 449 | self.handle_status_update(v) | 486 | self.handle_status_update(v) |
| 487 | elif k == self.hints_data_storage_key: | ||
| 488 | self.update_hints() | ||
| 450 | elif cmd == "SetReply": | 489 | elif cmd == "SetReply": |
| 451 | if args["key"] == self.get_datastorage_key("keyboard1"): | 490 | if args["key"] == self.get_datastorage_key("keyboard1"): |
| 452 | self.handle_keyboard_update(1, args) | 491 | self.handle_keyboard_update(1, args) |
| @@ -464,6 +503,10 @@ class Lingo2ClientContext(CommonContext): | |||
| 464 | updates = self.manager.update_latches(door_ids) | 503 | updates = self.manager.update_latches(door_ids) |
| 465 | if len(updates) > 0: | 504 | if len(updates) > 0: |
| 466 | self.manager.game_ctx.send_update_latches(updates) | 505 | self.manager.game_ctx.send_update_latches(updates) |
| 506 | elif args["key"] == self.get_datastorage_key("ignored_locations"): | ||
| 507 | self.manager.game_ctx.send_ignored_locations(args["value"]) | ||
| 508 | elif args["key"] == self.hints_data_storage_key: | ||
| 509 | self.update_hints() | ||
| 467 | 510 | ||
| 468 | def get_datastorage_key(self, name: str): | 511 | def get_datastorage_key(self, name: str): |
| 469 | return f"Lingo2_{self.slot}_{name}" | 512 | return f"Lingo2_{self.slot}_{name}" |
| @@ -559,6 +602,36 @@ class Lingo2ClientContext(CommonContext): | |||
| 559 | }] | 602 | }] |
| 560 | }]) | 603 | }]) |
| 561 | 604 | ||
| 605 | async def add_ignored_location(self, loc_id: int): | ||
| 606 | await self.send_msgs([{ | ||
| 607 | "cmd": "Set", | ||
| 608 | "key": self.get_datastorage_key("ignored_locations"), | ||
| 609 | "want_reply": True, | ||
| 610 | "operations": [{ | ||
| 611 | "operation": "update", | ||
| 612 | "value": [loc_id] | ||
| 613 | }] | ||
| 614 | }]) | ||
| 615 | |||
| 616 | async def remove_ignored_location(self, loc_id: int): | ||
| 617 | await self.send_msgs([{ | ||
| 618 | "cmd": "Set", | ||
| 619 | "key": self.get_datastorage_key("ignored_locations"), | ||
| 620 | "want_reply": True, | ||
| 621 | "operations": [{ | ||
| 622 | "operation": "remove", | ||
| 623 | "value": loc_id | ||
| 624 | }] | ||
| 625 | }]) | ||
| 626 | |||
| 627 | def update_hints(self): | ||
| 628 | hints = self.stored_data.get(self.hints_data_storage_key, []) | ||
| 629 | |||
| 630 | hinted_locations = set(hint["location"] for hint in hints if hint["finding_player"] == self.slot) | ||
| 631 | updates = self.manager.update_hinted_locations(hinted_locations) | ||
| 632 | if len(updates) > 0: | ||
| 633 | self.manager.game_ctx.send_update_hinted_locations(updates) | ||
| 634 | |||
| 562 | 635 | ||
| 563 | async def pipe_loop(manager: Lingo2Manager): | 636 | async def pipe_loop(manager: Lingo2Manager): |
| 564 | while not manager.client_ctx.exit_event.is_set(): | 637 | while not manager.client_ctx.exit_event.is_set(): |
| @@ -635,6 +708,10 @@ async def process_game_cmd(manager: Lingo2Manager, args: dict): | |||
| 635 | updates = manager.update_latches({args["door"]}) | 708 | updates = manager.update_latches({args["door"]}) |
| 636 | if len(updates) > 0: | 709 | if len(updates) > 0: |
| 637 | async_start(manager.client_ctx.update_latches(updates), name="client update latches") | 710 | async_start(manager.client_ctx.update_latches(updates), name="client update latches") |
| 711 | elif cmd == "IgnoreLocation": | ||
| 712 | async_start(manager.client_ctx.add_ignored_location(args["id"]), name="client ignore loc") | ||
| 713 | elif cmd == "UnignoreLocation": | ||
| 714 | async_start(manager.client_ctx.remove_ignored_location(args["id"]), name="client unignore loc") | ||
| 638 | elif cmd == "Quit": | 715 | elif cmd == "Quit": |
| 639 | manager.client_ctx.exit_event.set() | 716 | manager.client_ctx.exit_event.set() |
| 640 | 717 | ||
