about summary refs log tree commit diff stats
path: root/apworld/context.py
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2025-11-01 14:32:04 -0400
committerStar Rauchenberger <fefferburbia@gmail.com>2025-11-01 14:32:04 -0400
commitf3b490b10aeac32ba859b929ff13ff882d818a17 (patch)
tree1fbcc27fc7fed2646ea86fede3f28b26a58a0297 /apworld/context.py
parent21013ec0d1e3953276f11bd8cce12f33d46a33ec (diff)
downloadlingo2-archipelago-f3b490b10aeac32ba859b929ff13ff882d818a17.tar.gz
lingo2-archipelago-f3b490b10aeac32ba859b929ff13ff882d818a17.tar.bz2
lingo2-archipelago-f3b490b10aeac32ba859b929ff13ff882d818a17.zip
Store stable IDs in multiworld state
Diffstat (limited to 'apworld/context.py')
-rw-r--r--apworld/context.py31
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 = {
30REVERSE_KEY_STORAGE_MAPPING = {t: k for k, t in KEY_STORAGE_MAPPING.items()} 30REVERSE_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.
33class Lingo2Manager: 43class 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: