Skip to content

Cookbook: Multiplayer 3D Game Engine ​

When developers think of web frameworks, they usually think of HTTP requests and JSON payloads. Building a real-time multiplayer game backend is an entirely different beast. You need high-frequency server ticks (60Hz), WebSockets for low-latency UDP-like streaming, and aggressive server-side validation to prevent players from cheating.

In traditional stacks, this means abandoning your web framework entirely and writing custom C++ or Rust servers.

Because Carotene executes via an ultra-fast compiled WASM engine and handles loop primitives natively, you can actually architect an authoritative 3D multiplayer server right alongside your standard web APIs.

Here is a secure, authoritative 60Hz game server in a single file.

1. The Architecture Blueprint ​

We start by defining our 3D space and the entities within it. Because a game state updates 60 times a second, we don't want to save this to a slow physical Postgres database. We use a state block instead of a store, meaning Carotene will manage it entirely in the high-speed RAM of the Vapor Runtime.

dart
domain Arena {
  
  // 1. Core Data Structures
  model Vector3 {
    x: Float
    y: Float
    z: Float
  }

  model Player {
    id: UUID
    position: Vector3
    velocity: Vector3
    isGrounded: Boolean
    health: Int
  }

  // 2. The In-Memory Game World
  state GameWorld {
    model: Player
  }
}

2. The Authoritative Server Tick ​

Multiplayer games do not trust the client. If a player moves forward, they don't send their new position; they send their input (velocity), and the server calculates where they should be.

We use a high-frequency loop to represent our 60Hz server tick. This loop calculates gravity, applies velocity, and handles collision for every player simultaneously.

dart
backend GameServer {

  // ==========================================
  // THE 60Hz SERVER TICK (Physics Loop)
  // ==========================================
  loop PhysicsTick {
    config {
      interval: "16ms"
    }
    reads state.GameWorld
    updates state.GameWorld

    implements {
      allPlayers = state.GameWorld.all()
      
      // We use the Generative Operator to handle 3D vector math and basic collision.
      // The AI synthesizes the heavy math into the underlying C/Rust/Go layer.
      newStates = @("Iterate over 'allPlayers'. Apply a gravity vector of -9.8 to the Y-axis if 'isGrounded' is false. Add 'velocity' to 'position' taking a 16ms delta-time into account. Prevent the Y position from dropping below 0 (the floor).")

      // Batch update the RAM store with the new calculated positions
      state.GameWorld.batchUpdate(newStates)
    }
  }
}

3. High-Frequency Input (WebSocket / UDP) ​

To handle continuous player input, standard HTTP function blocks are too slow. Carotene provides the socket primitive to establish persistent, low-latency connections.

This endpoint receives joystick/keyboard input and updates the player's velocity in RAM, but it strictly limits the maximum speed to prevent "speed hacks."

dart
backend GameServer {

  // ==========================================
  // PLAYER INPUT HANDLER
  // ==========================================
  socket HandleInput(playerId: UUID, inputVector: Vector3) {
    requires session.isAuthenticated
    requires session.userId == playerId // You can only move yourself
    updates state.GameWorld
    
    implements {
      player = state.GameWorld.find(playerId)
      
      // Calculate the magnitude of the input vector
      inputSpeed = Math.sqrt((inputVector.x * inputVector.x) + (inputVector.z * inputVector.z))
      
      // Anti-Cheat: Max movement speed is 5.0 units per second
      if (inputSpeed > 5.0) {
        throws "Speed hack detected"
      }
      
      player.velocity = inputVector
    }
  }
}

4. The Proof (Anti-Cheat Validation) ​

Game logic is notoriously difficult to test because it relies on time and physics math. Because the Carotene Sandbox can freeze time and perfectly control the execution environment, writing an anti-cheat test is trivial.

We will test that a player cannot exploit the input handler, and that gravity actually works during the physics tick.

dart
// Test 1: Anti-Cheat Input Validation
test "Instantly rejects velocity inputs that exceed max speed" {
  given mock session { userId: "player_1", isAuthenticated: true }
  given mock state.GameWorld { id: "player_1", position: {x: 0, y: 0, z: 0} }
  
  // A cheater tries to inject a massive velocity vector
  cheaterInput = { x: 100.0, y: 0.0, z: 100.0 }
  
  // Assert the socket drops the packet and flags the cheat
  asserts HandleInput("player_1", cheaterInput) throws "Speed hack detected"
}

// Test 2: AI Physics Generation
test "Gravity pulls airborne players to the floor" {
  config { sandbox: EmbeddedWASM }

  // 1. Mock a player hovering in the air (Y: 10.0)
  given mock state.GameWorld { 
    id: "player_1", 
    position: { x: 0.0, y: 10.0, z: 0.0 },
    velocity: { x: 0.0, y: 0.0, z: 0.0 },
    isGrounded: false
  }
  
  // 2. Trigger exactly one 16ms server tick
  trigger loop PhysicsTick
  
  // 3. Assert the AI correctly applied gravity math (Y should be slightly less than 10.0)
  postTickPlayer = state.GameWorld.find("player_1")
  asserts postTickPlayer.position.y < 10.0
}

5. Compile and Deploy ​

When you run carrot build:

  1. The compiler sees the state block and generates an ultra-fast Redis or local WASM memory pool instead of Postgres.
  2. It translates the socket primitive into a highly optimized WebSocket (or WebRTC data channel) listener.
  3. The AI synthesizes your @(...) prompt into optimized 3D vector math.
  4. The Sandbox runs a simulated server tick, proving mathematically that gravity works and cheaters are caught.