diff options
Diffstat (limited to 'apworld/context.py')
| -rw-r--r-- | apworld/context.py | 31 | 
1 files changed, 25 insertions, 6 deletions
| diff --git a/apworld/context.py b/apworld/context.py index e2d80cd..52b04ae 100644 --- a/apworld/context.py +++ b/apworld/context.py | |||
| @@ -30,6 +30,16 @@ KEY_STORAGE_MAPPING = { | |||
| 30 | REVERSE_KEY_STORAGE_MAPPING = {t: k for k, t in KEY_STORAGE_MAPPING.items()} | 30 | REVERSE_KEY_STORAGE_MAPPING = {t: k for k, t in KEY_STORAGE_MAPPING.items()} | 
| 31 | 31 | ||
| 32 | 32 | ||
| 33 | # There is a distinction between an object's ID and its AP ID. The latter is stable between releases, whereas the former | ||
| 34 | # can change and is also namespaced based on the object type. We should only store AP IDs in multiworld state (such as | ||
| 35 | # slot data and data storage) to increase compatability between releases. The data we currently store is: | ||
| 36 | # - Port pairings for worldport shuffle (slot data) | ||
| 37 | # - Checked worldports for worldport shuffle (data storage) | ||
| 38 | # - Latched doors (data storage) | ||
| 39 | # The client generally deals in the actual object IDs rather than the stable IDs, although it does have to convert the | ||
| 40 | # port pairing IDs when reading them from slot data. The context (this file here) does the work of converting back and | ||
| 41 | # forth between the values. AP IDs are converted to IDs after reading them from data storage, and IDs are converted to | ||
| 42 | # AP IDs before sending them to data storage. | ||
| 33 | class Lingo2Manager: | 43 | class Lingo2Manager: | 
| 34 | game_ctx: "Lingo2GameContext" | 44 | game_ctx: "Lingo2GameContext" | 
| 35 | client_ctx: "Lingo2ClientContext" | 45 | client_ctx: "Lingo2ClientContext" | 
| @@ -74,6 +84,7 @@ class Lingo2Manager: | |||
| 74 | 84 | ||
| 75 | return ret | 85 | return ret | 
| 76 | 86 | ||
| 87 | # Input should be real IDs, not AP IDs | ||
| 77 | def update_worldports(self, new_worldports: set[int]) -> set[int]: | 88 | def update_worldports(self, new_worldports: set[int]) -> set[int]: | 
| 78 | ret = new_worldports.difference(self.worldports) | 89 | ret = new_worldports.difference(self.worldports) | 
| 79 | self.worldports.update(new_worldports) | 90 | self.worldports.update(new_worldports) | 
| @@ -227,6 +238,7 @@ class Lingo2GameContext: | |||
| 227 | 238 | ||
| 228 | async_start(self.send_msgs([msg]), name="update keyboard") | 239 | async_start(self.send_msgs([msg]), name="update keyboard") | 
| 229 | 240 | ||
| 241 | # Input should be real IDs, not AP IDs | ||
| 230 | def send_update_worldports(self, worldports): | 242 | def send_update_worldports(self, worldports): | 
| 231 | if self.server is None: | 243 | if self.server is None: | 
| 232 | return | 244 | return | 
| @@ -441,13 +453,15 @@ class Lingo2ClientContext(CommonContext): | |||
| 441 | elif args["key"] == self.get_datastorage_key("keyboard2"): | 453 | elif args["key"] == self.get_datastorage_key("keyboard2"): | 
| 442 | self.handle_keyboard_update(2, args) | 454 | self.handle_keyboard_update(2, args) | 
| 443 | elif args["key"] == self.get_datastorage_key("worldports"): | 455 | elif args["key"] == self.get_datastorage_key("worldports"): | 
| 444 | updates = self.manager.update_worldports(set(args["value"])) | 456 | port_ids = set(Lingo2World.static_logic.port_id_by_ap_id[ap_id] for ap_id in args["value"]) | 
| 457 | updates = self.manager.update_worldports(port_ids) | ||
| 445 | if len(updates) > 0: | 458 | if len(updates) > 0: | 
| 446 | self.manager.game_ctx.send_update_worldports(updates) | 459 | self.manager.game_ctx.send_update_worldports(updates) | 
| 447 | elif args["key"] == self.victory_data_storage_key: | 460 | elif args["key"] == self.victory_data_storage_key: | 
| 448 | self.handle_status_update(args["value"]) | 461 | self.handle_status_update(args["value"]) | 
| 449 | elif args["key"] == self.get_datastorage_key("latches"): | 462 | elif args["key"] == self.get_datastorage_key("latches"): | 
| 450 | updates = self.manager.update_latches(set(args["value"])) | 463 | door_ids = set(Lingo2World.static_logic.door_id_by_ap_id[ap_id] for ap_id in args["value"]) | 
| 464 | updates = self.manager.update_latches(door_ids) | ||
| 451 | if len(updates) > 0: | 465 | if len(updates) > 0: | 
| 452 | self.manager.game_ctx.send_update_latches(updates) | 466 | self.manager.game_ctx.send_update_latches(updates) | 
| 453 | 467 | ||
| @@ -515,14 +529,16 @@ class Lingo2ClientContext(CommonContext): | |||
| 515 | if len(updates) > 0: | 529 | if len(updates) > 0: | 
| 516 | self.manager.game_ctx.send_update_keyboard(updates) | 530 | self.manager.game_ctx.send_update_keyboard(updates) | 
| 517 | 531 | ||
| 532 | # Input should be real IDs, not AP IDs | ||
| 518 | async def update_worldports(self, updates: set[int]): | 533 | async def update_worldports(self, updates: set[int]): | 
| 534 | port_ap_ids = [Lingo2World.static_logic.objects.ports[port_id].ap_id for port_id in updates] | ||
| 519 | await self.send_msgs([{ | 535 | await self.send_msgs([{ | 
| 520 | "cmd": "Set", | 536 | "cmd": "Set", | 
| 521 | "key": self.get_datastorage_key("worldports"), | 537 | "key": self.get_datastorage_key("worldports"), | 
| 522 | "want_reply": True, | 538 | "want_reply": True, | 
| 523 | "operations": [{ | 539 | "operations": [{ | 
| 524 | "operation": "update", | 540 | "operation": "update", | 
| 525 | "value": updates | 541 | "value": port_ap_ids | 
| 526 | }] | 542 | }] | 
| 527 | }]) | 543 | }]) | 
| 528 | 544 | ||
| @@ -532,13 +548,14 @@ class Lingo2ClientContext(CommonContext): | |||
| 532 | self.manager.game_ctx.send_accessible_locations() | 548 | self.manager.game_ctx.send_accessible_locations() | 
| 533 | 549 | ||
| 534 | async def update_latches(self, updates: set[int]): | 550 | async def update_latches(self, updates: set[int]): | 
| 551 | door_ap_ids = [Lingo2World.static_logic.objects.doors[door_id].ap_id for door_id in updates] | ||
| 535 | await self.send_msgs([{ | 552 | await self.send_msgs([{ | 
| 536 | "cmd": "Set", | 553 | "cmd": "Set", | 
| 537 | "key": self.get_datastorage_key("latches"), | 554 | "key": self.get_datastorage_key("latches"), | 
| 538 | "want_reply": True, | 555 | "want_reply": True, | 
| 539 | "operations": [{ | 556 | "operations": [{ | 
| 540 | "operation": "update", | 557 | "operation": "update", | 
| 541 | "value": updates | 558 | "value": door_ap_ids | 
| 542 | }] | 559 | }] | 
| 543 | }]) | 560 | }]) | 
| 544 | 561 | ||
| @@ -590,12 +607,14 @@ async def process_game_cmd(manager: Lingo2Manager, args: dict): | |||
| 590 | async_start(manager.client_ctx.update_keyboard(updates), name="client update keyboard") | 607 | async_start(manager.client_ctx.update_keyboard(updates), name="client update keyboard") | 
| 591 | elif cmd == "CheckWorldport": | 608 | elif cmd == "CheckWorldport": | 
| 592 | port_id = args["port_id"] | 609 | port_id = args["port_id"] | 
| 610 | port_ap_id = Lingo2World.static_logic.objects.ports[port_id].ap_id | ||
| 593 | worldports = {port_id} | 611 | worldports = {port_id} | 
| 594 | 612 | ||
| 595 | # Also check the reverse port if it's a two-way connection. | 613 | # Also check the reverse port if it's a two-way connection. | 
| 596 | port_pairings = manager.client_ctx.slot_data["port_pairings"] | 614 | port_pairings = manager.client_ctx.slot_data["port_pairings"] | 
| 597 | if str(port_id) in port_pairings and port_pairings.get(str(port_pairings[str(port_id)]), None) == port_id: | 615 | if str(port_ap_id) in port_pairings and\ | 
| 598 | worldports.add(port_pairings[str(port_id)]) | 616 | port_pairings.get(str(port_pairings[str(port_ap_id)]), None) == port_ap_id: | 
| 617 | worldports.add(Lingo2World.static_logic.port_id_by_ap_id[port_pairings[str(port_ap_id)]]) | ||
| 599 | 618 | ||
| 600 | updates = manager.update_worldports(worldports) | 619 | updates = manager.update_worldports(worldports) | 
| 601 | if len(updates) > 0: | 620 | if len(updates) > 0: | 
