Added Godot based server
This commit is contained in:
17
tools/server/core/Client.go
Normal file
17
tools/server/core/Client.go
Normal 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
149
tools/server/core/Server.go
Normal 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
3
tools/server/go.mod
Normal file
@ -0,0 +1,3 @@
|
||||
module server
|
||||
|
||||
go 1.21.6
|
20
tools/server/login.go
Normal file
20
tools/server/login.go
Normal 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
14
tools/server/main.go
Normal 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)
|
||||
}
|
8
tools/server/packet/types.go
Normal file
8
tools/server/packet/types.go
Normal file
@ -0,0 +1,8 @@
|
||||
package packet
|
||||
|
||||
const (
|
||||
PING = 1
|
||||
LOGIN = 2
|
||||
LOGOUT = 3
|
||||
MOVE = 10
|
||||
)
|
11
tools/server/ping.go
Normal file
11
tools/server/ping.go
Normal 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
3
tools/stresstest/go.mod
Normal file
@ -0,0 +1,3 @@
|
||||
module stresstest
|
||||
|
||||
go 1.21.6
|
61
tools/stresstest/main.go
Normal file
61
tools/stresstest/main.go
Normal 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()
|
||||
}
|
Reference in New Issue
Block a user