Files
tactical-shooter/gdextension.disabled/simulation/README.md
T
shawn e7299b17e9 Phase 7: netfox + godot-jolt stack upgrade
Stack installed:
- netfox v1.35.3 (core + extras + noray + internals)
- godot-jolt v0.16.0-stable

Architecture:
- Server: ENet transport (works headless, no netfox deps)
- Client/Editor: netfox rollback (RollbackSynchronizer, TickInterpolator)

New/modified:
- docs/migration-netfox-plan.md — migration architecture
- scripts/network/network_manager.gd — netfox-aware ENet fallback
- scripts/network/player.gd — clean base player
- client/characters/player_netfox.gd — rollback player w/ WeaponManager
- client/characters/input/player_net_input.gd — BaseNetInput subclass
- client/characters/character/fps_character_controller.gd — netfox input feed
- client/weapons/ — weapon data, registry, TacticalWeaponHitscan, WeaponManager
- client/scripts/round_replicator.gd — client-side round state bridge
- server/scripts/round_manager.gd — improved state machine
- server/scripts/plugin_api/plugin_manager.gd — refined plugin system
- config: enemy_tag, ally_tag for meatball targeting

Removed: old C++ SimulationServer GDExtension (replaced by netfox rollback)
2026-07-02 17:39:22 -04:00

160 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
```bash
# 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)
```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