-
Notifications
You must be signed in to change notification settings - Fork 477
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
Create a settings window #39
Comments
I just want to jot down some thoughts on this... I see a setting objects in PyBoy acting as both a namespace/object where all configurable variables are stored, and also as a GUI for adjusting those variables. Since the internals are still under development and it would be bad form to tightly couple this one object to all of the various modules, the
The settings module has both global and instance-level behavior, so either all instances of the object have to have some global awareness, or a single global instance makes namespaces for different instances of pyboy that register with it. Either way, pyboyb's submodules will probably need something passed to them so they either know which settings instance is their own, or have a name or reference to the pyboy instance for looking up in a global version. When registering their variables, modules should also leave a callback that can be used to update the values they've received (they keep local copies so they don't have to look it up constantly, but maybe this isn't necessary and they can look up every time). If the callback does not exist or raises an exception, the settings window can display some sort of warning that pyboy will need to restart to apply the changes. Anyway, if I were to write it today, that's probably how I would go about it, but I want to finish sound first and this would be a lot of effort so I thought I'd throw this up for some discussion before committing to the design. Thoughts? |
Okay, so @kr1tzy and I have been talking about how to approach this for a while, and it's been a very good discussion. I think we've worked out a plan for this that will work quite well for now and for the future. We wrote up our ideas together, which I'll paste in here. Hopefully it's clear enough that it gives a solid idea of how it works, and @Baekalfen perhaps you can ask questions or offer feedback if there's anything you don't feel sure about. After that, @kr1tzy can get started on implementing it when he's got time. PyBoy Settings SpecificationPyBoy’s configurable options (key bindings, window coloring, speed, etc.) are either hardcoded and left to recompilation or flow through several layers of constructors after being passed into PyBoy via CLI. There are many parts of PyBoy that we’ve wanted to configure, but every change requires edits in several places to accommodate new parameters. We’ve also wanted to add a settings window to allow for this configuration, but without changes to how settings are handled, it would increase the complexity and coupling of PyBoy even more. Summary
How It Works
Implementation
|
Great work both of you! Looks like a solid foundation for the task.
Is settings a singleton object, that lives for each process? Wouldn't it be easier to simple import said object instead of passing it down from the PyBoy constructor? Or am I misunderstanding it?
For simplicity, we could always just default to save the settings, save state, restart PyBoy and load state again. If our save/load state works as intended, this would fix practically all cases? Then for example key mappings only need to be taken care of in the constructor for the window. We can always make it better down the line.
Would it be any help to use
Great idea with the optional callbacks
Remember not to break too much of existing user's experience and documentation.
Is this needed? Lastly, we should probably already start thinking about adding a version number, and possibly a simple settings migrator. |
That was how I thought to do it at first as well, but then if you want to have multiple instances of pyboy that behave differently, it gets complicated. I couldn't think of a good way to have multiple ones without passing them explicitly, but the other option would be to only use a settings object for the interactive side of pyboy, and keep the core stuff out of it so headless instances just don't need it. I'd be very interested in opinions on this if you think it would be better. It makes sense, in a lot of ways, from a user perspective, so it warrants discussion.
That's an interesting idea. I would think allowing for on-the-fly changes is still a good idea for simple settings but you're right that that could be left out for the first run to get the critical parts working if loading state can make it smooth.
I don't think so.
We can definitely preserve as many of the existing arguments as necessary, but I figure we can talk about which should stay and which can change to exclusively
Raising an error, or the whole
Do you mean to create a numbered branch again, or just to plan to increment when this merges to master? What would the settings migrator do? Anyway, thanks for the responses! I know it's a lot, and it's taken a long while to get here as well, haha. Hopefully it gets the job done! |
I think that sounds like a more convenient way of handling it on the programming side. I kind of imagined, that the settings were loaded from the config file only on start-up and when opening the settings window, we load the file and save to it. This makes it a lot easier to code, and it also fits with the save-state/restart/load-state in regards to configs. In regards to conflicting configs, we can just let the newest instance override the settings object. It's already a bit of a weird use-case.
Definitely. I know it's tempting to do the "final" version first, but I think it's better to take it in increments, so it won't be an enormous change at once. We can always improve later. And I don't think this will code us into a corner.
How are we going to store the settings? I have imagined something like this in {
"version": 1
"main" : {
"scale" : {
"type": "int",
"value": "3"
},
...
},
"window" : {
"BUTTON_A": {
"type": "enum_key",
"value": "A"
}
}
}
Yes, let's do that when we get started. Also, we can deprecate some settings and keep them, while hiding them in the documentation.
Makes sense. Just thought it sounded like more work to break if somebody creates a new settings object etc. But validating the options with warnings make sense.
Where would settings come from down the line? Bad Python code?
When we eventually change the format or the available settings, we want to automatically migrate to the new format. Say that an int becomes a float or a string becomes an enum. |
Ah, there's a crucial point here that I think I didn't make well enough in the first place: the config file only stores the names and values (as strings) with no extra information like types or defaults or version. All that information is in the code of the modules where the values are used, and then stored at runtime in the settings object. As long as the various settings can parse the strings used to encode their values and return the correct type, there won't be any need for an upgrader or meta data (in fact, if we make an update that changes the type for a certain value, we can make the upgrade in the setting parser functions so it's backwards compatible during the transition. So a .ini with settings would look something like this:
Obviously that's not everything, and we can decide to have more sections (e.g., a depth of three so
So does this mean you like the idea of only having a settings object for the singleton GUI instance, and tell headless copies to do something else, or what "that" do you mean?
This becomes a problem if you have plugins that create their own instances, or if you have a bunch of instances that are owned by different threads running concurrently like we do with the automated tests. |
How are you able to detect if you're reading a settings file in an old format, if there's neither a version number nor a datatype?
I like the idea of being able to import the active settings-object directly in each file it's needed (like
Have you decided on how to access the settings GUI?
Plugins that create their own instances of PyBoy or the settings object? Either way, we can just say it's not the way to do it, and it's unsupported.
That is a limitation. We could probably make a trick to keep track of the PyBoy instances? |
Hey, sorry it took me so long to get back to this! I got distracted with holidays and forgot it was my turn to reply. On the version detection, I don't think that's something we should worry about. None of the data in the settings file is critical or hard to reproduce--it's just trivial settings. Bundling backwards compatibility like we do for the save states would be more work than it's worth, especially if it limits readability. If an update requires that a user tweak their preferences file, then we don't care. It won't happen that often anyway. So let's consider whether a single settings object along with a singleton GUI instance of PyBoy is the way to go... it would mean that there are some settings that wouldn't make sense, like a setting for which window plugin to use, or really anything that we'd need to access from non-primary instances. That might not be a bad thing, though: it would force us to treat this feature as a helpful thing for users running the main emulator rather than feature creeping all the way up to some global data structure that would impose extra restrictions on us. Maybe we could have some kind of flag that goes to Oh, and for what it's worth, if the setting object is a singleton object, then we can do Sorry that's a little all over the place. It's been a while since I thought about this. But I think we're moving in the right direction still. |
I don't agree on this premise. We could easily have to change formats to something that might break a users config, and you'll have to fix it themselves. And ignoring it, will either make the config parser crash, or ignore their settings ― both of which are frustrating. By adding a simple version counter, we can prevent a lot of issues in the future. We should go for what makes it easy for us to program against. I doubt we are going to see a lot of people trying to change the settings depending on each instance of PyBoy. And if they do, they should fall back to make separate processes without shared memory. |
It would be nice to have a settings window to change keybindings, ROM directory, screen recording settings etc.
The text was updated successfully, but these errors were encountered: