End‐to‐end High‐level Logic - jmdisher/OctoberProject GitHub Wiki

This is meant to be a basic summary of how data flows through the system. Last updated for demo8.

Terms

  • Entity - A client's "player" within the world, defining what they can do and what they can see. Note that AI creatures in the world also count as entities.
  • Block - A single block in the world
  • Mutation - A change object to apply to an entity or a block, changing the entity/block and potentially scheduling follow-up mutations
  • Speculative Projection - The name given to the client's perspective on the world

Client submits a mutation

Clients can only change the world by submitting a mutation to the server. Locally, they create this mutation and apply it to their own speculative projection of the world state. Any follow-up mutations are run directly against this projection.

If the mutation applies cleanly, it is assigned a commit number and sent to the server. If not, it is dropped by the client.

Server receives a mutation

The server receives a mutation from a client and schedules it against their entity in the following tick.

The tick processing is done in parallel across many threads. To avoid extraneous locking and data sharing, a mutation can only see the current state of the block or entity it is mutating and has read-only access to the previous tick snapshot of the rest of the world.

If the mutation applies cleanly, any changes it made to the entity will be scheduled to be sent to the clients. Any follow-up mutations are scheduled for the following tick. This means that it is not possible to force 2 dependent operations to run within the same tick or atomically.

Client receives a list of changes at the end of a server tick

A client receives a list of changes to the world applied by the server within a tick. These will be snapshots of the state of updated entities and updated blocks. This will include the consequences of the mutations sent by all of the clients, follow-up mutations to previous mutations sent by clients, or other server-originating mutations.

Aside: Note that the client used to receive the actual mutations but this was changed since the client doesn't have the entire world state so it may not resolve all mutations the same way as the server.

The client discards any local changes it has made to its speculative projection and applies all snapshot changes from the server.

The client then attempts to re-apply any of the mutations it had previously applied to the speculative projection, dropping any which are not more recent than the commit number which was sent back from the server (as this commit number is per-client).

Discussion

This allows the clients and server to be kept in sync while allowing each client to maintain a speculative set of changes applied to its local state.

A note about Copy-On-Write

The core of the OctoberProject logic engine is designed around the data model being entirely copy-on-write. This means that read-only access to previous snapshots is trivially available (for parallel logic in the next tick, but also network-level buffering and storage write-back). This mechanism also makes detecting deltas between snapshots feasible as instance-comparisons can be used at every level of deep data structures.

How does this apply to single-player?

Technically, there has never been a "single-player OctoberProject". The entire design is based around client and server perspectives of the world. This means that even a single-player game still starts a local server and connects to it over the loopback network device. In the future, this may change to allow more direct access control since this means anyone can connect to a single-player game.