Networked player pawns
This commit is contained in:
@ -20,9 +20,7 @@ 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"
|
||||
NetworkHandler="*res://scripts/network/network_handler.gd"
|
||||
|
||||
[dotnet]
|
||||
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
extends Node3D
|
||||
extends Area3D
|
||||
class_name Spawner
|
||||
|
||||
@onready var mp_spawner: MultiplayerSpawner = $MultiplayerSpawner
|
||||
|
||||
# This spawner will register itself with the GameManager when it enters the scene.
|
||||
func _ready():
|
||||
# super()
|
||||
@ -8,5 +10,7 @@ func _ready():
|
||||
await get_tree().process_frame
|
||||
GameManager.register_spawner(self)
|
||||
|
||||
func can_spawn() -> bool:
|
||||
return get_overlapping_bodies().is_empty()
|
||||
# We can add properties to the spawner later, like which faction it belongs to,
|
||||
# or a reference to the body it's orbiting for initial velocity calculation.
|
||||
|
||||
@ -1,7 +1,16 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://dvpy3urgtm62n"]
|
||||
[gd_scene load_steps=3 format=3 uid="uid://dvpy3urgtm62n"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://db1u2qqihhnq4" path="res://scenes/ship/components/hardware/spawner.gd" id="1_lldyu"]
|
||||
|
||||
[node name="Spawner" type="Node2D"]
|
||||
[sub_resource type="SphereShape3D" id="SphereShape3D_lldyu"]
|
||||
radius = 1.0
|
||||
|
||||
[node name="Spawner" type="Area3D"]
|
||||
script = ExtResource("1_lldyu")
|
||||
metadata/_custom_type_script = "uid://calosd13bkakg"
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
shape = SubResource("SphereShape3D_lldyu")
|
||||
|
||||
[node name="MultiplayerSpawner" type="MultiplayerSpawner" parent="."]
|
||||
_spawnable_scenes = PackedStringArray("uid://7yc6a07xoccy")
|
||||
spawn_path = NodePath("..")
|
||||
|
||||
@ -2,8 +2,6 @@
|
||||
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
|
||||
@ -54,6 +52,9 @@ func _ready():
|
||||
else:
|
||||
printerr("GripDetector Area3D node not found on CharacterPawn!")
|
||||
|
||||
if is_multiplayer_authority():
|
||||
camera.make_current()
|
||||
|
||||
|
||||
func _physics_process(delta: float):
|
||||
# 1. Apply Mouse Rotation (Universal head look)
|
||||
@ -149,3 +150,8 @@ func _on_ladder_area_exited(area: Area3D): if area == overlapping_ladder_area: o
|
||||
func _reset_head_yaw(delta: float):
|
||||
# Smoothly apply the reset target to the actual pivot rotation
|
||||
camera_pivot.rotation.y = lerpf(camera_pivot.rotation.y, 0.0, delta * head_turn_lerp_speed)
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
match what:
|
||||
NOTIFICATION_ENTER_TREE:
|
||||
set_multiplayer_authority(int(name))
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
[gd_scene load_steps=9 format=3 uid="uid://7yc6a07xoccy"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cdmmiixa75f3x" path="res://scenes/tests/3d/character_pawn_3d.gd" id="1_4frsu"]
|
||||
[ext_resource type="Script" uid="uid://vjfk3xnapfti" path="res://scenes/tests/3d/player_controller_3d.gd" id="2_r62el"]
|
||||
[ext_resource type="PackedScene" uid="uid://bm1rbv4tuppbc" path="res://eva_suit_controller.tscn" id="3_gnddn"]
|
||||
[ext_resource type="Script" uid="uid://y3vo40i16ek3" path="res://scenes/tests/3d/zero_g_movement_component.gd" id="4_8jhjh"]
|
||||
[ext_resource type="PackedScene" uid="uid://ba3ijdstp2bvt" path="res://scenes/tests/3d/player_controller_3d.tscn" id="4_bcy3l"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_6vm80"]
|
||||
|
||||
@ -12,10 +12,25 @@
|
||||
[sub_resource type="SphereShape3D" id="SphereShape3D_gnddn"]
|
||||
radius = 1.0
|
||||
|
||||
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_8jhjh"]
|
||||
properties/0/path = NodePath("CharacterPawn3d:global_transform")
|
||||
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_gnddn"]
|
||||
properties/0/path = NodePath(".:position")
|
||||
properties/0/spawn = true
|
||||
properties/0/replication_mode = 1
|
||||
properties/1/path = NodePath(".:rotation")
|
||||
properties/1/spawn = true
|
||||
properties/1/replication_mode = 1
|
||||
properties/2/path = NodePath("CameraPivot/SpringArm/Camera3D:position")
|
||||
properties/2/spawn = true
|
||||
properties/2/replication_mode = 1
|
||||
properties/3/path = NodePath("CameraPivot/SpringArm/Camera3D:rotation")
|
||||
properties/3/spawn = true
|
||||
properties/3/replication_mode = 1
|
||||
properties/4/path = NodePath("CameraPivot:position")
|
||||
properties/4/spawn = true
|
||||
properties/4/replication_mode = 1
|
||||
properties/5/path = NodePath("CameraPivot:rotation")
|
||||
properties/5/spawn = true
|
||||
properties/5/replication_mode = 1
|
||||
|
||||
[node name="CharacterPawn3D" type="CharacterBody3D"]
|
||||
script = ExtResource("1_4frsu")
|
||||
@ -27,10 +42,6 @@ shape = SubResource("CapsuleShape3D_6vm80")
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
|
||||
mesh = SubResource("CapsuleMesh_6vm80")
|
||||
|
||||
[node name="PlayerController3D" type="Node" parent="."]
|
||||
script = ExtResource("2_r62el")
|
||||
metadata/_custom_type_script = "uid://vjfk3xnapfti"
|
||||
|
||||
[node name="CameraPivot" type="Node3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.75, 0)
|
||||
|
||||
@ -38,7 +49,6 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.75, 0)
|
||||
spring_length = 3.0
|
||||
|
||||
[node name="Camera3D" type="Camera3D" parent="CameraPivot/SpringArm"]
|
||||
current = true
|
||||
|
||||
[node name="GripDetector" type="Area3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1)
|
||||
@ -56,4 +66,6 @@ metadata/_custom_type_script = "uid://y3vo40i16ek3"
|
||||
[node name="EVAMovementComponent" parent="." instance=ExtResource("3_gnddn")]
|
||||
|
||||
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."]
|
||||
replication_config = SubResource("SceneReplicationConfig_8jhjh")
|
||||
replication_config = SubResource("SceneReplicationConfig_gnddn")
|
||||
|
||||
[node name="PlayerController3d" parent="." instance=ExtResource("4_bcy3l")]
|
||||
|
||||
@ -2,12 +2,7 @@
|
||||
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
|
||||
@onready var possessed_pawn: CharacterPawn3D = get_parent()
|
||||
|
||||
# --- Mouse Sensitivity ---
|
||||
@export var mouse_sensitivity: float = 0.002 # Radians per pixel motion
|
||||
@ -24,6 +19,7 @@ class KeyInput:
|
||||
|
||||
func _unhandled_input(event: InputEvent):
|
||||
if not is_multiplayer_authority() or not is_instance_valid(possessed_pawn):
|
||||
# print("Peer ID: %s, Node Authority: %s" % [multiplayer.get_unique_id(), get_multiplayer_authority()])
|
||||
return
|
||||
|
||||
# Handle mouse motion input directly here
|
||||
@ -75,12 +71,18 @@ func server_process_clicks(l_action: KeyInput, r_action: KeyInput):
|
||||
|
||||
func possess(pawn_to_control: CharacterPawn3D):
|
||||
possessed_pawn = pawn_to_control
|
||||
possessed_pawn.camera.make_current()
|
||||
print("PlayerController3D possessed: ", possessed_pawn.name)
|
||||
|
||||
#print("PlayerController3D %d possessed: %s" % [multiplayer.get_unique_id(), possessed_pawn.name])
|
||||
|
||||
# Optional: Release mouse when losing focus
|
||||
func _notification(what):
|
||||
if what == NOTIFICATION_WM_WINDOW_FOCUS_OUT:
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
|
||||
elif what == NOTIFICATION_WM_WINDOW_FOCUS_IN:
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
||||
match what:
|
||||
NOTIFICATION_WM_WINDOW_FOCUS_OUT:
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
|
||||
NOTIFICATION_WM_WINDOW_FOCUS_IN:
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
||||
NOTIFICATION_EXIT_TREE:
|
||||
print("PlayerController %s exited tree" % multiplayer.get_unique_id())
|
||||
NOTIFICATION_ENTER_TREE:
|
||||
print("PlayerController %s entered tree" % multiplayer.get_unique_id())
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
[gd_scene load_steps=7 format=3 uid="uid://ddfsn0rtdnfda"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://5noqmp8b267n" path="res://scenes/tests/3d/grips/single_handhold.tscn" id="1_jlvj7"]
|
||||
[ext_resource type="Script" uid="uid://db1u2qqihhnq4" path="res://scenes/ship/components/hardware/spawner.gd" id="2_jlvj7"]
|
||||
[ext_resource type="PackedScene" uid="uid://dvpy3urgtm62n" path="res://scenes/ship/components/hardware/spawner.tscn" id="2_jlvj7"]
|
||||
|
||||
[sub_resource type="BoxMesh" id="BoxMesh_kateb"]
|
||||
size = Vector3(50, 1, 50)
|
||||
@ -47,6 +47,7 @@ shape = SubResource("BoxShape3D_25xtv")
|
||||
transform = Transform3D(-4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0, 0, 1, 25, 0, 0)
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="StaticBody3D3"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.050821304, 0.029743195, -0.014732361)
|
||||
mesh = SubResource("BoxMesh_kateb")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D3"]
|
||||
@ -158,10 +159,5 @@ mesh = SubResource("CylinderMesh_nvgim")
|
||||
transform = Transform3D(4.33065e-08, 0.00434584, -0.999991, 4.38977e-08, -0.999991, -0.00434584, -1, -4.35214e-08, -4.37722e-08, 0, 0, 0)
|
||||
shape = SubResource("CylinderShape3D_nvgim")
|
||||
|
||||
[node name="Spawner" type="Node3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 21.143803, 0, -10.62656)
|
||||
script = ExtResource("2_jlvj7")
|
||||
metadata/_custom_type_script = "uid://db1u2qqihhnq4"
|
||||
|
||||
[node name="MultiplayerSpawner" type="MultiplayerSpawner" parent="."]
|
||||
spawn_path = NodePath("../Spawner")
|
||||
[node name="Spawner" parent="." instance=ExtResource("2_jlvj7")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 12.309784, 0, -12.84836)
|
||||
|
||||
50
scripts/network/network_handler.gd
Normal file
50
scripts/network/network_handler.gd
Normal file
@ -0,0 +1,50 @@
|
||||
extends Node
|
||||
|
||||
var port = 42069
|
||||
|
||||
func create_server() -> void:
|
||||
print(multiplayer.multiplayer_peer)
|
||||
var peer = ENetMultiplayerPeer.new()
|
||||
|
||||
setup_connections()
|
||||
|
||||
var error = peer.create_server(port)
|
||||
if error:
|
||||
push_error(error)
|
||||
return
|
||||
|
||||
multiplayer.multiplayer_peer = peer
|
||||
|
||||
print("Server Unique ID: ", multiplayer.get_unique_id())
|
||||
|
||||
func create_client() -> void:
|
||||
setup_connections()
|
||||
|
||||
var peer = ENetMultiplayerPeer.new()
|
||||
var error = peer.create_client("127.0.0.1", port)
|
||||
if error:
|
||||
push_error(error)
|
||||
return
|
||||
|
||||
multiplayer.multiplayer_peer = peer
|
||||
print("Client Unique ID: ", multiplayer.get_unique_id())
|
||||
|
||||
func setup_connections():
|
||||
multiplayer.peer_connected.connect(on_peer_connected)
|
||||
multiplayer.peer_disconnected.connect(on_peer_disconnected)
|
||||
multiplayer.connected_to_server.connect(on_connected_to_server)
|
||||
|
||||
func on_peer_connected(peer_id: int) -> void:
|
||||
print("Peer %s recieved connection: %s" % [multiplayer.get_unique_id(), peer_id])
|
||||
|
||||
# For each peer that connects, we put them in the queue to spawn
|
||||
if multiplayer.is_server():
|
||||
GameManager.queue_spawn_player(peer_id)
|
||||
|
||||
|
||||
func on_peer_disconnected(peer_id: int) -> void:
|
||||
print("Peer %s lost connection to: %s" % [multiplayer.get_unique_id(), peer_id])
|
||||
print(multiplayer.get_peers())
|
||||
|
||||
func on_connected_to_server() -> void:
|
||||
print("%s connected to server!" % multiplayer.get_unique_id())
|
||||
1
scripts/network/network_handler.gd.uid
Normal file
1
scripts/network/network_handler.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://dw3hm45dog1sk
|
||||
@ -28,121 +28,70 @@ 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():
|
||||
LowLevelNetworkHandler.start_server()
|
||||
NetworkHandler.create_server()
|
||||
elif "--host" in OS.get_cmdline_args():
|
||||
NetworkHandler.create_server()
|
||||
# Host also acts as a player, so we need to handle its own connection.
|
||||
NetworkHandler.on_peer_connected.call_deferred(1)
|
||||
else:
|
||||
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()
|
||||
print("GameManager: Starting as CLIENT.")
|
||||
NetworkHandler.create_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
|
||||
func _process(_delta):
|
||||
if find_available_spawner():
|
||||
_try_spawn_waiting_player()
|
||||
|
||||
# Called when the game starts (e.g., from _ready() in StarSystemGenerator)
|
||||
func start_game():
|
||||
pass
|
||||
# For a single-player game, we simulate a player connecting with ID 1.
|
||||
# on_player_connected(1)
|
||||
|
||||
# This would be connected to a network signal in a multiplayer game.
|
||||
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():
|
||||
return
|
||||
|
||||
# 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.owner_id = id
|
||||
controller.name = "PlayerController_%d" % id
|
||||
# Set the authority for this controller to the player who just connected.
|
||||
controller.set_multiplayer_authority(id)
|
||||
add_child(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 'id'.
|
||||
# client_set_controller.rpc_id(id, controller.get_path())
|
||||
|
||||
# 3. Attempt to spawn a pawn for them immediately.
|
||||
_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):
|
||||
if registered_spawners.is_empty():
|
||||
# No spawners available, add the player to the waiting queue.
|
||||
if not player_id in waiting_players:
|
||||
waiting_players.append(player_id)
|
||||
print("GameManager: No spawners available. Player %d is now waiting." % player_id)
|
||||
# You could show a "Waiting for available spawner..." UI here.
|
||||
else:
|
||||
# Spawners are available, proceed with spawning.
|
||||
server_spawn_player_pawn.rpc(player_id)
|
||||
func queue_spawn_player(player_id: int):
|
||||
waiting_players.append(player_id)
|
||||
print("GameManager: Player %d queued for spawn." % player_id)
|
||||
|
||||
|
||||
# function to process the waiting queue.
|
||||
func _try_spawn_waiting_player():
|
||||
if not waiting_players.is_empty() and not registered_spawners.is_empty():
|
||||
var player_to_spawn = waiting_players.pop_front()
|
||||
print("GameManager: Spawner is now available. Spawning waiting player %d." % player_to_spawn)
|
||||
server_spawn_player_pawn.rpc(player_to_spawn)
|
||||
|
||||
@rpc("call_local")
|
||||
func server_spawn_player_pawn(player_id: int):
|
||||
# This function now runs on ALL clients, ensuring everyone sees the new pawn.
|
||||
if not player_controllers.has(player_id):
|
||||
push_error("Cannot spawn pawn for non-existent player %d" % player_id)
|
||||
return
|
||||
var player_id = waiting_players.pop_back()
|
||||
|
||||
# --- NEW SPAWNING LOGIC ---
|
||||
if registered_spawners.is_empty():
|
||||
push_error("GameManager: No spawners available to create pawn!")
|
||||
return
|
||||
print("GameManager: Spawner is now available. Spawning waiting player %d." % player_id)
|
||||
var spawn_point = find_available_spawner()
|
||||
|
||||
# For now, we'll just pick the first available spawner.
|
||||
# Later, you could present a UI for the player to choose.
|
||||
var spawn_point: Spawner = registered_spawners[0]
|
||||
if not is_instance_valid(spawn_point):
|
||||
push_error("GameManager: Spawn point not found!")
|
||||
return
|
||||
|
||||
# 1. Instantiate the pawn and add it to the scene.
|
||||
if spawn_point:
|
||||
_spawn_player_pawn(player_id)
|
||||
|
||||
var pawn = player_pawns[player_id]
|
||||
|
||||
pawn.set_multiplayer_authority(player_id)
|
||||
|
||||
spawn_point.add_child(pawn)
|
||||
|
||||
print("GameManager peer %s: Player %d spawned successfully." % [multiplayer.get_unique_id(), player_id])
|
||||
else:
|
||||
waiting_players.append(player_id)
|
||||
print("GameManager peer %s: Failed to spawn player %d." % [multiplayer.get_unique_id(), player_id])
|
||||
|
||||
func find_available_spawner() -> Spawner:
|
||||
var idx = registered_spawners.find_custom(func(spawner: Spawner) -> bool:
|
||||
return spawner.can_spawn()
|
||||
)
|
||||
return registered_spawners[idx] if idx != -1 else null
|
||||
|
||||
# @rpc("call_local")
|
||||
func _spawn_player_pawn(player_id: int):
|
||||
var pawn = character_pawn_3d_scene.instantiate()
|
||||
# Add the pawn as a child of the main scene root, not a module.
|
||||
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
|
||||
|
||||
player_pawns[player_id] = pawn
|
||||
|
||||
# 3. Possess the pawn with the player's controller.
|
||||
player_controllers[player_id].possess(pawn)
|
||||
print("GameManager: Spawned 3D Pawn for player %d" % player_id)
|
||||
|
||||
pawn.name = str(player_id)
|
||||
|
||||
player_pawns[player_id] = pawn
|
||||
|
||||
pawn.set_multiplayer_authority(player_id)
|
||||
|
||||
print("GameManager: Spawned 3D Pawn for player %d" % player_id)
|
||||
|
||||
|
||||
# Any scene that generates a star system will call this function to register itself.
|
||||
func register_star_system(system_node):
|
||||
@ -162,7 +111,7 @@ func register_spawner(spawner_node: Spawner):
|
||||
print("GameManager: Spawner '%s' registered." % spawner_node.name)
|
||||
|
||||
# NEW: If a player is waiting, try to spawn them now.
|
||||
_try_spawn_waiting_player()
|
||||
# _try_spawn_waiting_player()
|
||||
|
||||
# A helper function for easily accessing the system's data.
|
||||
func get_system_data() -> SystemData:
|
||||
@ -188,9 +137,3 @@ func get_all_trackable_bodies() -> Array[OrbitalBody2D]:
|
||||
func request_server_action(action_name: String, args: Array = []):
|
||||
# This function's body only runs on the SERVER.
|
||||
print("Server received request: ", action_name, " with args: ", args)
|
||||
# Here, the server would validate and execute the action.
|
||||
# match action_name:
|
||||
# "start_match":
|
||||
# _server_start_match()
|
||||
# "player_chose_loadout":
|
||||
# _server_process_loadout(multiplayer.get_remote_sender_id(), args)
|
||||
|
||||
Reference in New Issue
Block a user