Files
tactical-shooter/PLANS/PROJECT_PLAN.md
T
2026-06-30 21:00:57 -04:00

10 KiB

Project: Tactical Shooter (CS-Clone) — Build Plan

1. Vision

A competitive, round-based tactical FPS (Counter-Strike-style: buy phase, bomb/objective rounds, economy) built in Godot 4, targeting:

  • Good visual fidelity on low/mid-end hardware (Valorant-tier art direction, not AAA realism)
  • 128-tick authoritative dedicated servers, community-hostable
  • Official map-making tools/SDK so the community can build and distribute maps
  • Server browser + map distribution pipeline (Steam-Workshop-like, but self-hosted)

2. Core Tech Decisions (locked)

Area Decision
Engine Godot 4, Forward+ renderer
Server model Authoritative dedicated server, headless Godot (--headless) export
Netcode GDExtension (C++) simulation core from day one; ENet or GodotSteam transport (evaluate both in Phase 0)
Tick rate 128 tick, firm target. Simulation hot path built in GDExtension (C++) from day one — not GDScript — to make this realistic. See Section 3a.
Lighting Baked lightmaps (LightmapGI) + reflection probes, no SDFGI/real-time GI
Map tooling Built-in CSG nodes (Hammer-like blockout) + custom map template project
Hit detection Server-side hitscan with lag compensation (rewind)
Distribution Self-hosted CDN/GitHub releases for .pck map bundles, custom server browser API

3a. Notes on Hitting 128 Tick

128 tick is achievable in Godot but requires committing to the harder architecture from the start, not retrofitting it later:

  • Simulation core in GDExtension (C++), not GDScript. Movement, hit detection, and state serialization for the authoritative server should live in a compiled GDExtension module. GDScript can drive high-level game logic (round state, economy, UI) but should not be in the 128Hz hot loop.
  • Delta-compressed snapshots, not full state per tick. At 128Hz, sending full world state every tick is a bandwidth non-starter. Serialize only changed fields per entity per tick.
  • Decouple simulation tick from network send rate. The server can simulate at 128Hz while only sending snapshots to clients at a lower rate (e.g. 64Hz) with interpolation — this is standard practice (Source engine itself decouples these). Confirm with the team whether "128 tick" means simulation rate, network send rate, or both — this materially changes the engineering plan.
  • Benchmark in Phase 0, not Phase 1. Stand up a synthetic load test (bots or dummy clients) hitting 128Hz simulation with realistic player counts (10v10) before building gameplay on top of it. If GDExtension can't sustain it on target hardware, that needs to be known before Phase 1 starts, not discovered mid-project.
  • Physics FPS setting: Godot's _physics_process must be set to 128 (Project Settings → Physics → Common → Physics Ticks Per Second) and profiled for headroom, since this affects both simulation and physics engine cost.

3b. Libraries & Plugins

Plugin/Library Purpose Notes Owning Agent
FuncGodot Import TrenchBroom .map/.vmf-style files into Godot as scenes Successor to Qodot; gives community a Hammer/TrenchBroom-equivalent mapping workflow — evaluate as primary mapping tool over raw CSG Mapping SDK Agent
godot-tbloader Alternative TrenchBroom loader Lighter-weight C++ port; benchmark against FuncGodot in Phase 0/5, pick one Mapping SDK Agent
GodotSteam (GDExtension) Low-level networking transport (wraps GameNetworkingSockets) Works over plain IP, not just Steam lobbies; strong candidate to replace/augment ENet for reliability and lane prioritization Netcode Agent
GodotSteam Server (GDExtension) Server-side counterpart to GodotSteam Useful if considering optional Steam-based server discovery later Server Infra Agent
steam-multiplayer-peer MultiplayerPeer implementation over Steam Sockets Only relevant if adopting Godot's high-level multiplayer API on top of Steam transport Netcode Agent
ENet (built-in) Baseline transport Keep as Phase 0 fallback/reference implementation while evaluating GodotSteam Netcode Agent
Custom GDExtension (C++) Simulation hot path Not a plugin — build in-house. See Section 3a. Netcode Agent

Deliberately not using:

  • GD-Sync — relay-based hosted multiplayer service; conflicts with the self-hosted community dedicated server model.
  • godot-rollback-netcode (Snopek Games) — built for rollback-style netcode (fighting games), not authoritative-server shooters. Wrong model for this project.

4. Phases & Milestones

Phase 0 — Foundations (spike/prototype)

  • Stand up empty Godot project, Forward+ renderer configured
  • Headless dedicated server export pipeline working (--headless, build + run on Linux VPS)
  • Basic client-server connection via ENet or GodotSteam transport, player join/leave, position replication
  • GDExtension simulation core scaffolded; synthetic load test at 128Hz with realistic player counts (see Section 3a)
  • Exit criteria: two clients can connect to a dedicated server and see each other move, and the 128Hz load test hits target headroom on reference hardware

