Skip to main content

STYLY NetSync Server - Multiplayer framework for Location-Based Entertainment VR/MR experiences

Project description

Prepare develop environment

Option A: Use Dev Container (Recommended)

Prerequisites

Open Dev Container

Click the >< icon in the bottom-left corner of the window. (Or press Cmd + Shift + P) and select Dev Containers: Reopen in Container.

Option B:

Prerequisites

  • Python >=3.11
  • uv

Install styly-netsync-server in development mode

pip install -e .

Usage — STYLY NetSync server & CLIs

When you install the package with pip install -e . (editable / development mode), changes to the package's Python source files in your working tree are reflected immediately when you run the commands below — you don't need to reinstall.

# Start STYLY NetSync Server
styly-netsync-server

# Simulate 50 clients
styly-netsync-simulator --clients 50

# Custom server and room
styly-netsync-simulator --server tcp://localhost --room my_room --clients 50

Wire protocol compatibility

  • Current wire protocol is protocolVersion = 8.
  • Transport uses three ZeroMQ sockets: control (control_port, default 5555) for RPC, Network Variables, ownership, ID mapping, and client hello; transform uplink (transform_port, default 5557) for client/object poses; PUB/SUB (pub_port, default 5556) for room pose and room object downlink. The REST bridge (rest_api_port, default 8800) is also required for server startup.
  • Discovery responses use STYLY-NETSYNC3|controlPort|transformPort|pubPort|restApiPort|serverName; legacy discovery responses are explicitly incompatible.
  • dealer_port / --dealer-port remain a one-release compatibility alias for control_port / --control-port.
  • Transform messages use MSG_CLIENT_POSE (11) and MSG_ROOM_POSE (12) with the compact pose body. Clients register control identity with MSG_CLIENT_HELLO (19).
  • Moving-floor-local poses set the MovingFloorLocal pose flag. Bound avatars send head, hands, and virtual transforms in the registered moving floor's local coordinates, and reuse the existing 8-byte physical slot as direct physical position/yaw.
  • Unbound poses keep the xrOriginDelta semantics: xrOriginDelta carries a Y component as a 4th int16 (dx, dy, dz, dyaw = 8 bytes), so receivers can reconstruct the sender's rig-Y motion.
  • Legacy transform protocols (v2/v3) and JSON transform fallback are not supported.
  • Deploy Unity and Python updates together when changing transform protocol behavior.
  • Protocol v8 position quantization ranges:
    • Absolute (headPosAbs only): signed int24 at 0.01 m per unit, per-axis range [-83,886.08 m, 83,886.07 m].
    • XROrigin locomotion delta for unbound poses (xrOriginDelta, 4×int16: dx, dy, dz, dyaw): 0.01 m per unit for translation, 0.1° for yaw. Receivers reconstruct physicalPos = invDeltaRot * (headPos − deltaPos); it is not on the wire as a separate absolute field.
    • Direct physical payload for moving-floor-local poses (physical, 4×int16: x, y, z, yaw): 0.01 m per unit for translation, 0.1° for yaw.
    • Head-relative (right/left/virtual): signed int16 at 0.005 m per unit, per-axis range [-163.84 m, 163.835 m].
  • These are encoding limits, not a hard world-size cap. Worlds can be larger, but encoded axis values are clamped if they exceed the representable range.

Coordinate Range Expansion Options (Design Notes)

The following options summarize trade-offs when expanding absolute-position range.

Assumed unbound baseline (protocolVersion=8, MovingFloorLocal off):

  • Client pose body with Physical+Head+Right+Left valid and virtualCount=0: 46 bytes (matches test_client_body_size_with_full_pose_no_virtuals).
  • Room per-client entry (clientNo + poseTime + clientBody): 56 bytes.
Option Absolute Position Encoding Per-axis Range Client Body Delta Room Per-client Delta
A. Coarser scale (current integer width) int24 @ 0.02m [-167,772.16m, 167,772.14m] +0B (46 -> 46) +0B (56 -> 56)
B. Cell + local cell(i16, 256m) + local(int24 @ 0.01m) [-8,472,494.08m, 8,472,238.07m] +6B (46 -> 52, +13.0%) +6B (56 -> 62, +10.7%)
C. Cell + local (large cell) cell(i16, 1024m) + local(int24 @ 0.01m) [-33,638,318.08m, 33,637,294.07m] +6B (46 -> 52, +13.0%) +6B (56 -> 62, +10.7%)

