From 37a311b0c5784813876d1f33318acc649e769ac7 Mon Sep 17 00:00:00 2001 From: architect-bot Date: Mon, 17 Aug 2026 18:15:38 +0000 Subject: [PATCH] WIP checkpoint: Implement Godot dedicated server scene + player spawning --- scenes/player.tscn | 2 +- scripts/player_spawner.gd | 13 ++++----- scripts/server.gd | 58 +++++++++++++++++++++++++-------------- 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/scenes/player.tscn b/scenes/player.tscn index 6c6f8be..0d35af8 100644 --- a/scenes/player.tscn +++ b/scenes/player.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3] -[ext_resource type="Script" path="res://scripts/player_node.gd" id="1"] +[ext_resource type="Script" path="res://scripts/player_spawner.gd" id="1"] [node name="Player" type="Node3D"] script = ExtResource("1") diff --git a/scripts/player_spawner.gd b/scripts/player_spawner.gd index a91c5f2..4316aa0 100644 --- a/scripts/player_spawner.gd +++ b/scripts/player_spawner.gd @@ -4,9 +4,6 @@ extends CharacterBody3D ## Represents a connected player on the server. Handles movement, ## input processing, and state synchronization. -class_name PlayerNode - - # --- Signals --- signal health_changed(new_health: int) signal player_died(player_node: PlayerNode) @@ -65,12 +62,14 @@ func _physics_process(delta: float) -> void: # Apply friction on ground if is_on_floor(): - var move_dir = velocity.xz + var move_dir := Vector2(velocity.x, velocity.z) if move_dir.length() > 0.01: - var friction_factor = clampf(FRICTION * delta, 0.0, 1.0) - velocity.xz = move_dir.normalized() * move_dir.length() * (1.0 - friction_factor) + var friction_factor := clampf(FRICTION * delta, 0.0, 1.0) + velocity.x = move_dir.normalized().x * move_dir.length() * (1.0 - friction_factor) + velocity.z = move_dir.normalized().y * move_dir.length() * (1.0 - friction_factor) else: - velocity.xz = Vector2.ZERO + velocity.x = 0.0 + velocity.z = 0.0 # Move and slide velocity.y = clampf(velocity.y, -max_fall_speed, max_fall_speed) diff --git a/scripts/server.gd b/scripts/server.gd index b105f98..9206b3e 100644 --- a/scripts/server.gd +++ b/scripts/server.gd @@ -3,6 +3,7 @@ ## and runs the simulation tick loop at 20 Hz. extends Node + ## Maximum number of concurrent players. const MAX_PLAYERS := 16 @@ -30,13 +31,18 @@ var is_running: bool = false ## Accumulated real time for fixed timestep simulation. var accumulator: float = 0.0 +## Player scene resource (loaded once). +var _player_scene: PackedScene + func _ready() -> void: - # Set this node as the multiplayer authority. - multiplayer.multiplayer_peer = _create_server_peer() - multiplayer.server = self + # Preload the player scene once. + _player_scene = preload("res://scenes/player.tscn") + # Create and assign the ENet server peer. + network_peer = _create_server_peer() if network_peer: + multiplayer.multiplayer_peer = network_peer is_running = true print("[Server] Started on port %d" % SERVER_PORT) else: @@ -44,23 +50,27 @@ func _ready() -> void: get_tree().quit(1) +# --------------------------------------------------------------------------- +# Server creation +# --------------------------------------------------------------------------- + func _create_server_peer() -> ENetMultiplayerPeer: var peer := ENetMultiplayerPeer.new() var error := peer.create_server(SERVER_PORT, MAX_PLAYERS) if error != OK: printerr("[Server] create_server error: %d" % error) - return ENetMultiplayerPeer() + return null return peer +# --------------------------------------------------------------------------- +# Fixed-timestep simulation loop +# --------------------------------------------------------------------------- + func _process(delta: float) -> void: if not is_running: return - # Handle disconnect events. - if multiplayer.peer_disconnected.is_connected(_on_peer_disconnected): - pass # already connected - # Fixed timestep accumulator. accumulator += delta while accumulator >= TICK_DURATION: @@ -74,7 +84,7 @@ func _tick() -> void: # Process player inputs and update state. for peer_id in players: - var player: Node = players[peer_id] + var player: Node3D = players[peer_id] if player and player.has_method("process_server_input"): player.process_server_input(tick) @@ -86,17 +96,19 @@ func _broadcast_snapshot() -> void: """Broadcast the current authoritative state to all connected clients.""" var snapshot_data := { "tick": tick, - "players": {} + "players": {}, } for peer_id in players: - var player: Node = players[peer_id] + var player: Node3D = players[peer_id] if player and player.has_method("get_server_state"): snapshot_data.players[peer_id] = player.get_server_state() - # Send snapshot to all peers except the server itself. + # Send snapshot to all peers except the server itself (peer_id == 1). var peer_ids: Array = multiplayer.get_peers() for pid in peer_ids: + if pid == 1: # 1 is the server itself + continue rpc_id(pid, "_receive_snapshot", snapshot_data) @@ -106,7 +118,9 @@ func _receive_snapshot(snapshot_data: Dictionary) -> void: pass # Handled by client scene. -# --- Connection lifecycle --- +# --------------------------------------------------------------------------- +# Connection lifecycle +# --------------------------------------------------------------------------- func _on_peer_connected(peer_id: int) -> void: print("[Server] Peer %d connected." % peer_id) @@ -117,7 +131,7 @@ func _on_peer_connected(peer_id: int) -> void: return # Spawn a Player node for this peer. - var player := preload("res://scripts/player.tscn").instantiate() + var player: Node3D = _player_scene.instantiate() player.name = "Player_%d" % peer_id player.peer_id = peer_id add_child(player) @@ -129,18 +143,20 @@ func _on_peer_connected(peer_id: int) -> void: func _on_peer_disconnected(peer_id: int) -> void: print("[Server] Peer %d disconnected." % peer_id) if players.has(peer_id): - var player: Node = players[peer_id] + var player: Node3D = players[peer_id] if player.is_inside_tree(): player.queue_free() players.erase(peer_id) -# --- RPCs --- +# --------------------------------------------------------------------------- +# RPCs +# --------------------------------------------------------------------------- @rpc("any_peer", "call_remote", "reliable") -func _kick(peer_id: int, reason: String) -> void: - print("[Server] Kicking peer %d: %s" % [peer_id, reason]) - rpc_id(peer_id, "_disconnect", reason) +func _kick(reason: String) -> void: + print("[Server] Kicking caller: %s" % reason) + rpc_id(get_rpc_sender_id(), "_disconnect", reason) @rpc("call_remote", "reliable") @@ -149,7 +165,9 @@ func _disconnect(reason: String) -> void: get_tree().quit(0) -# --- Signal connections --- +# --------------------------------------------------------------------------- +# Signal connections +# --------------------------------------------------------------------------- func _enter_tree() -> void: multiplayer.peer_connected.connect(_on_peer_connected)