From b885d7062587876f1ade7855254ba7bcfe0ef192 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Mon, 15 Jan 2024 17:08:26 +0100 Subject: [PATCH] Improved network code --- client/Client.gd | 19 ++++++- player/Player.gd | 4 +- project.godot | 2 +- server/core/Server.go | 58 ++++++++++++++----- server/login.go | 13 +++++ server/main.go | 13 ++--- server/packet/types.go | 8 +++ server/ping.go | 14 +++++ ui/Ping.gd | 19 +++---- world/Environment.tres | 12 ++-- world/{World.gd => Game.gd} | 0 world/Game.tscn | 108 ++++++++++++++++++++++++++++++++++++ world/ProceduralSky.tres | 4 -- world/RotateY.gd | 6 ++ world/Sky.tres | 6 -- world/Sun.gd | 5 ++ world/World.tscn | 22 -------- 17 files changed, 241 insertions(+), 72 deletions(-) create mode 100644 server/login.go create mode 100644 server/packet/types.go create mode 100644 server/ping.go rename world/{World.gd => Game.gd} (100%) create mode 100644 world/Game.tscn delete mode 100644 world/ProceduralSky.tres create mode 100644 world/RotateY.gd delete mode 100644 world/Sky.tres create mode 100644 world/Sun.gd delete mode 100644 world/World.tscn diff --git a/client/Client.gd b/client/Client.gd index 76344b8..f688038 100644 --- a/client/Client.gd +++ b/client/Client.gd @@ -5,6 +5,23 @@ var udp := PacketPeerUDP.new() func _ready(): udp.connect_to_host("127.0.0.1", 4242) + send_login() + +func _process(_delta): + if Client.udp.get_available_packet_count() <= 0: + return + + var packet := Client.udp.get_packet() + var type := packet.decode_u8(0) + print("Packet type %d data size %d" % [type, packet.size()-1]) + +func send_login(): + var login_data = PackedByteArray() + login_data.push_back(2) + udp.put_packet(login_data) + print("Connecting...") + +func spawn_player(): var player = PLAYER.instantiate() add_child(player) - print("Ready.") + diff --git a/player/Player.gd b/player/Player.gd index 407028f..dce3bc7 100644 --- a/player/Player.gd +++ b/player/Player.gd @@ -3,5 +3,5 @@ extends Node3D func _ready(): pass -func _process(delta): - rotate_y(delta) +func _process(_delta): + pass diff --git a/project.godot b/project.godot index ce69b08..3c50ed4 100644 --- a/project.godot +++ b/project.godot @@ -11,7 +11,7 @@ config_version=5 [application] config/name="Battle of Mages" -run/main_scene="res://world/World.tscn" +run/main_scene="res://world/Game.tscn" config/features=PackedStringArray("4.2", "Forward Plus") config/icon="res://ui/icon.svg" diff --git a/server/core/Server.go b/server/core/Server.go index d1e79f3..93e69ce 100644 --- a/server/core/Server.go +++ b/server/core/Server.go @@ -3,6 +3,8 @@ package core import ( "fmt" "net" + "sync" + "sync/atomic" "time" ) @@ -12,15 +14,38 @@ type Handler func([]byte, *Client) // Server represents a UDP server. type Server struct { socket *net.UDPConn - clients map[string]*Client handlers [256]Handler + clients sync.Map + count atomic.Int64 } // New creates a new server. func New() *Server { - return &Server{ - clients: make(map[string]*Client), - } + timeout := 3 * time.Second + interval := time.Second + server := &Server{} + + go func() { + ticker := time.NewTicker(interval) + defer ticker.Stop() + + for range ticker.C { + now := time.Now() + + server.clients.Range(func(key, value interface{}) bool { + item := value.(*Client) + + if !item.lastPacket.IsZero() && now.After(item.lastPacket.Add(timeout)) { + server.clients.Delete(key) + server.count.Add(-1) + } + + return true + }) + } + }() + + return server } // AddHandler adds the handler for the given byte code. @@ -28,8 +53,14 @@ func (s *Server) AddHandler(code byte, handler Handler) { s.handlers[code] = handler } -// SendTo sends the data to a client. -func (s *Server) SendTo(data []byte, client *Client) { +// Count returns the number of connected clients. +func (s *Server) Count() int { + return int(s.count.Load()) +} + +// Send sends the data prefixed with the byte code to the client. +func (s *Server) Send(code byte, data []byte, client *Client) { + data = append([]byte{code}, data...) _, err := s.socket.WriteToUDP(data, client.address) if err != nil { @@ -91,25 +122,26 @@ func (s *Server) handle(data []byte, addr *net.UDPAddr) { handler := s.handlers[data[0]] if handler == nil { - fmt.Println("Unknown packet type.") + fmt.Printf("No callback registered for packet type %d\n", data[0]) return } - handler(data, c) + handler(data[1:], c) } // getClient either returns a new or existing client for the requested address. func (s *Server) getClient(addr *net.UDPAddr) *Client { - c, exists := s.clients[addr.String()] + obj, exists := s.clients.Load(addr.String()) if exists { - return c + return obj.(*Client) } - c = &Client{ + client := &Client{ address: addr, } - s.clients[addr.String()] = c - return c + s.clients.Store(addr.String(), client) + s.count.Add(1) + return client } diff --git a/server/login.go b/server/login.go new file mode 100644 index 0000000..3b1fee5 --- /dev/null +++ b/server/login.go @@ -0,0 +1,13 @@ +package main + +import ( + "fmt" + "server/core" + "server/packet" +) + +// login checks the account credentials and gives a network peer access to an account. +func login(data []byte, client *core.Client) { + fmt.Println("2 - login!") + server.Send(packet.LOGIN, nil, client) +} diff --git a/server/main.go b/server/main.go index 435dd35..bb8e65b 100644 --- a/server/main.go +++ b/server/main.go @@ -2,16 +2,13 @@ package main import ( "server/core" + "server/packet" ) +var server = core.New() + func main() { - server := core.New() - - server.AddHandler(0, func(data []byte, client *core.Client) { - // count := data[1] - // fmt.Println(count) - server.SendTo(data, client) - }) - + server.AddHandler(packet.PING, ping) + server.AddHandler(packet.LOGIN, login) server.Run(4242) } diff --git a/server/packet/types.go b/server/packet/types.go new file mode 100644 index 0000000..f722be9 --- /dev/null +++ b/server/packet/types.go @@ -0,0 +1,8 @@ +package packet + +const ( + PING = 1 + LOGIN = 2 + LOGOUT = 3 + MOVE = 10 +) diff --git a/server/ping.go b/server/ping.go new file mode 100644 index 0000000..abcf4e9 --- /dev/null +++ b/server/ping.go @@ -0,0 +1,14 @@ +package main + +import ( + "fmt" + "server/core" + "server/packet" +) + +// ping is used as a heartbeat and latency check. +func ping(data []byte, client *core.Client) { + fmt.Println("1 - ping!") + server.Send(packet.PING, data, client) + fmt.Println(server.Count(), "clients") +} diff --git a/ui/Ping.gd b/ui/Ping.gd index 07edfee..37065b6 100644 --- a/ui/Ping.gd +++ b/ui/Ping.gd @@ -8,7 +8,6 @@ var pingSent: Array[float] = [] func _ready(): var timer := Timer.new() add_child(timer) - timer.autostart = true timer.wait_time = 1 timer.connect("timeout", self._ping) timer.start() @@ -16,18 +15,18 @@ func _ready(): pingSent.resize(HISTORY_SIZE) func _process(_delta): - if Client.udp.get_available_packet_count() > 0: - #print("Received: %s" % udp.get_packet().get_string_from_utf8()) - var bytes := Client.udp.get_packet() - var count := bytes.decode_u8(1) - var timeSent := pingSent[count] - var duration := Time.get_unix_time_from_system() - timeSent - var ping := duration * 1000 - text = str(snapped(ping, 0.01)) + pass + #if Client.udp.get_available_packet_count() > 0: + #var bytes := Client.udp.get_packet() + #var count := bytes.decode_u8(1) + #var timeSent := pingSent[count] + #var duration := Time.get_unix_time_from_system() - timeSent + #var ping := duration * 1000 + #text = str(snapped(ping, 0.01)) func _ping(): var buffer := StreamPeerBuffer.new() - buffer.put_8(0) + buffer.put_8(1) buffer.put_8(pingCount) Client.udp.put_packet(buffer.data_array) diff --git a/world/Environment.tres b/world/Environment.tres index 883f82f..76f3fe8 100644 --- a/world/Environment.tres +++ b/world/Environment.tres @@ -1,8 +1,10 @@ -[gd_resource type="Environment" load_steps=2 format=3 uid="uid://dixa0yso2s1u3"] - -[ext_resource type="Sky" uid="uid://b0q75qnaj0r5h" path="res://world/Sky.tres" id="1_utnj1"] +[gd_resource type="Environment" format=3 uid="uid://dixa0yso2s1u3"] [resource] -sky = ExtResource("1_utnj1") +background_mode = 1 +background_color = Color(0.317647, 0.541176, 0.713726, 1) ambient_light_source = 3 -reflected_light_source = 2 +ambient_light_color = Color(0.607843, 0.756863, 0.92549, 1) +ambient_light_sky_contribution = 0.17 +tonemap_mode = 2 +adjustment_saturation = 0.01 diff --git a/world/World.gd b/world/Game.gd similarity index 100% rename from world/World.gd rename to world/Game.gd diff --git a/world/Game.tscn b/world/Game.tscn new file mode 100644 index 0000000..d2836ea --- /dev/null +++ b/world/Game.tscn @@ -0,0 +1,108 @@ +[gd_scene load_steps=16 format=3 uid="uid://b40y7iuskv1ar"] + +[ext_resource type="Script" path="res://world/Game.gd" id="1_xmqq4"] +[ext_resource type="PackedScene" uid="uid://bxotvk73tbgw0" path="res://ui/UI.tscn" id="2_x1l7l"] +[ext_resource type="Script" path="res://world/RotateY.gd" id="3_4gn6n"] +[ext_resource type="Shader" path="res://world/shader/Outline.gdshader" id="4_gweie"] +[ext_resource type="Environment" uid="uid://dixa0yso2s1u3" path="res://world/Environment.tres" id="5_bll74"] +[ext_resource type="Script" path="res://world/Sun.gd" id="5_pf5uw"] +[ext_resource type="CameraAttributesPractical" uid="uid://b835orxyqq6w5" path="res://world/CameraAttributes.tres" id="6_8wfwf"] + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_r8n03"] +diffuse_mode = 3 +albedo_color = Color(0.482353, 0.470588, 0.47451, 1) + +[sub_resource type="BoxMesh" id="BoxMesh_0bujj"] +material = SubResource("StandardMaterial3D_r8n03") +size = Vector3(20, 0.5, 20) + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_4w7ln"] +diffuse_mode = 3 +albedo_color = Color(0.294118, 0.356863, 0.439216, 1) + +[sub_resource type="BoxMesh" id="BoxMesh_hf021"] +material = SubResource("StandardMaterial3D_4w7ln") + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ahmjh"] +diffuse_mode = 3 +albedo_color = Color(0.14902, 0.517647, 1, 1) +roughness = 0.08 + +[sub_resource type="TorusMesh" id="TorusMesh_mu45b"] +material = SubResource("StandardMaterial3D_ahmjh") + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_qm56v"] +render_priority = 0 +shader = ExtResource("4_gweie") +shader_parameter/depth_threshold = 0.05 +shader_parameter/reverse_depth_threshold = 0.25 +shader_parameter/normal_threshold = 0.6 +shader_parameter/darken_amount = 0.3 +shader_parameter/lighten_amount = 1.5 +shader_parameter/light_direction = Vector3(0.122788, -0.707107, -0.696364) + +[sub_resource type="QuadMesh" id="QuadMesh_7yiqd"] +material = SubResource("ShaderMaterial_qm56v") +flip_faces = true +size = Vector2(2, 2) + +[node name="Game" type="Node"] +script = ExtResource("1_xmqq4") + +[node name="World" type="Node3D" parent="."] + +[node name="Ground" type="MeshInstance3D" parent="World"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.25, 0) +mesh = SubResource("BoxMesh_0bujj") +skeleton = NodePath("../..") + +[node name="Box" type="MeshInstance3D" parent="World"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, -4) +mesh = SubResource("BoxMesh_hf021") +skeleton = NodePath("../../SubViewportContainer/SubViewport") + +[node name="Torus" type="MeshInstance3D" parent="World"] +transform = Transform3D(0.7, 0, 0, 0, -3.0598e-08, -0.7, 0, 0.7, -3.0598e-08, 0, 1.79933, -4) +mesh = SubResource("TorusMesh_mu45b") +skeleton = NodePath("../../SubViewportContainer/SubViewport") +script = ExtResource("3_4gn6n") + +[node name="UI" parent="." instance=ExtResource("2_x1l7l")] + +[node name="SubViewportContainer" type="SubViewportContainer" parent="."] +texture_filter = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +stretch = true +stretch_shrink = 3 + +[node name="SubViewport" type="SubViewport" parent="SubViewportContainer"] +handle_input_locally = false +size = Vector2i(384, 216) +render_target_update_mode = 4 + +[node name="Camera" type="Camera3D" parent="SubViewportContainer/SubViewport"] +transform = Transform3D(0.707107, 0.331966, -0.624338, 0, 0.882947, 0.469472, 0.707107, -0.331966, 0.624338, -5, 5, 5) +projection = 1 +current = true +fov = 90.0 +size = 9.6 +near = 0.001 + +[node name="PostProcessing" type="MeshInstance3D" parent="SubViewportContainer/SubViewport/Camera"] +unique_name_in_owner = true +extra_cull_margin = 16384.0 +mesh = SubResource("QuadMesh_7yiqd") + +[node name="Sun" type="DirectionalLight3D" parent="SubViewportContainer/SubViewport"] +transform = Transform3D(0.984808, 0.122788, -0.122788, 0, 0.707107, 0.707107, 0.173648, -0.696364, 0.696364, 0, 5, 0) +shadow_enabled = true +directional_shadow_split_1 = 0.028 +script = ExtResource("5_pf5uw") + +[node name="Environment" type="WorldEnvironment" parent="SubViewportContainer/SubViewport"] +environment = ExtResource("5_bll74") +camera_attributes = ExtResource("6_8wfwf") diff --git a/world/ProceduralSky.tres b/world/ProceduralSky.tres deleted file mode 100644 index 3844b50..0000000 --- a/world/ProceduralSky.tres +++ /dev/null @@ -1,4 +0,0 @@ -[gd_resource type="ProceduralSkyMaterial" format=3 uid="uid://b7q6crweeh3jv"] - -[resource] -ground_bottom_color = Color(0.0313726, 0.0470588, 0.160784, 1) diff --git a/world/RotateY.gd b/world/RotateY.gd new file mode 100644 index 0000000..df387a2 --- /dev/null +++ b/world/RotateY.gd @@ -0,0 +1,6 @@ +extends Node3D + +@export var speed: float = 1.0 + +func _process(delta): + rotate_y(speed * delta) diff --git a/world/Sky.tres b/world/Sky.tres deleted file mode 100644 index 7c5edf7..0000000 --- a/world/Sky.tres +++ /dev/null @@ -1,6 +0,0 @@ -[gd_resource type="Sky" load_steps=2 format=3 uid="uid://b0q75qnaj0r5h"] - -[ext_resource type="Material" uid="uid://b7q6crweeh3jv" path="res://world/ProceduralSky.tres" id="1_7mt7h"] - -[resource] -sky_material = ExtResource("1_7mt7h") diff --git a/world/Sun.gd b/world/Sun.gd new file mode 100644 index 0000000..e05e025 --- /dev/null +++ b/world/Sun.gd @@ -0,0 +1,5 @@ +@tool +extends DirectionalLight3D + +func _process(_delta): + (%PostProcessing as MeshInstance3D).mesh.surface_get_material(0).set_shader_parameter("light_direction", -global_basis.z) diff --git a/world/World.tscn b/world/World.tscn deleted file mode 100644 index a11e9bf..0000000 --- a/world/World.tscn +++ /dev/null @@ -1,22 +0,0 @@ -[gd_scene load_steps=5 format=3 uid="uid://b40y7iuskv1ar"] - -[ext_resource type="Script" path="res://world/World.gd" id="1_2lci4"] -[ext_resource type="Environment" uid="uid://dixa0yso2s1u3" path="res://world/Environment.tres" id="1_qb8w4"] -[ext_resource type="CameraAttributesPractical" uid="uid://b835orxyqq6w5" path="res://world/CameraAttributes.tres" id="2_1nt3m"] -[ext_resource type="PackedScene" uid="uid://bxotvk73tbgw0" path="res://ui/UI.tscn" id="4_c6x8y"] - -[node name="World" type="Node"] -script = ExtResource("1_2lci4") - -[node name="UI" parent="." instance=ExtResource("4_c6x8y")] - -[node name="Camera" type="Camera3D" parent="."] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 5) -fov = 90.0 - -[node name="Sun" type="DirectionalLight3D" parent="."] -transform = Transform3D(0.904299, 0.26004, -0.33856, 0, 0.793066, 0.609135, 0.4269, -0.55084, 0.717169, 0, 0, 0) - -[node name="Environment" type="WorldEnvironment" parent="."] -environment = ExtResource("1_qb8w4") -camera_attributes = ExtResource("2_1nt3m")