t_p1_hitscan: Add lag-compensated hitscan weapon system

NEW: server/scripts/combat/lag_compensation.gd
  - 128-entry ring buffer of player positions per server tick
  - rewind_and_raycast(tick, origin, direction, range, exclude):
    saves current positions, rewinds to tick-time positions,
    performs PhysicsDirectSpaceState3D.intersect_ray(), restores
  - Falls back to normal raycast if history missing for tick
  - shot_processed signal

NEW: scripts/combat/damage_processor.gd
  - Processes WeaponServer hit results: applies damage, tracks kills
  - Optional distance-based damage falloff
  - player_damaged / player_killed signals
  - Health and kill-count queries

MODIFY: server/scripts/weapons/weapon_server.gd
  - fire() now takes tick parameter: fire(tick, player_id, ...)
  - _perform_hitscan() uses LagCompensation.rewind_and_raycast()
    when lag_compensation reference is set and tick >= 0
  - Non-compensated fallback path preserved

MODIFY: server/scripts/game_server.gd
  - Adds _current_tick counter, incremented each simulation tick
  - Creates WeaponServer + LagCompensation + DamageProcessor as children
  - record_tick() called before each tick's simulation
  - register/unregister_player_node() for player lifecycle
  - _on_player_killed() relays kill events via player_damaged signal

MODIFY: scripts/network/server_main.gd
  - _spawn_player() registers node with GameServer.register_player_node()
  - _despawn_player() unregisters via unregister_player_node()
This commit is contained in:
2026-07-01 20:26:18 -04:00
parent 3465922be4
commit 705b068ed2
5 changed files with 457 additions and 8 deletions
+7
View File
@@ -171,6 +171,10 @@ func _spawn_player(peer_id: int) -> void:
if entity_id >= 0:
# Store entity_id on player node for reference
player.set_meta(&"entity_id", entity_id)
# Register with lag compensation and damage processor
# (player is a Node3D, which is a valid argument for register_player_node)
if _game_server.has_method(&"register_player_node"):
_game_server.register_player_node(entity_id, player)
print("[ServerMain] Player %d assigned entity %d" % [peer_id, entity_id])
# Broadcast spawn to all clients so they create a visual player node
@@ -185,6 +189,9 @@ func _despawn_player(peer_id: int) -> void:
# Despawn entity from GameServer
if _game_server != null and player.has_meta(&"entity_id"):
var entity_id: int = player.get_meta(&"entity_id")
# Unregister from lag compensation and damage processor first
if _game_server.has_method(&"unregister_player_node"):
_game_server.unregister_player_node(entity_id)
_game_server.despawn_player_entity(entity_id)
player.queue_free()