Fix headless class_name dependencies

- Replaced TeamData.Team type hints with int in all scripts
- Added explicit preloads for headless mode class resolution
- Created stub LagCompensation and DamageProcessor scripts
- Fixed PluginManager, SpawnManager, EconomyManager, BuyZone,
  RoundManager, BuyMenuHandler, BombObjective class_name references
- Updated server config to match ServerConfig.gd format

Gray screen root cause: server scripts failed to parse in headless
mode due to Godot 4 class_name loading order (Resource after Node),
leaving server_main.gd non-functional — accepted connections but
never spawned players.
This commit is contained in:
2026-07-02 10:09:28 -04:00
parent ad48f38ca5
commit 4a5264c5b0
14 changed files with 202 additions and 284 deletions
+10 -7
View File
@@ -20,6 +20,9 @@
class_name WeaponServer
extends Node
# Preload dependencies for headless compat
const _wd = preload("res://scripts/combat/weapon_definitions.gd")
# ---------------------------------------------------------------------------
# State shape
# ---------------------------------------------------------------------------
@@ -46,7 +49,7 @@ var physics_world: World3D = null
## Optional LagCompensation controller for server-side rewinding.
## When set, _perform_hitscan uses lag-compensated raycasting;
## otherwise it uses the current-frame physics world directly.
var lag_compensation: LagCompensation = null
var lag_compensation = null
# ---------------------------------------------------------------------------
# Lifecycle
@@ -81,7 +84,7 @@ func unregister_player(player_id: int) -> void:
## Also calls register_player implicitly.
func give_weapon(player_id: int, weapon_id: String) -> void:
register_player(player_id)
var data := WeaponDefinitions.get_weapon(weapon_id)
var data := _wd.get_weapon(weapon_id)
if data == null:
push_warning("WeaponServer: unknown weapon '%s'" % weapon_id)
return
@@ -105,7 +108,7 @@ func get_ammo_info(player_id: int, weapon_id: String) -> Dictionary:
var st := get_weapon_state(player_id, weapon_id)
if st.is_empty():
return {}
var data := WeaponDefinitions.get_weapon(weapon_id)
var data := _wd.get_weapon(weapon_id)
return {
ammo = st.get("ammo", 0),
reserve = st.get("reserve", 0),
@@ -119,7 +122,7 @@ func get_ammo_info(player_id: int, weapon_id: String) -> Dictionary:
## Returns true if the player can fire the specified weapon right now.
## Checks: weapon known, ammo available, not reloading, cooldown elapsed.
func can_fire(player_id: int, weapon_id: String) -> bool:
var data := WeaponDefinitions.get_weapon(weapon_id)
var data := _wd.get_weapon(weapon_id)
if data == null:
return false
@@ -153,7 +156,7 @@ func can_fire(player_id: int, weapon_id: String) -> bool:
## Start reloading the player's weapon. Returns true if reload initiated.
func start_reload(player_id: int, weapon_id: String) -> bool:
var data := WeaponDefinitions.get_weapon(weapon_id)
var data := _wd.get_weapon(weapon_id)
if data == null:
return false
@@ -196,7 +199,7 @@ func process_reloads(player_id: int, delta: float) -> bool:
return any_completed
func _complete_reload(player_id: int, weapon_id: String, st: Dictionary) -> void:
var data := WeaponDefinitions.get_weapon(weapon_id)
var data := _wd.get_weapon(weapon_id)
if data == null:
return
@@ -227,7 +230,7 @@ func _complete_reload(player_id: int, weapon_id: String, st: Dictionary) -> void
## }
##
func fire(tick: int, player_id: int, weapon_id: String, origin: Vector3, direction: Vector3) -> Dictionary:
var data := WeaponDefinitions.get_weapon(weapon_id)
var data := _wd.get_weapon(weapon_id)
if data == null:
return _miss_result(weapon_id)