diff --git a/client/character/Character.gd b/client/character/Character.gd index a4dd39a..7f23e9b 100644 --- a/client/character/Character.gd +++ b/client/character/Character.gd @@ -11,11 +11,12 @@ const DECELERATE := 0.75 var direction: Vector3 var angle: float var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity") +var controller: Node func _process(delta): - if direction: + if direction != Vector3.ZERO: angle = atan2(direction.x, direction.z) - + model.rotation.y = lerp_angle(model.rotation.y, angle, rotation_speed * delta) func _physics_process(delta): diff --git a/client/network/PlayerAdd.gd b/client/network/PlayerAdd.gd index 4daed23..0146188 100644 --- a/client/network/PlayerAdd.gd +++ b/client/network/PlayerAdd.gd @@ -21,13 +21,16 @@ func handle_packet(data: PackedByteArray, _peer: PacketPeer): var player := spawn_player() player.position = server_position - player.set_character_name(player_name) + player.set_character_name(player_id) if player_id == Global.account_id: Global.player = player - var controller := PlayerController.new() - controller.character = Global.player - Global.player.add_child(controller) + player.controller = PlayerController.new() + else: + player.controller = ProxyController.new() + + player.controller.character = player + player.add_child(player.controller) func spawn_player() -> Player: var player = player_scene.instantiate() diff --git a/client/network/PlayerMove.gd b/client/network/PlayerMove.gd new file mode 100644 index 0000000..5075c26 --- /dev/null +++ b/client/network/PlayerMove.gd @@ -0,0 +1,43 @@ +extends PacketHandler + +@export var delay := 50 + +var last_sent := Time.get_ticks_msec() +var last_sent_position := Vector3.ZERO + +func handle_packet(data: PackedByteArray, _peer: PacketPeer): + var buffer := StreamPeerBuffer.new() + buffer.data_array = data + + var player_id_length := buffer.get_size() - 8 + var player_id := buffer.get_string(player_id_length) + var x := buffer.get_float() + # var y := buffer.get_float() + var z := buffer.get_float() + + var player := %Players.get_node(player_id) + var controller := player.controller as ProxyController + controller.server_position.x = x + controller.server_position.z = z + +func _physics_process(_delta): + if Global.player == null: + return + + if Global.player.position == last_sent_position: + return + + if Time.get_ticks_msec() < last_sent + delay: + return + + send_position() + last_sent = Time.get_ticks_msec() + last_sent_position = Global.player.position + +func send_position(): + var buffer := StreamPeerBuffer.new() + buffer.put_8(PacketHandler.Packet.PLAYER_MOVE) + buffer.put_float(Global.player.position.x) + # buffer.put_float(Global.player.position.y) + buffer.put_float(Global.player.position.z) + %Client.socket.put_packet(buffer.data_array) diff --git a/client/player/controller/ProxyController.gd b/client/player/controller/ProxyController.gd new file mode 100644 index 0000000..95f03a0 --- /dev/null +++ b/client/player/controller/ProxyController.gd @@ -0,0 +1,22 @@ +class_name ProxyController +extends Node + +## The character that we're controlling. +@export var character: Character + +var server_position: Vector3 + +func _ready(): + server_position = character.position + +func _process(_delta): + var move := server_position - character.position + move.y = 0.0 + + if move.length_squared() < 0.01: + # character.position = server_position + character.direction = Vector3.ZERO + return + + character.direction = Vector3(move.x, 0, move.z) + character.direction = character.direction.normalized() \ No newline at end of file diff --git a/client/project.godot b/client/project.godot index 4dc6b78..967aa24 100644 --- a/client/project.godot +++ b/client/project.godot @@ -21,6 +21,8 @@ Global="*res://Global.gd" [display] +window/size/viewport_width=960 +window/size/viewport_height=540 window/vsync/vsync_mode=0 [gui] diff --git a/client/world/Client.tscn b/client/world/Client.tscn index 79618ff..ba4f471 100644 --- a/client/world/Client.tscn +++ b/client/world/Client.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=17 format=3 uid="uid://b40y7iuskv1ar"] +[gd_scene load_steps=18 format=3 uid="uid://b40y7iuskv1ar"] [ext_resource type="Script" path="res://Game.gd" id="1_pux6q"] [ext_resource type="Script" path="res://network/Client.gd" id="2_1ofik"] @@ -6,6 +6,7 @@ [ext_resource type="Script" path="res://network/Login.gd" id="4_k8n1i"] [ext_resource type="Script" path="res://network/PlayerAdd.gd" id="5_1dcs7"] [ext_resource type="PackedScene" uid="uid://2lcnu3dy54lx" path="res://player/Player.tscn" id="5_22pku"] +[ext_resource type="Script" path="res://network/PlayerMove.gd" id="7_rjgcp"] [ext_resource type="Environment" uid="uid://dixa0yso2s1u3" path="res://world/Environment.tres" id="8_5uta8"] [ext_resource type="CameraAttributesPractical" uid="uid://b835orxyqq6w5" path="res://camera/CameraAttributes.tres" id="9_w4cdu"] [ext_resource type="PackedScene" uid="uid://tgmbtt7u172g" path="res://world/Arena.blend" id="10_cje7b"] @@ -39,6 +40,7 @@ packet_type = 1 autostart = true [node name="Login" type="Node" parent="Client"] +unique_name_in_owner = true script = ExtResource("4_k8n1i") packet_type = 2 @@ -51,6 +53,10 @@ script = ExtResource("5_1dcs7") player_scene = ExtResource("5_22pku") packet_type = 10 +[node name="PlayerMove" type="Node" parent="Client"] +script = ExtResource("7_rjgcp") +packet_type = 12 + [node name="World" type="Node3D" parent="."] [node name="Sun" type="DirectionalLight3D" parent="World"] @@ -84,7 +90,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 7.00829, 1.28057, -1.95247) [node name="Slime3" parent="World/Enemies" instance=ExtResource("12_6yrwn")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8.53558, 1.28057, -0.306177) -[node name="Players" type="Node3D" parent="World"] +[node name="Players" type="Node3D" parent="."] unique_name_in_owner = true [node name="UI" parent="." instance=ExtResource("13_s76b0")] @@ -100,7 +106,7 @@ stretch = true [node name="SubViewport" type="SubViewport" parent="Viewport"] handle_input_locally = false -size = Vector2i(1152, 648) +size = Vector2i(960, 540) render_target_update_mode = 4 [node name="CameraPivot" type="Node3D" parent="Viewport/SubViewport"] diff --git a/server/game/Game.go b/server/game/Game.go index 9937978..5fea9a0 100644 --- a/server/game/Game.go +++ b/server/game/Game.go @@ -41,6 +41,7 @@ func (game *Game) start() { func (game *Game) network() { game.server.SetHandler(packet.Ping, game.Ping) game.server.SetHandler(packet.Login, game.Login) + game.server.SetHandler(packet.PlayerMove, game.Move) game.server.Run(4242) } diff --git a/server/game/Login.go b/server/game/Login.go index 7f3b203..59210ae 100644 --- a/server/game/Login.go +++ b/server/game/Login.go @@ -3,7 +3,6 @@ package game import ( "crypto/rand" "encoding/base64" - "encoding/binary" "encoding/json" "errors" "net" @@ -46,20 +45,14 @@ func (game *Game) Login(data []byte, address *net.UDPAddr, server *Server) error player.KeepAlive() response := []byte{Success} - response = appendString(response, account.ID) - response = appendString(response, player.authToken) + response = AppendString(response, account.ID) + response = AppendString(response, player.authToken) server.Send(packet.Login, response, address) game.onLogin(player) return nil } -func appendString(data []byte, str string) []byte { - data = binary.LittleEndian.AppendUint32(data, uint32(len(str))) - data = append(data, []byte(str)...) - return data -} - // Inform the newly logged in player about existing players. // Also inform existing players about the newly logged in player. func (game *Game) onLogin(player *Player) { diff --git a/server/game/Move.go b/server/game/Move.go new file mode 100644 index 0000000..b77e4bf --- /dev/null +++ b/server/game/Move.go @@ -0,0 +1,30 @@ +package game + +import ( + "encoding/binary" + "math" + "net" + "server/game/packet" +) + +// Move updates the location and direction the client is currently moving towards. +func (game *Game) Move(data []byte, address *net.UDPAddr, server *Server) error { + player := game.players.Get(address) + player.Position.X = math.Float32frombits(binary.LittleEndian.Uint32(data)) + player.Position.Z = math.Float32frombits(binary.LittleEndian.Uint32(data[4:])) + + update := []byte(player.ID) + update = AppendFloat(update, player.Position.X) + update = AppendFloat(update, player.Position.Z) + + game.players.Each(func(other *Player) bool { + if other == player { + return true + } + + game.server.Send(packet.PlayerMove, update, other.address) + return true + }) + + return nil +} diff --git a/server/game/Player.go b/server/game/Player.go index 6db2fa5..a1278ea 100644 --- a/server/game/Player.go +++ b/server/game/Player.go @@ -40,9 +40,9 @@ func (c *Player) Tick() { // State returns the player state (ID, name and position). func (player *Player) State() []byte { state := []byte{} - state = appendString(state, player.ID) - state = appendString(state, player.Name) - state = appendVector3(state, player.Position) + state = AppendString(state, player.ID) + state = AppendString(state, player.Name) + state = AppendVector3(state, player.Position) return state } diff --git a/server/game/String.go b/server/game/String.go new file mode 100644 index 0000000..0679a0c --- /dev/null +++ b/server/game/String.go @@ -0,0 +1,10 @@ +package game + +import "encoding/binary" + +// AppendString appends the length of the string followed by its contents. +func AppendString(data []byte, str string) []byte { + data = binary.LittleEndian.AppendUint32(data, uint32(len(str))) + data = append(data, []byte(str)...) + return data +} diff --git a/server/game/Vector3.go b/server/game/Vector3.go index 81da73f..a93e965 100644 --- a/server/game/Vector3.go +++ b/server/game/Vector3.go @@ -12,8 +12,8 @@ type Vector3 struct { Z float32 `json:"z"` } -// appendVector3 adds the raw bits of the vector to the given byte slice in XYZ order. -func appendVector3(data []byte, vector Vector3) []byte { +// AppendVector3 appends the raw bits of the vector to the given byte slice in XYZ order. +func AppendVector3(data []byte, vector Vector3) []byte { bits := math.Float32bits(vector.X) data = binary.LittleEndian.AppendUint32(data, bits) @@ -23,3 +23,17 @@ func appendVector3(data []byte, vector Vector3) []byte { bits = math.Float32bits(vector.Z) return binary.LittleEndian.AppendUint32(data, bits) } + +// AppendFloat appends the raw bits of the float to the given byte slice in XYZ order. +func AppendFloat(data []byte, value float32) []byte { + bits := math.Float32bits(value) + return binary.LittleEndian.AppendUint32(data, bits) +} + +func Vector3FromBytes(data []byte) Vector3 { + return Vector3{ + X: math.Float32frombits(binary.LittleEndian.Uint32(data)), + Y: math.Float32frombits(binary.LittleEndian.Uint32(data[4:])), + Z: math.Float32frombits(binary.LittleEndian.Uint32(data[8:])), + } +}