boardgameio / boardgame.io

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

Some ideas about game state updates #754

Closed cyberbeat closed 4 years ago

cyberbeat commented 4 years ago

I enjoyed implementing some games for boardgamearena.com lately, they have an open api and in my opinion they do some things better:

Thats more intuitive, to handle events at client-side, for example move a card from position x to position y, play some sounds,...

With boardgame.io much useless data is transferred at each state-update (could slow down complex games), and the client does not know, what has changed. It's also nice to be able to display the complete human readable game-log all the time.

Imagine a complex game, where a players move results in many "events" (earn this, score some points, lose that,..), it is nice to have multiple single notifications for each computed action, so that the player understands what happend.

Have a look here: http://en.doc.boardgamearena.com/Main_game_logic:_yourgamename.game.php (Server side code is php there, which make things a little more complicated)

delucis commented 4 years ago

Thanks for the feedback @cyberbeat.

Regarding events on the client, that is also something that has come up before. (For example, having the client emit events like “turnEnd” or “turnBegin”, which could be handy.) More generally, whether events are preferred to a declarative approach probably depends on how the UI is being built and programmer preference. That said, G is completely flexible so you can structure it to trigger events when consumed on the client. I’ve been prototyping a plugin for exactly this kind of functionality.

cyberbeat commented 4 years ago

I think your plugin could be useful to me. But what about human readable game logs? For me, it does not make much sense, to add them all to G. Or would ctx via plugins be used for something like that? In the documentation there is not much to read about, what is possible with ctx. There should be some difference on server & client, if G is transferred on "client-connect" (init or browser refresh) or updates.

"Incremental state changes" alone would not help much, because a "diff" without saying, what and why something changed, seems useless. Think as you would commit something to git without a commit message. So I would like more freedom here.

I don't see the requirement, that the client has to see raw G (or a part of it), because the client has very different requirements as the server. The server could only send to the client, what it needs for displaying the UI.

delucis commented 4 years ago

That plugin is for ephemeral events (i.e. its state gets wiped every action), so not designed for logs really, but plugins do support persistent state too. boardgame.io does already maintain a log, which is available on the client. Entries look something like this:

{
  _stateID: 0,
  phase: 'game-phase',
  turn: 1,
  action: {
    type: 'MAKE_MOVE', // or 'GAME_EVENT'
    payload: {
      type: 'moveName', // or event type, e.g. 'endTurn'
      args: [/* arguments */],
      playerID: '0',
    },
  },
}

There is also an open issue (https://github.com/boardgameio/boardgame.io/issues/686) to support custom logs.

I personally maintain a log field in G that I add to when moves are made instead. In a very simplified form:

const addToLog = (G, ctx, message) => {
  G.log.push({ message, playerID: ctx.playerID })
}

const moveA = (G, ctx, args) => {
  addToLog(G, ctx, 'moveA was made')
}

const moveA = (G, ctx, args) => {
  addToLog(G, ctx, 'moveB was made')
}

That allows pretty detailed customisation of wording etc. based on context that isn’t included in the default log and the client doesn’t have to try and piece things together to display a message.

Regarding the client receiving a “raw” G: a core part of the boardgame.io architecture is that the whole game engine can run on both client and server. When not dealing with secret state, this allows clients to perform optimistic updates, and also makes singleplayer/local games possible without the need for a server.

cyberbeat commented 4 years ago

boardgame.io does already maintain a log, which is available on the client.

This is not documented, or did I miss something? Where can I read more about that, and how it is transfered?

delucis commented 4 years ago

It’s not really documented, no, just listed in the client docs: https://boardgame.io/documentation/#/api/Client?id=properties

When the client connects, it receives the log so far, then for future actions receives a “deltalog” for the new entries. The full log is available as props.log in React or as the log property of a plain JS client instance.

The client’s log middleware might be the most helpful reference seeing as there’s no other documentation:

https://github.com/boardgameio/boardgame.io/blob/e8f07355b7f0d910b081fa79c4518efe2bf2a7d6/src/client/client.ts#L177-L231

popey456963 commented 4 years ago

In a previous game system we migrated from ws to uws (https://github.com/uNetworking/uWebSockets.js) for a ~4x websocket speed increase and from full 15kb state changes to small .2kb state changes for a further ~10x increase. We used jsondiffpatch with the optional google-diff-match-patch after serialisation to do character by character diff patching (surprisingly, this turned out to be faster than their intelligent implementation).

We purely measured number of state changes able to be sent by a single server per second, not the impact on latency for a client.