boardgameio / boardgame.io

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

Phases doesnt work when switch to multiplayer #301

Closed pociej closed 5 years ago

pociej commented 5 years ago

Im jumped on multiplayer to be able to use storage ( btw dont you think its strange that even in single player game if you want to have db storage you have to use multiplayer option ? ). My game works just fine as single player :

const App = Client({
  game: Transition,
  board: TransitionBoard,
  numPlayers: 1,
  currentPlayer: "1",
});

but once i change few lines to work with multiplayer :

const MultiPlayerClient = Client({
  game: Transition,
  board: TransitionBoard,
  numPlayers: 1,
  currentPlayer: "1",
  multiplayer: {
   //Just temp to check if multiplayer option works, mongo storage will be connected. 
    local: true
  }
});

const App = () => (
  <div>
    <MultiPlayerClient playerID="0" />
  </div>
);

My game is stuck to phase 1, neither endPhaseIf nor ctx.events.endPhase(); works,endPhaseIf is not called at all after G update on move. What could be that?

nicolodavis commented 5 years ago

Do you have a link to your entire code so I can try to reproduce the problem?

pociej commented 5 years ago

I cant share code as im not an owner. I will create reproduction if above is not enough for you.

nicolodavis commented 5 years ago

Any minimal example that reproduces the problem will do.

pociej commented 5 years ago

@nicolodavis its strange i tried to reproduce and im unable but in orginal repo ( i made it public temporary) problem persists. Here it is if you could be so polite and take a look https://bitbucket.org/planszowki/change_management/src/0c0de55cf9e25da8bf4b55bb2dc936e39d796639/imports/ui/main.jsx?at=master&fileviewer=file-view-default.

nicolodavis commented 5 years ago

You're running an old version of boardgame.io (which if I recall correctly contained a bug that is similar to what you're describing). Can you upgrade to the latest version and let me know if that fixes it?

pociej commented 5 years ago

Yes thx a lot, update helps for this, but unfortunately it produces another issue. In case of single player game with multiplayer option set, each single move fires twice ( event that calls move fires only once, i checked). Im going to check if i can reproduce it out of my app.

Breaking change is 0.26.1. Below this version i observe original issue but also moves works as should.

nicolodavis commented 5 years ago

About moves firing twice, I would expect the move to fire once on the client and once on the master, which is also running on the browser in "local multiplayer" mode. Is this what you are observing?

pociej commented 5 years ago

When i put multiplayer : { server : 'loalhost:3010' } then i works normally, so you might be right its rerun on master, but look, every move from my game defined like this :

Game({
  setup : ctx => {
    ....
  }, 
 moves : {
   a : (G,ctx,name) => { ... }
   b : (G,ctx,name) => { ... }
 }
}}

When i call it from board component by : this.props.moves.a()

is called twice, and as its not idempotent G state is updated twice, and on my board i see effect of double move rendered. Is it expected behaviour? Isn't it make { multiplayer : {local : true} }

nicolodavis commented 5 years ago

You should definitely not see your G object updated twice. The client and master maintain two distinct game objects that are updated independently.

I can take a closer look later. In the meantime, a minimal example that demonstrates the problem would be quite helpful (if you have the time).

pociej commented 5 years ago

I see 0.27.0 is released i updated to this version and now orginal issue is back. So all moves are called once as should, but phase ending doesn't work any more with multiplayer.

a minimal example that demonstrates the problem would be quite helpful (if you have the time).

In app i linked above it doesnt work, i tried to make super minimal reproduction and it worked fine so issue must be a bit more complex. In meantime you can check in repo linked above.

Note. on 0.26.3

When :

{
  multiplayer : { server : 'localhost:3000' }
}

ending phase api doesnt work, but moves fires once

{
  multiplayer : { local : true } 
}

i can end phase but moves are called twice.

pociej commented 5 years ago

Ok i see where is trap with double calling : if you return from move function modified version then everything works, but if in function body you modify G object like G.myArray.push() then two calls ends up with double push. Sorry, and thx a lot for your help. To avoid this, maybe it reasonable to use frozen G to prevent any mutation within move function body?

nicolodavis commented 5 years ago

Ah glad this is resolved. I'm actually planning to bundle in Immer in a future release which will avoid this problem https://github.com/mweststrate/immer.

So, with 0.27.0 does everything work correctly? I'll close this issue if so. Feel free to reopen if there is anything else we should fix right now.