boardgame.io
boardgame.io copied to clipboard
Improve documentation about plugins
Hi there,
Not sure if this is the best place to ask questions, so if there's a better one please point me there.
I'm trying to use plugins, and am failing hard. I'm making a few observations that I don't understand, thus I'm not sure if they are bugs or features. The documentation about plugins is quite terse, so hopefully those questions will help improving it afterwards (I'll be happy to open a PR for that).
- I cannot access
ctx.events
from inside my plugin, thus my plugin'sapi
cannot advance the game state. - I cannot access my plugin's data in client-side React components,
ctx.myplugin
is undefined. - I don't understand how the data works. I've looked at the PlayerPlugin for reference, and it seems it copies the plugin's data in the
api
call, the exposes that data, then inflush
it returns the data exposed byapi
. That seems overly complex to me (and is not explained in the docs). I've thus tried to simply usedata
and haveflush
return that, and it seems to work fine. Any reason not to do that?
Once again, I can provide code examples if needed.
Thanks!
PS: I'm naming this "Improve documentation about plugins" because that's the outcome that I'd like to see here, and I'll do my best to make it happen. :-)
The plugin API is very much in flux, and I didn't want to document it too much until it settles down a bit. Let me add some background about plugins, which should hopeful answer some of your questions (and we can add some of this info to the docs as well):
Each plugin gets its own private state
If you have plugins A
and B
, the game state is initialized to:
{
G: {},
ctx: {},
plugins: {
A: { data: {} },
B: { data: {} },
}
}
The setup function initializes data
This is probably not that confusing, despite the terseness of the docs.
api
creates an ephemeral object (that is not persisted)
The game state looks like this after applying the api
functions from all the plugins:
{
G: {},
ctx: {},
plugins: {
A: { data: {}, api: {} },
B: { data: {}, api: {} },
}
}
These new ephemeral objects are made available in ctx
as ctx.A
and ctx.B
. The intention is that the user will interact with these objects, and it will update its own in-memory state in response to that.
flush
then persists any necessary data back into data
This completes one cycle. This whole circus is to ensure that api
does not get persisted, and it allows providing the user with a nice object in ctx
to interact with.
Specific Questions
I cannot access ctx.events from inside my plugin, thus my plugin's api cannot advance the game state.
The plugins are intended to be independent. events
and random
are also plugins, so you shouldn't be using values from there in your plugins (i.e. this is not a supported use-case).
If you do want to advance the game state in your plugin, you'll have to copy over the code from the events
plugin, which uses the undocumented and dangerous flushRaw
(use this only if you understand the internals of boardgame.io really well).
I cannot access my plugin's data in client-side React components, ctx.myplugin is undefined.
If you look at the state object at the top of this comment, that should show you how to access plugin data:
client.getState().plugins.A.data // for example
For React, you should see the plugins data in props:
function Board(props) {
return (
<pre>{JSON.stringify(props.plugins.A.data)}</pre>
);
}
Thanks for the answers and help!
Regarding the state / data thing, can you confirm there is nothing wrong with using the data
object passed to api
as your state, and then have flush
simply return the same data
object? As far as I understand (and as far as my tests go) this works fine. If that's the case, I would even encourage you to make that the default use case, and make flush
optional, as that was what was intuitive to me.
The plugins are intended to be independent.
events
andrandom
are also plugins, so you shouldn't be using values from there in your plugins (i.e. this is not a supported use-case).
I understand why you would want that to be the case, but I will argue that events
and random
are too much core to the engine to be treated as regular plugins. I see plugins as a very elegant way to organize code and make bits sharable / reusable, but not being able to access core functionalities of the engine makes them a lot less useful to me. I also think that it will be very confusing to developers as, once again, events
and random
are core to boardgame.io.
One way we can resolve this is to have data from plugins initialized earlier be available to plugins that are initialized later. Then, we just need to guarantee that events
and random
are processed before any user supplied plugins (this is already true).
That's interesting, being able to build plugins on top of other plugins. Might be too much power though? I'm afraid it might lead to difficulties for programmers because you have to be aware of the plugins loading order when building your plugin.
Anyway, if that's simpler to do than treating a few "system" plugins as exceptions, I personally don't mind. :)