From 95eeab0dfe4a821d7ff47752515e412d467873dc Mon Sep 17 00:00:00 2001 From: teatov Date: Mon, 10 Feb 2025 23:32:17 +1000 Subject: [PATCH] add basic networking --- project.godot | 14 ++++++- scenes/main.tscn | 9 +++-- scenes/player.tscn | 22 +++++++++- scenes/title.tscn | 78 ++++++++++++++++++++++++++++++++++++ scripts/globals/cursorer.gd | 17 ++++++++ scripts/globals/networker.gd | 73 +++++++++++++++++++++++++++++++++ scripts/main.gd | 4 -- scripts/player.gd | 20 ++++++--- scripts/ui/play_menu.gd | 19 +++++++++ 9 files changed, 240 insertions(+), 16 deletions(-) create mode 100644 scenes/title.tscn create mode 100644 scripts/globals/cursorer.gd create mode 100644 scripts/globals/networker.gd create mode 100644 scripts/ui/play_menu.gd diff --git a/project.godot b/project.godot index 6138aa2..354519d 100644 --- a/project.godot +++ b/project.godot @@ -12,7 +12,7 @@ config_version=5 config/name="cadastery" config/version="0.0.1" -run/main_scene="res://scenes/main.tscn" +run/main_scene="res://scenes/title.tscn" config/use_custom_user_dir=true config/custom_user_dir_name="Cadastery" config/features=PackedStringArray("4.3", "Forward Plus") @@ -23,6 +23,8 @@ config/icon="res://icon.svg" [autoload] Settings="*res://scripts/globals/settings.gd" +Networker="*res://scripts/globals/networker.gd" +Cursorer="*res://scripts/globals/cursorer.gd" [debug] @@ -90,6 +92,16 @@ toggle_fullscreen={ , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194342,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } +toggle_cursor_capture={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194332,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} + +[layer_names] + +3d_physics/layer_1="geometry" +3d_physics/layer_2="players" [physics] diff --git a/scenes/main.tscn b/scenes/main.tscn index 57f724d..72d2a64 100644 --- a/scenes/main.tscn +++ b/scenes/main.tscn @@ -1,7 +1,6 @@ -[gd_scene load_steps=5 format=3 uid="uid://b7fc42grqckl0"] +[gd_scene load_steps=4 format=3 uid="uid://b7fc42grqckl0"] [ext_resource type="Script" path="res://scripts/main.gd" id="1_80y7k"] -[ext_resource type="PackedScene" uid="uid://bvleufyobery5" path="res://scenes/player.tscn" id="1_lntus"] [ext_resource type="Material" uid="uid://00ldcihmubqo" path="res://resources/materials/test_triplanar.tres" id="3_vk6ds"] [sub_resource type="Environment" id="Environment_wyusq"] @@ -11,8 +10,6 @@ script = ExtResource("1_80y7k") [node name="Players" type="Node" parent="."] -[node name="Player" parent="Players" instance=ExtResource("1_lntus")] - [node name="Geometry" type="Node" parent="."] [node name="CSGCombiner3D" type="CSGCombiner3D" parent="Geometry"] @@ -30,3 +27,7 @@ transform = Transform3D(0.866025, -0.433013, 0.25, 0, 0.5, 0.866025, -0.5, -0.75 [node name="WorldEnvironment" type="WorldEnvironment" parent="."] environment = SubResource("Environment_wyusq") + +[node name="PlayerSpawner" type="MultiplayerSpawner" parent="."] +_spawnable_scenes = PackedStringArray("res://scenes/player.tscn") +spawn_path = NodePath("../Players") diff --git a/scenes/player.tscn b/scenes/player.tscn index c018e65..35b49c9 100644 --- a/scenes/player.tscn +++ b/scenes/player.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=5 format=3 uid="uid://bvleufyobery5"] +[gd_scene load_steps=6 format=3 uid="uid://bvleufyobery5"] [ext_resource type="Script" path="res://scripts/player.gd" id="1_82m0t"] @@ -9,7 +9,19 @@ [sub_resource type="BoxMesh" id="BoxMesh_v4nai"] size = Vector3(0.4, 0.4, 0.8) +[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_j6f6x"] +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("Camera3D:rotation") +properties/2/spawn = true +properties/2/replication_mode = 1 + [node name="Player" type="CharacterBody3D"] +collision_layer = 2 script = ExtResource("1_82m0t") [node name="CollisionShape3D" type="CollisionShape3D" parent="."] @@ -22,8 +34,14 @@ mesh = SubResource("CapsuleMesh_5kiq6") [node name="Camera3D" type="Camera3D" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.5, 0) -current = true [node name="MeshInstance3D" type="MeshInstance3D" parent="Camera3D"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.2) mesh = SubResource("BoxMesh_v4nai") + +[node name="NameLabel" type="Label3D" parent="."] +transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 2.15, 0) +text = "name" + +[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."] +replication_config = SubResource("SceneReplicationConfig_j6f6x") diff --git a/scenes/title.tscn b/scenes/title.tscn new file mode 100644 index 0000000..246c6d0 --- /dev/null +++ b/scenes/title.tscn @@ -0,0 +1,78 @@ +[gd_scene load_steps=2 format=3 uid="uid://bnlglddkrxuf0"] + +[ext_resource type="Script" path="res://scripts/ui/play_menu.gd" id="1_gunvo"] + +[node name="Title" type="Node"] + +[node name="Menu" type="Control" parent="."] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="PlayMenu" type="Panel" parent="Menu"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -216.0 +offset_top = -84.0 +offset_right = 216.0 +offset_bottom = 84.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_gunvo") + +[node name="MarginContainer" type="MarginContainer" parent="Menu/PlayMenu"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_top = 20 +theme_override_constants/margin_right = 20 +theme_override_constants/margin_bottom = 20 + +[node name="GridContainer" type="GridContainer" parent="Menu/PlayMenu/MarginContainer"] +layout_mode = 2 +theme_override_constants/h_separation = 10 +theme_override_constants/v_separation = 20 +columns = 3 + +[node name="NameLabel" type="Label" parent="Menu/PlayMenu/MarginContainer/GridContainer"] +layout_mode = 2 +theme_override_font_sizes/font_size = 26 +text = "Name:" + +[node name="NameEdit" type="LineEdit" parent="Menu/PlayMenu/MarginContainer/GridContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 26 +text = "ass" + +[node name="HostButton" type="Button" parent="Menu/PlayMenu/MarginContainer/GridContainer"] +layout_mode = 2 +theme_override_font_sizes/font_size = 26 +text = "Host" + +[node name="IpLabel" type="Label" parent="Menu/PlayMenu/MarginContainer/GridContainer"] +layout_mode = 2 +theme_override_font_sizes/font_size = 26 +text = "IP:" + +[node name="IpEdit" type="LineEdit" parent="Menu/PlayMenu/MarginContainer/GridContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_font_sizes/font_size = 26 +text = "127.0.0.1" + +[node name="JoinButton" type="Button" parent="Menu/PlayMenu/MarginContainer/GridContainer"] +layout_mode = 2 +theme_override_font_sizes/font_size = 26 +text = "Join" diff --git a/scripts/globals/cursorer.gd b/scripts/globals/cursorer.gd new file mode 100644 index 0000000..cfbff6f --- /dev/null +++ b/scripts/globals/cursorer.gd @@ -0,0 +1,17 @@ +extends Node + + +func _ready() -> void: + Networker.main_loaded.connect(_on_multiplayerer_main_loaded) + + +func _input(event: InputEvent) -> void: + if event.is_action_pressed("toggle_cursor_capture"): + if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED: + Input.mouse_mode = Input.MOUSE_MODE_VISIBLE + else: + Input.mouse_mode = Input.MOUSE_MODE_CAPTURED + + +func _on_multiplayerer_main_loaded() -> void: + Input.mouse_mode = Input.MOUSE_MODE_CAPTURED diff --git a/scripts/globals/networker.gd b/scripts/globals/networker.gd new file mode 100644 index 0000000..5fcc338 --- /dev/null +++ b/scripts/globals/networker.gd @@ -0,0 +1,73 @@ +extends Node + +signal main_loaded + +const DEFAULT_PORT: int = 10567 +const MAX_PEERS: int = 12 + +var _peer: ENetMultiplayerPeer + +var _main_scene := preload("res://scenes/main.tscn") +var _player_scene := preload("res://scenes/player.tscn") + + +func _ready() -> void: + multiplayer.peer_connected.connect(_on_multiplayer_peer_connected) + multiplayer.peer_disconnected.connect(_on_multiplayer_peer_disconnected) + + +func host_game() -> void: + _peer = ENetMultiplayerPeer.new() + _peer.create_server(DEFAULT_PORT, MAX_PEERS) + multiplayer.set_multiplayer_peer(_peer) + + _load_main() + _add_player(multiplayer.get_unique_id()) + + +func join_game(ip: String) -> void: + _peer = ENetMultiplayerPeer.new() + _peer.create_client(ip, DEFAULT_PORT) + multiplayer.set_multiplayer_peer(_peer) + + _load_main() + + +func _load_main() -> void: + var main := _main_scene.instantiate() + get_tree().get_root().add_child(main) + + var title: Node = $/root/Title + title.queue_free() + + get_tree().paused = false + main_loaded.emit() + + +func _add_player(peer_id: int) -> void: + if not multiplayer.is_server(): + return + + var player := _player_scene.instantiate() as Player + player.name = str(peer_id) + + var player_holder: Node = $/root/Main/Players + player_holder.add_child(player, true) + + +func _remove_player(peer_id: int) -> void: + if not multiplayer.is_server(): + return + + var player_holder: Node = $/root/Main/Players + var player := player_holder.get_node_or_null(str(peer_id)) + if player: + player.queue_free() + + +func _on_multiplayer_peer_connected(peer_id: int) -> void: + _add_player(peer_id) + + +func _on_multiplayer_peer_disconnected(peer_id: int) -> void: + _remove_player(peer_id) diff --git a/scripts/main.gd b/scripts/main.gd index 7ae481b..09f5e6c 100644 --- a/scripts/main.gd +++ b/scripts/main.gd @@ -1,10 +1,6 @@ extends Node -func _ready() -> void: - Input.mouse_mode = Input.MOUSE_MODE_CAPTURED - - func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("menu"): get_tree().quit() diff --git a/scripts/player.gd b/scripts/player.gd index 6c0fe5b..19f512f 100644 --- a/scripts/player.gd +++ b/scripts/player.gd @@ -14,10 +14,20 @@ const FALL_ACCELERATION: float = 25 var _respawn_point: Vector3 @onready var _camera: Camera3D = $Camera3D +@onready var _name_label: Label3D = $NameLabel + + +func _enter_tree() -> void: + set_multiplayer_authority(str(name).to_int()) func _ready() -> void: + _name_label.text = str(name) + if not is_multiplayer_authority(): + return + _respawn_point = global_position + _camera.make_current() func _process(_delta: float) -> void: @@ -27,6 +37,9 @@ func _process(_delta: float) -> void: func _physics_process(delta: float) -> void: + if not is_multiplayer_authority(): + return + _lateral_movement(delta) _vertical_movement(delta) @@ -36,11 +49,8 @@ func _physics_process(delta: float) -> void: func _unhandled_input(event: InputEvent) -> void: - if event is InputEventMouseMotion: - var mouse_event := event as InputEventMouseMotion - rotate_y(-mouse_event.screen_relative.x * 0.005) - _camera.rotate_x(-mouse_event.screen_relative.y * 0.005) - _camera.rotation.x = clamp(_camera.rotation.x, -PI / 2, PI / 2) + if not is_multiplayer_authority(): + return if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: if event is InputEventMouseMotion: diff --git a/scripts/ui/play_menu.gd b/scripts/ui/play_menu.gd new file mode 100644 index 0000000..85eab38 --- /dev/null +++ b/scripts/ui/play_menu.gd @@ -0,0 +1,19 @@ +extends Panel + +@onready var name_edit: LineEdit = $MarginContainer/GridContainer/NameEdit +@onready var ip_edit: LineEdit = $MarginContainer/GridContainer/IpEdit +@onready var host_button: Button = $MarginContainer/GridContainer/HostButton +@onready var join_button: Button = $MarginContainer/GridContainer/JoinButton + + +func _ready() -> void: + host_button.pressed.connect(_on_host_button_pressed) + join_button.pressed.connect(_on_join_button_pressed) + + +func _on_host_button_pressed() -> void: + Networker.host_game() + + +func _on_join_button_pressed() -> void: + Networker.join_game(ip_edit.text)