Improved server
This commit is contained in:
parent
816337ede5
commit
0172a81bdf
@ -1,8 +1,11 @@
|
|||||||
extends PacketHandler
|
extends PacketHandler
|
||||||
|
|
||||||
var auth_token: String
|
var auth_token: String
|
||||||
|
var instance_id := OS.get_process_id() % 4
|
||||||
|
var username := "user%d" % instance_id
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
|
DisplayServer.window_set_title(username)
|
||||||
send_login()
|
send_login()
|
||||||
|
|
||||||
func handle_packet(data: PackedByteArray, _peer: PacketPeer):
|
func handle_packet(data: PackedByteArray, _peer: PacketPeer):
|
||||||
@ -17,10 +20,11 @@ func handle_packet(data: PackedByteArray, _peer: PacketPeer):
|
|||||||
func send_login():
|
func send_login():
|
||||||
if is_logged_in():
|
if is_logged_in():
|
||||||
return
|
return
|
||||||
|
|
||||||
|
var password := "password"
|
||||||
var buffer := StreamPeerBuffer.new()
|
var buffer := StreamPeerBuffer.new()
|
||||||
buffer.put_8(Packet.LOGIN)
|
buffer.put_8(Packet.LOGIN)
|
||||||
buffer.put_data(JSON.stringify(["user1", "password"]).to_utf8_buffer())
|
buffer.put_data(JSON.stringify([username, password]).to_utf8_buffer())
|
||||||
%Client.socket.put_packet(buffer.data_array)
|
%Client.socket.put_packet(buffer.data_array)
|
||||||
print("[Client] Connecting...")
|
print("[Client] Connecting...")
|
||||||
|
|
||||||
|
@ -17,9 +17,14 @@ func handle_packet(data: PackedByteArray, _peer: PacketPeer):
|
|||||||
print(server_position)
|
print(server_position)
|
||||||
|
|
||||||
var player := spawn_player()
|
var player := spawn_player()
|
||||||
player.name = player_name
|
|
||||||
player.position = server_position
|
player.position = server_position
|
||||||
Global.player = player
|
player.set_character_name(player_name)
|
||||||
|
|
||||||
|
if false:
|
||||||
|
Global.player = player
|
||||||
|
var controller := PlayerController.new()
|
||||||
|
controller.character = Global.player
|
||||||
|
Global.player.add_child(controller)
|
||||||
|
|
||||||
func spawn_player() -> Player:
|
func spawn_player() -> Player:
|
||||||
var player = player_scene.instantiate()
|
var player = player_scene.instantiate()
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
class_name Player
|
class_name Player
|
||||||
extends Character
|
extends Character
|
||||||
|
|
||||||
func _ready():
|
func set_character_name(new_name: String):
|
||||||
var controller := PlayerController.new()
|
name = new_name
|
||||||
controller.character = self
|
get_node("Label").text = name
|
||||||
add_child(controller)
|
|
@ -1,8 +1,7 @@
|
|||||||
[gd_scene load_steps=6 format=3 uid="uid://2lcnu3dy54lx"]
|
[gd_scene load_steps=5 format=3 uid="uid://2lcnu3dy54lx"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://player/Player.gd" id="1_8gebs"]
|
[ext_resource type="Script" path="res://player/Player.gd" id="1_8gebs"]
|
||||||
[ext_resource type="PackedScene" uid="uid://2bbycjulf00g" path="res://character/health/HealthComponent.tscn" id="2_np5ag"]
|
[ext_resource type="PackedScene" uid="uid://2bbycjulf00g" path="res://character/health/HealthComponent.tscn" id="2_np5ag"]
|
||||||
[ext_resource type="Script" path="res://player/controller/PlayerController.gd" id="3_oox5k"]
|
|
||||||
|
|
||||||
[sub_resource type="PrismMesh" id="PrismMesh_y7abh"]
|
[sub_resource type="PrismMesh" id="PrismMesh_y7abh"]
|
||||||
size = Vector3(0.5, 1.6, 0.5)
|
size = Vector3(0.5, 1.6, 0.5)
|
||||||
@ -29,6 +28,7 @@ shape = SubResource("CapsuleShape3D_2f50n")
|
|||||||
|
|
||||||
[node name="Health" parent="." instance=ExtResource("2_np5ag")]
|
[node name="Health" parent="." instance=ExtResource("2_np5ag")]
|
||||||
|
|
||||||
[node name="Controller" type="Node" parent="." node_paths=PackedStringArray("character")]
|
[node name="Label" type="Label3D" parent="."]
|
||||||
script = ExtResource("3_oox5k")
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.8, 0)
|
||||||
character = NodePath("..")
|
billboard = 1
|
||||||
|
text = "ABC"
|
||||||
|
@ -68,10 +68,10 @@ camera_attributes = ExtResource("9_w4cdu")
|
|||||||
[node name="Trees" type="Node3D" parent="World"]
|
[node name="Trees" type="Node3D" parent="World"]
|
||||||
|
|
||||||
[node name="Tree" parent="World/Trees" instance=ExtResource("11_wlyv1")]
|
[node name="Tree" parent="World/Trees" instance=ExtResource("11_wlyv1")]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5.11323, 0, -4.64839)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5.11323, 0, -7.57074)
|
||||||
|
|
||||||
[node name="Tree2" parent="World/Trees" instance=ExtResource("11_wlyv1")]
|
[node name="Tree2" parent="World/Trees" instance=ExtResource("11_wlyv1")]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5.11323, 0, 5.35161)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5.11323, 0, 7.80548)
|
||||||
|
|
||||||
[node name="Enemies" type="Node3D" parent="World"]
|
[node name="Enemies" type="Node3D" parent="World"]
|
||||||
|
|
||||||
@ -118,6 +118,7 @@ follow_speed = 5.0
|
|||||||
|
|
||||||
[node name="PostProcessing" type="MeshInstance3D" parent="Viewport/SubViewport/CameraPivot/Camera"]
|
[node name="PostProcessing" type="MeshInstance3D" parent="Viewport/SubViewport/CameraPivot/Camera"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
visible = false
|
||||||
extra_cull_margin = 16384.0
|
extra_cull_margin = 16384.0
|
||||||
mesh = SubResource("QuadMesh_7yiqd")
|
mesh = SubResource("QuadMesh_7yiqd")
|
||||||
|
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
package game
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Client represents a logged in client.
|
|
||||||
type Client struct {
|
|
||||||
address *net.UDPAddr
|
|
||||||
lastPacket time.Time
|
|
||||||
authToken string
|
|
||||||
player Player
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewClient creates a new client.
|
|
||||||
func NewClient(address *net.UDPAddr) *Client {
|
|
||||||
return &Client{
|
|
||||||
address: address,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String shows the client address.
|
|
||||||
func (c *Client) String() string {
|
|
||||||
return c.address.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// KeepAlive sets the last packet time to now.
|
|
||||||
func (c *Client) KeepAlive() {
|
|
||||||
c.lastPacket = time.Now()
|
|
||||||
}
|
|
67
server/game/Game.go
Normal file
67
server/game/Game.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"server/game/packet"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Game represents the entire state of the game server.
|
||||||
|
type Game struct {
|
||||||
|
server *Server
|
||||||
|
players *PlayerManager
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new game.
|
||||||
|
func New() *Game {
|
||||||
|
return &Game{
|
||||||
|
server: NewServer(),
|
||||||
|
players: NewPlayerManager(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run starts all game systems.
|
||||||
|
func (game *Game) Run() {
|
||||||
|
game.start()
|
||||||
|
close := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(close, os.Interrupt)
|
||||||
|
<-close
|
||||||
|
}
|
||||||
|
|
||||||
|
// start starts all game systems.
|
||||||
|
func (game *Game) start() {
|
||||||
|
go game.network()
|
||||||
|
go game.physics()
|
||||||
|
go game.statistics()
|
||||||
|
}
|
||||||
|
|
||||||
|
// network will listen for new packets and process them.
|
||||||
|
func (game *Game) network() {
|
||||||
|
game.server.SetHandler(packet.Ping, game.Ping)
|
||||||
|
game.server.SetHandler(packet.Login, game.Login)
|
||||||
|
game.server.Run(4242)
|
||||||
|
}
|
||||||
|
|
||||||
|
// physics periodically runs the Tick function for each player.
|
||||||
|
func (game *Game) physics() {
|
||||||
|
updater := time.NewTicker(20 * time.Millisecond)
|
||||||
|
|
||||||
|
for range updater.C {
|
||||||
|
game.players.Each(func(c *Player) bool {
|
||||||
|
c.Tick()
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// statistics periodically shows server statistics on the command line.
|
||||||
|
func (game *Game) statistics() {
|
||||||
|
ticker := time.NewTicker(time.Second)
|
||||||
|
|
||||||
|
for range ticker.C {
|
||||||
|
fmt.Printf("%d players | %d packets\n", game.players.Count(), game.server.PacketCount())
|
||||||
|
game.server.ResetPacketCount()
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package core
|
package game
|
||||||
|
|
||||||
import "net"
|
import "net"
|
||||||
|
|
@ -3,70 +3,78 @@ package game
|
|||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/binary"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"net"
|
"net"
|
||||||
"server/core"
|
"server/game/packet"
|
||||||
"server/packet"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Clients = NewManager()
|
|
||||||
LoginSuccess = []byte{0}
|
LoginSuccess = []byte{0}
|
||||||
LoginFailure = []byte{1}
|
LoginFailure = []byte{1}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Login checks the account credentials and gives a network peer access to an account.
|
// Login checks the account credentials and gives a network peer access to an account.
|
||||||
func Login(data []byte, address *net.UDPAddr, server *core.Server) error {
|
func (game *Game) Login(data []byte, address *net.UDPAddr, server *Server) error {
|
||||||
var loginRequest [2]string
|
username, password, err := getLoginData(data)
|
||||||
err := json.Unmarshal(data, &loginRequest)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
server.Send(packet.Login, LoginFailure, address)
|
server.Send(packet.Login, LoginFailure, address)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
username := loginRequest[0]
|
|
||||||
password := loginRequest[1]
|
|
||||||
|
|
||||||
if password != "password" {
|
if password != "password" {
|
||||||
server.Send(packet.Login, LoginFailure, address)
|
server.Send(packet.Login, LoginFailure, address)
|
||||||
return errors.New("login failure")
|
return errors.New("login failure")
|
||||||
}
|
}
|
||||||
|
|
||||||
randomBytes := make([]byte, 32)
|
player := game.players.Get(address)
|
||||||
_, err = rand.Read(randomBytes)
|
player.AuthToken = createAuthToken()
|
||||||
|
player.Name = username
|
||||||
|
player.KeepAlive()
|
||||||
|
|
||||||
if err != nil {
|
if username == "user0" {
|
||||||
server.Send(packet.Login, LoginFailure, address)
|
player.Position.X = 5.0
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client := Clients.Get(address)
|
if username == "user1" {
|
||||||
client.KeepAlive()
|
player.Position.X = -5.0
|
||||||
client.authToken = base64.StdEncoding.EncodeToString(randomBytes)
|
}
|
||||||
client.player.Name = username
|
|
||||||
client.player.Position.X = 5.0
|
|
||||||
playerState := []byte(client.player.Name + "\u0000")
|
|
||||||
playerState = appendVector3(playerState, client.player.Position)
|
|
||||||
|
|
||||||
server.Send(packet.Login, append(LoginSuccess, []byte(client.authToken)...), address)
|
if username == "user2" {
|
||||||
server.Send(packet.PlayerState, playerState, address)
|
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()
|
||||||
|
|
||||||
|
game.players.Each(func(other *Player) bool {
|
||||||
|
server.Send(packet.PlayerState, other.State(), address)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
fmt.Printf("%s logged in.\n", username)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendVector3(data []byte, vector Vector3) []byte {
|
func getLoginData(data []byte) (string, string, error) {
|
||||||
bits := math.Float32bits(vector.X)
|
loginRequest := [2]string{}
|
||||||
data = binary.LittleEndian.AppendUint32(data, bits)
|
err := json.Unmarshal(data, &loginRequest)
|
||||||
|
|
||||||
bits = math.Float32bits(vector.Y)
|
if err != nil {
|
||||||
data = binary.LittleEndian.AppendUint32(data, bits)
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
bits = math.Float32bits(vector.Z)
|
username := loginRequest[0]
|
||||||
return binary.LittleEndian.AppendUint32(data, bits)
|
password := loginRequest[1]
|
||||||
|
return username, password, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createAuthToken() string {
|
||||||
|
randomBytes := make([]byte, 32)
|
||||||
|
rand.Read(randomBytes)
|
||||||
|
return base64.StdEncoding.EncodeToString(randomBytes)
|
||||||
}
|
}
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
package game
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Manager keeps tracks of all player connections.
|
|
||||||
type Manager struct {
|
|
||||||
clients sync.Map
|
|
||||||
count atomic.Int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewManager creates a new manager.
|
|
||||||
func NewManager() *Manager {
|
|
||||||
manager := &Manager{}
|
|
||||||
timeout := 5 * time.Second
|
|
||||||
interval := time.Second
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
ticker := time.NewTicker(interval)
|
|
||||||
defer ticker.Stop()
|
|
||||||
|
|
||||||
for range ticker.C {
|
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
manager.clients.Range(func(key, value interface{}) bool {
|
|
||||||
item := value.(*Client)
|
|
||||||
|
|
||||||
if !item.lastPacket.IsZero() && now.After(item.lastPacket.Add(timeout)) {
|
|
||||||
manager.clients.Delete(key)
|
|
||||||
manager.count.Add(-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return manager
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get either returns a new or existing client for the requested address.
|
|
||||||
func (m *Manager) Get(addr *net.UDPAddr) *Client {
|
|
||||||
obj, exists := m.clients.Load(addr.String())
|
|
||||||
|
|
||||||
if exists {
|
|
||||||
return obj.(*Client)
|
|
||||||
}
|
|
||||||
|
|
||||||
client := NewClient(addr)
|
|
||||||
m.clients.Store(addr.String(), client)
|
|
||||||
m.count.Add(1)
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contains tells you whether the address is already a registered client.
|
|
||||||
func (m *Manager) Contains(addr *net.UDPAddr) bool {
|
|
||||||
_, exists := m.clients.Load(addr.String())
|
|
||||||
return exists
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count returns the number of clients.
|
|
||||||
func (m *Manager) Count() int {
|
|
||||||
return int(m.count.Load())
|
|
||||||
}
|
|
@ -2,16 +2,15 @@ package game
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"server/core"
|
"server/game/packet"
|
||||||
"server/packet"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ping is used as a heartbeat and latency check.
|
// Ping is used as a heartbeat and latency check.
|
||||||
func Ping(data []byte, address *net.UDPAddr, server *core.Server) error {
|
func (game *Game) Ping(data []byte, address *net.UDPAddr, server *Server) error {
|
||||||
server.Send(packet.Ping, data, address)
|
server.Send(packet.Ping, data, address)
|
||||||
|
|
||||||
if Clients.Contains(address) {
|
if game.players.Contains(address) {
|
||||||
Clients.Get(address).KeepAlive()
|
game.players.Get(address).KeepAlive()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -1,6 +1,53 @@
|
|||||||
package game
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Player represents a logged in client.
|
||||||
type Player struct {
|
type Player struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Position Vector3 `json:"position"`
|
Position Vector3 `json:"position"`
|
||||||
|
AuthToken string
|
||||||
|
address *net.UDPAddr
|
||||||
|
lastPacket time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient creates a new client.
|
||||||
|
func NewClient(address *net.UDPAddr) *Player {
|
||||||
|
return &Player{
|
||||||
|
address: address,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String shows the client address.
|
||||||
|
func (c *Player) String() string {
|
||||||
|
return c.address.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeepAlive sets the last packet time to now.
|
||||||
|
func (c *Player) KeepAlive() {
|
||||||
|
c.lastPacket = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tick is run on every tick.
|
||||||
|
func (c *Player) Tick() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
// State returns the player state (name and position).
|
||||||
|
func (player *Player) State() []byte {
|
||||||
|
state := []byte(player.Name + "\u0000")
|
||||||
|
state = appendVector3(state, player.Position)
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
func (player *Player) OnConnect() {
|
||||||
|
fmt.Printf("%s connected.\n", player.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (player *Player) OnDisconnect() {
|
||||||
|
fmt.Printf("%s disconnected.\n", player.Name)
|
||||||
}
|
}
|
||||||
|
76
server/game/PlayerManager.go
Normal file
76
server/game/PlayerManager.go
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PlayerManager keeps tracks of all players.
|
||||||
|
type PlayerManager struct {
|
||||||
|
players sync.Map
|
||||||
|
count atomic.Int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPlayerManager creates a new player manager.
|
||||||
|
func NewPlayerManager() *PlayerManager {
|
||||||
|
m := &PlayerManager{}
|
||||||
|
timeout := 5 * time.Second
|
||||||
|
interval := time.Second
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ticker := time.NewTicker(interval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for range ticker.C {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
m.players.Range(func(key, value interface{}) bool {
|
||||||
|
player := value.(*Player)
|
||||||
|
|
||||||
|
if !player.lastPacket.IsZero() && now.After(player.lastPacket.Add(timeout)) {
|
||||||
|
player.OnDisconnect()
|
||||||
|
m.players.Delete(key)
|
||||||
|
m.count.Add(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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())
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count returns the number of clients.
|
||||||
|
func (m *PlayerManager) Count() int {
|
||||||
|
return int(m.count.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each calls the callback function for each client.
|
||||||
|
func (m *PlayerManager) Each(callback func(*Player) bool) {
|
||||||
|
m.players.Range(func(key, value any) bool {
|
||||||
|
return callback(value.(*Player))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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())
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
return obj.(*Player)
|
||||||
|
}
|
||||||
|
|
||||||
|
client := NewClient(addr)
|
||||||
|
m.players.Store(addr.String(), client)
|
||||||
|
m.count.Add(1)
|
||||||
|
return client
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package core
|
package game
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -12,8 +12,8 @@ type Server struct {
|
|||||||
packetCount int
|
packetCount int
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new server.
|
// NewServer creates a new server.
|
||||||
func New() *Server {
|
func NewServer() *Server {
|
||||||
return &Server{}
|
return &Server{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ func listen(port int) *net.UDPConn {
|
|||||||
|
|
||||||
// read is a blocking call which will read incoming packets and handle them.
|
// read is a blocking call which will read incoming packets and handle them.
|
||||||
func (s *Server) read() {
|
func (s *Server) read() {
|
||||||
buffer := make([]byte, 4096)
|
buffer := make([]byte, 16384)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
n, addr, err := s.socket.ReadFromUDP(buffer)
|
n, addr, err := s.socket.ReadFromUDP(buffer)
|
@ -1,7 +1,25 @@
|
|||||||
package game
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Vector3 represents a 3-dimensional vector using 32-bit float precision.
|
||||||
type Vector3 struct {
|
type Vector3 struct {
|
||||||
X float32 `json:"x"`
|
X float32 `json:"x"`
|
||||||
Y float32 `json:"y"`
|
Y float32 `json:"y"`
|
||||||
Z float32 `json:"z"`
|
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 {
|
||||||
|
bits := math.Float32bits(vector.X)
|
||||||
|
data = binary.LittleEndian.AppendUint32(data, bits)
|
||||||
|
|
||||||
|
bits = math.Float32bits(vector.Y)
|
||||||
|
data = binary.LittleEndian.AppendUint32(data, bits)
|
||||||
|
|
||||||
|
bits = math.Float32bits(vector.Z)
|
||||||
|
return binary.LittleEndian.AppendUint32(data, bits)
|
||||||
|
}
|
||||||
|
@ -1,29 +1,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"server/core"
|
|
||||||
"server/game"
|
"server/game"
|
||||||
"server/packet"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Init server
|
game.New().Run()
|
||||||
server := core.New()
|
|
||||||
server.SetHandler(packet.Ping, game.Ping)
|
|
||||||
server.SetHandler(packet.Login, game.Login)
|
|
||||||
|
|
||||||
// Show statistics
|
|
||||||
ticker := time.NewTicker(time.Second)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for range ticker.C {
|
|
||||||
fmt.Printf("%d packets per second, %d clients\n", server.PacketCount(), game.Clients.Count())
|
|
||||||
server.ResetPacketCount()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Start listening
|
|
||||||
server.Run(4242)
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user