Boardgame.io: setActivePlayers function in onBegin hooks only work once

Created on 3 Oct 2019  Â·  9Comments  Â·  Source: boardgameio/boardgame.io

{
  turn: {
    onBegin: (G, { numPlayers, currentPlayer, events }) => {
      const value = {};
      for (let i = 0; i < numPlayers; i++) {
        value[i] = currentPlayer === String(i) ? 'main' : 'others';
      }
      events.setActivePlayers({ value });
      return G;
    }
  }
};

in the next turn ctx.activePlayers will be null

All 9 comments

This might be related to #482. I haven’t tested if setActivePlayers works in onBegin.

That said, at least for your example, you could achieve this more simply like this:

{
  turn: {
    activePlayers: { player: 'main', others: 'others' }
  }
}

Documentation →

it works, thanks so much

I don't think #482 is involved here (that bug only kicks in if an event does something like ending the turn which further triggers an onEnd that contains another event).

@delucis has already pointed out the more idiomatic way of doing this, but I'm still curious why the original example does not work. I'll keep this issue open till we figure that out.

For your reference,
I have try setActivePlayers in below example.

{
  onBegin: (G, ctx) => {
    ctx.setActivePlayers({ player: 'main', others: 'others' });
  };
}

The player who last takes the move in the previous phase is in main stage but not the ctx.currentPlayer

Just a quick note to say I ran into this myself today. I had a situation where I wanted to configure turn active players conditionally, based on game state. In a simplified form:

turn: {
  onBegin: (G, ctx) => {
    if (G.flag) ctx.events.setActivePlayers(/*...*/)
  }
}

But this always results in ctx.activePlayers === null

I guess this would also come up if you wanted to pass different values to setActivePlayers based on some game state.

I’ll try to dig into why this is happening, but if you have any ideas @nicolodavis, let me know!

So, the events flow is a nightmare to debug, but I can confirm what @Pong420 reported, that the playerID used by events dispatched in turn.onBegin seems to be stale. While currentPlayer has been incremented, playerID will still be the player who ended the previous turn.

I _think_ the bug is that playerID here is provided by the Events class when it creates the event action:

https://github.com/nicolodavis/boardgame.io/blob/9d74966ad4656021c971b012e59973ebedcc618b/src/core/events.js#L62

Prior to StartTurn, which dispatches turn.onBegin, UpdateTurnOrderState moves ctx.playOrderPos and ctx.currentPlayer along.

A call to setActivePlayers (or other playerID-based events) in turn.onBegin should probably have a playerID that reflects the new ctx.currentPlayer, but because it’s still being processed by the Events instance created in the previous turn, it doesn’t (unless the new current player happens to have been active and ended the previous turn).

The relevant ContextEnhancer is created in the reducer:

https://github.com/nicolodavis/boardgame.io/blob/9d74966ad4656021c971b012e59973ebedcc618b/src/core/reducer.js#L83

I’m not sure what the best approach is. Presumably, the UpdateTurnOrderState needs some way to update the playerID stored in Events? And presumably the assumption is that playerID in turn.onBegin is equivalent to currentPlayer? That seems to be a question more broadly for turn and phase hooks too. Any active player could call setPhase for example, but should a subsequent phase.onBegin reflect that too? Or the new current player? I’m not sure. In a way, events in moves and events in hooks play slightly different roles, because moves belong to specific players who have a playerID, while hooks are driven by the game structure.

Thanks for looking into this! A bit busy this week, so will take a look over the weekend.

Just to confirm, we still don't know what the cause of the original problem (of activePlayers being null on the second turn) is, correct?

Correct, but I’m almost certain it is in fact #482.

I think the setActivePlayers in turn.onBegin is not being processed because the turn was ended via a move. In the absence of turn.activePlayers, activePlayers is reset to null at the start of a turn and then because the events called in the onBegin function are never processed, activePlayers remains null.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nicolodavis picture nicolodavis  Â·  6Comments

adngdb picture adngdb  Â·  5Comments

nicolodavis picture nicolodavis  Â·  3Comments

br0p0p picture br0p0p  Â·  4Comments

yangboz picture yangboz  Â·  5Comments