>>38975071>you import not the module itself but its signature which always makes the implementation and the concrete types used behind the scenes opaque.Agreed. This is the opposite of what I thought you were suggesting with lines like
>The lack of 'abstraction boundary' around the state object is in fact a good thing>Nope, the game_logic function does not own the state.Here's roughly what I think the sig for the engine module (and related definitions) should look like:
type player_id = int
type input =
(* player moves to a new position *)
Move of player_id * float * float |
(* player shoots along a vector *)
Shoot of player_id * float * float
module type Engine = sig
type state
val init_state : unit -> state
val game_logic : state -> input -> state
(* Remaining functions are just accessors; none of them return a state *)
val get_player_pos : state -> player_id -> float * float
(* etc *)
end
Now, how do you obtain a mixed state (e.g. the t=4/t=6 state from the old diagram) for Source Engine lag compensation? I would do it by adding a new function to the Engine signature, something like this:
val splice_state : player_id -> state -> state -> state
But you claim you can do this without modifying the engine. I can see two ways of doing this, both of which are bad software engineering:
1. Expose the internal definition of the state type, so that the netcode module can directly construct the mixed state, bypassing the normal init_state/game_logic functions. This completely breaks down the abstraction boundary between the two modules, and removes the engine module's ability to enforce invariants using the language's type system and module system.
2. Have the netcode module reverse-engineer a series of inputs that it can pass to game_logic to get to exactly the state it wants. The inputs need to pass whatever validity checks game_logic performs (e.g. Move must have a position within a certain distance of the player's current position, to prevent speedhacks), so the netcode module needs to know about those checks. The result is that the netcode and engine modules become tightly coupled.
>create objectsJust in case it wasn't clear from the above, what I meant was "the netcode module creates states DIRECTLY", bypassing any constructor functions and instead setting fields directly. Essentially, the game state equivalent of let zero: t = 0
>calculate_diffs(state, state) -> inputAgain, I cannot imagine a single reason why you would ever want or need such a function. I think your idea of an "input" is radically different from the way the term is commonly used in game development, where it refers to player inputs (either raw inputs like key down / key up, or a higher-level version like "move forward" / "open door").
>if you are a bit more creative with what the input object can containCan you please explain specifically what an "input" consists of, in your view? It must be very different from the input type I gave above.