Skip to content

Websocket Client-Server Framework Spike #1198

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: dev
Choose a base branch
from

Conversation

ariesninjadev
Copy link
Contributor

@ariesninjadev ariesninjadev commented Jul 8, 2025

Multiplayer Framework Spike: Websocket / Authoritative Server on Lazy Client Implementation

This PR contains multiple elements:

  • A test folder at /multiplayer-spike containing a barebones server and client that meet the spike requirements as specified by the ticket
    • A quick setup guide
  • An implementation guide for hooking the server into Fission/Jolt
  • Findings regarding plausability and UX

Test Stack

Included is a basic client-server setup that is meant to represent the framework that would be integrated into Synthesis. It pipes client inputs, simulates the world and physics on the server, and broadcasts the authoritative state to all clients on a fixed basis.

The spike server features:

  • A fixed game loop
  • Input processing
  • World simulation
  • State broadcasting
  • Performance monitoring

The spike client features:

  • Real-time rendering
  • Input registry
  • State syncing

This spike does not feature:

  • Room/session seperation
  • Jolt physics
  • Input validation (anticheat)
  • Peripherals (score, timers, etc.)

Quick Setup

Online Test

An instance of the spike is live here until July 15th.


Testing the spike locally is very simple.

Prerequists:

  • Node.js 18+
  • npm or yarn (weird ahh)

Installation

cd multiplayer-spike
npm install

Run

npm run dev

The server will start on port 3000 by default

Independent Tests

To verify the functionality, carry out the following tests:

Multi-Client Stability

  • Test: Open 2-6 browser tabs
  • Verify: Each tab shows a red robot (your robot), and n-1 green robots (other robots). Robots update visually in sync.

Input Latency

  • Test: Press the WASD keys to move the robot
  • Verify: Movement should feel responsive (<80ms perceived delay), and the ping internally (or server ping when on localhost) should stay below 50ms.

Connection Health

  • Test:
    • Kill server during activity
    • Restart/pause server
  • Verify: Connection status indicates drops, and the client reconnects when the server becomes available.

Measured Metrics

All metrics observed on Windows

Performance

  • Flat Tick Rate: 60Hz (16.6ms interval per frame)
  • Medium Load Tick Rate (4 clients): 59.95Hz (99.9%)
  • High Load Tick Rate (10 clients): 59.1Hz (98.5%)

Memory

  • Base Server Consumption: 50-80MB
  • High Consumption (10 clients moving randomly): 200-250MB
  • Estimated Consumption per Session: 100MB

Based on these metrics, an implementation that uses authoritative server is probably plausible. It's also possible that other implementations including WebRTC are less memory intensive while being less performant.

Integration into Fission

These are thrown together code fragments that could serve as a basis for hooking this implementation into Synthesis.

System Integration

The multiplayer server should probably be integrated as a new system in Fission:

import { WebSocketServer } from "ws";
import World from "@/systems/World";
import { SimulationSystem } from "@/systems/simulation/SimulationSystem";

class MultiplayerSystem {
    private server: MultiplayerServer;
    private isHost: boolean = false;
    private isClient: boolean = false;

    public EnableAsHost(port: number = 3000) {
        this.isHost = true;
        this.server = new MultiplayerServer(port);
        this.server.start();
    }

    public ConnectAsClient(serverUrl: string) {
        this.isClient = true;
    }

    public Update(deltaT: number) {
        if (this.isHost) {
            this.server.tick(deltaT);
        }
    }
}

Jolt Integration

Replace the test physics in the spike with Jolt physics. This is easily the hardest part of this effort, unless I'm missing something, which I probably am. As an example, however, to sync the robot body:

import { PhysicsSystem } from "@/systems/physics/PhysicsSystem";
import Mechanism from "@/systems/physics/Mechanism";

class MultiplayerPhysicsSync {
    public syncRobotState(robot: MirabufSceneObject) {
        const body = robot.mechanism?.body;
        if (body) {
            return {
                position: JoltVec3_ThreeVector3(body.GetPosition()),
                rotation: JoltQuat_ThreeQuaternion(body.GetRotation()),
                velocity: JoltVec3_ThreeVector3(body.GetLinearVelocity()),
            };
        }
    }

    public applyRobotState(robot: MirabufSceneObject, state: any) {
        const body = robot.mechanism?.body;
        if (body) {
            body.SetPosition(ThreeVector3_JoltVec3(state.position));
            body.SetRotation(ThreeQuaternion_JoltQuat(state.rotation));
            body.SetLinearVelocity(ThreeVector3_JoltVec3(state.velocity));
        }
    }
}

MatchMode Integration

Thanks to the MatchMode system, we can hook some features easily, and add the following to make it actually functional:

class MatchMode {
    // ...
    private multiplayerSystem?: MultiplayerSystem;

    public enableMultiplayer(isHost: boolean, serverUrl?: string) {
        this.multiplayerSystem = new MultiplayerSystem();

        if (isHost) {
            this.multiplayerSystem.EnableAsHost();
        } else if (serverUrl) {
            this.multiplayerSystem.ConnectAsClient(serverUrl);
        }
    }

    public start(openModal: (modalName: string) => void) {
        this.matchEnabled = true;
        this.autonomousModeStart(openModal);
        SimulationSystem.ResetScores();

        if (this.multiplayerSystem) {
            this.multiplayerSystem.broadcastMatchStart();
        }
    }
}

Input System Integration

To allow inputs from Fission to work correctly through the server:

import { MultiplayerSystem } from "@/systems/multiplayer/MultiplayerSystem";

class InputSystem {
    // ...
    private multiplayerSystem?: MultiplayerSystem;

    public Update(): void {
        // ...
        if (this.multiplayerSystem?.isConnected()) {
            const inputs = this.getActiveInputs();
            this.multiplayerSystem.sendInputs(inputs);
        }
    }
}

Global Configuration Changes

Changes required to support the server in a production environment

Dependencies

Modifications to package,json

{
    "dependencies": {
        // ...
        "ws": "^8.18.0",
        "uuid": "^9.0.1",
        "express": "^4.18.2"
    }
}

Build Config

Modifications to vite.config.ts

export default defineConfig({
    // ...
    server: {
        proxy: {
            "/multiplayer": "http://localhost:3000",
        },
    },
});

Considerations and Optimizations

Beyond the missing features, these are things that should probably be implemented before multiplayer goes public.

Server Optimizations

  • Compression for state updates
  • Connection pooling
  • Data caching

Security

  • Authentication (perhaps through APS)
  • Rate limiting/DDoS protection
  • Input validation & anticheat

Workflow

  • Server added to Vite script so it runs with Fission
  • Debugging tools

Production

  • Separate server deployment with Node.js
  • Load balancing

This concludes the Spike PR. Do not merge.

@Autodesk Autodesk deleted a comment from autodesk-chorus bot Jul 8, 2025
@Dhruv-0-Arora Dhruv-0-Arora marked this pull request as draft July 8, 2025 21:48
Copy link
Collaborator

@Dhruv-0-Arora Dhruv-0-Arora left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is super cool.

Note

Not merging into dev.

@Dhruv-0-Arora Dhruv-0-Arora added gameplay Relating to the playability of Synthesis and removed testing labels Jul 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
gameplay Relating to the playability of Synthesis no-merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants