hathora/builder

Why mutability ?

Opened this issue · 2 comments

Coming from non-game environments (FP inspired Javascript notably, like well known flux architecture or even react at its core); I was heavily surprised the philosophy of State update has mutation at its core.

To challenge that, asking here a naive but important question :

Why not taking a non-mutative approach ?
ie

  onTick(prevState: InternalState, ctx: Context, timeDelta: number): InternalState {
    return {
      ...prevState,
      testValue: 1
    }
  }

instead of

  onTick(state: InternalState, ctx: Context, timeDelta: number): void {
    state.testValue = 1
  }

IMHO, this approach is broadly proven to be more easily testable and determinism-friendly

hpx7 commented

Hi @cyrilchapon, you ask a fair question, and it's certainly something I've thought about.

One big reason for mutability is efficiency. The onTick method can run 20+ times a second, and game states may be quite large -- it would be inefficient to copy the full state on each run of the method. There are tools like ImmerJs which do clever tricks to help here, but nothing is faster than doing no copying at all.

Another reason I went for mutability is to allow easier integration with the existing ecosystem. Consider the chess example -- the chess.move() method from chess.js mutates the internal board state that chess.js uses, and if Hathora forbid any kind of mutability, then this library would not be so straightforward to integrate with.

Additionally, Hathora uses primitives like the replay log which help a lot with testability and determinism at large.

Let me know your thoughts to this, happy to discuss further!

I like the pattern of the chess example. Keep deterministic game rules within its own module, and have Hathora interact with it. I've found it easier to test an independent game rule module that didn't have to worry about linking to types in ../api/types.ts