Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stepping through log outside of debug? #892

Open
joepinion opened this issue Jan 18, 2021 · 3 comments
Open

Stepping through log outside of debug? #892

joepinion opened this issue Jan 18, 2021 · 3 comments

Comments

@joepinion
Copy link

Is there a way, built-in already, to take the initial game state and step through the log to receive a G object back at each step?

I see the ability to do this in debug, but how can we do this in the client regular user interface? The use case is, being able to rewind time to review previous moves, especially after loading the page mid-game.

@delucis
Copy link
Member

delucis commented Jan 19, 2021

There isn’t, although it might be possible to adapt the debug panel logic. You can import { CreateGameReducer } from 'boardgame.io/internal', get client.initialState, and then replay each log action as in the debug panel. There’s some Svelte-specific syntax in the linked code, but let { log } = $client is basically getting log from the client.subscribe callback, so it’s equivalent to:

client.subscribe((state) => {
  if (!state) return;
  let { log } = state;
  // ...
});

But that requires access to the plain JavaScript client rather than the React client.

For better reusability, it might make sense to move the log stepping logic into the client itself and expose some kind of API like:

interface LogStepAPI {
  stepForward: () => void;
  stepBack: () => void;
  stepTo: (idx: number) => void;
  reset: () => void; // return control to normal client state
}

Better still would be refactoring this in a way that a client could request a past state from the server when in use as that would allow us to use log stepping even in scenarios where secret state means a client can’t compute all the steps locally.

@richardl62
Copy link

Currently the 'logs' box on the homepage reads as if logs and time travel are full supported. As a short term measure would it be worth changing the text to make it clear that these features limited to the debug panel?

@on3iro
Copy link
Contributor

on3iro commented Mar 4, 2023

Hey folks,

I am currently building something similar. And had a question regarding the example @delucis linked to:
As far as I can tell, the log might have multiple action entries per _stateID. The example above accounts for automatic actions by skipping them, but there still maybe for example a MAKE_MOVE-action and a GAME_EVENT action for the same _stateID.
Now whenever the reducer runs it produces an updated _stateID.

My assumption was that by running all actions for _stateID = 0 I will end up at _stateID = 1 however the new _stateID will be the original id + the amount of stepped through log entries event though there might be other moves with a lower _stateID.

For example my resulting _stateID will be 2 when I only call the reducer twice applying only the first two actions of the following log:

[
  {
    action: {
      type: 'MAKE_MOVE',
      payload: {
        type: 'pickCamps',
        args: [[0, 1, 2], '0'],
        playerID: '0',
      },
    },
    _stateID: 0,
    turn: 1,
    phase: 'campSelection',
    redact: true,
  },
  {
    action: {
      type: 'GAME_EVENT',
      payload: {
        type: 'endStage',
      },
    },
    _stateID: 0,
    turn: 1,
    phase: 'campSelection',
  },
  {
    action: {
      type: 'MAKE_MOVE',
      payload: {
        type: 'pickCamps',
        args: [[0, 1, 4], '1'],
        playerID: '1',
      },
    },
    _stateID: 1,
    turn: 1,
    phase: 'campSelection',
    redact: true,
  },
  {
    action: {
      type: 'GAME_EVENT',
      payload: {
        type: 'endTurn',
      },
    },
    _stateID: 1,
    turn: 1,
    phase: 'campSelection',
    automatic: true,
  },
  {
    action: {
      type: 'GAME_EVENT',
      payload: {
        type: 'endPhase',
        args: true,
      },
    },
    _stateID: 1,
    turn: 1,
    phase: 'campSelection',
    automatic: true,
  },
]

However this does only seem to be happening, when I manually call the reducer. Inside an actual game the correct _stateID seems to be constructed correctly. I am not quite sure what I am missing here.

What I want to achieve is the following:

  1. I save the last seen _stateID with its snapshot of a specific player to the database
  2. When the player reconnects to a game i check if the lastseen ID is the most recent _stateID
  3. If it is not, I apply all actions up to that point from the players last seen state snapshot

The stepping is being done manually.

PS: I might implement the suggested log-stepping functionality desribed by @delucis above eventually and if I do so, will create a PR. However as of now I am still in an exploratory phase ;)

EDIT: Ok I think I know where I went wrong: I only need to account for actual player moves, all other actions, like GAME_EVENT are created inside my actual player move and only then become part of the delta log. I assume this has been left out of the debug log example for the sake of simplicity?

EDIT2: Hm, I am still missing something here - now I get invalid moves, I shouldn't get...

EDIT3: Alright, I regenerated my test-fixture files, because I wasn't sure if I accidentally deleted something from them. The new fixture files appear to work - so my second edit was probably related to an issue on my side

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants