Fresh start: replace with naxIO/netfox-cs-sample foundation

Complete replacement of the tactical-shooter project with the
netfox-cs-sample (MIT) — a CS 1.6 inspired multiplayer FPS built
with Godot 4 and netfox.

## What's new
- Full CS-style gameplay: teams (T/CT), rounds, economy, buy menu
- 6 weapons: Knife, Glock, USP, AK-47, M4A1, AWP
- Bomb plant/defuse with 2 bombsites
- Flashbang & smoke grenades
- Proper netfox rollback netcode at 64 tick
- Network popup UI for host/join
- HUD, crosshair, round timer, scoreboard
- All netfox singletons registered as autoloads (works in exported builds)

## Architecture
- Listen-server (host from client, no dedicated server binary)
- Multiplayer-fps game lives at examples/multiplayer-fps/
- Netfox addons registered as autoloads for exported build compat
- Godot 4.7 with Forward+ renderer

## Removed
- Old headless-server architecture (client_main, server_main, player.gd, etc.)
- Custom netfox bootstrap with ENet fallback
- Old ChaffGames FPS template (2,420 lines, 844 KB)
- SimulationServer GDExtension stub
- Godot-jolt physics (netfox sample uses default Godot physics)
- Duplicate weapon_data.gd, anti_cheat.gd, round_manager.gd, etc.
- Server browser API Python venv (87 MB)
- test_range map and modular assets

## Preserved
- Git history
- Server config at config/default_server_config.cfg
- Windows export preset
- Build directory (gitignored)

Co-authored-by: naxIO <naxIO@users.noreply.github.com>
This commit is contained in:
2026-07-02 20:55:20 -04:00
parent ce39b237c3
commit b0c83af092
4416 changed files with 57418 additions and 902676 deletions
+121
View File
@@ -0,0 +1,121 @@
# Rollback caveats
As with most things, rollback has some drawbacks along with its benefits.
### CharacterBody velocity
Godot's `move_and_slide()` uses the `velocity` property, which is set in
meters/second. The method assumes a delta time based on what kind of frame is
being run. However, it is not aware of *netfox*'s network ticks, which means
that movement speed will be off.
To counteract this, multiply velocity with `NetworkTime.physics_factor`, which
will adjust for the difference between Godot's *assumed* delta time and the
delta time *netfox* is using.
If you don't want to lose your original velocity ( e.g. because it accumulates
acceleration over time ), divide by the same property after using any built-in
method. For example:
```gdscript
# Apply movement
velocity *= NetworkTime.physics_factor
move_and_slide()
velocity /= NetworkTime.physics_factor
```
### CharacterBody on floor
CharacterBodies only update their `is_on_floor()` state only after a
`move_and_slide()` call.
This means that during rollback, the position is updated, but the
`is_on_floor()` state is not.
As a work-around, do a zero-velocity move before checking if the node is on the
floor:
```gdscript
extends CharacterBody3D
func _rollback_tick(delta, tick, is_fresh):
# Add the gravity.
_force_update_is_on_floor()
if not is_on_floor():
velocity.y -= gravity * delta
# ...
func _force_update_is_on_floor():
var old_velocity = velocity
velocity = Vector3.ZERO
move_and_slide()
velocity = old_velocity
```
### Physics updates
Godot's physics system is updated only during `_physics_process`, while
rollback updates the game state multiple times during a single frame.
Unfortunately, Godot does not support manually updating or stepping the physics
system, [at least at the time of writing](https://github.com/godotengine/godot/pull/76462).
This means that:
* Rollback and physics-based games ( RigidBodies ) don't work at the moment
* Collision detection can work, but with workarounds
If there's a way to force an update for your given node type, it should work,
i.e.
* ShapeCast (2D and 3D) - [force_shapecast_update()]
* ChacacterBody (2D and 3D) - [move_and_collide()] ( which has a test only
mode )
While kinematic nodes like `CharacterBody3D` can be used with rollback, physics
queries can still cause issues (e.g.
`PhysicsDirectSpaceState3D.intersect_shape()`). This is due to the lack of
updates mentioned earlier. To work around this, run the following for each
`CollisionObject` that has its position rolled back before each tick of the
rollback loop:
```gdscript
# Works for both Jolt and GodotPhysics3D.
func _force_update_physics_transform():
PhysicsServer3D.body_set_mode(get_rid(), PhysicsServer3D.BODY_MODE_STATIC)
PhysicsServer3D.body_set_state(get_rid(), PhysicsServer3D.BODY_STATE_TRANSFORM, global_transform)
PhysicsServer3D.body_set_mode(get_rid(), PhysicsServer3D.BODY_MODE_KINEMATIC)
```
The above forces an update by setting the object to static, updating its
transform, and then setting it back to its original, kinematic state.
Note that the above code needs to run for any kinematic object that is to be
detected by the query and is manipulated during rollback.
!!!tip
The *netfox.extras* addon provides optional support for physics simulation
with rollback. See [Physics](../../netfox.extras/guides/physics.md)
### State Machines
State machines don't usually expect to be updated multiple times in a single
frame or be snapped back to a previous point in time. Be cautious of:
- Safeguards that implement a cooldown to changes.
- States based on values not updated in `_rollback_tick`.
- Transitions that enforce a specific order to state changes.
- Transitions that trigger on any state change.
The key concept to keep in mind is that netfox stores the configured states for
each processed tick. When it rolls back everything is snapped back to that
point in time and then played forward to the present in a single frame.
!!!tip
The *netfox.extras* module provides an implementation of state machines
compatible with rollback. See
[RewindableStateMachine](../../netfox.extras/guides/rewindable-state-machine.md)
[force_shapecast_update()]: https://docs.godotengine.org/en/stable/classes/class_shapecast3d.html#class-shapecast3d-method-force-shapecast-update
[move_and_collide()]: https://docs.godotengine.org/en/stable/classes/class_physicsbody3d.html#class-physicsbody3d-method-move-and-collide