Notes:

  • Only headPosAbs is on the wire as an absolute field; physicalPos is reconstructed from headPosAbs + xrOriginDelta. Option B/C deltas therefore apply to headPosAbs only.
  • Option B/C can reduce average overhead if cell is transmitted only when changed, but that requires extra state and flags in the wire format.

Configuration

The server uses TOML configuration files. Default values are bundled in src/styly_netsync/default.toml.

To customize settings:

  1. Get the default config file:

    • From local clone: cp src/styly_netsync/default.toml my-config.toml
    • Or download from GitHub: default.toml
  2. Edit my-config.toml and keep only the settings you want to change (delete the rest)

  3. Run the server with your config file:

    styly-netsync-server --config my-config.toml
    

Configuration priority: CLI arguments > user config > default config

Example minimal config:

# Only override what you need
server_name = "My-Custom-Server"
transform_broadcast_rate = 30  # 30Hz instead of default 10Hz

Logging

File output: add --log-dir DIR to write JSON lines to DIR/netsync-server.log (DEBUG level).

# output logs at ./logs directory
styly-netsync-simulator --log-dir logs

Console output: human-friendly text by default. Use --log-json-console for JSON and --log-level-console LEVEL to change the level.

Rotation & retention: default is 10 MB or 7 days, keeping the newest 20 files. Override with --log-rotation / --log-retention (loguru syntax like "10 MB", "1 day", "keep 5 files").

Bridging: stdlib logging is routed to loguru automatically.

REST bridge

The server launches an embedded FastAPI application that exposes REST endpoints for managing Network Variables. Default port: 8800 (override with --rest-api-port CLI argument or rest_api_port in config file). The REST bridge is required: if it cannot start, server startup fails before discovery is advertised. Server discovery includes the REST bridge port only after the bridge has started successfully.

Client variables

  • Endpoint: POST /v1/rooms/{roomId}/devices/{deviceId}/client-variables
  • Payload body:
{
  "variables": {
    "name": "Jack",
    "lang": "EN"
  }
}
  • Constraints:
    • Variable names: 1–64 characters
    • Values: up to 1024 characters
    • Total variables per client: 20 (additional keys return HTTP 409)
  • Behavior:
    • If a device has not connected yet, the values are queued in an in-memory preseed store and automatically applied once the server assigns a clientNo.
    • If the device is already connected, the variables are sent immediately through the existing ZeroMQ pathway.
  • Example:
curl -sS -X POST "http://127.0.0.1:8800/v1/rooms/default_room/devices/00000000-0000-0000-0000-000000000000/client-variables" \
  -H "Content-Type: application/json" \
  -d '{"variables":{"name":"Jack","lang":"EN"}}'

The response includes the current mapping status (clientNo or null) and whether each key was "applied" or "queued".

  • Read endpoints:

    • GET /v1/rooms/{roomId}/devices/{deviceId}/client-variables — returns all client variables for the device.

      {"clientNo": 7, "variables": {"name": "Jack", "lang": "EN"}}
      

      If the device has no clientNo mapping yet, returns {"clientNo": null, "variables": {}}.

    • GET /v1/rooms/{roomId}/devices/{deviceId}/client-variables/{name} — returns a single variable.

      {"clientNo": 7, "value": "Jack"}
      

      Returns 404 if the device is unmapped or the variable is not set.

  • Example:

    curl -sS "http://127.0.0.1:8800/v1/rooms/default_room/devices/00000000-0000-0000-0000-000000000000/client-variables"
    curl -sS "http://127.0.0.1:8800/v1/rooms/default_room/devices/00000000-0000-0000-0000-000000000000/client-variables/name"
    

Global variables

  • Endpoint: POST /v1/rooms/{roomId}/global-variables
  • Payload body:
{
  "variables": {
    "score": "42",
    "stage": "lobby"
  }
}
  • Constraints:
    • Variable names: 1–64 characters
    • Values: up to 1024 characters
    • Total global variables per room: 100 (additional keys return HTTP 409)
  • Behavior:
    • If the bridge client for the room is connected, variables are applied immediately via set_global_variable.
    • If not yet connected, variables are queued and automatically flushed once the bridge connects. Successfully applied variables are removed from the queue; failed variables are re-queued for the next flush cycle.
  • Example:
