d060ac449d
- Create missing server/plugins/ dir with .gdignore (stops PluginManager warning) - Create config/default_server_config.cfg (stops ServerConfig error) - Reorder light look_at after add_child (stops 'not in tree' warning) - Switch rendering_method from opengl3 to forward_plus (RTX 2080 Vulkan works fine; gray screen was server-scene issue not OpenGL)
125 lines
4.2 KiB
GDScript
125 lines
4.2 KiB
GDScript
## Client Main — Client entry point for testing.
|
|
##
|
|
## Connects to the server. Receives broadcast_spawn_player /
|
|
## broadcast_despawn_player RPCs from the server and creates
|
|
## visual player nodes locally so each client sees every other player.
|
|
##
|
|
## Phase 0: simple position replication via RPC + box mesh player nodes.
|
|
|
|
extends Node3D
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Exports
|
|
# ---------------------------------------------------------------------------
|
|
@export var server_host: String = "68.202.6.107"
|
|
@export var server_port: int = 34197
|
|
@export var player_scene: PackedScene = preload("res://scenes/player.tscn")
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# State
|
|
# ---------------------------------------------------------------------------
|
|
# remote_players[peer_id] = Node3D — visual representation of other players
|
|
var remote_players: Dictionary = {}
|
|
var connected: bool = false
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Lifecycle
|
|
# ---------------------------------------------------------------------------
|
|
func _ready() -> void:
|
|
# Connect to server after a short delay
|
|
await get_tree().create_timer(0.5).timeout
|
|
_connect_to_server()
|
|
|
|
func _connect_to_server() -> void:
|
|
if OS.has_environment("SERVER_HOST"):
|
|
server_host = OS.get_environment("SERVER_HOST")
|
|
if OS.has_environment("SERVER_PORT"):
|
|
server_port = int(OS.get_environment("SERVER_PORT"))
|
|
|
|
print("[ClientMain] Connecting to %s:%d ..." % [server_host, server_port])
|
|
|
|
var err: Error = NetworkManager.join_server(server_host, server_port)
|
|
if err != OK:
|
|
push_error("[ClientMain] Connection failed: %s" % error_string(err))
|
|
await get_tree().create_timer(2.0).timeout
|
|
_connect_to_server()
|
|
return
|
|
|
|
# Setup camera and lights
|
|
_setup_scene()
|
|
|
|
connected = true
|
|
|
|
# Connect replication signals from NetworkManager
|
|
# These fire when the server broadcasts spawn_player / despawn_player
|
|
NetworkManager.remote_player_spawned.connect(_on_remote_player_spawned)
|
|
NetworkManager.remote_player_despawned.connect(_on_remote_player_despawned)
|
|
|
|
print("[ClientMain] Connected to server. Peer ID: %d" % multiplayer.get_unique_id())
|
|
|
|
func _setup_scene() -> void:
|
|
# Simple 3/4 top-down camera for Phase 0 testing
|
|
var camera := Camera3D.new()
|
|
camera.current = true
|
|
camera.position = Vector3(0, 16, 12)
|
|
camera.rotation_degrees.x = -55
|
|
add_child(camera)
|
|
|
|
# Ambient light so we can see the box meshes
|
|
var light := DirectionalLight3D.new()
|
|
light.light_energy = 1.0
|
|
light.shadow_enabled = true
|
|
light.position = Vector3(10, 20, 10)
|
|
add_child(light)
|
|
light.look_at(Vector3.ZERO)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Player replication handlers (called when server broadcasts via RPC)
|
|
# ---------------------------------------------------------------------------
|
|
func _on_remote_player_spawned(peer_id: int, pos: Vector3) -> void:
|
|
if peer_id == multiplayer.get_unique_id():
|
|
# This is OUR player — the server has authority over it,
|
|
# but we don't need to create a duplicate. Our local player
|
|
# node is managed by the server's authority system.
|
|
# The player.gd script will handle input on this peer.
|
|
return
|
|
|
|
if peer_id in remote_players:
|
|
push_warning("[ClientMain] Remote player %d already exists, skipping" % peer_id)
|
|
return
|
|
|
|
# Create a remote player node for visualization
|
|
var player: Node3D
|
|
if player_scene:
|
|
player = player_scene.instantiate()
|
|
else:
|
|
player = Node3D.new()
|
|
player.set_script(preload("res://scripts/network/player.gd"))
|
|
|
|
player.name = "RemotePlayer_%d" % peer_id
|
|
player.set_multiplayer_authority(peer_id)
|
|
player.position = pos
|
|
|
|
add_child(player, true)
|
|
remote_players[peer_id] = player
|
|
|
|
print("[ClientMain] Spawned remote player %d at (%.1f, %.1f)" % [peer_id, pos.x, pos.z])
|
|
|
|
func _on_remote_player_despawned(peer_id: int) -> void:
|
|
if peer_id == multiplayer.get_unique_id():
|
|
return
|
|
|
|
if peer_id in remote_players:
|
|
remote_players[peer_id].queue_free()
|
|
remote_players.erase(peer_id)
|
|
print("[ClientMain] Despawned remote player %d" % peer_id)
|
|
|
|
func _exit_tree() -> void:
|
|
if connected:
|
|
NetworkManager.stop()
|
|
|
|
# Clean up any remaining remote players
|
|
for p in remote_players.values():
|
|
p.queue_free()
|
|
remote_players.clear()
|