diff --git a/src/modules/3d_test_ship.tscn b/src/modules/3d_test_ship.tscn index 9bd94e9..00bef01 100644 --- a/src/modules/3d_test_ship.tscn +++ b/src/modules/3d_test_ship.tscn @@ -8,8 +8,15 @@ properties/0/path = NodePath(".:position") properties/0/spawn = true properties/0/replication_mode = 1 +properties/1/path = NodePath(".:linear_velocity") +properties/1/spawn = true +properties/1/replication_mode = 1 +properties/2/path = NodePath(".:rotation") +properties/2/spawn = true +properties/2/replication_mode = 1 [node name="3dTestShip" type="RigidBody3D" unique_id=246037729] +physics_interpolation_mode = 1 script = ExtResource("1_ktv2t") physics_mode = 1 base_mass = 10000.0 @@ -543,9 +550,6 @@ physics_mode = 2 transform = Transform3D(0.99989736, 0, 0.014328662, -0.014328662, -4.371139e-08, 0.99989736, 6.263257e-10, -1, -4.37069e-08, -1.5009866, -1.4999988, 1.9908428) physics_mode = 2 -[node name="Spawner" parent="." unique_id=6714366 instance=ExtResource("3_ism2t")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 2) - [node name="OmniLight3D" type="OmniLight3D" parent="." unique_id=1071155008] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.4, 1, -3) @@ -562,5 +566,8 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.4, 1, 4) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 3) current = true +[node name="Spawner" parent="." unique_id=6714366 instance=ExtResource("3_ism2t")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 2) + [node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="." unique_id=2096937457] replication_config = SubResource("SceneReplicationConfig_ism2t") diff --git a/src/scripts/barycenter.gd b/src/scenes/celestial_bodies/barycenter.gd similarity index 100% rename from src/scripts/barycenter.gd rename to src/scenes/celestial_bodies/barycenter.gd diff --git a/src/scripts/barycenter.gd.uid b/src/scenes/celestial_bodies/barycenter.gd.uid similarity index 100% rename from src/scripts/barycenter.gd.uid rename to src/scenes/celestial_bodies/barycenter.gd.uid diff --git a/src/scenes/celestial_bodies/barycenter.tscn b/src/scenes/celestial_bodies/barycenter.tscn new file mode 100644 index 0000000..d3e01bd --- /dev/null +++ b/src/scenes/celestial_bodies/barycenter.tscn @@ -0,0 +1,22 @@ +[gd_scene load_steps=3 format=3 uid="uid://b7bh45nrtdom5"] + +[ext_resource type="Script" uid="uid://b2hb3bwrlh40c" path="res://scenes/celestial_bodies/barycenter.gd" id="1_e776o"] + +[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_e776o"] +properties/0/path = NodePath(".:linear_velocity") +properties/0/spawn = true +properties/0/replication_mode = 1 +properties/1/path = NodePath(".:position") +properties/1/spawn = true +properties/1/replication_mode = 1 + +[node name="Barycenter" type="RigidBody3D" unique_id=1389317234] +script = ExtResource("1_e776o") +metadata/_custom_type_script = "uid://wlm40n8ywr" + +[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="." unique_id=717759965] +replication_config = SubResource("SceneReplicationConfig_e776o") + +[node name="MultiplayerSpawner" type="MultiplayerSpawner" parent="." unique_id=2061784354] +_spawnable_scenes = PackedStringArray("uid://dv18eg4xrlefe") +spawn_path = NodePath("..") diff --git a/src/scenes/celestial_bodies/celestial_body.gd b/src/scenes/celestial_bodies/celestial_body.gd index 6adeac8..5a4eba6 100644 --- a/src/scenes/celestial_bodies/celestial_body.gd +++ b/src/scenes/celestial_bodies/celestial_body.gd @@ -2,11 +2,12 @@ class_name CelestialBody extends OrbitalBody3D # --- Set in corresponding scene --- # var auto_proxy_gravity = false -@export var radius: float = 100.0 +@export var radius: float = 100.0: + set(value): + radius = value + _set_radi() -func set_radius(value: float): - radius = value - +func _set_radi(): if $Surface.mesh is SphereMesh: $Surface.mesh.radius = radius $Surface.mesh.height = radius * 2.0 diff --git a/src/scenes/celestial_bodies/celestial_body.tscn b/src/scenes/celestial_bodies/celestial_body.tscn index 16d5f69..05d09ca 100644 --- a/src/scenes/celestial_bodies/celestial_body.tscn +++ b/src/scenes/celestial_bodies/celestial_body.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=5 format=3 uid="uid://dv18eg4xrlefe"] +[gd_scene load_steps=6 format=3 uid="uid://dv18eg4xrlefe"] [ext_resource type="Script" uid="uid://dok35h0q4pseh" path="res://scenes/celestial_bodies/celestial_body.gd" id="1_uxu4s"] [ext_resource type="Material" uid="uid://de0xnmjf12ted" path="res://scenes/celestial_bodies/materials/sun_mat.tres" id="2_vi0nt"] @@ -11,18 +11,38 @@ height = 4000.0 [sub_resource type="SphereShape3D" id="SphereShape3D_uxu4s"] -[node name="CelestialBody" type="RigidBody3D"] +[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_vi0nt"] +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(".:linear_velocity") +properties/2/spawn = true +properties/2/replication_mode = 1 +properties/3/path = NodePath(".:angular_velocity") +properties/3/spawn = true +properties/3/replication_mode = 1 +properties/4/path = NodePath(".:radius") +properties/4/spawn = true +properties/4/replication_mode = 1 + +[node name="CelestialBody" type="RigidBody3D" unique_id=345490070] script = ExtResource("1_uxu4s") auto_proxy_gravity = false metadata/_custom_type_script = "uid://dok35h0q4pseh" -[node name="Surface" type="MeshInstance3D" parent="."] +[node name="Surface" type="MeshInstance3D" parent="." unique_id=193823349] mesh = SubResource("SphereMesh_vi0nt") -[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=232085687] shape = SubResource("SphereShape3D_uxu4s") -[node name="OmniLight3D" type="OmniLight3D" parent="."] +[node name="OmniLight3D" type="OmniLight3D" parent="." unique_id=1965995953] light_color = Color(0.958646, 0.7997282, 0.55087835, 1) omni_range = 200000.0 omni_attenuation = 2.0 + +[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="." unique_id=2090029903] +replication_config = SubResource("SceneReplicationConfig_vi0nt") diff --git a/src/scenes/character/character_pawn_3d.gd b/src/scenes/character/character_pawn_3d.gd index f2adac1..a0adda4 100644 --- a/src/scenes/character/character_pawn_3d.gd +++ b/src/scenes/character/character_pawn_3d.gd @@ -17,8 +17,8 @@ var _pitch_yaw_input: Vector2 = Vector2.ZERO ## Rotation Variables @onready var camera_anchor: Marker3D = $CameraAnchor -@onready var camera_pivot: Node3D = $CameraPivot -@onready var camera: Camera3D = $CameraPivot/SpringArm/Camera3D +@onready var camera_pivot: Node3D = $CameraAnchor/CameraPivot +@onready var camera: Camera3D = $CameraAnchor/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) @@ -52,10 +52,10 @@ func _ready(): else: printerr("GripDetector Area3D node not found on CharacterPawn!") - if is_multiplayer_authority(): + if name == str(multiplayer.get_unique_id()): camera.make_current() camera.process_mode = Node.PROCESS_MODE_ALWAYS - + func _process(_delta: float) -> void: camera_pivot.global_transform = camera_anchor.get_global_transform_interpolated() @@ -66,14 +66,25 @@ func _physics_process(_delta: float): _reset_inputs() + func _integrate_forces(state: PhysicsDirectBodyState3D): - # Let the active movement controller apply its forces + if not is_multiplayer_authority(): return + super (state) + + + # print("Integrating forces for pawn %s" % name) + # print(" Move Input: %s, Vertical Input: %f, Roll Input: %f" % [_move_input, _vertical_input, _roll_input]) + + # Zero-G Movement if zero_g_movemement_component: # We pass the physics state zero_g_movemement_component.process_movement(state, _move_input, _vertical_input, _roll_input, _l_click_input, _r_click_input) + + # EVA Suit Movement if eva_suit_component and zero_g_movemement_component.movement_state == ZeroGMovementComponent.MovementState.IDLE: eva_suit_component.process_eva_movement(state, _move_input, _vertical_input, _roll_input, _r_click_input) + # --- Universal Rotation --- func _apply_mouse_rotation(): if _pitch_yaw_input != Vector2.ZERO: @@ -120,7 +131,8 @@ func _reset_head_yaw(delta: float): # Smoothly apply the reset target to the actual pivot rotation camera_anchor.rotation.y = lerpf(camera_anchor.rotation.y, 0.0, delta * head_turn_lerp_speed) -func _notification(what: int) -> void: - match what: - NOTIFICATION_ENTER_TREE: - set_multiplayer_authority(int(name)) +# TODO: Re-enable when multiplayer authority per pawn is functional +# func _notification(what: int) -> void: +# match what: +# NOTIFICATION_ENTER_TREE: +# set_multiplayer_authority(int(name)) diff --git a/src/scenes/character/character_pawn_3d.tscn b/src/scenes/character/character_pawn_3d.tscn index d4afe20..9c8cba1 100644 --- a/src/scenes/character/character_pawn_3d.tscn +++ b/src/scenes/character/character_pawn_3d.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=9 format=3 uid="uid://7yc6a07xoccy"] +[gd_scene load_steps=10 format=3 uid="uid://7yc6a07xoccy"] [ext_resource type="Script" uid="uid://cdmmiixa75f3x" path="res://scenes/character/character_pawn_3d.gd" id="1_4frsu"] [ext_resource type="PackedScene" uid="uid://bm1rbv4tuppbc" path="res://scenes/character/eva_suit_controller.tscn" id="3_gnddn"] @@ -9,6 +9,10 @@ [sub_resource type="CapsuleMesh" id="CapsuleMesh_6vm80"] +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_673rh"] +radius = 0.1 +height = 1.0 + [sub_resource type="SphereShape3D" id="SphereShape3D_gnddn"] radius = 1.0 @@ -19,11 +23,19 @@ properties/0/replication_mode = 1 properties/1/path = NodePath(".:rotation") properties/1/spawn = true properties/1/replication_mode = 1 -properties/2/path = NodePath("CameraPivot:rotation") +properties/2/path = NodePath("CameraAnchor:rotation") properties/2/spawn = true -properties/2/replication_mode = 2 +properties/2/replication_mode = 1 +properties/3/path = NodePath(".:linear_velocity") +properties/3/spawn = false +properties/3/replication_mode = 0 +properties/4/path = NodePath(".:angular_velocity") +properties/4/spawn = false +properties/4/replication_mode = 0 [node name="CharacterPawn3D" type="RigidBody3D" unique_id=288275840] +physics_interpolation_mode = 1 +top_level = true script = ExtResource("1_4frsu") metadata/_custom_type_script = "uid://cdmmiixa75f3x" @@ -34,16 +46,17 @@ shape = SubResource("CapsuleShape3D_6vm80") mesh = SubResource("CapsuleMesh_6vm80") [node name="CameraAnchor" type="Marker3D" parent="." unique_id=462168232] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.7000000000000001, 0) -[node name="CameraPivot" type="Node3D" parent="." unique_id=794640520] +[node name="CameraPivot" type="Node3D" parent="CameraAnchor" unique_id=794640520] physics_interpolation_mode = 1 -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.75, 0) -top_level = true -[node name="SpringArm" type="SpringArm3D" parent="CameraPivot" unique_id=1399441728] -spring_length = 3.0 +[node name="SpringArm" type="SpringArm3D" parent="CameraAnchor/CameraPivot" unique_id=1399441728] +shape = SubResource("CapsuleShape3D_673rh") +spring_length = 2.0 +margin = 0.1 -[node name="Camera3D" type="Camera3D" parent="CameraPivot/SpringArm" unique_id=1779046272] +[node name="Camera3D" type="Camera3D" parent="CameraAnchor/CameraPivot/SpringArm" unique_id=1779046272] far = 200000.0 [node name="GripDetector" type="Area3D" parent="." unique_id=734413990] @@ -60,8 +73,9 @@ script = ExtResource("4_8jhjh") metadata/_custom_type_script = "uid://y3vo40i16ek3" [node name="EVAMovementComponent" parent="." unique_id=1806288315 instance=ExtResource("3_gnddn")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.13939085347041424, 0.5148942200402955) + +[node name="PlayerController3d" parent="." unique_id=1450011826 instance=ExtResource("4_bcy3l")] [node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="." unique_id=732324183] replication_config = SubResource("SceneReplicationConfig_gnddn") - -[node name="PlayerController3d" parent="." unique_id=1450011826 instance=ExtResource("4_bcy3l")] diff --git a/src/scenes/character/eva_movement_component.gd b/src/scenes/character/eva_movement_component.gd index a70c9f9..073b914 100644 --- a/src/scenes/character/eva_movement_component.gd +++ b/src/scenes/character/eva_movement_component.gd @@ -30,7 +30,7 @@ func _ready(): ## Called by Pawn's _integrate_forces when suit equipped func process_eva_movement(state: PhysicsDirectBodyState3D, move_input: Vector2, vertical_input: float, roll_input: float, orienting_input: PlayerController3D.KeyInput): - # --- 1. Handle Orient Input --- + # --- 1. Handle Orient Input --- if orienting_input.pressed or orienting_input.held: _set_auto_orient_target(state) @@ -70,7 +70,6 @@ func _apply_floating_movement(state: PhysicsDirectBodyState3D, move_input: Vecto _is_auto_orienting = false # Cancel auto-orientation if rolling manually var roll_acceleration = state.transform.basis.z * (-roll_input) * roll_torque_acceleration - # Apply the global torque vector using the pawn's helper function state.apply_torque(roll_acceleration) diff --git a/src/scenes/character/eva_suit_controller.tscn b/src/scenes/character/eva_suit_controller.tscn index 47480d2..525b82c 100644 --- a/src/scenes/character/eva_suit_controller.tscn +++ b/src/scenes/character/eva_suit_controller.tscn @@ -1,6 +1,12 @@ -[gd_scene load_steps=2 format=3 uid="uid://bm1rbv4tuppbc"] +[gd_scene load_steps=3 format=3 uid="uid://bm1rbv4tuppbc"] [ext_resource type="Script" uid="uid://d4jka2etva22s" path="res://scenes/character/eva_movement_component.gd" id="1_mb22m"] -[node name="EVASuitController" type="Node3D"] +[sub_resource type="BoxMesh" id="BoxMesh_rlk1u"] +size = Vector3(1, 1, 0.4) + +[node name="EVASuitController" type="Node3D" unique_id=241443807] script = ExtResource("1_mb22m") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=1434211019] +mesh = SubResource("BoxMesh_rlk1u") diff --git a/src/scenes/character/player_controller_3d.gd b/src/scenes/character/player_controller_3d.gd index 9c1739a..75a2347 100644 --- a/src/scenes/character/player_controller_3d.gd +++ b/src/scenes/character/player_controller_3d.gd @@ -8,6 +8,7 @@ class_name PlayerController3D @export var mouse_sensitivity: float = 0.002 # Radians per pixel motion var _mouse_motion_input: Vector2 = Vector2.ZERO + class KeyInput: var pressed: bool = false var held: bool = false @@ -18,9 +19,26 @@ class KeyInput: held = _h released = _r + func _to_dict(): + return { + "pressed": pressed, + "held": held, + "released": released + } + + static func from_dict(dict: Dictionary) -> KeyInput: + return KeyInput.new(dict.get("pressed", false), dict.get("held", false), dict.get("released", false)) + +func _ready(): + # If we are spawned dynamically, the owner_id might be set by GameManager. + # Fallback: assume the pawn's name is the player ID (common pattern). + if get_parent().name.is_valid_int(): + set_multiplayer_authority(int(get_parent().name)) + func _unhandled_input(event: InputEvent): + + # Check if THIS client is the owner of this controller 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 @@ -28,6 +46,7 @@ func _unhandled_input(event: InputEvent): _mouse_motion_input += Vector2(event.relative.x, -event.relative.y) func _physics_process(_delta): + # Check if THIS client is the owner if not is_multiplayer_authority() or not is_instance_valid(possessed_pawn): return @@ -36,7 +55,7 @@ func _physics_process(_delta): var sensitivity_modified_mouse_input = Vector2(_mouse_motion_input.x, _mouse_motion_input.y) * mouse_sensitivity # Send rotation input via RPC immediately - server_process_rotation_input.rpc_id(multiplayer.get_unique_id(), sensitivity_modified_mouse_input) + server_process_rotation_input.rpc_id(1, sensitivity_modified_mouse_input) # Reset the buffer _mouse_motion_input = Vector2.ZERO @@ -50,29 +69,29 @@ 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.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) + server_process_movement_input.rpc_id(1, move_vec, roll_input, vertical_input) + server_process_interaction_input.rpc_id(1, interact_input._to_dict()) + server_process_clicks.rpc_id(1, l_input._to_dict(), r_input._to_dict()) -@rpc("authority", "call_local") +@rpc("any_peer", "call_local") func server_process_movement_input(move: Vector2, roll: float, vertical: float): if is_instance_valid(possessed_pawn): possessed_pawn.set_movement_input(move, roll, vertical) -@rpc("authority", "call_local") -func server_process_interaction_input(interact_input: KeyInput): +@rpc("any_peer", "call_local") +func server_process_interaction_input(interact_input: Dictionary): if is_instance_valid(possessed_pawn): - possessed_pawn.set_interaction_input(interact_input) + possessed_pawn.set_interaction_input(KeyInput.from_dict(interact_input)) -@rpc("authority", "call_local") +@rpc("any_peer", "call_local") func server_process_rotation_input(input: Vector2): if is_instance_valid(possessed_pawn): possessed_pawn.set_rotation_input(input) -@rpc("authority", "call_local") -func server_process_clicks(l_action: KeyInput, r_action: KeyInput): +@rpc("any_peer", "call_local") +func server_process_clicks(l_action: Dictionary, r_action: Dictionary): if is_instance_valid(possessed_pawn): - possessed_pawn.set_click_input(l_action, r_action) + possessed_pawn.set_click_input(KeyInput.from_dict(l_action), KeyInput.from_dict(r_action)) func possess(pawn_to_control: CharacterPawn3D): possessed_pawn = pawn_to_control diff --git a/src/scenes/ship/builder/pieces/structural_piece.gd b/src/scenes/ship/builder/pieces/structural_piece.gd index 7cf208a..536d0df 100644 --- a/src/scenes/ship/builder/pieces/structural_piece.gd +++ b/src/scenes/ship/builder/pieces/structural_piece.gd @@ -55,7 +55,7 @@ func _scan_and_weld_neighbors(): _create_weld_to(other_piece) func _create_weld_to(neighbor: StructuralPiece): - print("Welding %s to %s" % [self.name, neighbor.name]) + # print("Welding %s to %s" % [self.name, neighbor.name]) # 1. Create the Joint var joint = Generic6DOFJoint3D.new() diff --git a/src/scenes/ship/components/hardware/spawner.gd b/src/scenes/ship/components/hardware/spawner.gd index 6b9b5b8..d270ca9 100644 --- a/src/scenes/ship/components/hardware/spawner.gd +++ b/src/scenes/ship/components/hardware/spawner.gd @@ -1,5 +1,4 @@ -extends Area3D -class_name Spawner +class_name Spawner extends Area3D @onready var mp_spawner: MultiplayerSpawner = $MultiplayerSpawner @export var disabled: bool = false @@ -10,6 +9,7 @@ func _ready(): # We wait one frame to ensure singletons are ready. await get_tree().process_frame GameManager.register_spawner(self) + mp_spawner.spawn_path = get_parent().get_path() func can_spawn() -> bool: return false if disabled else get_overlapping_bodies().is_empty() diff --git a/src/scenes/ship/components/hardware/spawner.tscn b/src/scenes/ship/components/hardware/spawner.tscn index c833162..4a3c315 100644 --- a/src/scenes/ship/components/hardware/spawner.tscn +++ b/src/scenes/ship/components/hardware/spawner.tscn @@ -5,12 +5,12 @@ [sub_resource type="SphereShape3D" id="SphereShape3D_lldyu"] radius = 1.0 -[node name="Spawner" type="Area3D"] +[node name="Spawner" type="Area3D" unique_id=1511371555] script = ExtResource("1_lldyu") -[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1330579270] shape = SubResource("SphereShape3D_lldyu") -[node name="MultiplayerSpawner" type="MultiplayerSpawner" parent="."] +[node name="MultiplayerSpawner" type="MultiplayerSpawner" parent="." unique_id=1624471299] _spawnable_scenes = PackedStringArray("uid://7yc6a07xoccy") spawn_path = NodePath("..") diff --git a/src/scripts/network/network_handler.gd b/src/scripts/network/network_handler.gd index dcc0e2d..220b26b 100644 --- a/src/scripts/network/network_handler.gd +++ b/src/scripts/network/network_handler.gd @@ -1,11 +1,14 @@ extends Node var port = 42069 +var default_ip = "127.0.0.1" func create_server() -> void: - print(multiplayer.multiplayer_peer) + print("Starting Server on port %d..." % port) var peer = ENetMultiplayerPeer.new() + # Ensure we disconnect old signals if any + _disconnect_signals() setup_connections() var error = peer.create_server(port) @@ -14,37 +17,66 @@ func create_server() -> void: return multiplayer.multiplayer_peer = peer - print("Server Unique ID: ", multiplayer.get_unique_id()) -func create_client() -> void: +func create_client(ip: String = "") -> void: + var target_ip = ip if not ip.is_empty() else default_ip + print("Connecting to Server at %s:%d..." % [target_ip, port]) + + _disconnect_signals() setup_connections() var peer = ENetMultiplayerPeer.new() - var error = peer.create_client("127.0.0.1", port) + var error = peer.create_client(target_ip, port) if error: push_error(error) return multiplayer.multiplayer_peer = peer - print("Client Unique ID: ", multiplayer.get_unique_id()) + print("Client waiting for connection...") + +func close_connection(): + if multiplayer.multiplayer_peer: + multiplayer.multiplayer_peer.close() + multiplayer.multiplayer_peer = null + print("Connection closed.") + _disconnect_signals() 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) + if not multiplayer.peer_connected.is_connected(on_peer_connected): + multiplayer.peer_connected.connect(on_peer_connected) + if not multiplayer.peer_disconnected.is_connected(on_peer_disconnected): + multiplayer.peer_disconnected.connect(on_peer_disconnected) + if not multiplayer.connected_to_server.is_connected(on_connected_to_server): + multiplayer.connected_to_server.connect(on_connected_to_server) + if not multiplayer.server_disconnected.is_connected(on_server_disconnected): + multiplayer.server_disconnected.connect(on_server_disconnected) + +func _disconnect_signals(): + if multiplayer.peer_connected.is_connected(on_peer_connected): + multiplayer.peer_connected.disconnect(on_peer_connected) + if multiplayer.peer_disconnected.is_connected(on_peer_disconnected): + multiplayer.peer_disconnected.disconnect(on_peer_disconnected) + if multiplayer.connected_to_server.is_connected(on_connected_to_server): + multiplayer.connected_to_server.disconnect(on_connected_to_server) + if multiplayer.server_disconnected.is_connected(on_server_disconnected): + multiplayer.server_disconnected.disconnect(on_server_disconnected) 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 + print("Peer %s received connection: %s" % [multiplayer.get_unique_id(), peer_id]) 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()) + # TODO: GameManager should cleanup the player's pawn func on_connected_to_server() -> void: print("%s connected to server!" % multiplayer.get_unique_id()) + # If we are in the main menu, this is the trigger to switch scenes + if get_tree().current_scene.name == "MainMenu": + get_tree().change_scene_to_file("res://main.tscn") + +func on_server_disconnected() -> void: + print("Disconnected from server.") + get_tree().change_scene_to_file("res://scenes/UI/main_menu.tscn") \ No newline at end of file diff --git a/src/scripts/orbital_body_3d.gd b/src/scripts/orbital_body_3d.gd index 0ef3043..0e910aa 100644 --- a/src/scripts/orbital_body_3d.gd +++ b/src/scripts/orbital_body_3d.gd @@ -42,7 +42,7 @@ func _ready(): if physics_mode == PhysicsMode.ANCHORED: _update_gravity_proxy() linear_velocity = gravity_proxy_parent.linear_velocity if is_instance_valid(gravity_proxy_parent) else Vector3.ZERO - print(name, " initialized as ANCHORED with gravity proxy: ", gravity_proxy_parent) + # print(name, " initialized as ANCHORED with gravity proxy: ", gravity_proxy_parent) func _notification(what): # Automatically update gravity proxy when the scene hierarchy changes @@ -68,10 +68,10 @@ func _update_gravity_proxy(): # 2. Assign the proxy if new_proxy != gravity_proxy_parent: gravity_proxy_parent = new_proxy - if new_proxy: - print(name, " auto-parented gravity proxy to: ", new_proxy.name) - else: - print(name, " detached from gravity proxy (Independent Mode).") + # if new_proxy: + # print(name, " auto-parented gravity proxy to: ", new_proxy.name) + # else: + # print(name, " detached from gravity proxy (Independent Mode).") # --- PUBLIC FORCE APPLICATION METHODS --- @@ -86,6 +86,7 @@ func _update_mass_and_inertia(): func _integrate_forces(state: PhysicsDirectBodyState3D): + # if not is_multiplayer_authority(): return # Safety Check for Division by Zero if mass <= 0.0: accumulated_force = Vector3.ZERO diff --git a/src/scripts/singletons/game_manager.gd b/src/scripts/singletons/game_manager.gd index 40d4d94..a678297 100644 --- a/src/scripts/singletons/game_manager.gd +++ b/src/scripts/singletons/game_manager.gd @@ -58,16 +58,17 @@ func _try_spawn_waiting_player(): var player_id = waiting_players.pop_back() print("GameManager: Spawner is now available. Spawning waiting player %d." % player_id) - var spawn_point = find_available_spawner() + var spawn_point: Spawner = find_available_spawner() if spawn_point: _spawn_player_pawn(player_id) var pawn = player_pawns[player_id] - pawn.set_multiplayer_authority(player_id) + # pawn.set_multiplayer_authority(player_id) - spawn_point.add_child(pawn) + get_node(spawn_point.mp_spawner.spawn_path).add_child(pawn) + pawn.global_transform = spawn_point.global_transform # Traverse up to find the physics body (Ship/Module) we just spawned inside var parent_body = _get_orbital_body_ancestor(spawn_point) @@ -96,7 +97,7 @@ func _spawn_player_pawn(player_id: int): player_pawns[player_id] = pawn - pawn.set_multiplayer_authority(player_id) + # pawn.set_multiplayer_authority(player_id) print("GameManager: Spawned 3D Pawn for player %d" % player_id) diff --git a/src/scripts/star_system.gd b/src/scripts/star_system.gd index 7f10329..4a7b184 100644 --- a/src/scripts/star_system.gd +++ b/src/scripts/star_system.gd @@ -4,16 +4,33 @@ extends Node3D @export_group("System Metadata") @export var system_name: String = "Kepler-186" +@export var system_seed: int = 0 @export var galactic_coordinates: Vector2i = Vector2i.ZERO var system_data: SystemData func _ready(): # 1. Create the generator tool. - var generator = StarSystemGenerator.new() + if multiplayer.is_server(): + if system_seed == 0: + system_seed = randi() + seed(system_seed) + print("StarSystem (Server): Generating system with seed: %d" % system_seed) + + var generator = StarSystemGenerator.new() + system_data = generator.generate(self) + + # At this point, the generator has added children to 'self'. + # Because we have a MultiplayerSpawner watching 'self', these new children + # will automatically be replicated to connected clients! + else: + print("StarSystem (Client): Waiting for planets from server...") + # The client does NOT generate. It just waits for the Spawner to do its job. + # We can reconstruct 'system_data' by scanning children if needed, + # or just let GameManager find them via 'get_children()'. + # 2. Tell the generator to build the system within this StarSystem node. - system_data = generator.generate(self) # 3. Register the completed system with the GameManager. GameManager.register_star_system(self) diff --git a/src/scripts/star_system.tscn b/src/scripts/star_system.tscn new file mode 100644 index 0000000..b04ec0b --- /dev/null +++ b/src/scripts/star_system.tscn @@ -0,0 +1,15 @@ +[gd_scene load_steps=3 format=3 uid="uid://b554pkth6hox4"] + +[ext_resource type="Script" uid="uid://bkcouefvi7iup" path="res://scripts/star_system.gd" id="1_gbrlo"] + +[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_gbrlo"] + +[node name="StarSystem" type="Node3D" unique_id=1547322980] +script = ExtResource("1_gbrlo") + +[node name="MultiplayerSpawner" type="MultiplayerSpawner" parent="." unique_id=1117979460] +_spawnable_scenes = PackedStringArray("uid://dv18eg4xrlefe", "uid://b7bh45nrtdom5", "uid://bkwogkfqk2uxo") +spawn_path = NodePath("..") + +[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="." unique_id=801189521] +replication_config = SubResource("SceneReplicationConfig_gbrlo") diff --git a/src/scripts/star_system_generator.gd b/src/scripts/star_system_generator.gd index 04ea4c6..fb3a164 100644 --- a/src/scripts/star_system_generator.gd +++ b/src/scripts/star_system_generator.gd @@ -11,6 +11,7 @@ const MAX_PLANETS = 8 const MAX_MOONS_PER_PLANET = 5 const ORBIT_SAFETY_FACTOR = 5 +var BarycenterScene: PackedScene = preload("res://scenes/celestial_bodies/barycenter.tscn") var CelestialBodyScene: PackedScene = preload("res://scenes/celestial_bodies/celestial_body.tscn") func generate(star_system: StarSystem) -> SystemData: @@ -20,7 +21,7 @@ func generate(star_system: StarSystem) -> SystemData: system_data.star = star star.name = "Star" - star.set_radius(2000.0) + star.radius = 2000.0 star.base_mass = STAR_MASS star_system.add_child(star) @@ -28,12 +29,12 @@ func generate(star_system: StarSystem) -> SystemData: var current_orbit_radius = 15000.0 for i in range(num_planets): - var planet_barycenter = Barycenter.new() + var planet_barycenter = BarycenterScene.instantiate() planet_barycenter.name = "PlanetSystem_%d" % (i + 1) star_system.add_child(planet_barycenter) var planet: CelestialBody = CelestialBodyScene.instantiate() - planet.set_radius(randf_range(50.0, 200.0)) + planet.radius = randf_range(50.0, 200.0) system_data.planets.append(planet) planet.name = "Planet_%d" % (i + 1) planet.base_mass = randf_range(PLANET_MASS * 0.2, PLANET_MASS * 5.0) @@ -69,7 +70,7 @@ func _generate_moons(planet: OrbitalBody3D, planet_barycenter: Barycenter, syste for i in range(num_moons): var moon = CelestialBodyScene.instantiate() - moon.set_radius(10.0) + moon.radius = 10.0 system_data.moons.append(moon) planet_barycenter.add_child(moon) planet_barycenter.recalculate_total_mass()