curl -sS -X POST "http://127.0.0.1:8800/v1/rooms/default_room/global-variables" \
  -H "Content-Type: application/json" \
  -d '{"variables":{"score":"42","stage":"lobby"}}'

The response includes the room ID and whether each key was "applied", "queued", or "failed".

  • Read endpoints:

    • GET /v1/rooms/{roomId}/global-variables — returns all global variables for the room.

      {"variables": {"score": "42", "stage": "lobby"}}
      
    • GET /v1/rooms/{roomId}/global-variables/{name} — returns a single variable.

      {"value": "42"}
      

      Returns 404 if the variable is not set.

  • Example:

    curl -sS "http://127.0.0.1:8800/v1/rooms/default_room/global-variables"
    curl -sS "http://127.0.0.1:8800/v1/rooms/default_room/global-variables/score"
    

Log export

  • Endpoint: GET /logs/export

  • Requires file logging to be enabled (--log-dir DIR, see Logging). Without it the endpoint returns 404 {"detail": "file logging is not enabled"}.

  • Streams matching JSONL entries from DIR/netsync-server*.log (including rotated files) as application/x-ndjson — one original loguru JSON line per row.

  • Query parameters:

    • from (required): ISO 8601 start timestamp, inclusive.
    • to (required): ISO 8601 end timestamp, inclusive. Must be greater than or equal to from.
    • min_severity (optional): minimum log level to include. One of TRACE, DEBUG, INFO, SUCCESS, WARN, WARNING, ERROR, CRITICAL (case-insensitive). Lines below this level are skipped.
  • Timestamps are compared in UTC; naive (offset-less) values are treated as UTC.

  • Errors:

    • 400 — unparseable from/to, to earlier than from, or an unsupported min_severity.
    • 404 {"detail": "file logging is not enabled"}--log-dir was not configured.
    • 404 {"detail": "log directory is not available"}--log-dir was configured but the directory does not exist.
  • Example:

    curl -sS "http://127.0.0.1:8800/logs/export?from=2026-06-26T00:00:00Z&to=2026-06-26T23:59:59Z&min_severity=ERROR"
    

Read consistency

GET endpoints return a snapshot of the REST bridge's in-process cache, which is populated by control-lane sync messages from the server. The first request to a room lazily creates a bridge and may return an empty snapshot until the initial sync arrives — retry after a short delay if needed.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

styly_netsync_server-0.17.1.tar.gz (150.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

styly_netsync_server-0.17.1-py3-none-any.whl (105.4 kB view details)

Uploaded Python 3

File details

Details for the file styly_netsync_server-0.17.1.tar.gz.

File metadata

  • Download URL: styly_netsync_server-0.17.1.tar.gz
  • Upload date:
  • Size: 150.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for styly_netsync_server-0.17.1.tar.gz
Algorithm Hash digest
SHA256 cd52a1ead4e3dbd2daa60df0a448299b323eb8382553a3fd7f2cf68d126d0136
MD5 a898a527ce7941992ea9f48d1aabcb0c
BLAKE2b-256 ec5a4d6b5cc26d7b90f5cd5a597ea64aea207f8e6902b332e6e6044e766b9f2f

See more details on using hashes here.

Provenance

The following attestation bundles were made for styly_netsync_server-0.17.1.tar.gz:

Publisher: publish-to-pypi.yml on styly-dev/STYLY-NetSync

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file styly_netsync_server-0.17.1-py3-none-any.whl.

File metadata

File hashes

Hashes for styly_netsync_server-0.17.1-py3-none-any.whl
Algorithm Hash digest
SHA256 19b354dc782afa412842614d0abcea2aff5b7010692d31ff6d5a83db7fc6b4b8
MD5 f9953617bef1e4b39ca24387ea7e08e6
BLAKE2b-256 e3824e3013570e4a205f1ccb4d6bba32cb6fed5a08b33280ed3309ae50751826

See more details on using hashes here.

Provenance

The following attestation bundles were made for styly_netsync_server-0.17.1-py3-none-any.whl:

Publisher: publish-to-pypi.yml on styly-dev/STYLY-NetSync

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page