Added Godot based server

This commit is contained in:
2024-01-20 22:18:58 +01:00
parent ce699ee6db
commit 1f73d22cd6
28 changed files with 338 additions and 135 deletions

View File

@ -0,0 +1,17 @@
package core
import (
"net"
"time"
)
// Client represents a UDP client.
type Client struct {
address *net.UDPAddr
lastPacket time.Time
}
// String shows the client address.
func (c *Client) String() string {
return c.address.String()
}

149
tools/server/core/Server.go Normal file
View File

@ -0,0 +1,149 @@
package core
import (
"fmt"
"net"
"sync"
"sync/atomic"
"time"
)
// Handler is a byte code specific packet handler.
type Handler func([]byte, *Client)
// Server represents a UDP server.
type Server struct {
socket *net.UDPConn
handlers [256]Handler
clients sync.Map
count atomic.Int64
}
// New creates a new server.
func New() *Server {
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.
func (s *Server) AddHandler(code byte, handler Handler) {
s.handlers[code] = handler
}
// 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 {
fmt.Println("Error sending response:", err)
}
}
// Run starts the server on the given port.
func (s *Server) Run(port int) {
s.socket = listen(port)
defer s.socket.Close()
s.read()
}
// listen creates a socket for the server and starts listening for incoming packets.
func listen(port int) *net.UDPConn {
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", port))
if err != nil {
panic(err)
}
connection, err := net.ListenUDP("udp", addr)
if err != nil {
panic(err)
}
return connection
}
// read is a blocking call which will read incoming packets and handle them.
func (s *Server) read() {
buffer := make([]byte, 4096)
for {
n, addr, err := s.socket.ReadFromUDP(buffer)
if err != nil {
fmt.Println("Error reading from UDP connection:", err)
continue
}
if n == 0 {
continue
}
s.handle(buffer[:n], addr)
}
}
var packets = 0
// handle deals with an incoming packet.
func (s *Server) handle(data []byte, addr *net.UDPAddr) {
c := s.getClient(addr)
c.lastPacket = time.Now()
//fmt.Printf("Received %d bytes from %s: %s\n", len(data), c, string(data))
handler := s.handlers[data[0]]
if handler == nil {
fmt.Printf("No callback registered for packet type %d\n", data[0])
return
}
handler(data[1:], c)
packets++
fmt.Println(packets)
}
// getClient either returns a new or existing client for the requested address.
func (s *Server) getClient(addr *net.UDPAddr) *Client {
obj, exists := s.clients.Load(addr.String())
if exists {
return obj.(*Client)
}
client := &Client{
address: addr,
}
s.clients.Store(addr.String(), client)
s.count.Add(1)
return client
}

3
tools/server/go.mod Normal file
View File

@ -0,0 +1,3 @@
module server
go 1.21.6

20
tools/server/login.go Normal file
View File

@ -0,0 +1,20 @@
package main
import (
"bytes"
"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) {
if !bytes.Equal(data, []byte("password")) {
fmt.Println("login failure")
server.Send(packet.LOGIN, []byte{1}, client)
return
}
fmt.Println("login success")
server.Send(packet.LOGIN, []byte{0}, client)
}

14
tools/server/main.go Normal file
View File

@ -0,0 +1,14 @@
package main
import (
"server/core"
"server/packet"
)
var server = core.New()
func main() {
server.AddHandler(packet.PING, ping)
server.AddHandler(packet.LOGIN, login)
server.Run(4242)
}

View File

@ -0,0 +1,8 @@
package packet
const (
PING = 1
LOGIN = 2
LOGOUT = 3
MOVE = 10
)

11
tools/server/ping.go Normal file
View File

@ -0,0 +1,11 @@
package main
import (
"server/core"
"server/packet"
)
// ping is used as a heartbeat and latency check.
func ping(data []byte, client *core.Client) {
server.Send(packet.PING, data, client)
}

3
tools/stresstest/go.mod Normal file
View File

@ -0,0 +1,3 @@
module stresstest
go 1.21.6

61
tools/stresstest/main.go Normal file
View File

@ -0,0 +1,61 @@
package main
import (
"flag"
"fmt"
"net"
"sync"
"time"
)
const (
serverAddress = "127.0.0.1:4242"
)
var (
numClients = flag.Int("c", 10, "number of clients")
message = []byte{1}
)
func init() {
flag.Parse()
}
func udpClient(wg *sync.WaitGroup) {
defer wg.Done()
clientAddr, err := net.ResolveUDPAddr("udp", serverAddress)
if err != nil {
fmt.Println("Error resolving UDP address:", err)
return
}
conn, err := net.DialUDP("udp", nil, clientAddr)
if err != nil {
fmt.Println("Error connecting to UDP server:", err)
return
}
defer conn.Close()
for {
_, err := conn.Write(message)
if err != nil {
fmt.Println("Error sending message:", err)
return
}
time.Sleep(50 * time.Millisecond) // Adjust the sleep duration as needed
}
}
func main() {
var wg sync.WaitGroup
// Start multiple UDP clients in separate goroutines
for i := 0; i < *numClients; i++ {
wg.Add(1)
go udpClient(&wg)
}
// Signal all client goroutines to stop
wg.Wait()
}