9ea98aa7b8
- Fix: export_presets.cfg — platform='Windows Desktop' (not 'Windows'), Windows as [preset.0] - Fix: plugin_manager.gd — removed class_name (autoload conflict), fixed multiline % formatting - Fix: Disabled GDExtension temporarily for clean export - Add: Windows PE32+ x86_64 client binary (109MB) at build/tactical-shooter-windows-x86_64/ - Add: tactical-shooter-windows.zip (portable zip package) - Build: Linux server binary (78MB) rebuilt
godot-cpp
@ d5cc777a89
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