boardgameio / boardgame.io

State Management and Multiplayer Networking for Turn-Based Games
https://boardgame.io
MIT License
10.03k stars 709 forks source link

State changes that require secret data #76

Closed davidgovea closed 6 years ago

davidgovea commented 6 years ago

Hi there!

First, let me just say that this project is really exciting - love the activity and where this is heading. Great work!

I'm working on a variation of the "battleship" game type - choose a secret layout, then guess the opponent's layout. This is working nicely for multiplayer: false with the 'phases' feature, and a move reducer that checks the opponents secret layout.

Today, I implemented the new PlayerView.STRIP_SECRETS player view, but I'm running into some issues:


This seems somewhat similar to the 'randomness' work being done (storing a secret RNG seed for dice rolls, etc). For a simple "roll and advance [DICE] spaces" move, the outcome of a roll must be shared with the clients. Similarly, a battleship (x, y) guess must be reported as a HIT or MISS.

Just wanted to start the discussion. Let me know if this belongs somewhere else, or if I'm missing something.

Option brainstorm:

nicolodavis commented 6 years ago

Thanks for reporting this!

The easy fix is to have the client only send actions to the server and not compute any state locally (including the current player).

This is not particularly desirable though because you want to see some state changes without network latency, so we need some mechanism to indicate what is acceptable to compute on the client and what needs to wait for a state update from the server.

All this while still maintaining the abstractions and not forcing the user to really think about client vs server (one of the goals of this framework).

I'll think about this a little more after the weekend and report back with some concrete recommendations!

nicolodavis commented 6 years ago

I think the following should work:

The move reducers are analogous to a player moving their own tokens, and shouldn't rely on secret state. Die rolls can work by having the move just initiate the roll, and the actual value is computed on the server.

nicolodavis commented 6 years ago

@scheijan FYI

Something to take into account for #68

nicolodavis commented 6 years ago

This is now resolved in https://github.com/google/boardgame.io/commit/af3a7b591759ed65d22d27ce036d9d33caaaf4f5 and released in 0.16.7.

Feel free to re-open if this doesn't address your issue!

davidgovea commented 6 years ago

Wrote this earler, didn't submit.

Just trying to work my head through this.

Battleship scenario

  1. Player guesses (x, y)

    • In the real game, player places token on guess board
    • This state can be computed on the client
  2. Server hook runs (endTurnIf or earlier hook)

    • New state is determined using secrets
    • In the case of battleship, turn ends
  3. Both clients are informed of new state.

Roll & Move scenario

In this phase, available moves are rollDice and move. Or possibly, roll and move are different phases?

  1. Player rolls dice

    • does the game state change?
  2. Server hook runs (endTurnIf or earlier hook)

    • Dice value is added to state
    • Turn NOT over
  3. Both clients are informed of new state, including dice value

  4. Player moves

    • Value of the dice is used

endTurnIf is not for modifying full game state - it seems like moves are the place for that.

Can run-after hooks be defined per-move? Could this be created using phases? :: requestRoll phase followed by rollDice phase? How would the "server move" get initiated?

nicolodavis commented 6 years ago

Die rolls are the tricky bits here. One way to implement them would be:

Game({
  moves: {
    rollDie: G => ({ ...G, requestDieRoll: true })
  }

  flow: {
    triggers: [
      {
         condition: G => G.requestDieRoll,
         action: G => make die roll
      }
    ]

No need for separate phases (triggers are run whenever a condition is met). This is still not satisfactory IMO. I think a die rolling API should be added that takes care of this more elegantly (but the above should work at the moment).

davidgovea commented 6 years ago

Ah! Trigger with action is what I was missing here. Thank you!