Open pociej opened 4 years ago
setup
has to be run on the server to ensure it is consistent and predictable. If you need some additional user input to correctly set up a game, you can use the setupData
argument to your setup
function (docs).
Using the Lobby REST API, you can send this setupData
along with your game-creation request (docs). (This is not currently supported out-of-the-box by the React Lobby implementation, but you’d presumably need some custom UI for input in any case.)
Currently, setupData
has to be sent to the /create
endpoint, which initialises the game state (source). This makes it impossible for each player to provide setup data before initialisation, because the game is created before any players have joined. Is that the kind of thing you’re talking about @pociej?
For now, I don’t think having a setup
phase at the beginning of a game is bad practice — it allows you to rely on all of boardgame.io
’s guarantees, such as validating moves, state mutations etc.
For the future, @nicolodavis, what do you think about delaying game state initialisation until after the last player has joined? That would allow the /join
endpoint to receive player-specific setupData
to stash in gameMetadata
, which could be consumed by CreateGame
once the final seat is taken.
@delucis Sorry that i didn't properly describe the full case. I opened two issues one after another and i had context of #537 when was posting this. You are 100% right that in case of real multiplayer game it has to be set server side to be sure of data consistency between player. However in case i described in #537 there is only one player. But we still have to use multiplayer
just with numPlayers : 1
. In this case i would like to be able to setup only client side. I like your idea of having player specific setupData
. Currently i solve this problem by creating a bit artifical move
setup
which is called on client when data are available.
@pociej If you’re storing game state in a database somewhere (so players can save games etc.), I think you’d still be better off generating and validating that state on the server, rather than posting the state object directly from the client. You can do that either with a setup
move or with setupData
, depending on your exact needs.
If there’s only one player, you can already use setupData
to achieve what you need: have the client provide the extra information needed for setup before requesting to create the game on the server. Here’s a method adapted from the lobby connection implementation:
const lobbyUrl = 'https://...'
const gameName = 'my-game'
const numPlayers = 1
async function create(setupData) {
try {
const resp = await fetch(`${lobbyUrl}/${gameName}/create`, {
method: 'POST',
body: JSON.stringify({
numPlayers,
setupData,
}),
headers: { 'Content-Type': 'application/json' },
});
if (resp.status !== 200) throw new Error(`HTTP status ${resp.status}`);
} catch (error) {
throw new Error(
`failed to create room for ${gameName} ('${error}')`
);
}
}
Pass whatever data you need to this create
method, then use setupData
in your game setup function, e.g.:
function setup (ctx, setupData) {
const G = {}
if (setupData) G.setupData = true
return G
}
You might want to immediately POST a request to the /join
endpoint if the game is created successfully to automatically add a player to the games they create.
For the future, @nicolodavis, what do you think about delaying game state initialisation until after the last player has joined? That would allow the /join endpoint to receive player-specific setupData to stash in gameMetadata, which could be consumed by CreateGame once the final seat is taken.
Sorry about missing this thread earlier. What's an example of per-player setup data in a real game? Are we talking about true metadata (like a player name or badge) or something that has a meaning within the game?
I'm definitely open to supporting the use-case if we can find some compelling examples.
I don't have a super concrete example myself. Aside from metadata, I can think of something where players might need to choose a class of character or a strategy that might change setup. I'd be interested in other people's needs. I don't think it's super urgent as it can always be handled using setup phases/moves instead but I wanted to mention it in the other thread in case it was a consideration for other lobby changes.
I see. It makes sense to have players bring in their own metadata if this choice of character class or some other game aspect happens outside the view that boardgame.io handles (for example in a custom lobby component).
On the other hand, it's easy enough to have players make the choice inside the game itself like you mentioned. Perhaps the difficulty that arises is the fact that these moves logically belong to a "pre-game" phase.
On a related note, we have toyed around with the idea of a "start game" event before (analogous to "start phase"). I wonder if that makes these use-cases more naturally implementable.
To be fair, what would such a start game event/phase do that a handmade setup phase couldn't? I think you're right that it's partly confusing because people think of these setup tasks as “pre-game”. Maybe it's just a case of some documentation of that pattern, e.g. a more advanced tutorial than the tic-tac-toe one that uses a setup phase and perhaps stages seeing as neither of those are covered by the introductory tutorial. It's simple enough to toggle a game view based on the current phase so even if a setup phase needs custom UI it's fairly straightforward to do.
(As a sidenote: In the fork of the lobby server I'm using, I did end up initialising game state after all players have joined so I could pull player nicknames from the metadata into the game state. I did that for logging messages that should say things like “Nickname did X”. In theory, I could use some kind of template substitution to store the ID in game logic and replace it with the nickname from metadata on client, but this made my life easier at the time.)
One other option would be to have setupData
arguments accepted by both the create
and join
endpoints. The server could create a game (without yet initialising state) with some data that impacts what players can then choose in the lobby when joining, then the join action accepts player-scoped data. Once the final player joins, the game would be initialised with the setupData
and the player-specific data.
An example use case:
You can create a game with different character sets
You can join the game choosing from the selected character set
I'll spend more time digging into the lobby implementation as I try to convert it to Svelte, so I'll have a more informed opinion after that. My intuition tells me that choices that the players make on the lobby screen should be handled outside boardgame.io, with the game given the eventual setupData
when it is finally instantiated after all players have joined.
Choices made once the game has started (using a "pre-game" phase) can be handled by boardgame.io quite easy without any changes. It just requires documenting the pattern like you say.
I have the case where initial setup is generated client side, based on things that server is completly not aware of. Problem is that
setup
is defined onGame
object that is imported by both client and server so only way to have dynamic client side generated setup is to have artificialsetSetup
move and call it. Isn't better to expose setup api to be pure client side and then overwrite the server state if called?