From 2cd93f2a395a3aa48591af16fc085f1bcaf8670d Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Fri, 26 Jan 2024 12:50:46 +0100 Subject: [PATCH] Added player identification --- client/Global.gd | 3 + client/network/Login.gd | 27 +++++--- .../network/{PlayerState.gd => PlayerAdd.gd} | 19 +++--- client/network/shared/PacketHandler.gd | 5 +- client/player/Player.tscn | 4 +- client/world/Client.tscn | 6 +- server/game/Account.go | 9 +++ server/game/Database.go | 33 +++++++++ server/game/Login.go | 68 +++++++++++-------- server/game/Player.go | 16 +++-- server/game/PlayerManager.go | 26 ++++--- server/game/packet/packet.go | 10 +-- 12 files changed, 153 insertions(+), 73 deletions(-) rename client/network/{PlayerState.gd => PlayerAdd.gd} (58%) create mode 100644 server/game/Account.go create mode 100644 server/game/Database.go diff --git a/client/Global.gd b/client/Global.gd index 11e57bc..90fd911 100644 --- a/client/Global.gd +++ b/client/Global.gd @@ -2,3 +2,6 @@ extends Node var camera: Camera3D var player: Player +var instance_id := OS.get_process_id() % 4 +var username := "user%d" % instance_id +var account_id: String diff --git a/client/network/Login.gd b/client/network/Login.gd index 31c35f7..8f53c09 100644 --- a/client/network/Login.gd +++ b/client/network/Login.gd @@ -1,21 +1,28 @@ extends PacketHandler var auth_token: String -var instance_id := OS.get_process_id() % 4 -var username := "user%d" % instance_id func _ready(): - DisplayServer.window_set_title(username) + DisplayServer.window_set_title(Global.username) send_login() func handle_packet(data: PackedByteArray, _peer: PacketPeer): - if data[0] != 0: - print("[Client] Login failed.") + var buffer := StreamPeerBuffer.new() + buffer.data_array = data + + var error := buffer.get_8() + if error != 0: + print("[%s] Login failed." % Global.username) return - print("[Client] Login succeeded.") - auth_token = data.slice(1).get_string_from_ascii() - print("[Client] Auth token: %s" % auth_token) + Global.account_id = buffer.get_string() + auth_token = buffer.get_string() + + print("[%s] Login succeeded." % Global.username) + print("[%s] ID: %s" % [Global.username, Global.account_id]) + print("[%s] Auth token: %s" % [Global.username, auth_token]) + + DisplayServer.window_set_title("%s - %s" % [Global.username, Global.account_id]) func send_login(): if is_logged_in(): @@ -24,9 +31,9 @@ func send_login(): var password := "password" var buffer := StreamPeerBuffer.new() buffer.put_8(Packet.LOGIN) - buffer.put_data(JSON.stringify([username, password]).to_utf8_buffer()) + buffer.put_data(JSON.stringify([Global.username, password]).to_utf8_buffer()) %Client.socket.put_packet(buffer.data_array) - print("[Client] Connecting...") + print("[%s] Connecting..." % Global.username) func is_logged_in() -> bool: return auth_token != "" \ No newline at end of file diff --git a/client/network/PlayerState.gd b/client/network/PlayerAdd.gd similarity index 58% rename from client/network/PlayerState.gd rename to client/network/PlayerAdd.gd index 7fb535d..4daed23 100644 --- a/client/network/PlayerState.gd +++ b/client/network/PlayerAdd.gd @@ -6,21 +6,24 @@ func _ready(): assert(player_scene) func handle_packet(data: PackedByteArray, _peer: PacketPeer): - var player_name := data.get_string_from_utf8() - print(player_name) + var buffer := StreamPeerBuffer.new() + buffer.data_array = data + + var player_id := buffer.get_string() + var player_name := buffer.get_string() - var position_data := data.slice(player_name.length() + 1) var server_position := Vector3() - server_position.x = position_data.decode_float(0) - server_position.y = position_data.decode_float(4) - server_position.z = position_data.decode_float(8) - print(server_position) + server_position.x = buffer.get_float() + server_position.y = buffer.get_float() + server_position.z = buffer.get_float() + + print("[%s] Add player: %s %s @ %v" % [Global.username, player_id, player_name, server_position]) var player := spawn_player() player.position = server_position player.set_character_name(player_name) - if false: + if player_id == Global.account_id: Global.player = player var controller := PlayerController.new() controller.character = Global.player diff --git a/client/network/shared/PacketHandler.gd b/client/network/shared/PacketHandler.gd index 95bb5c1..7b3f2e8 100644 --- a/client/network/shared/PacketHandler.gd +++ b/client/network/shared/PacketHandler.gd @@ -6,8 +6,9 @@ enum Packet { PING = 1, LOGIN = 2, LOGOUT = 3, - PLAYER_STATE = 10, - PLAYER_MOVE = 11, + PLAYER_ADD = 10, + PLAYER_REMOVE = 11, + PLAYER_MOVE = 12, } @export var packet_type: Packet diff --git a/client/player/Player.tscn b/client/player/Player.tscn index 715e433..b993a27 100644 --- a/client/player/Player.tscn +++ b/client/player/Player.tscn @@ -29,6 +29,6 @@ shape = SubResource("CapsuleShape3D_2f50n") [node name="Health" parent="." instance=ExtResource("2_np5ag")] [node name="Label" type="Label3D" parent="."] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.8, 0) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, 0) billboard = 1 -text = "ABC" +text = "Player" diff --git a/client/world/Client.tscn b/client/world/Client.tscn index 4512ee7..79618ff 100644 --- a/client/world/Client.tscn +++ b/client/world/Client.tscn @@ -4,8 +4,8 @@ [ext_resource type="Script" path="res://network/Client.gd" id="2_1ofik"] [ext_resource type="Script" path="res://network/Ping.gd" id="3_4h5la"] [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/PlayerState.gd" id="5_wttxq"] [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"] @@ -46,8 +46,8 @@ packet_type = 2 wait_time = 5.0 autostart = true -[node name="PlayerState" type="Node" parent="Client"] -script = ExtResource("5_wttxq") +[node name="PlayerAdd" type="Node" parent="Client"] +script = ExtResource("5_1dcs7") player_scene = ExtResource("5_22pku") packet_type = 10 diff --git a/server/game/Account.go b/server/game/Account.go new file mode 100644 index 0000000..1d9cff9 --- /dev/null +++ b/server/game/Account.go @@ -0,0 +1,9 @@ +package game + +// Account represents a player account +type Account struct { + ID string `json:"id"` + Name string `json:"name"` + Password string `json:"password"` + Position Vector3 `json:"position"` +} diff --git a/server/game/Database.go b/server/game/Database.go new file mode 100644 index 0000000..d13761a --- /dev/null +++ b/server/game/Database.go @@ -0,0 +1,33 @@ +package game + +var accounts = map[string]*Account{ + "user0": { + ID: "4J6qpK1ve", + Name: "user0", + Password: "password", + Position: Vector3{5, 0, 0}, + }, + "user1": { + ID: "I_vyeZamg", + Name: "user1", + Password: "password", + Position: Vector3{-5, 0, 0}, + }, + "user2": { + ID: "VJOK1ckvx", + Name: "user2", + Password: "password", + Position: Vector3{0, 0, 5}, + }, + "user3": { + ID: "EkCcqbwFl", + Name: "user3", + Password: "password", + Position: Vector3{0, 0, -5}, + }, +} + +// GetAccountByName retrieves the account with the given name. +func GetAccountByName(name string) *Account { + return accounts[name] +} diff --git a/server/game/Login.go b/server/game/Login.go index 5e863cb..7f3b203 100644 --- a/server/game/Login.go +++ b/server/game/Login.go @@ -3,61 +3,75 @@ package game import ( "crypto/rand" "encoding/base64" + "encoding/binary" "encoding/json" "errors" "net" "server/game/packet" ) -var ( - LoginSuccess = []byte{0} - LoginFailure = []byte{1} +const ( + Success = 0 + Failure = 1 ) // Login checks the account credentials and gives a network peer access to an account. func (game *Game) Login(data []byte, address *net.UDPAddr, server *Server) error { + if game.players.Get(address) != nil { + server.Send(packet.Login, []byte{Failure}, address) + return errors.New("already logged in") + } + username, password, err := getLoginData(data) if err != nil { - server.Send(packet.Login, LoginFailure, address) + server.Send(packet.Login, []byte{Failure}, address) return err } + account := GetAccountByName(username) + + if account == nil { + server.Send(packet.Login, []byte{Failure}, address) + return errors.New("unknown account name") + } + if password != "password" { - server.Send(packet.Login, LoginFailure, address) + server.Send(packet.Login, []byte{Failure}, address) return errors.New("login failure") } - player := game.players.Get(address) - player.AuthToken = createAuthToken() - player.Name = username + player := game.players.Add(address, account) + player.authToken = createAuthToken() player.KeepAlive() - if username == "user0" { - player.Position.X = 5.0 - } + response := []byte{Success} + response = appendString(response, account.ID) + response = appendString(response, player.authToken) + server.Send(packet.Login, response, address) - if username == "user1" { - player.Position.X = -5.0 - } + game.onLogin(player) + return nil +} - if username == "user2" { - player.Position.Z = -5.0 - } - - if username == "user3" { - player.Position.Z = 5.0 - } - - server.Send(packet.Login, append(LoginSuccess, []byte(player.AuthToken)...), address) - player.OnConnect() +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) { game.players.Each(func(other *Player) bool { - server.Send(packet.PlayerState, other.State(), address) + game.server.Send(packet.PlayerAdd, other.State(), player.address) + + if other != player { + game.server.Send(packet.PlayerAdd, player.State(), other.address) + } + return true }) - - return nil } func getLoginData(data []byte) (string, string, error) { diff --git a/server/game/Player.go b/server/game/Player.go index 2918e1c..6db2fa5 100644 --- a/server/game/Player.go +++ b/server/game/Player.go @@ -8,16 +8,16 @@ import ( // Player represents a logged in client. type Player struct { - Name string `json:"name"` - Position Vector3 `json:"position"` - AuthToken string + *Account + authToken string address *net.UDPAddr lastPacket time.Time } -// NewClient creates a new client. -func NewClient(address *net.UDPAddr) *Player { +// NewPlayer creates a new player. +func NewPlayer(account *Account, address *net.UDPAddr) *Player { return &Player{ + Account: account, address: address, } } @@ -37,9 +37,11 @@ func (c *Player) Tick() { // ... } -// State returns the player state (name and position). +// State returns the player state (ID, name and position). func (player *Player) State() []byte { - state := []byte(player.Name + "\u0000") + state := []byte{} + state = appendString(state, player.ID) + state = appendString(state, player.Name) state = appendVector3(state, player.Position) return state } diff --git a/server/game/PlayerManager.go b/server/game/PlayerManager.go index 568267a..8251361 100644 --- a/server/game/PlayerManager.go +++ b/server/game/PlayerManager.go @@ -43,9 +43,18 @@ func NewPlayerManager() *PlayerManager { return m } +// Add adds a new player with the given address and account. +func (m *PlayerManager) Add(address *net.UDPAddr, account *Account) *Player { + player := NewPlayer(account, address) + m.players.Store(address.String(), player) + m.count.Add(1) + player.OnConnect() + return player +} + // Contains tells you whether the address is already a registered client. -func (m *PlayerManager) Contains(addr *net.UDPAddr) bool { - _, exists := m.players.Load(addr.String()) +func (m *PlayerManager) Contains(address *net.UDPAddr) bool { + _, exists := m.players.Load(address.String()) return exists } @@ -62,15 +71,12 @@ func (m *PlayerManager) Each(callback func(*Player) bool) { } // Get either returns a new or existing client for the requested address. -func (m *PlayerManager) Get(addr *net.UDPAddr) *Player { - obj, exists := m.players.Load(addr.String()) +func (m *PlayerManager) Get(address *net.UDPAddr) *Player { + obj, exists := m.players.Load(address.String()) - if exists { - return obj.(*Player) + if !exists { + return nil } - client := NewClient(addr) - m.players.Store(addr.String(), client) - m.count.Add(1) - return client + return obj.(*Player) } diff --git a/server/game/packet/packet.go b/server/game/packet/packet.go index b1585bc..3eec437 100644 --- a/server/game/packet/packet.go +++ b/server/game/packet/packet.go @@ -1,8 +1,10 @@ package packet const ( - Ping = 1 - Login = 2 - Logout = 3 - PlayerState = 10 + Ping = 1 + Login = 2 + Logout = 3 + PlayerAdd = 10 + PlayerRemove = 11 + PlayerMove = 12 )