Skip to content

Latest commit

 

History

History
107 lines (71 loc) · 3.7 KB

CONTRIBUTING.md

File metadata and controls

107 lines (71 loc) · 3.7 KB

Building

Requirements

  • CMake 3.21+
  • Ninja
  • vcpkg for dependency management

Installation

Install packages:

cd /path/to/coherence-cpp
/path/to/vcpkg/vcpkg install

If using VS Code, add vcpkg to cmake.configureSettings:

"cmake.configureSettings": {
    "CMAKE_TOOLCHAIN_FILE": "/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake",
    "VCPKG_TARGET_TRIPLET": "x64-windows-static"
}

The triplet is only required on windows to build a static lib. On macOS, the arm64-osx should be fine.

Testing

Run all tests (from the project root):

python tests

Run specific test, suite, or test prefix:

python tests --filter test_set_viewport_format
python tests --filter TestTrackedChanges
python tests --filter test_set

More commands available via:

python tests --help

Use the @tag('skip_client') decorator on a test to skip setting up a client instance within setUp(). Use this tag on syncing feature tests for new clients (re)connecting to a server with pre-existing data.

Architecture

Typical entity pattern

There's a POD {type}State struct in Library.h that contains the public data for the entity that's made available to host applications. These get exposed through the API as getters and are modified either through direct setters or other manipulators.

Methods on entities typically fall under the pattern:

  • get_ are const property accessors for whatever is currently stored locally for that entity.
  • set_ methods will update internal state and replicate to the connected application.
  • on_ methods receive network events, change internal state to match, and add a change tracking record to the scene.

The entity should be responsible for all network messages in/out of itself.

Instantiating entities

Instantiation process looks like:

  1. (REMOTE) creates an entity through Scene::create with an initial state
  2. (REMOTE) calls on_create to do any necessary work after instantiation
  3. (REMOTE) calls sync() to sync all of entity data over the network in one go.
  4. (LOCAL) instantiates a copy of the entity.
  5. (LOCAL) sets initial state from create_* network event payload.
  6. (LOCAL) calls on_create for the new entity and adds a change tracking record to the scene.

Change tracking

External applications query for changes after network updates to get an aggregate list of all entity changes. This was chosen over callbacks to decouple network update frequencies from app updates - e.g. frequent transform changes do not need to be immediately handled in apps, and to simplify the API a bit so we don't need a plethora of callback handler types.

The general idea is:

  1. (REMOTE) performs changes on one of end of the pipeline (e.g. .SetTransform)
  2. (REMOTE) and (LOCAL) both sync up network messages via .Update
  3. (LOCAL) queries for a batch of changes on a fixed interval (say 60 FPS)
  4. (LOCAL) pops off a Change and calls .GetTransform for the entity that has changed to use for the host application
  5. (REMOTE) can continue to write more changes in the background.
    • Currently, this is synchronous and only happens during an .Update call, but may change in the future.

Change tracking events

Current table of (REMOTE) events and the changes they trigger:

Event Flag
Scene.on_create() CHG_CREATED
Scene.on_destroy() CHG_DESTROYED
{entity}.on_update() CHG_UPDATED_STATE
SceneObject.on_transform() CHG_TRANSFORMED
Component.on_update_property() CHG_UPDATED_PROPERTY
Image.on_pixels() CHG_UPDATED_PIXELS

Note that if an entity is created and destroyed before the changes are polled, it'll create a single record with CREATED | DESTROYED. TBD whether or not that should be fixed somehow.