Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c5b6b05801 | |||
| ef4e4ccb80 | |||
| 7db8434f7e | |||
| 7bbe4ba71d | |||
| 1ccd26bbab |
@@ -75,12 +75,16 @@ func _input(event: InputEvent) -> void:
|
|||||||
|
|
||||||
func _gather():
|
func _gather():
|
||||||
if !is_setup:
|
if !is_setup:
|
||||||
|
print("[INPUT DEBUG] _gather first call - setting up")
|
||||||
setup()
|
setup()
|
||||||
|
|
||||||
# Movement (continuous)
|
# Movement (continuous)
|
||||||
var mx = Input.get_axis("move_west", "move_east")
|
var mx = Input.get_axis("move_west", "move_east")
|
||||||
var mz = Input.get_axis("move_north", "move_south")
|
var mz = Input.get_axis("move_north", "move_south")
|
||||||
|
var old_mv = movement
|
||||||
movement = Vector3(mx, 0, mz)
|
movement = Vector3(mx, 0, mz)
|
||||||
|
if movement != old_mv and (mx != 0 or mz != 0):
|
||||||
|
print("[INPUT DEBUG] _gather movement=%s (mx=%.2f mz=%.2f)" % [movement, mx, mz])
|
||||||
|
|
||||||
jump = Input.is_action_pressed("move_jump")
|
jump = Input.is_action_pressed("move_jump")
|
||||||
|
|
||||||
|
|||||||
@@ -173,6 +173,15 @@ func _spawn(id: int):
|
|||||||
if rollback_sync and rollback_sync.has_method("process_settings"):
|
if rollback_sync and rollback_sync.has_method("process_settings"):
|
||||||
rollback_sync.process_settings()
|
rollback_sync.process_settings()
|
||||||
|
|
||||||
|
# On the client side, initialize weapon loadout locally so the player
|
||||||
|
# has weapons immediately. The server will reconcile via rollback state sync.
|
||||||
|
if not multiplayer.is_server():
|
||||||
|
var weapon_mgr = avatar.find_child("WeaponManager")
|
||||||
|
if weapon_mgr and weapon_mgr.has_method("set_default_loadout"):
|
||||||
|
# Default to T team — server will correct via state sync
|
||||||
|
weapon_mgr.set_default_loadout(1) # TeamManager.Team.T = 1
|
||||||
|
print("[Spawner] Client-side weapon loadout initialized for peer %s" % id)
|
||||||
|
|
||||||
func get_team_spawn_point(peer_id: int, spawn_idx: int = 0) -> Vector3:
|
func get_team_spawn_point(peer_id: int, spawn_idx: int = 0) -> Vector3:
|
||||||
var team := team_manager.get_team(peer_id)
|
var team := team_manager.get_team(peer_id)
|
||||||
var points: Array[Marker3D]
|
var points: Array[Marker3D]
|
||||||
|
|||||||
@@ -123,21 +123,30 @@ func _after_tick_loop():
|
|||||||
func _process(_delta: float):
|
func _process(_delta: float):
|
||||||
if not _is_local:
|
if not _is_local:
|
||||||
if input.is_multiplayer_authority():
|
if input.is_multiplayer_authority():
|
||||||
|
print("[PLAYER DEBUG] First _process — calling _setup_local_camera (authority: %d)" % multiplayer.get_unique_id())
|
||||||
_setup_local_camera()
|
_setup_local_camera()
|
||||||
else:
|
else:
|
||||||
|
print("[PLAYER DEBUG] _process skipping — input not authority (unique_id=%d, input_auth=%d)" % [multiplayer.get_unique_id(), input.get_multiplayer_authority()])
|
||||||
return
|
return
|
||||||
|
|
||||||
# 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)
|
||||||
|
|
||||||
# Frame-rate mouse look (recoil only affects raycast, not camera)
|
# Frame-rate mouse look (recoil only affects raycast, not camera)
|
||||||
|
var old_yaw = _sim_yaw
|
||||||
rotation.y = _sim_yaw + (-input.mouse_rotation.y)
|
rotation.y = _sim_yaw + (-input.mouse_rotation.y)
|
||||||
|
if _sim_yaw != old_yaw or input.mouse_rotation.length_squared() > 0:
|
||||||
|
pass # mouse look active
|
||||||
head.rotation.x = clamp(_sim_pitch + (-input.mouse_rotation.x), -1.57, 1.57)
|
head.rotation.x = clamp(_sim_pitch + (-input.mouse_rotation.x), -1.57, 1.57)
|
||||||
head.rotation.y = 0
|
head.rotation.y = 0
|
||||||
head.rotation.z = 0
|
head.rotation.z = 0
|
||||||
|
|
||||||
func _rollback_tick(delta: float, tick: int, is_fresh: bool) -> void:
|
func _rollback_tick(delta: float, tick: int, is_fresh: bool) -> void:
|
||||||
|
# DEBUG movement state
|
||||||
|
if input and input.movement.length_squared() > 0:
|
||||||
|
print("[PLAYER RT] tick=%d movement=%s hp=%d death_tick=%d frozen=%s" % [tick, input.movement, health, death_tick, round_manager and round_manager.freeze_end_tick >= 0 and tick < round_manager.freeze_end_tick])
|
||||||
|
|
||||||
# Handle round respawn teleport inside rollback
|
# Handle round respawn teleport inside rollback
|
||||||
if respawn_tick >= 0 and tick == respawn_tick:
|
if respawn_tick >= 0 and tick == respawn_tick:
|
||||||
global_position = respawn_position
|
global_position = respawn_position
|
||||||
|
|||||||
@@ -158,8 +158,11 @@ func give_weapon(weapon_id: int):
|
|||||||
NetworkRollback.mutate(self)
|
NetworkRollback.mutate(self)
|
||||||
|
|
||||||
func _rollback_tick(delta: float, tick: int, _is_fresh: bool):
|
func _rollback_tick(delta: float, tick: int, _is_fresh: bool):
|
||||||
if rollback_synchronizer.is_predicting():
|
# NOTE: On the client, the player CharacterBody3D is owned by the server
|
||||||
return
|
# (peer 1), so `rollback_synchronizer.is_predicting()` may return true
|
||||||
|
# on early ticks when input history hasn't been built up yet.
|
||||||
|
# We process weapon actions anyway — the server reconciles via rollback.
|
||||||
|
# Only skip if the player is dead or frozen (checked below).
|
||||||
|
|
||||||
# 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:
|
||||||
|
|||||||
@@ -72,16 +72,35 @@ func join():
|
|||||||
print("Client started")
|
print("Client started")
|
||||||
connect_ui.hide()
|
connect_ui.hide()
|
||||||
|
|
||||||
|
# Force window focus for keyboard input (Windows GUI apps need this)
|
||||||
|
_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(__): connect_ui.hide())
|
NetworkEvents.on_client_start.connect(func(__):
|
||||||
NetworkEvents.on_server_start.connect(func(): connect_ui.hide())
|
connect_ui.hide()
|
||||||
|
_ensure_input_focus()
|
||||||
|
)
|
||||||
|
NetworkEvents.on_server_start.connect(func():
|
||||||
|
connect_ui.hide()
|
||||||
|
_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,20 +0,0 @@
|
|||||||
#!/usr/bin/env -S godot --headless --script
|
|
||||||
extends SceneTree
|
|
||||||
|
|
||||||
func _init():
|
|
||||||
var paths = [
|
|
||||||
"res://assets/kenney/models/blaster.glb",
|
|
||||||
"res://examples/multiplayer-fps/models/knife.fbx",
|
|
||||||
"res://examples/multiplayer-fps/models/grenade.glb",
|
|
||||||
]
|
|
||||||
for p in paths:
|
|
||||||
var scene = load(p)
|
|
||||||
if scene:
|
|
||||||
var inst = scene.instantiate()
|
|
||||||
print(p, " -> type: ", inst.get_class(), " children: ", inst.get_child_count())
|
|
||||||
for c in inst.get_children():
|
|
||||||
print(" child: ", c.get_class(), " ", c.name)
|
|
||||||
inst.queue_free()
|
|
||||||
else:
|
|
||||||
print(p, " -> FAILED TO LOAD")
|
|
||||||
quit()
|
|
||||||
Reference in New Issue
Block a user