-
Notifications
You must be signed in to change notification settings - Fork 658
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Core: Replace item link locations with modified collect/remove #4084
base: main
Are you sure you want to change the base?
Conversation
There didn't seem to be any issues with changing the name in multiworld.player_names, but that's not to say there aren't any. Changing `get_name_string_for_object` should be safer.
The type of `first_player_counts` seemed like it could be unclear, so a type hint has been added.
`linked_items_tuple` used to be a tuple in earlier development, but is now a list, so the name needed to be changed.
@@ -300,6 +300,51 @@ def link_items(self) -> None: | |||
"""Called to link together items in the itempool related to the registered item link groups.""" | |||
from worlds import AutoWorld | |||
|
|||
def patch_collect_and_remove(group_world: AutoWorld.World, linked_items: Dict[str, List[List[Item]]]): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could not find a good place to modify the collect/remove methods. I don't think monkey patching is great, but I don't think there are any good options.
MultiWorld.add_group()
currently contains no behaviour that is clearly specific for supporting item links. From what I've seen discussed, the less that core has to specifically support item links behaviour the better.
Even if the collect/remove methods for handling item links were set in MultiWorld.add_group()
, the item links have not been set up at that point, meaning that the collect/remove methods would have to get the linked Item
s to collect/remove from somewhere, likely the group's World
or the Group
(TypedDict
) instance itself, and MultiWorld.link_items()
would have to set the linked Item
s onto whichever type is chosen.
- Storing the linked
Item
s on theGroup
isn't ideal because theGroup
already contains a reference to theWorld
and this would result in theWorld
's collect/remove now having reference to theGroup
. At which point, it feels like they should just be stored on theWorld
instead, however... - Storing the linked
Item
s on theWorld
isn't ideal because to get any type checking for where the linkedItem
s are stored would requiring modifyingAutoWorld.World
to include a typed attribute for item links. As has already been mentioned, it is preferable to avoid modifying core specifically to support item links. - Possibly the linked
Item
s could be stored on theMultiWorld
itself sinceMultiWorld
already contains some theitem_links
attribute that is specific to supporting item links, though this would require an extra step of storing all the linkedItem
s in a dictionary keyed by the group ID because there is only oneMultiWorld
shared by all theWorld
s/Group
s.
@@ -400,7 +455,17 @@ def get_game_worlds(self, game_name: str): | |||
player not in self.groups and self.game[player] == game_name) | |||
|
|||
def get_name_string_for_object(self, obj: HasNameAndPlayer) -> str: | |||
return obj.name if self.players == 1 else f'{obj.name} ({self.get_player_name(obj.player)})' | |||
if self.players == 1: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initially, I modified the name set in MultiWorld.player_name
in MultiWorld.add_group()
, when creating a Group
for item links, to include all the players in the group. This seemed to work, but I suspect was not safe to do.
player = obj.player | ||
if player in self.groups: | ||
# Include all the players in the group. | ||
group_players = self.groups[player]["players"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
group_players
is a set
, so has no particular ordering. It might be beneficial if the players were sorted into a specific order. Sorting group_players
within each get_name_string_for_object
call does not seem like it would be a good idea, but maybe a "sorted_players"
key could be added to Group
that will contain the players after sorting.
self.regions.append(region) | ||
locations = region.locations | ||
linked_items: Dict[str, List[List[Item]]] = {item_name: [[] for _ in range(item_count)] | ||
for item_name, item_count in first_player_counts.items()} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As an extension to this PR, it would be possible to start with an empty List[List[Item]]
and only append to it from within the loop below. Doing so would eliminate the creation of many empty List[Item]
in the case of item linking non-advancement items.
I had a quick glance, this seems like the best solution so far. The only thing I'm concerned about is whether this still plays nicely with accessibility. Thinking about it, it should, but I'm not 100% sure immediately. |
This PR breaks HK's On main: Before this PR, if HK had wanted to count the number of Grubs belonging to a player in the multiworld, then all it would have needed to do was count the number of Grub items belonging to each player, there was no need to look at groups at all. I'll submit a PR to fix this in HK. Archipelago/worlds/hk/__init__.py Line 491 in 33daebe
From looking at the HK code, I think there is a point to be made that there does need to be some way for worlds to access the linked items or tell the difference between an unplaced item and an item linked item. Currently, the HK code assumes that each of the Grub items are classified as progression, which is not a problem, and if it needed to check how many were classified as progression, it could do so. But with this PR, the only items it would be able to access and check are the group items which do not give any information about the individual items that have been linked. An alternative is to keep track of the items as they are created, but it would then need to be able to tell the difference between an item that was not placed in the multiworld and an item that was item linked, which is not currently possible with this PR. |
For the record: This PR also breaks Witness Audio Log Hints. This is because Witness keeps references to its item instances for the sake of performance ( Witness would greatly appreciate a way to tell easily whether an item was linked without having to search the multiworld. |
RE Grubhunt
since the current code is explicitly working around an implementation detail of how item links used to work it will absolutely need to be rewritten, but my actual usecase is pretty simple under the assumption that replacement items will also be collected to the world(s) that they will be granted to by the server (because then its just how many grubs do i have in all-state for both) |
I think the Due to how linked items are collected based on a count of group items collected, meaning the group items can be collected in any order and the linked items will be collected in a consistent order, adding extra group locations and linked items to the multiworld for The HK code does not look correct even when there are no linked replacement grubs:
|
looks like you found a bug that got introduced in berserker and vi's attempts to optimize grubhunt #4094
Am I supposed to do something with this? If you fix item links (both link replacement items not existing for subworlds and locations being unreachable) in a way that I can just check all state for logically required grubs and total grubs to be sent client, then that code can be simplified and I'd be happy with that. |
Nothing for you to do. I just think that any full fix/rewrite for item links should either have all individually receivable items present in the multiworld, or the multiworld should hide away linked items as if they no longer exist and force worlds to treat group items like another item that belongs to their world. Ideally, to get the number of receivable grubs for each player you would end up being able to do: grub_count_per_player = Counter(item.player for item in multiworld.get_items()
if item.name == "Grub" and item.player in all_grub_players) or if linked items are hidden away and not visible to the multiworld: grub_count_per_player = Counter(item.player for item in multiworld.get_items()
if item.name == "Grub" and item.player in all_grub_players_and_group_ids)
for group_id in all_grub_groups:
group_count = grub_count_per_player[group_id]
for player in multiworld.groups[group_id]["players"]:
grub_count_per_player[player] += group_count |
I'm removing release-blocker as #4096 is merged, putting a bandaid on the issue for now. I'm adding meta: help wanted because I want this PR to be considered "important" + I think there are a lot of design problems to work out with games that do things like in-game hints so I would like some of those games (calling myself out here) to see what happens with this. |
What is this fixing or adding?
This patch removes the locations from item links, fixing a couple of issues with item links:
However, there are some downsides:
multiworld.get_spheres()
or similar.How was this tested?
I generated seeds with Bumper Stickers with
Everything
item linked, as well as A Hat in Time with linked Time Pieces, Hookshot Badge and Scooter Badge.If this makes graphical changes, please attach screenshots.
In a spoiler, a location/item belonging to a group will now also write the names of the players within that group: