boardgameio / boardgame.io

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

Turn goes twice when phase is end #193

Closed qsona closed 6 years ago

qsona commented 6 years ago

Hi, I'm facing a problem. I set endTurnIf hook to always return true. When phase is end, it seems that the turn automatically goes twice.

I've not dug deeply yet, but I found that the endTurn event is dispatched in function endPhaseEvent and endPhase event is dispatched in function endTurnEvent in src/core/flow.js . I feel the former should be removed. What do you think?

Reproduce steps

Here's minimum code for reproducing. Prepares a project as tutorials, then rewrites src/App.js as follows:

import { Client } from 'boardgame.io/react';
import { Game, TurnOrder } from 'boardgame.io/core';

const Foo = Game({
  setup: () => {
    return {
      players: {
        '0': { },
        '1': { },
      },
    };
  },

  moves: {
    skip: (G) => G,
  },
  flow: {
    turnOrder: TurnOrder.DEFAULT,
    endTurnIf: () => true,
    phases: [
      {
        name: 'a',
        allowedMoves: ['skip'],
        endPhaseIf: (G, ctx) => ctx.turn === 2,
      },
      {
        name: 'b',
      },
    ],
  }
});

const App = Client({
  game: Foo,
});

export default App;

then run npm start, and open the web cli (localhost:3000).

then,

nicolodavis commented 6 years ago

Thanks for the report (and especially for the steps to reproduce)! Will take a look in the next couple of days.

francoijs commented 6 years ago

It seems like this change of behavior was introduced between versions 0.21.3 and 0.21.4. Before version 0.21.4, we had to manually endPhase at the end of a turn, so that the next turn (of next player) begins at the proper phase.

nicolodavis commented 6 years ago

Fixed now (will be available in the next release). Thanks again for the report!

theredwillow commented 4 years ago

This appears to still be a problem for me in version ^0.36.0. It doesn't matter if I call it by event or by endIf inside the phase definition.

Edit: Nevermind. I just read the below in the docs. This seems like an undesirable behavior imo because it requires your code to either endTurn or endPhase. Aren't their moveLimits so that you can endPhase after everyone has gone a certain number of times? Am I misunderstanding something?

Whenever a phase ends, the current player's turn is first ended automatically.

Here is my example.

  phases: {

    getQuestions: {
      moves: { submitQuestion },
      start: true,
      next: 'answerQuestions'
      // NOTE: I'd rather just endIf here with parseInt(ctx.currentPlayer) !== ctx.numPlayers - 1)
    },
  onSubmit(question) {
    if (!this.props.isActive) return false;
    this.props.moves.submitQuestion(question);
    if (parseInt(this.props.ctx.currentPlayer) !== this.props.ctx.numPlayers - 1) {
      this.props.events.endTurn(); // NOTE: This could have been the only part, much shorter and sensical
    }
    else {
      this.props.events.endPhase();
    }
  }
nicolodavis commented 4 years ago

Have you considered using TurnOrder.ONCE: https://boardgame.io/documentation/#/turn-order

Does that solve your use case?