Open toomanybrians opened 7 years ago
As part of this refactor, asset loading should also be considered. While we probably can get rid of the mode controller and modes in the MC, a mechanism is needed to load asset groups so assets are ready when needed for immediate play response. It probably makes sense to have assets grouped by mode for example so when a mode starts a command is sent from MPF to the MC to tell it to load a group of assets and to unload them when the mode stops. We can of course continue to have machine-wide assets that load on init as well, but some kind of dynamic asset loading/unloading scheme should be included in this refactor.
The Unity MC keeps a store of all player variables and machine variables to make it easy to display current values on the screen no matter when the object is displayed. This also makes display updates automatic. Might want to do something similar in the Kivy MC rather than forcing variable substitution before sending a play message. That would make display updates much easier.
While working on the display refactoring I've been thinking about this big MC refactor. Here are a few thoughts:
All config parsing and validation should be done on the MPF side. The MC should not have to know anything about config files (other than its own basic configuration (kivy config, paths, config players, widgets, port, etc.). MC should be able to assume that all config dicts sent via BCP have already been validated.
It would be nice to refactor the big MPF config spec to allow each MPF plugin to provide its own config spec. It really doesn't make sense to put MC-specific config items in the master MPF spec. Makes it difficult for custom plugins to use the config validation stuff.
Asset dictionaries can be stored on the MPF side and sent via BCP as needed. However, corresponding large binary asset files should be loaded and stored on the MC side (videos, sounds, images, etc.). The Asset discovery process needs some thought, especially if the MC is allowed to be on a separate machine. Should asset source file paths be specified on the MPF side and sent during initialization, or can we continue to crawl the asset folders at startup on the MC side to discover assets? Perhaps some kind of hybrid design. The less intelligence the MC needs the better.
For dynamic widget text (and other runtime widget setting updates), I like the idea of being able to send only a widget ID and a setting/value pair to update an existing widget (or other asset). That basically requires every widget or other asset instance to have a unique identifier that MPF is aware of. State would have to be kept on the MPF side (list of active instance IDs), at least for any widgets/assets that require updating.
Context should be managed on the MPF side as well. The MC should not have to know anything about modes. This could be done in a various number of ways. The simplest would be to add a context parameter to all widgets and assets on the MC side and use it just like a key (stop all sounds with the following context, remove all widgets with the specified context, etc.). The context setting would be assigned on the MPF side, just like all other parameter dict entries. We of course could just use the existing key parameter for that use, but I like having them separate. As an alternative, we could have MPF keep track of the active item IDs in the context and send individual stop/remove events.
More thoughts to come as I start to work on this some more...
For config players that do stuff in MPF-MC (slide player, widget player, and sound player), there are actually two different ways they're triggered via BCP, depending on whether the configs are from a config file or a step in a show.
For example, a slide_player: section of a config file contains sub-entries for events. In the current design of MPF, this config section is processed by both the MPF and MPF-MC side. (The plugin player on the MPF side, and the mpfmc config player on the MC side.) The MPF plugin player doesn't do much other than parsing the config to look for event which it needs to set up triggers for. (This is mostly handled in the PluginPlayer base class, though, for example, the slide player has its own code which also looks for animation and transition events.)
So anyway, the plugin player on the MPF side just sends BCP triggers based on events.
On the MC side, when MPF-MC starts, the MC config player also parses the configs and does all the config validation and stuff, and it also looks for events in the config so it can watch for incoming BCP triggers, and when it sees them, it then looks to the config and does whatever it's supposed to do.
So the whole thing is a two-part design, with some duplication on both sides. It also means that the MC needs to know the state of MPF, because it needs to know which modes are active and mode priorities and player vars and all that stuff since it needs to know what to do when those triggers come in.
All of this so far just applies to config player entries directly included in config files. However show steps work completely different since show steps don't have events associated with them. When a show step is played with content from a plugin player, the MPF side simply bundles up the entire config for the step into JSON and sends it via a BCP trigger with the name "xxx_play" and a kwarg of the serialized json payload. (xxx_play is based on the plugin player "show_section" attribute, so "slides_play", "widgets_play", etc.)
So the idea is "Why are there two completely different ways to handle MC plugin players via BCP?", and "Can we just use the 'show' way of doing things for all plugin players?"
I'm thinking that can make the MC more dumb, since then it wouldn't have to track modes (or even events) at all. Basically all incoming config player commands would come in via a trigger (along with a key/name, a context, and a priority) and then the MC just plays them immediately (or schedules them in a queue or whatever). Also all conditional logic would be supported since it's all handled on the MC side.
I also expect this could be used with the Unity MC (or others) since now a lot of the logic for what's going on is moved to the MPF side and MPF doesn't really care what the payloads are, and each MC can do whatever it wants with them.
I would expect that the PluginPlayer concept would still exist, and we'd move the hard-core config validation and processing from the MC side to the MPF side via plugin players so the MC (hopefully) always gets something it expects.
There are some challenges that will have to be addressed.
First, the "magic" events that are generated on the MC side (slide shown, slide active, sound done, etc.) will need to send triggers back to MPF, and the plugin player on the MPF side will need to send the next payload of whatever was supposed to happen then.
We also need to think about whether the MC returns anything to MPF on an xxx_play trigger. (Should it let MPF know if a sound was queued instead of played immediately, or if a slide was not able to be shown because its priority was lower than the current slide?) This could be useful in our conversation from a year ago where we talked about how to handle shows that include both MPF and MC components (typically video+sound animations plus a light show). Depending on the priority of the show and what's going on at the time, the MC might choose to delay a slide or sound, in which case we'd want the MPF side to also delay the light show so it's in sync with the sound. (This could even be a simple slide + sound where the sound is delayed but the slide shows.)
Maybe we make it so that it's expected that if every xxx_play event has a key/name, then whenever it is actually "played" it sends a BCP event back to MPF? So for slides, it could be when that slide was actually shown, or for a sound, when that sound was actually played, so then MPF can ignore that or trigger a synced event? Or it could be a simple bool for whether it could be handled now or not and MPF can retry until it works or MPF gives up?
There's another architectural issue to think about too, and that is whether the MC maintains any state at all? Using the slide player as an example, I was thinking the MC should maintain all "active" slides (each with its own priority, key/name, and context), because if (for example), some slide is showing which is playing a video or animation, and then another higher priority slide comes in (something like "TILT WARNING) for 2s, then when that temp slide is done, it would be nice if the MPF trigger could just remove that slide (perhaps with transition payload instructions) and the MC would go back to the previous slide (which was still playing in the background most likely)
This change will also mean that monitoring player vars for text widget changes will happen on the MPF side, and if MPF noticed a change then it would just send an update to the MC. But how does that update work? Is it a whole new slide? Or just a new widget? Or is there some kind of placeholder property of widgets that it updates? While we're at it, which should think about extending the "text widget mapping to a player var" to any attribute for any slide. (See #187 for details.) I don't know that we need to solve that here, but something to keep in mind while we're refactoring.
I think that's it for now? I am assuming that with this full implementation, we can remove the following components from the MC:
We can also remove the following MPF components which are also loaded and used by the MC from the MC code:
That will allow use to probably simplify some things in each of those (like self.machine_config shadowing self.config since in Kivy self.config is the kivy config), and probably we can simplify the MC in general and maybe make it more like a normal kivy app?
This will also mean that things like the Unity MC will be more similar (and full featured) to the MPF MC since a lot of the logic will be in MPF instead of the MC. (Maybe that's what we call these things too? Unity MC and Kivy MC?)
BTW, all of this will probably fix #489 too.