4 Commits

Author SHA1 Message Date
shawn 695e4db5cd Add input debug to window title for diagnostic
Shows Input.get_axis() values, weapon slot states, and fire
state in the window title bar — visible even when print()
output is lost on Windows GUI exports.
2026-07-03 23:13:42 -04:00
shawn c5b6b05801 Fix keyboard input: use OS.move_to_foreground() for window focus
get_window().grab_focus() was insufficient on Windows GUI exports.
Replaced with OS.move_to_foreground() + Input.set_mouse_mode(CAPTURED)
+ deferred retries to ensure the game window holds keyboard focus
after the connection UI is dismissed.
2026-07-03 22:34:50 -04:00
shawn ef4e4ccb80 Clean up test diagnostic files 2026-07-03 20:07:49 -04:00
shawn 7db8434f7e Fix duplicate _rollback_tick causing WeaponManager compile error
A debug-print attempt from earlier inadvertently appended a second
_rollback_tick function (with spaces instead of tabs) at the end of
weapon_manager.gd:419, causing the entire script to fail compilation.
This meant the WeaponManager node had NO script attached:
- 'has_method("set_default_loadout")' returned false
- Client-side loadout init never ran
- All weapon operations silently did nothing

This is the real root cause of why weapons never worked on the client.
2026-07-03 20:07:40 -04:00
5 changed files with 20 additions and 15 deletions
@@ -129,6 +129,12 @@ func _process(_delta: float):
print("[PLAYER DEBUG] _process skipping — input not authority (unique_id=%d, input_auth=%d)" % [multiplayer.get_unique_id(), input.get_multiplayer_authority()]) print("[PLAYER DEBUG] _process skipping — input not authority (unique_id=%d, input_auth=%d)" % [multiplayer.get_unique_id(), input.get_multiplayer_authority()])
return return
# DEBUG: Show input state in window title
if _is_local and input:
var mx := Input.get_axis("move_west", "move_east")
var mz := Input.get_axis("move_north", "move_south")
get_window().title = "TS [mv=%.1f,%.1f] [sl=%s%s%s%s] [fire=%s]" % [mx, mz, input.slot_1, input.slot_2, input.slot_3, input.slot_4, input.fire_held]
# Smooth position interpolation between ticks # Smooth position interpolation between ticks
var f := NetworkTime.tick_factor var f := NetworkTime.tick_factor
global_position = _lerp_from_pos.lerp(_lerp_to_pos, f) global_position = _lerp_from_pos.lerp(_lerp_to_pos, f)
@@ -164,10 +164,6 @@ func _rollback_tick(delta: float, tick: int, _is_fresh: bool):
# We process weapon actions anyway — the server reconciles via rollback. # We process weapon actions anyway — the server reconciles via rollback.
# Only skip if the player is dead or frozen (checked below). # Only skip if the player is dead or frozen (checked below).
# Skip if player dead
if input and (input.slot_1 or input.slot_2 or input.slot_3 or input.slot_4):
print("[WEAPON DEBUG] _rollback_tick tick=%d slots=[s1=%s s2=%s s3=%s s4=%s] active=%d inv=%s" % [tick, input.slot_1, input.slot_2, input.slot_3, input.slot_4, active_slot, inventory])
# Skip if player dead # Skip if player dead
if player.death_tick >= 0 and tick >= player.death_tick: if player.death_tick >= 0 and tick >= player.death_tick:
return return
@@ -413,8 +409,3 @@ func _spawn_grenade(origin: Vector3, direction: Vector3, owner_id: int, type_idx
get_tree().current_scene.add_child(grenade) get_tree().current_scene.add_child(grenade)
var gtype: Grenade.GrenadeType = Grenade.GrenadeType.FLASH if type_idx == 0 else Grenade.GrenadeType.SMOKE var gtype: Grenade.GrenadeType = Grenade.GrenadeType.FLASH if type_idx == 0 else Grenade.GrenadeType.SMOKE
grenade.setup(origin, direction, owner_id, gtype) grenade.setup(origin, direction, owner_id, gtype)
func _rollback_tick(delta: float, tick: int, _is_fresh: bool):
# DEBUG: Log when this is called with non-default input
if input and (input.slot_1 or input.slot_2 or input.slot_3 or input.slot_4):
print("[WEAPON DEBUG] _rollback_tick tick=%d slots=[%s %s %s %s]" % [tick, input.slot_1, input.slot_2, input.slot_3, input.slot_4])
+14 -4
View File
@@ -72,24 +72,34 @@ func join():
print("Client started") print("Client started")
connect_ui.hide() connect_ui.hide()
# Ensure game window has keyboard focus for input # Force window focus for keyboard input (Windows GUI apps need this)
get_window().grab_focus() _ensure_input_focus()
# Only start manually if NetworkEvents is not handling it (e.g. multiplayer-simple) # Only start manually if NetworkEvents is not handling it (e.g. multiplayer-simple)
if not NetworkEvents.enabled: if not NetworkEvents.enabled:
NetworkTime.start() NetworkTime.start()
func _ensure_input_focus() -> void:
## Force the game window to have keyboard focus and captured mouse.
## On Windows GUI exports, the window may not hold keyboard focus after
## the connection UI is dismissed, causing Input.get_axis() to return 0.
OS.move_to_foreground()
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
# Deferred retry after window settles
get_window().call_deferred("grab_focus")
Input.call_deferred("set_mouse_mode", Input.MOUSE_MODE_CAPTURED)
func _enter_tree(): func _enter_tree():
# Hide and show UI as appropriate # Hide and show UI as appropriate
# These handlers are necessary, since the game could have started via # These handlers are necessary, since the game could have started via
# autoconnect, or any other method # autoconnect, or any other method
NetworkEvents.on_client_start.connect(func(__): NetworkEvents.on_client_start.connect(func(__):
connect_ui.hide() connect_ui.hide()
get_window().grab_focus() _ensure_input_focus()
) )
NetworkEvents.on_server_start.connect(func(): NetworkEvents.on_server_start.connect(func():
connect_ui.hide() connect_ui.hide()
get_window().grab_focus() _ensure_input_focus()
) )
NetworkEvents.on_client_stop.connect(func(): connect_ui.show()) NetworkEvents.on_client_stop.connect(func(): connect_ui.show())
NetworkEvents.on_server_stop.connect(func(): connect_ui.show()) NetworkEvents.on_server_stop.connect(func(): connect_ui.show())
-1
View File
@@ -1 +0,0 @@
uid://4mlr8ca8nlg7
-1
View File
@@ -1 +0,0 @@
uid://h5lroui7prfv