Skip to content
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: Add "CounterOption", use it for generic "StartInventory" and Witness "TrapWeights" #3756

Open
wants to merge 20 commits into
base: main
Choose a base branch
from

Conversation

NewSoupVi
Copy link
Member

@NewSoupVi NewSoupVi commented Aug 9, 2024

The purpose of this is best explained here:
#3754

Basically, CounterOption is an option whose value is a collections.Counter.

This means that (unlike an OptionDict), the values are always ints.
This means they can be rendered on the Website using the same code as ItemDicts, as long as they have valid_keys defined.

image

In fact, the ItemDict class has been changed to be a subclass of the new CounterOption.
For backwards compatibility (ensuring that in checks resolve the same way as before), ItemDicts make sure to cull 0s first before making the Counter.

This does make a change to Starting Inventory behavior: starting_inventory = {"Sword": 0} now behaves as if you had written starting_inventory = {}, instead of crashing. Imo this is an improvement, but your mileage may vary.

Tested:
Webhost Player Options: "Export Options" button, "Generate Single-Player Game" button
Generate: With yamls with varying keys missing or present

Not tested:
Advanced Options Page

Thing I'm unsure about:
Whether the Utils.py change is safe

@github-actions github-actions bot added affects: core Issues/PRs that touch core and may need additional validation. affects: webhost Issues/PRs that touch webhost and may need additional validation. waiting-on: peer-review Issue/PR has not been reviewed by enough people yet. labels Aug 9, 2024
@NewSoupVi NewSoupVi added the is: enhancement Issues requesting new features or pull requests implementing new features. label Aug 9, 2024
@NewSoupVi
Copy link
Member Author

Well, what did I do then

@Exempt-Medic
Copy link
Member

Exempt-Medic commented Aug 9, 2024

Of note, this makes Subnautica fail differently if all values are set to 0. Previously it failed trying to generate the options, but now it would fail in Subnautica's own generate_early

@NewSoupVi
Copy link
Member Author

NewSoupVi commented Aug 9, 2024

Of note, this makes Subnautica fail differently if all values are set to 0. Previously it failed trying to generate the options, but now it would fail in Subnautica's own generate_early

The same thing happens on main when you put filler_items_distribution: {} in your yaml, actually. Which is a perfectly valid input, so I might claim this is just "exposing a different bug" at worst

In fact, the same should be true for all cases - "All 0s" will yield the same behavior as {} used to, if I did everything right.

@NewSoupVi
Copy link
Member Author

NewSoupVi commented Aug 9, 2024

Ok, Exempt-Medic and I went over a lot of the cases of worlds using start_inventory and ItemDict. It seems none of them are doing any of the operations that would make this change to start_inventory dangerous.

The big problem would be this:

try:
    self.options.start_inventory["Non existent item"]
catch KeyError:
    foo()

as this would no longer work - Counter objects never KeyError.

I think every other difference between a dict and a Counter can never be an issue due to how I've implemented CounterOption & ItemDict.

The only supported worlds using ItemDict right now are Subnautica and KH2. For KH2, the new implementation of ItemDict works fine. For Subnautica, there is currently a bug with filler_items_distribution = {}, that can now be "reached" by doing filler_items_distribution = {"Valid Key": 0} as well, which was not a valid input before, but is an "alias" for {} with my PR, so this is expected and acceptable to me.
Edit: There's also Lufia2, which seems to be fine with this change.

Happy to be proven wrong, in which case I can either amend the implementation of CounterOption, or drop the idea of changing StartInventory/ItemDict to a CounterOption if necessary.

@alwaysintreble
Copy link
Collaborator

you're only doing the culling and negative erroring for ItemDict which is a subclass of CounterOption, so TrapWeights can have negative values which sounds unintended from your description.

@NewSoupVi
Copy link
Member Author

NewSoupVi commented Aug 10, 2024

you're only doing the culling and negative erroring for ItemDict which is a subclass of CounterOption, so TrapWeights can have negative values which sounds unintended from your description.

TrapWeights still has a Schema. On main, it is an OptionDict with a Schema, not an ItemDict. I had a PR open changing it to an ItemDict, but I closed that in favor of this.

Update: TrapWeights no longer has a schema, as I added min and max arguments.

Options.py Outdated Show resolved Hide resolved
Options.py Outdated


class CounterOption(OptionDict):
min: typing.Optional[int] = None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debatable but I think a default min of 1 probably has the most universal use cases

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about that? I'm aware of three options that use this or would want to use this, and two of them want min = 0.

Options.py Outdated
def __init__(self, value: typing.Dict[str, int]):
if any(item_count < 1 for item_count in value.values()):
raise Exception("Cannot have non-positive item counts.")
# Backwards compatibility: Cull 0s to make "in" checks behave the same as when this wasn't a CounterOption
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is just for back compat and verify is called after the option itself is initialized, min should probably be 1 and just leave the culling.

Separate conversation ig but it seems weird to leave in the culling at all since it's not like the actual option was getting those keys with 0 Val already

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohh, hm, I definitely intended that for start_inventory, {"Sword": 0} is a valid input now.

Options.py Outdated Show resolved Hide resolved
@Exempt-Medic
Copy link
Member

Exempt-Medic commented Dec 6, 2024

test_option_presets probably needs to updated from ItemDict to CounterOption. Also "options api.md" should probably include information on the new option type

@@ -93,8 +93,8 @@ <h1>Player Options</h1>
{% elif issubclass(option, Options.FreeText) %}
{{ inputs.FreeText(option_name, option) }}

{% elif issubclass(option, Options.ItemDict) and option.verify_item_name %}
{{ inputs.ItemDict(option_name, option) }}
{% elif issubclass(option, Options.OptionCounter) and option.valid_keys %}
Copy link
Member

@Exempt-Medic Exempt-Medic Dec 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change from verify_item_name to valid_keys makes StartInventory no longer appear anywhere.

Copy link
Member Author

@NewSoupVi NewSoupVi Dec 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gonna have to investigate that, because I thought verify_item_names makes it build a valid_keys from the metaclass
Been a while since I tested this now tho

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it did not work the way I thought it did. StartInventory should be back & code is now more flexible but also significantly uglier :/

@alwaysintreble
Copy link
Collaborator

Code seems fine (I didn't test it :)) but the docs definitely should be updated as part of this PR.

@NewSoupVi
Copy link
Member Author

True, I'll mark this as waiting on author for docs changes

@NewSoupVi NewSoupVi added the waiting-on: author Issue/PR is waiting for feedback or changes from its author. label Dec 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
affects: core Issues/PRs that touch core and may need additional validation. affects: webhost Issues/PRs that touch webhost and may need additional validation. is: enhancement Issues requesting new features or pull requests implementing new features. waiting-on: author Issue/PR is waiting for feedback or changes from its author. waiting-on: peer-review Issue/PR has not been reviewed by enough people yet.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants