Files
tactical-shooter/gdextension/simulation
shawn e9dc05983c Save workspace artifacts: character-controller, gdextension scaffold, network scripts, map-pipeline, server config
Includes code from task workspaces that was never pushed:
- client/characters/ — fps_character_controller.gd (400 lines), fps_camera.gd, input_handler.gd
- gdextension/simulation/ — C++ GDExtension scaffold: entity, movement, hit detection, simulation server, state serializer, bitstream (14 files)
- scripts/network/ — ENet-based network_manager.gd, server/client_main.gd, player.gd
- scripts/map_packaging/ — PCK pipeline: pack_map.gd, map_downloader.gd, map_registry_server.py, README
- scripts/config/ — server_config.gd (480 lines)
- scenes/ — client_main, server_main, player, test_range
- project.godot, export_presets.cfg
2026-07-01 18:30:44 -04:00
..

Tactical Shooter — GDExtension Simulation Core

GDExtension C++ simulation core for a 128Hz tactical FPS. Movement, hit detection, and state serialization run entirely in C++ — the GDScript hot loop handles only network I/O and rendering.

Architecture

┌─────────────────────────────────────────────────────┐
│                 GDScript Layer                       │
│  _process(delta) → can_tick() → tick() → send()     │
├─────────────────────────────────────────────────────┤
│              SimulationServer (C++)                   │
│  ┌──────────┐  ┌───────────────┐  ┌───────────────┐ │
│  │Movement   │  │HitDetection   │  │StateSerializer│ │
│  │Component  │  │(ray/overlap)  │  │(delta-compress)│ │
│  └──────────┘  └───────────────┘  └───────────────┘ │
│  ┌──────────────────────────────────────────────────┐│
│  │ Entity[N]  (position, velocity, health, input)   ││
│  └──────────────────────────────────────────────────┘│
├─────────────────────────────────────────────────────┤
│              Network Layer (GDScript → ENet)          │
└─────────────────────────────────────────────────────┘

Design Principles

  • Hot path in C++: All simulation ticks (movement, hit detection, serialization) run in GDExtension. No GDScript VM overhead per tick.
  • Custom replication: Not using Godot's scene-replication (too expensive at 128Hz). Delta-compressed binary snapshots via StateSerializer.
  • Abstracted systems: Movement parameters are configurable from GDScript. Hit detection uses geometric checks (no PhysicsServer3D dependency in Phase 1).
  • Benchmark-ready: SimulationServer::populate_bots() + get_stats() provide the hook for the 128Hz load test (task t_f671f48a).

Prerequisites

  • Godot 4.2+ (recommended: 4.3+)
  • godot-cpp (v4.3 branch): git submodule update --init
  • SCons 4.0+: pip install scons
  • C++17 compiler: GCC 11+ / Clang 14+ / MSVC 2022

Setup

# Clone with submodules
git clone <repo-url> tactical-shooter
cd tactical-shooter
git submodule update --init --recursive

# Build (debug)
scons -j$(nproc)

# Build (release)
scons target=template_release -j$(nproc)

The compiled .so/.dll will be placed in gdextension/bin/<platform>/.

Usage (GDScript)

var server = SimulationServer.new()
server.tick_rate = 128
server.start()

# Spawn some entities
server.spawn_entity(Vector3(0, 0, 0))
server.spawn_entity(Vector3(5, 0, 0))

# Main loop
func _process(delta):
    while server.can_tick(delta):
        var snapshot = server.tick()
        if snapshot.size() > 0:
            send_to_clients(snapshot)

# Apply player input
func _input(event):
    var input_dict = {
        move_direction = input_vector,
        look_yaw = camera_yaw,
        look_pitch = camera_pitch,
        jump = Input.is_action_just_pressed("jump"),
        sprint = Input.is_action_pressed("sprint"),
        shoot = Input.is_action_just_pressed("shoot"),
        input_sequence = input_seq
    }
    server.apply_input(player_entity_id, input_dict)

Project Structure

├── SConstruct                    # Build system
├── src/
│   ├── register_types.cpp/.h     # GDExtension entry point
│   ├── simulation_server.cpp/.h  # Main orchestrator (GDScript-facing)
│   ├── entity.cpp/.h             # Entity state (GDScript-facing)
│   ├── movement_component.cpp/.h # FPS movement simulation
│   ├── hit_detection.cpp/.h      # Ray/sphere hit detection
│   ├── state_serializer.cpp/.h   # Delta-compressed snapshot I/O
│   └── bitstream.h               # Bit-level packing (header-only)
├── gdextension/
│   ├── simulation.gdextension    # Godot extension config
│   └── bin/                      # Build output
└── tests/
    └── test_simulation.cpp       # Unit test harness

Movement Parameters

All tunable from GDScript:

Parameter Default Description
walk_speed 4.0 Ground speed (units/s)
sprint_speed 6.5 Sprint speed (units/s)
crouch_speed 2.0 Crouch speed (units/s)
acceleration 20.0 Ground acceleration (units/s²)
air_acceleration 4.0 Air acceleration (units/s²)
friction 8.0 Ground deceleration
jump_velocity 5.0 Initial upward velocity (units/s)
gravity -20.0 Gravity (units/s², negative = down)

Snapshot Wire Format

[uint32 tick]           # Server tick number
[uint16 count]          # Number of changed entities
[uint16 base_tick]      # 0 = full snapshot
--- per entity ---
[uint16 entity_id]
[uint32 change_mask]    # Which fields changed
[fields per change_mask] # Only changed fields, packed as:
  position:  3 × 16-bit quantized floats (range ±1024)
  velocity:  3 × 12-bit quantized floats (range ±32)
  rotation:  2 × 11-12 bit quantized floats
  health:    7-bit quantized (0-100)
  armor:     7-bit quantized (0-100)
  weapon_id: uint8
  ammo:      uint16
  flags:     uint16
  input_seq: uint32

Typical sizes at 128Hz:

  • Full snapshot (10 entities): ~300-400 bytes
  • Delta snapshot (5 changed): ~150-250 bytes

Task Dependencies

Phase 0 (done) → build:gdextension-simulation-scaffold (this) → bench:128hz-load-test
                                                              → build:fps-character-controller

Known Limitations (Phase 1)

  • Hit detection uses bounding sphere geometry (not PhysicsServer3D raycasts)
  • Ground detection is stub-only (no scene queries for floor normal)
  • No interpolation: clients receive raw tick snapshots
  • Single-threaded tick processing