Phase 1 — Core Gameplay Loop

  • First-person character controller (movement, jump, crouch, walk toggle)
  • Client-side prediction + server reconciliation for movement
  • Hitscan weapon (one gun), server-authoritative damage, lag compensation
  • Round system: round start/end, win condition (elimination), respawn/spectate
  • One grey-box test map
  • Exit criteria: two players can shoot each other and complete a round on the dedicated server

Phase 2 — Tactical Shooter Systems

  • Buy menu + economy (money per round, win/loss bonuses)
  • Bomb-defuse or objective mode (plant/defuse or equivalent)
  • Weapon set (rifle, pistol, sniper, utility — smoke/flash/grenade)
  • Team system, spawn zones, buy zones
  • Exit criteria: full round loop (buy → play → win/loss → economy carries over) playable end-to-end

Phase 3 — Visuals & Performance Pass

  • Baked lighting pass on test map (LightmapGI), reflection probes
  • Art pass: modular wall/floor kit, PBR materials at 1K, agreed art style guide
  • Performance budget defined (target frame time on reference low-end GPU) and profiled
  • LOD + occlusion culling setup
  • Exit criteria: test map hits performance budget on reference low-end hardware while looking presentable

Phase 4 — Dedicated Server Hardening

  • RCON-style remote admin console
  • Server config files (tick rate, map rotation, cvars)
  • Master server / server browser API (heartbeat + query)
  • Anti-cheat basics (server-authoritative validation, sanity checks on inputs)
  • Exit criteria: community member can download server binary, configure, and have it appear in a public server browser

Phase 5 — Mapmaking SDK

  • Map template Godot project (CSG-based prefabs, spawn/buy-zone/bombsite node types documented)
  • Lightmap baking + validation script (poly count, texture size, light count linting)
  • Map packaging as .pck addon + auto-download on client connect
  • Mapping documentation for community
  • Exit criteria: a team member unfamiliar with the codebase can build and ship a working custom map using only the SDK + docs

Phase 6 — Polish & Ecosystem

  • Plugin/scripting hook API for server admins (SourceMod-style, stretch goal)
  • Workshop-style map browser in-client
  • Bug bash, netcode edge cases (packet loss, high ping simulation testing)
  • Exit criteria: ready for closed community playtest

5. Repository Structure (proposed)

/client                 Godot client project
/server                 Headless server export config/scripts
/shared                 Code shared between client & server (game logic, data structs)
/maps
  /template              Base map project agents/community start from
  /test_maps
/tools
  /map-validator          Lint script for poly count, texture size, lighting
  /server-browser-api      Master server service
/docs
  /architecture.md
  /netcode.md
  /mapping-sdk.md
  /server-hosting.md

6. Workstream Ownership (for agent team assignment)

Workstream Scope
Netcode Agent GDExtension simulation core, client prediction, server reconciliation, lag compensation, 128Hz tick loop, snapshot delta compression, transport layer (ENet/GodotSteam)
Gameplay Agent Movement controller, weapons, round/economy logic, buy system
Rendering/Art Pipeline Agent Forward+ renderer config, LightmapGI baked lighting setup, material/texture standards, performance profiling
Server Infra Agent Headless export pipeline, RCON, config system, master server/server browser
Mapping SDK Agent Map template project, CSG prefab kit, validation/lint tooling, .pck packaging pipeline
Docs/Integration Agent Keeps /docs in sync, defines interfaces between workstreams, integration testing

7. Key Risks to Flag Early

  • 128 tick is the firm target — treat it as a Phase 0 gate, not a later optimization. If the load test in Phase 0 doesn't hit target headroom, stop and resolve it before Phase 1 gameplay work begins; don't build gameplay on an unproven simulation core.
  • GDScript must stay out of the 128Hz hot loop. All performance-critical simulation code goes in the GDExtension core from day one (see Section 3a) — this is not optional at this tick rate.
  • Clarify what "128 tick" covers with the team up front: simulation rate, network send rate, or both. This changes the bandwidth budget and delta-compression design significantly.
  • Scope: a full CS clone (weapon balance, anti-cheat, matchmaking, many maps) is large. Phases above are ordered so there's a playable end-to-end loop as early as Phase 2 — resist adding features before that loop is solid.
  • Lag compensation correctness is the single most likely source of "feels bad" gameplay bugs — budget real testing time here, not just implementation time.

8. Definition of Done for Each Phase

Each phase is done when its exit criteria (above) is met, testable by two or more clients connecting to an actual dedicated server build (not just in-editor play mode), with no P0/P1 bugs open in that phase's scope.