WIP Low level network handling
This commit is contained in:
@ -20,6 +20,9 @@ config/icon="res://icon.svg"
|
||||
OrbitalMechanics="*res://scripts/singletons/orbital_mechanics.gd"
|
||||
GameManager="*res://scripts/singletons/game_manager.gd"
|
||||
Constants="*res://scripts/singletons/constants.gd"
|
||||
ClientNetworkGlobals="*res://scripts/network/client_network_globals.gd"
|
||||
LowLevelNetworkHandler="*res://scripts/network/low_level_network_handler.gd"
|
||||
ServerNetworkGlobals="*res://scripts/network/server_network_globals.gd"
|
||||
|
||||
[dotnet]
|
||||
|
||||
|
||||
@ -76,4 +76,4 @@ func _generate_brachistochrone_plan(rendezvous_point: DataTypes.PathPoint) -> Ar
|
||||
# The key difference is that all calculations are now based on a confirmed possible intercept.
|
||||
var plan: Array[DataTypes.ImpulsiveBurnPlan] = []
|
||||
# ... logic to build the plan ...
|
||||
return plan
|
||||
return plan
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
extends CharacterBody3D
|
||||
class_name CharacterPawn3D
|
||||
|
||||
var owner_id: int
|
||||
|
||||
## Core Parameters
|
||||
@export var collision_energy_loss: float = 0.3
|
||||
@export var base_inertia: float = 1.0 # Pawn's inertia without suit
|
||||
@ -17,6 +19,7 @@ var _pitch_yaw_input: Vector2 = Vector2.ZERO
|
||||
|
||||
## Rotation Variables
|
||||
@onready var camera_pivot: Node3D = $CameraPivot
|
||||
@onready var camera: Camera3D = $CameraPivot/SpringArm/Camera3D
|
||||
@export_range(0.1, PI / 2.0) var max_yaw_rad: float = deg_to_rad(80.0)
|
||||
@export_range(-PI / 2.0 + 0.01, 0) var min_pitch_rad: float = deg_to_rad(-75.0)
|
||||
@export_range(0, PI / 2.0 - 0.01) var max_pitch_rad: float = deg_to_rad(60.0)
|
||||
|
||||
@ -2,8 +2,12 @@
|
||||
extends Node
|
||||
class_name PlayerController3D
|
||||
|
||||
var is_authority: bool:
|
||||
get: return !LowLevelNetworkHandler.is_server && owner_id == ClientNetworkGlobals.id
|
||||
|
||||
var owner_id: int = -1
|
||||
|
||||
@onready var possessed_pawn: CharacterPawn3D
|
||||
var player_id: int = -1
|
||||
|
||||
# --- Mouse Sensitivity ---
|
||||
@export var mouse_sensitivity: float = 0.002 # Radians per pixel motion
|
||||
@ -19,6 +23,9 @@ class KeyInput:
|
||||
released = _r
|
||||
|
||||
func _unhandled_input(event: InputEvent):
|
||||
if not is_multiplayer_authority() or not is_instance_valid(possessed_pawn):
|
||||
return
|
||||
|
||||
# Handle mouse motion input directly here
|
||||
if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
|
||||
# Calculate yaw and pitch based on mouse movement
|
||||
@ -28,13 +35,12 @@ func _unhandled_input(event: InputEvent):
|
||||
var sensitivity_modified_mouse_input = Vector2(yaw_input, pitch_input) * mouse_sensitivity
|
||||
|
||||
# Send rotation input via RPC immediately
|
||||
server_process_rotation_input.rpc_id(multiplayer.multiplayer_peer.get_unique_id(), sensitivity_modified_mouse_input)
|
||||
server_process_rotation_input.rpc_id(multiplayer.get_unique_id(), sensitivity_modified_mouse_input)
|
||||
|
||||
func _physics_process(_delta):
|
||||
if not is_multiplayer_authority() or not is_instance_valid(possessed_pawn):
|
||||
return
|
||||
|
||||
# 1. Gather Input
|
||||
var move_vec = Input.get_vector("move_left_3d", "move_right_3d", "move_backward_3d", "move_forward_3d")
|
||||
var roll_input = Input.get_action_strength("roll_right_3d") - Input.get_action_strength("roll_left_3d")
|
||||
var vertical_input = Input.get_action_strength("move_up_3d") - Input.get_action_strength("move_down_3d")
|
||||
@ -43,9 +49,9 @@ func _physics_process(_delta):
|
||||
var l_input = KeyInput.new(Input.is_action_just_pressed("left_click"), Input.is_action_pressed("left_click"), Input.is_action_just_released("left_click"))
|
||||
var r_input = KeyInput.new(Input.is_action_just_pressed("right_click"), Input.is_action_pressed("right_click"), Input.is_action_just_released("right_click"))
|
||||
|
||||
server_process_movement_input.rpc_id(multiplayer.multiplayer_peer.get_unique_id(), move_vec, roll_input, vertical_input)
|
||||
server_process_interaction_input.rpc_id(multiplayer.multiplayer_peer.get_unique_id(), interact_input)
|
||||
server_process_clicks.rpc_id(multiplayer.multiplayer_peer.get_unique_id(), l_input, r_input)
|
||||
server_process_movement_input.rpc_id(multiplayer.get_unique_id(), move_vec, roll_input, vertical_input)
|
||||
server_process_interaction_input.rpc_id(multiplayer.get_unique_id(), interact_input)
|
||||
server_process_clicks.rpc_id(multiplayer.get_unique_id(), l_input, r_input)
|
||||
|
||||
@rpc("any_peer", "call_local")
|
||||
func server_process_movement_input(move: Vector2, roll: float, vertical: float):
|
||||
@ -69,7 +75,8 @@ func server_process_clicks(l_action: KeyInput, r_action: KeyInput):
|
||||
|
||||
func possess(pawn_to_control: CharacterPawn3D):
|
||||
possessed_pawn = pawn_to_control
|
||||
print("PlayerController3D possessed: ", possessed_pawn.name)
|
||||
possessed_pawn.camera.make_current()
|
||||
print("PlayerController3D possessed: ", possessed_pawn.name)
|
||||
|
||||
# Optional: Release mouse when losing focus
|
||||
func _notification(what):
|
||||
|
||||
40
scripts/network/client_network_globals.gd
Normal file
40
scripts/network/client_network_globals.gd
Normal file
@ -0,0 +1,40 @@
|
||||
extends Node
|
||||
|
||||
signal handle_local_id_assignment(local_id: int)
|
||||
signal handle_remote_id_assignment(remote_id: int)
|
||||
signal handle_player_position(player_position: PlayerPosition)
|
||||
|
||||
var id: int = -1
|
||||
var remote_ids: Array[int]
|
||||
|
||||
func _ready() -> void:
|
||||
LowLevelNetworkHandler.on_client_packet.connect(on_client_packet)
|
||||
|
||||
|
||||
func on_client_packet(data: PackedByteArray) -> void:
|
||||
var packet_type: int = data.decode_u8(0)
|
||||
match packet_type:
|
||||
PacketInfo.PACKET_TYPE.ID_ASSIGNMENT:
|
||||
manage_ids(IDAssignment.create_from_data(data))
|
||||
|
||||
PacketInfo.PACKET_TYPE.PLAYER_POSITION:
|
||||
handle_player_position.emit(PlayerPosition.create_from_data(data))
|
||||
|
||||
_:
|
||||
push_error("Packet type with index ", data[0], " unhandled!")
|
||||
|
||||
|
||||
func manage_ids(id_assignment: IDAssignment) -> void:
|
||||
# TODO: This will cause issues if two clients believe they are the target for the ID assignment
|
||||
if id == -1: # When id == -1, the id sent by the server is for us
|
||||
id = id_assignment.id
|
||||
handle_local_id_assignment.emit(id_assignment.id)
|
||||
|
||||
remote_ids = id_assignment.remote_ids
|
||||
for remote_id in remote_ids:
|
||||
if remote_id == id: continue
|
||||
handle_remote_id_assignment.emit(remote_id)
|
||||
|
||||
else: # When id != -1, we already own an id, and just append the remote ids by the sent id
|
||||
remote_ids.append(id_assignment.id)
|
||||
handle_remote_id_assignment.emit(id_assignment.id)
|
||||
1
scripts/network/client_network_globals.gd.uid
Normal file
1
scripts/network/client_network_globals.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://cja10oadwmjen
|
||||
122
scripts/network/low_level_network_handler.gd
Normal file
122
scripts/network/low_level_network_handler.gd
Normal file
@ -0,0 +1,122 @@
|
||||
extends Node
|
||||
|
||||
# Server signals
|
||||
signal on_peer_connected(peer_id: int)
|
||||
signal on_peer_disconnected(peer_id: int)
|
||||
signal on_server_packet(peer_id: int, data: PackedByteArray)
|
||||
|
||||
# Client signals
|
||||
signal on_connected_to_server()
|
||||
signal on_disconnected_from_server()
|
||||
signal on_client_packet(data: PackedByteArray)
|
||||
|
||||
# Server variables
|
||||
var available_peer_ids: Array = range(255, -1, -1)
|
||||
var client_peers: Dictionary[int, ENetPacketPeer] # key: peer_id (int), value: peer (ENetPacketPeer). The peer_id should hold "id" meta data. Use get_meta("id")
|
||||
|
||||
# Client variables
|
||||
var server_peer: ENetPacketPeer
|
||||
|
||||
# General variables
|
||||
var connection: ENetConnection
|
||||
var is_server: bool = false
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if connection == null: return
|
||||
|
||||
handle_events()
|
||||
|
||||
|
||||
func handle_events() -> void:
|
||||
var packet_event: Array = connection.service()
|
||||
var event_type: ENetConnection.EventType = packet_event[0]
|
||||
|
||||
while event_type != ENetConnection.EVENT_NONE:
|
||||
var peer: ENetPacketPeer = packet_event[1]
|
||||
|
||||
match (event_type):
|
||||
ENetConnection.EVENT_ERROR:
|
||||
push_warning("Package resulted in an unknown error!")
|
||||
return
|
||||
|
||||
ENetConnection.EVENT_CONNECT:
|
||||
if is_server:
|
||||
peer_connected(peer)
|
||||
else:
|
||||
connected_to_server()
|
||||
|
||||
ENetConnection.EVENT_DISCONNECT:
|
||||
if is_server:
|
||||
peer_disconnected(peer)
|
||||
else:
|
||||
disconnected_from_server()
|
||||
return # Return because connection was set to null
|
||||
|
||||
ENetConnection.EVENT_RECEIVE:
|
||||
if is_server:
|
||||
on_server_packet.emit(peer.get_meta("id"), peer.get_packet())
|
||||
else:
|
||||
on_client_packet.emit(peer.get_packet())
|
||||
|
||||
# Call service() again to handle remaining packets in current while loop
|
||||
packet_event = connection.service()
|
||||
event_type = packet_event[0]
|
||||
|
||||
|
||||
func start_server(ip_address: String = "127.0.0.1", port: int = 42069) -> void:
|
||||
connection = ENetConnection.new()
|
||||
var error: Error = connection.create_host_bound(ip_address, port)
|
||||
if error:
|
||||
print("Server starting failed: ", error_string(error))
|
||||
connection = null
|
||||
return
|
||||
|
||||
print("Server started")
|
||||
is_server = true
|
||||
|
||||
|
||||
func peer_connected(peer: ENetPacketPeer) -> void:
|
||||
var peer_id: int = available_peer_ids.pop_back()
|
||||
peer.set_meta("id", peer_id)
|
||||
client_peers[peer_id] = peer
|
||||
|
||||
print("Peer connected with assigned id: ", peer_id)
|
||||
on_peer_connected.emit(peer_id)
|
||||
|
||||
|
||||
func peer_disconnected(peer: ENetPacketPeer) -> void:
|
||||
var peer_id: int = peer.get_meta("id")
|
||||
available_peer_ids.push_back(peer_id)
|
||||
client_peers.erase(peer_id)
|
||||
|
||||
print("Succesfully disconnected: ", peer_id, " from server!")
|
||||
on_peer_disconnected.emit(peer_id)
|
||||
|
||||
|
||||
func start_client(ip_address: String = "127.0.0.1", port: int = 42069) -> void:
|
||||
connection = ENetConnection.new()
|
||||
var error: Error = connection.create_host(1)
|
||||
if error:
|
||||
print("Client starting failed: ", error_string(error))
|
||||
connection = null
|
||||
return
|
||||
|
||||
print("Client started")
|
||||
server_peer = connection.connect_to_host(ip_address, port)
|
||||
|
||||
|
||||
func disconnect_client() -> void:
|
||||
if is_server: return
|
||||
|
||||
server_peer.peer_disconnect()
|
||||
|
||||
|
||||
func connected_to_server() -> void:
|
||||
print("Successfully connected to server!")
|
||||
on_connected_to_server.emit()
|
||||
|
||||
|
||||
func disconnected_from_server() -> void:
|
||||
print("Successfully disconnected from server!")
|
||||
on_disconnected_from_server.emit()
|
||||
connection = null
|
||||
1
scripts/network/low_level_network_handler.gd.uid
Normal file
1
scripts/network/low_level_network_handler.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://yhu84qgcru8e
|
||||
41
scripts/network/low_level_network_player.gd
Normal file
41
scripts/network/low_level_network_player.gd
Normal file
@ -0,0 +1,41 @@
|
||||
extends CharacterBody2D
|
||||
|
||||
const SPEED: float = 500.0
|
||||
|
||||
var is_authority: bool:
|
||||
get: return !LowLevelNetworkHandler.is_server && owner_id == ClientNetworkGlobals.id
|
||||
|
||||
var owner_id: int
|
||||
|
||||
func _enter_tree() -> void:
|
||||
ServerNetworkGlobals.handle_player_position.connect(server_handle_player_position)
|
||||
ClientNetworkGlobals.handle_player_position.connect(client_handle_player_position)
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
ServerNetworkGlobals.handle_player_position.disconnect(server_handle_player_position)
|
||||
ClientNetworkGlobals.handle_player_position.disconnect(client_handle_player_position)
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if !is_authority: return
|
||||
|
||||
velocity = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") * SPEED
|
||||
|
||||
move_and_slide()
|
||||
|
||||
PlayerPosition.create(owner_id, global_position).send(LowLevelNetworkHandler.server_peer)
|
||||
|
||||
|
||||
func server_handle_player_position(peer_id: int, player_position: PlayerPosition) -> void:
|
||||
if owner_id != peer_id: return
|
||||
|
||||
global_position = player_position.position
|
||||
|
||||
PlayerPosition.create(owner_id, global_position).broadcast(LowLevelNetworkHandler.connection)
|
||||
|
||||
|
||||
func client_handle_player_position(player_position: PlayerPosition) -> void:
|
||||
if is_authority || owner_id != player_position.id: return
|
||||
|
||||
global_position = player_position.position
|
||||
1
scripts/network/low_level_network_player.gd.uid
Normal file
1
scripts/network/low_level_network_player.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://b0ye6wj30inr6
|
||||
17
scripts/network/low_level_player_spawner.gd
Normal file
17
scripts/network/low_level_player_spawner.gd
Normal file
@ -0,0 +1,17 @@
|
||||
extends Node
|
||||
|
||||
# const LOW_LEVEL_NETWORK_PLAYER = preload("res://low_level_example/scenes/low_level_network_player.tscn")
|
||||
|
||||
func _ready() -> void:
|
||||
LowLevelNetworkHandler.on_peer_connected.connect(spawn_player)
|
||||
ClientNetworkGlobals.handle_local_id_assignment.connect(spawn_player)
|
||||
ClientNetworkGlobals.handle_remote_id_assignment.connect(spawn_player)
|
||||
|
||||
|
||||
func spawn_player(id: int) -> void:
|
||||
pass
|
||||
# var player = LOW_LEVEL_NETWORK_PLAYER.instantiate()
|
||||
# player.owner_id = id
|
||||
# player.name = str(id) # Optional, but it beats the name "@CharacterBody2D@2/3/4..."
|
||||
|
||||
# call_deferred("add_child", player)
|
||||
1
scripts/network/low_level_player_spawner.gd.uid
Normal file
1
scripts/network/low_level_player_spawner.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://dxf8dqkg3c4ph
|
||||
35
scripts/network/packet_info/id_assignment.gd
Normal file
35
scripts/network/packet_info/id_assignment.gd
Normal file
@ -0,0 +1,35 @@
|
||||
class_name IDAssignment extends PacketInfo
|
||||
|
||||
var id: int
|
||||
var remote_ids: Array[int]
|
||||
|
||||
static func create(id: int, remote_ids: Array[int]) -> IDAssignment:
|
||||
var info: IDAssignment = IDAssignment.new()
|
||||
info.packet_type = PACKET_TYPE.ID_ASSIGNMENT
|
||||
info.flag = ENetPacketPeer.FLAG_RELIABLE
|
||||
info.id = id
|
||||
info.remote_ids = remote_ids
|
||||
return info
|
||||
|
||||
|
||||
static func create_from_data(data: PackedByteArray) -> IDAssignment:
|
||||
var info: IDAssignment = IDAssignment.new()
|
||||
info.decode(data)
|
||||
return info
|
||||
|
||||
|
||||
func encode() -> PackedByteArray:
|
||||
var data: PackedByteArray = super.encode()
|
||||
data.resize(2 + remote_ids.size())
|
||||
data.encode_u8(1, id)
|
||||
for i in remote_ids.size():
|
||||
var id: int = remote_ids[i]
|
||||
data.encode_u8(2 + i, id)
|
||||
return data
|
||||
|
||||
|
||||
func decode(data: PackedByteArray) -> void:
|
||||
super.decode(data)
|
||||
id = data.decode_u8(1)
|
||||
for i in range(2, data.size()):
|
||||
remote_ids.append(data.decode_u8(i))
|
||||
1
scripts/network/packet_info/id_assignment.gd.uid
Normal file
1
scripts/network/packet_info/id_assignment.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://bingu8h4ddect
|
||||
32
scripts/network/packet_info/packet_info.gd
Normal file
32
scripts/network/packet_info/packet_info.gd
Normal file
@ -0,0 +1,32 @@
|
||||
## BASECLASS ##
|
||||
|
||||
class_name PacketInfo
|
||||
|
||||
# Don't make values above 255, since we send "packet_type" as a single byte
|
||||
enum PACKET_TYPE {
|
||||
ID_ASSIGNMENT = 0,
|
||||
PLAYER_POSITION = 10,
|
||||
}
|
||||
|
||||
var packet_type: PACKET_TYPE
|
||||
var flag: int
|
||||
|
||||
# Override function in derived classes
|
||||
func encode() -> PackedByteArray:
|
||||
var data: PackedByteArray
|
||||
data.resize(1)
|
||||
data.encode_u8(0, packet_type)
|
||||
return data
|
||||
|
||||
|
||||
# Override function in derived classes
|
||||
func decode(data: PackedByteArray) -> void:
|
||||
packet_type = data.decode_u8(0)
|
||||
|
||||
|
||||
func send(target: ENetPacketPeer) -> void:
|
||||
target.send(0, encode(), flag)
|
||||
|
||||
|
||||
func broadcast(server: ENetConnection) -> void:
|
||||
server.broadcast(0, encode(), flag)
|
||||
1
scripts/network/packet_info/packet_info.gd.uid
Normal file
1
scripts/network/packet_info/packet_info.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://bjdyqwbttme7c
|
||||
33
scripts/network/packet_info/player_position.gd
Normal file
33
scripts/network/packet_info/player_position.gd
Normal file
@ -0,0 +1,33 @@
|
||||
class_name PlayerPosition extends PacketInfo
|
||||
|
||||
var id: int
|
||||
var position: Vector2
|
||||
|
||||
static func create(id: int, position: Vector2) -> PlayerPosition:
|
||||
var info: PlayerPosition = PlayerPosition.new()
|
||||
info.packet_type = PACKET_TYPE.PLAYER_POSITION
|
||||
info.flag = ENetPacketPeer.FLAG_UNSEQUENCED
|
||||
info.id = id
|
||||
info.position = position
|
||||
return info
|
||||
|
||||
|
||||
static func create_from_data(data: PackedByteArray) -> PlayerPosition:
|
||||
var info: PlayerPosition = PlayerPosition.new()
|
||||
info.decode(data)
|
||||
return info
|
||||
|
||||
|
||||
func encode() -> PackedByteArray:
|
||||
var data: PackedByteArray = super.encode()
|
||||
data.resize(10)
|
||||
data.encode_u8(1, id)
|
||||
data.encode_float(2, position.x)
|
||||
data.encode_float(6, position.y)
|
||||
return data
|
||||
|
||||
|
||||
func decode(data: PackedByteArray) -> void:
|
||||
super.decode(data)
|
||||
id = data.decode_u8(1)
|
||||
position = Vector2(data.decode_float(2), data.decode_float(6))
|
||||
1
scripts/network/packet_info/player_position.gd.uid
Normal file
1
scripts/network/packet_info/player_position.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://c87eugk8d8d6w
|
||||
30
scripts/network/server_network_globals.gd
Normal file
30
scripts/network/server_network_globals.gd
Normal file
@ -0,0 +1,30 @@
|
||||
extends Node
|
||||
|
||||
signal handle_player_position(peer_id: int, player_position: PlayerPosition)
|
||||
|
||||
var peer_ids: Array[int]
|
||||
|
||||
func _ready() -> void:
|
||||
LowLevelNetworkHandler.on_peer_connected.connect(on_peer_connected)
|
||||
LowLevelNetworkHandler.on_peer_disconnected.connect(on_peer_disconnected)
|
||||
LowLevelNetworkHandler.on_server_packet.connect(on_server_packet)
|
||||
|
||||
|
||||
func on_peer_connected(peer_id: int) -> void:
|
||||
peer_ids.append(peer_id)
|
||||
|
||||
IDAssignment.create(peer_id, peer_ids).broadcast(LowLevelNetworkHandler.connection)
|
||||
|
||||
|
||||
func on_peer_disconnected(peer_id: int) -> void:
|
||||
peer_ids.erase(peer_id)
|
||||
|
||||
# Create IDUnassignment to broadcast to all still connected peers
|
||||
|
||||
|
||||
func on_server_packet(peer_id: int, data: PackedByteArray) -> void:
|
||||
match data[0]:
|
||||
PacketInfo.PACKET_TYPE.PLAYER_POSITION:
|
||||
handle_player_position.emit(peer_id, PlayerPosition.create_from_data(data))
|
||||
_:
|
||||
push_error("Packet type with index ", data[0], " unhandled!")
|
||||
1
scripts/network/server_network_globals.gd.uid
Normal file
1
scripts/network/server_network_globals.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://bo2hh6e60ikth
|
||||
@ -28,32 +28,24 @@ func _ready():
|
||||
|
||||
# Check command-line arguments to determine if this instance should be a server or client.
|
||||
# Godot's "Run Multiple Instances" feature adds "--server" to the main instance.
|
||||
|
||||
|
||||
|
||||
if "--server" in OS.get_cmdline_args():
|
||||
# This instance is the server.
|
||||
var peer = ENetMultiplayerPeer.new()
|
||||
var error = peer.create_server(5999)
|
||||
if error != OK:
|
||||
printerr("Failed to create ENet server. Error code: ", error)
|
||||
return
|
||||
multiplayer.multiplayer_peer = peer
|
||||
print("GameManager: ENet server created on port 5999.")
|
||||
# The server's own connection is handled by the peer_connected signal for ID 1.
|
||||
LowLevelNetworkHandler.start_server()
|
||||
else:
|
||||
# This instance is a client.
|
||||
var peer = ENetMultiplayerPeer.new()
|
||||
# The address "localhost" is used for local testing.
|
||||
peer.create_client("localhost", 5999)
|
||||
multiplayer.multiplayer_peer = peer
|
||||
print("GameManager: ENet client created, attempting to connect to localhost:5999.")
|
||||
|
||||
# Connect signals for all peers (server and clients) to handle new players.
|
||||
multiplayer.peer_connected.connect(on_player_connected)
|
||||
|
||||
LowLevelNetworkHandler.on_peer_connected.connect(on_player_connected)
|
||||
ClientNetworkGlobals.handle_local_id_assignment.connect(on_player_connected)
|
||||
ClientNetworkGlobals.handle_remote_id_assignment.connect(on_player_connected)
|
||||
LowLevelNetworkHandler.start_client()
|
||||
|
||||
# LowLevelNetworkHandler.on_peer_connected.connect(on_player_connected)
|
||||
# LowLevelNetworkHandler.on_peer_disconnected.connect(on_player_disconnected)
|
||||
|
||||
# If this is a client, we create a proxy node that allows us to easily
|
||||
# call RPCs on the server's GameManager (which always has node path /root/GameManager).
|
||||
if not multiplayer.is_server():
|
||||
multiplayer.get_remote_sender_id() # This is a placeholder to illustrate client-side setup
|
||||
# if not multiplayer.is_server():
|
||||
# multiplayer.get_remote_sender_id() # This is a placeholder to illustrate client-side setup
|
||||
|
||||
# Called when the game starts (e.g., from _ready() in StarSystemGenerator)
|
||||
func start_game():
|
||||
@ -62,8 +54,8 @@ func start_game():
|
||||
# on_player_connected(1)
|
||||
|
||||
# This would be connected to a network signal in a multiplayer game.
|
||||
func on_player_connected(player_id: int):
|
||||
print("GameManager: Player %d connected." % player_id)
|
||||
func on_player_connected(id: int):
|
||||
print("GameManager: Player %d connected." % id)
|
||||
|
||||
# The server is responsible for all spawning logic.
|
||||
if not multiplayer.is_server():
|
||||
@ -72,24 +64,28 @@ func on_player_connected(player_id: int):
|
||||
# This function runs on the SERVER when a client connects.
|
||||
# 1. Spawn a controller for the new player on the server.
|
||||
var controller: PlayerController3D = player_controller_3d_scene.instantiate()
|
||||
controller.player_id = player_id
|
||||
controller.name = "PlayerController_%d" % player_id
|
||||
controller.owner_id = id
|
||||
controller.name = "PlayerController_%d" % id
|
||||
# Set the authority for this controller to the player who just connected.
|
||||
controller.set_multiplayer_authority(player_id)
|
||||
controller.set_multiplayer_authority(id)
|
||||
add_child(controller)
|
||||
player_controllers[player_id] = controller
|
||||
player_controllers[id] = controller
|
||||
|
||||
# 2. Tell the newly connected client which controller is theirs.
|
||||
# This RPC will only be executed on the machine of the player with 'player_id'.
|
||||
client_set_controller.rpc_id(player_id, controller.get_path())
|
||||
# This RPC will only be executed on the machine of the player with 'id'.
|
||||
# client_set_controller.rpc_id(id, controller.get_path())
|
||||
|
||||
# 3. Attempt to spawn a pawn for them immediately.
|
||||
_attempt_to_spawn_player(player_id)
|
||||
_attempt_to_spawn_player(id)
|
||||
|
||||
func on_player_disconnected(player_id: int):
|
||||
print("GameManager: Player %d disconnected." % player_id)
|
||||
|
||||
@rpc("call_local")
|
||||
func client_set_controller(controller_path: NodePath):
|
||||
# This function runs on the CLIENT. It finds the controller node that the
|
||||
# server created for it and can be used for client-side setup if needed.
|
||||
|
||||
print("Client received controller path: ", controller_path)
|
||||
|
||||
func _attempt_to_spawn_player(player_id: int):
|
||||
@ -136,6 +132,7 @@ func server_spawn_player_pawn(player_id: int):
|
||||
pawn.name = "CharacterPawn_%d" % player_id
|
||||
get_tree().root.add_child(pawn)
|
||||
pawn.owner = get_tree().root
|
||||
pawn.set_multiplayer_authority(player_id)
|
||||
|
||||
# 2. Set its position and initial velocity from the spawner.
|
||||
pawn.global_position = spawn_point.global_position
|
||||
|
||||
Reference in New Issue
Block a user