missionpinball / mpf-mc

The Mission Pinball Framework Media Controller. Controls graphics, sounds, DMDs, and LCD displays in MPF.
http://missionpinball.org
MIT License
21 stars 42 forks source link

Refactor MC config_players & remove lots of stuff from the MC #241

Open toomanybrians opened 7 years ago

toomanybrians commented 7 years ago

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.

qcapen commented 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.

qcapen commented 7 years ago

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.

qcapen commented 7 years ago

While working on the display refactoring I've been thinking about this big MC refactor. Here are a few thoughts:

More thoughts to come as I start to work on this some more...