159 lines
4.3 KiB
Go
159 lines
4.3 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
r "math/rand"
|
|
"net"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/joho/godotenv"
|
|
)
|
|
|
|
type Payload struct {
|
|
SessionID string `json:"session_id,omitempty"`
|
|
Type string `json:"type"`
|
|
Message string `json:"msg,omitempty"`
|
|
Direction *Vector2 `json:"direction,omitempty"`
|
|
}
|
|
|
|
type InitPayload struct {
|
|
Type string `json:"type"`
|
|
PlayerClient *Client `json:"player_client"`
|
|
Clients []*Client `json:"clients"`
|
|
}
|
|
|
|
type DisconnectPayload struct {
|
|
Type string `json:"type"`
|
|
SessionID string `json:"session_id"`
|
|
}
|
|
|
|
type Client struct {
|
|
SessionID string `json:"session_id"`
|
|
LastSeen time.Time `json:"last_seen"`
|
|
Positon *Vector2 `json:"position"`
|
|
}
|
|
|
|
type Vector2 struct {
|
|
X float64 `json:"x,omitempty"`
|
|
Y float64 `json:"y,omitempty"`
|
|
}
|
|
|
|
// Generate a unique session ID
|
|
func generateSessionID() string {
|
|
b := make([]byte, 32)
|
|
if _, err := io.ReadFull(rand.Reader, b); err != nil {
|
|
return ""
|
|
}
|
|
return base64.URLEncoding.EncodeToString(b)
|
|
}
|
|
|
|
func main() {
|
|
godotenv.Load()
|
|
port := os.Getenv("PORT")
|
|
if port == "" {
|
|
port = "4477" // Default port if not specified
|
|
}
|
|
|
|
addr, err := net.ResolveUDPAddr("udp", ":"+port)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
|
|
conn, err := net.ListenUDP("udp", addr)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
|
|
fmt.Println("Server running on " + port)
|
|
|
|
buf := make([]byte, 1024)
|
|
clients := make(map[string]*Client) // Map to keep track of clients and their session IDs
|
|
|
|
for {
|
|
n, addr, err := conn.ReadFromUDP(buf)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
continue
|
|
}
|
|
|
|
var payload Payload
|
|
err = json.Unmarshal(buf[:n], &payload)
|
|
if err != nil {
|
|
fmt.Println("Error parsing JSON:", err)
|
|
continue
|
|
}
|
|
|
|
// Use the client's IP and port as the key to uniquely identify the connection
|
|
key := addr.String()
|
|
|
|
// Check if the client's connection already has a session ID
|
|
if _, ok := clients[key]; !ok {
|
|
// The client is new, generate a session ID
|
|
sessionID := generateSessionID()
|
|
clients[key] = &Client{SessionID: sessionID, LastSeen: time.Now(), Positon: &Vector2{X: randomFloatInRange(300, 600), Y: randomFloatInRange(300, 600)}} // Initialize a new Client struct and store its pointer
|
|
// Send the session ID back to the client
|
|
clientsSlice := make([]*Client, 0, len(clients))
|
|
for _, client := range clients {
|
|
clientsSlice = append(clientsSlice, client)
|
|
}
|
|
responsePayload := InitPayload{Type: "init_success", PlayerClient: clients[key], Clients: clientsSlice}
|
|
broadcastMessage(conn, clients, responsePayload)
|
|
}
|
|
|
|
clients[key].LastSeen = time.Now()
|
|
|
|
switch payload.Type {
|
|
case "init":
|
|
fmt.Printf("Received initiation generated SessionID: %s\n", clients[key].SessionID)
|
|
case "move":
|
|
fmt.Printf("Received move message from Session ID: %s : X=%f, Y=%f\n", clients[key].SessionID, payload.Direction.X, payload.Direction.Y)
|
|
broadcastMessage(conn, clients, payload)
|
|
|
|
case "disconnect":
|
|
// Disconnect the client
|
|
fmt.Printf("Client %s disconnected\n", clients[key].SessionID)
|
|
broadcastMessage(conn, clients, DisconnectPayload{Type: "disconnect", SessionID: clients[key].SessionID})
|
|
delete(clients, key)
|
|
case "message":
|
|
fmt.Printf("Received message from Session ID: %s : %s\n", clients[key].SessionID, payload.Message)
|
|
broadcastMessage(conn, clients, payload)
|
|
default:
|
|
fmt.Printf("Received unknown message type Session ID: %s\n", clients[key].SessionID)
|
|
}
|
|
|
|
// Check for disconnected clients and reset their session ID
|
|
for clientKey, client := range clients {
|
|
if time.Since(client.LastSeen) > 5*time.Minute { // 5 minutes timeout
|
|
delete(clients, clientKey)
|
|
fmt.Printf("Client %s disconnected and session ID reset\n", clients[clientKey].SessionID)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func broadcastMessage(conn *net.UDPConn, clients map[string]*Client, payload interface{}) {
|
|
for clientKey := range clients {
|
|
clientAddr, err := net.ResolveUDPAddr("udp", clientKey)
|
|
if err != nil {
|
|
fmt.Println("Error resolving UDP address:", err)
|
|
continue
|
|
}
|
|
response, _ := json.Marshal(payload)
|
|
conn.WriteToUDP(response, clientAddr)
|
|
}
|
|
}
|
|
|
|
func randomFloatInRange(min, max float64) float64 {
|
|
seed := time.Now().UnixNano()
|
|
rf := r.New(r.NewSource(seed))
|
|
return min + rf.Float64()*(max-min)
|
|
}
|