From 74694080b48b163cca752812c576aff98bdd9368 Mon Sep 17 00:00:00 2001 From: Fat mimir Date: Mon, 27 Feb 2023 21:27:32 -0600 Subject: [PATCH] Simple networking --- .gitattributes | 2 ++ .gitignore | 2 ++ icon.svg | 1 + icon.svg.import | 37 +++++++++++++++++++++++++ main.gd | 53 +++++++++++++++++++++++++++++++++++ main.tscn | 62 +++++++++++++++++++++++++++++++++++++++++ player.gd | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ player.tscn | 32 +++++++++++++++++++++ project.godot | 44 +++++++++++++++++++++++++++++ 9 files changed, 319 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 icon.svg create mode 100644 icon.svg.import create mode 100644 main.gd create mode 100644 main.tscn create mode 100644 player.gd create mode 100644 player.tscn create mode 100644 project.godot diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8ad74f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4709183 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Godot 4+ specific ignores +.godot/ diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..adc26df --- /dev/null +++ b/icon.svg @@ -0,0 +1 @@ + diff --git a/icon.svg.import b/icon.svg.import new file mode 100644 index 0000000..286319a --- /dev/null +++ b/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d0grjf1jatuoi" +path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.svg" +dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/main.gd b/main.gd new file mode 100644 index 0000000..84ebbce --- /dev/null +++ b/main.gd @@ -0,0 +1,53 @@ +extends Node3D + +const Player := preload("res://player.tscn") + +@onready +var ui_main_menu := $UIMainMenu + +@onready +var players := $Players + + +func _ready(): + pass + + +func _process(_delta): + pass + + +func _on_button_start_as_server_pressed(): + var peer := ENetMultiplayerPeer.new() + peer.create_server(9080, 10) + peer.peer_connected.connect(self._on_peer_connect) + peer.peer_disconnected.connect(self._on_peer_disconnected) + multiplayer.multiplayer_peer = peer + ui_main_menu.visible = false + + # Server connected + _on_peer_connect(get_multiplayer_authority()) + + +func _on_button_start_as_client_pressed(): + var peer := ENetMultiplayerPeer.new() + peer.create_client("localhost", 9080) + multiplayer.multiplayer_peer = peer + ui_main_menu.visible = false + + +func _on_peer_connect(id): + print("(server) peer connected: ", id) + var player: Node3D = Player.instantiate() + player.name = str(id) + player.set_multiplayer_authority(id) + player.global_transform = players.global_transform.translated(Vector3.UP * 3) + players.add_child(player, true) + + +func _on_peer_disconnected(id): + print("(server) peer disconnected: ", id) + var player := players.get_node_or_null(str(id)) + if player: + player.queue_free() + diff --git a/main.tscn b/main.tscn new file mode 100644 index 0000000..b5336a6 --- /dev/null +++ b/main.tscn @@ -0,0 +1,62 @@ +[gd_scene load_steps=3 format=3 uid="uid://cncputkhqxo0m"] + +[ext_resource type="Script" path="res://main.gd" id="1_mw2j2"] + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_p8g5e"] +albedo_color = Color(0.211765, 0.211765, 0.211765, 1) + +[node name="Main" type="Node3D"] +script = ExtResource("1_mw2j2") + +[node name="Map" type="CSGCombiner3D" parent="."] +use_collision = true + +[node name="Floor" type="CSGBox3D" parent="Map"] +size = Vector3(20, 0.1, 20) +material = SubResource("StandardMaterial3D_p8g5e") + +[node name="Sun" type="DirectionalLight3D" parent="."] +transform = Transform3D(0.707107, -0.5, 0.5, 0, 0.707107, 0.707107, -0.707107, -0.5, 0.5, -4.68001, 6.95, 4.07778) +shadow_enabled = true + +[node name="MainCamera" type="Camera3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 0.707107, 0.707107, 0, -0.707107, 0.707107, 0, 10, 13) + +[node name="Players" type="Node3D" parent="."] + +[node name="MultiplayerSpawner" type="MultiplayerSpawner" parent="."] +_spawnable_scenes = PackedStringArray("res://player.tscn") +spawn_path = NodePath("../Players") + +[node name="UIMainMenu" type="ColorRect" parent="."] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0, 0, 0, 1) + +[node name="ContainerOptions" type="VBoxContainer" parent="UIMainMenu"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -20.0 +offset_top = -20.0 +offset_right = 20.0 +offset_bottom = 20.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="ButtonStartAsServer" type="Button" parent="UIMainMenu/ContainerOptions"] +layout_mode = 2 +text = "Start as server" + +[node name="ButtonStartAsClient" type="Button" parent="UIMainMenu/ContainerOptions"] +layout_mode = 2 +text = "Start as client" + +[connection signal="pressed" from="UIMainMenu/ContainerOptions/ButtonStartAsServer" to="." method="_on_button_start_as_server_pressed"] +[connection signal="pressed" from="UIMainMenu/ContainerOptions/ButtonStartAsClient" to="." method="_on_button_start_as_client_pressed"] diff --git a/player.gd b/player.gd new file mode 100644 index 0000000..78451d5 --- /dev/null +++ b/player.gd @@ -0,0 +1,86 @@ +extends CharacterBody3D + +# We don't actually want to fully interpolate between states +# since it may result in "jumpy" transitions +const STATE_INTERP_MIN = 0.2 +const STATE_INTERP_MAX = 0.8 + +var gravity = ProjectSettings.get_setting("physics/3d/default_gravity") +var sync_state := PackedByteArray(): get = _get_sync_state, set = _set_sync_state + +@export var speed := 5.0 +@export var jump_speed := 5.0 +@onready var gametag := $Gametag +@onready var target_origin := global_transform.origin + +var last_snapshot_tag := 0 +var snapshot_interp_start := 0.0 +var snapshot_interp_end := 0.0 + +func _process(_delta): + gametag.text = "[%s %d]" % [name, get_multiplayer_authority()] + +func _physics_process(delta): + if is_multiplayer_authority(): + velocity.y += -gravity * delta + var input := Input.get_vector("left", "right", "forward", "backward") + var direction := transform.basis * Vector3(input.x, 0, input.y) + velocity.x = direction.x * speed + velocity.z = direction.z * speed + move_and_slide() + + if is_on_floor() and Input.is_action_just_pressed("jump"): + velocity.y = jump_speed + + else: + var interp_value = remap_range( + Time.get_ticks_msec(), + snapshot_interp_start, + snapshot_interp_end, + STATE_INTERP_MIN, + STATE_INTERP_MAX + ) + if interp_value >= 0.0 and interp_value <= 1.0: + global_transform.origin = global_transform.origin.slerp( + target_origin, interp_value + ) + else: + global_transform.origin = target_origin + + +func _enter_tree(): + if name == str(multiplayer.get_unique_id()): + set_multiplayer_authority(multiplayer.get_unique_id()) + + +func _get_sync_state(): + var encoder := PackedByteArray() + encoder.resize(20) + encoder.encode_u64(0, Time.get_ticks_msec()) + encoder.encode_float(8, transform.origin.x) + encoder.encode_float(12, transform.origin.y) + encoder.encode_float(16, transform.origin.z) + return encoder + +func _set_sync_state(value): + assert( + typeof(value) == TYPE_PACKED_BYTE_ARRAY and value.size() == 20, + "Invalid `sync_state` array type or size (must be TYPE_RAW_ARRAY of size 20)." + ) + var time_tag: int = value.decode_u64(0) + if time_tag > last_snapshot_tag: + var snapshot_time_diff = time_tag - last_snapshot_tag + if global_transform: + global_transform.origin = target_origin # Override last origin + + snapshot_interp_start = Time.get_ticks_msec() + snapshot_interp_end = Time.get_ticks_msec() + snapshot_time_diff + last_snapshot_tag = time_tag + target_origin = Vector3( + value.decode_float(8), + value.decode_float(12), + value.decode_float(16), + ) + +func remap_range(value, in_a, in_b, out_a, out_b): + return (value - in_a) / (in_b - in_a) * (out_b - out_a) + out_a diff --git a/player.tscn b/player.tscn new file mode 100644 index 0000000..bc9b147 --- /dev/null +++ b/player.tscn @@ -0,0 +1,32 @@ +[gd_scene load_steps=5 format=3 uid="uid://cq06n5dnq6xc2"] + +[ext_resource type="Script" path="res://player.gd" id="1_dokki"] + +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_k38on"] + +[sub_resource type="CapsuleMesh" id="CapsuleMesh_wtqcn"] + +[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_my5gd"] +properties/0/path = NodePath(".:sync_state") +properties/0/spawn = true +properties/0/sync = true + +[node name="Player" type="CharacterBody3D"] +script = ExtResource("1_dokki") + +[node name="Gametag" type="Label3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 3.148, 0) +pixel_size = 0.03 +text = "AAAAAAAAAAAAAA" + +[node name="CollisionShape" type="CollisionShape3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) +shape = SubResource("CapsuleShape3D_k38on") + +[node name="MeshInstance" type="MeshInstance3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) +mesh = SubResource("CapsuleMesh_wtqcn") + +[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."] +replication_interval = 0.1 +replication_config = SubResource("SceneReplicationConfig_my5gd") diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..247ad4e --- /dev/null +++ b/project.godot @@ -0,0 +1,44 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="Godot4 Standard Networking" +run/main_scene="res://main.tscn" +config/features=PackedStringArray("4.0", "Forward Plus") +config/icon="res://icon.svg" + +[input] + +forward={ +"deadzone": 0.5, +"events": [null, 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":87,"key_label":0,"unicode":119,"echo":false,"script":null) +] +} +backward={ +"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":83,"key_label":0,"unicode":115,"echo":false,"script":null) +] +} +left={ +"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":65,"key_label":0,"unicode":97,"echo":false,"script":null) +] +} +right={ +"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":68,"key_label":0,"unicode":100,"echo":false,"script":null) +] +} +jump={ +"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":32,"key_label":0,"unicode":32,"echo":false,"script":null) +] +}