Files
Master-Arbeit-Tom-Hempel/Unity-Master/Assets/avatar_sync_server.go
2025-09-21 22:42:26 +02:00

384 lines
9.9 KiB
Go

package main
import (
"bytes"
"encoding/binary"
"fmt"
"html/template"
"log"
"net"
"net/http"
"sync"
"time"
)
// Vector3 represents a 3D vector
type Vector3 struct {
X, Y, Z float32
}
// Quaternion represents a rotation quaternion
type Quaternion struct {
X, Y, Z, W float32
}
// Transform represents a 3D transform
type Transform struct {
WorldPosition Vector3
WorldRotation Quaternion
LocalScale Vector3
}
// BoneData represents bone transformation data
type BoneData struct {
BoneIndex int32
Transform Transform
}
// BlendShapeData represents blend shape data
type BlendShapeData struct {
ShapeIndex int32
Weight float32
}
// AvatarData represents the complete avatar data structure
type AvatarData struct {
RootTransform Transform
Timestamp float64
BoneCount int32
Bones []BoneData
BlendCount int32
BlendShapes []BlendShapeData
ServerTime float64
}
// AvatarSyncServer handles the avatar synchronization
type AvatarSyncServer struct {
playerData map[string]*AvatarData
mutex sync.RWMutex
}
// NewAvatarSyncServer creates a new server instance
func NewAvatarSyncServer() *AvatarSyncServer {
return &AvatarSyncServer{
playerData: map[string]*AvatarData{
"player1": nil,
"player2": nil,
},
}
}
// SerializeAvatarData converts AvatarData to binary format
func (a *AvatarData) SerializeAvatarData() ([]byte, error) {
buf := new(bytes.Buffer)
// Write root transform
if err := binary.Write(buf, binary.LittleEndian, a.RootTransform); err != nil {
return nil, err
}
// Write timestamp
if err := binary.Write(buf, binary.LittleEndian, a.Timestamp); err != nil {
return nil, err
}
// Write server time
if err := binary.Write(buf, binary.LittleEndian, a.ServerTime); err != nil {
return nil, err
}
// Write bone count
if err := binary.Write(buf, binary.LittleEndian, a.BoneCount); err != nil {
return nil, err
}
// Write bones
for _, bone := range a.Bones {
if err := binary.Write(buf, binary.LittleEndian, bone); err != nil {
return nil, err
}
}
// Write blend shape count
if err := binary.Write(buf, binary.LittleEndian, a.BlendCount); err != nil {
return nil, err
}
// Write blend shapes
for _, blend := range a.BlendShapes {
if err := binary.Write(buf, binary.LittleEndian, blend); err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}
// DeserializeAvatarData converts binary data back to AvatarData
func DeserializeAvatarData(data []byte) (*AvatarData, error) {
buf := bytes.NewReader(data)
avatar := &AvatarData{}
// Read root transform
if err := binary.Read(buf, binary.LittleEndian, &avatar.RootTransform); err != nil {
return nil, err
}
// Read timestamp
if err := binary.Read(buf, binary.LittleEndian, &avatar.Timestamp); err != nil {
return nil, err
}
// Read server time
if err := binary.Read(buf, binary.LittleEndian, &avatar.ServerTime); err != nil {
return nil, err
}
// Read bone count
if err := binary.Read(buf, binary.LittleEndian, &avatar.BoneCount); err != nil {
return nil, err
}
// Read bones
avatar.Bones = make([]BoneData, avatar.BoneCount)
for i := int32(0); i < avatar.BoneCount; i++ {
if err := binary.Read(buf, binary.LittleEndian, &avatar.Bones[i]); err != nil {
return nil, err
}
}
// Read blend shape count
if err := binary.Read(buf, binary.LittleEndian, &avatar.BlendCount); err != nil {
return nil, err
}
// Read blend shapes
avatar.BlendShapes = make([]BlendShapeData, avatar.BlendCount)
for i := int32(0); i < avatar.BlendCount; i++ {
if err := binary.Read(buf, binary.LittleEndian, &avatar.BlendShapes[i]); err != nil {
return nil, err
}
}
return avatar, nil
}
// setCORSHeaders sets CORS headers for cross-origin requests
func setCORSHeaders(w http.ResponseWriter) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
}
// handleRoot serves the status page
func (s *AvatarSyncServer) handleRoot(w http.ResponseWriter, r *http.Request) {
setCORSHeaders(w)
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
w.Header().Set("Content-Type", "text/html")
s.mutex.RLock()
player1Connected := s.playerData["player1"] != nil
player2Connected := s.playerData["player2"] != nil
s.mutex.RUnlock()
statusTemplate := `
<html>
<head><title>Avatar Sync Server (Go Binary)</title></head>
<body>
<h1>Avatar Sync Server (Go Binary)</h1>
<p>Server is running and using binary protocol for improved performance</p>
<h2>Available Endpoints:</h2>
<ul>
<li>/player1 - Player 1 data (binary)</li>
<li>/player2 - Player 2 data (binary)</li>
<li>/status - Server status</li>
</ul>
<h2>Player Status:</h2>
<ul>
<li>Player 1: {{if .Player1}}Connected{{else}}No data{{end}}</li>
<li>Player 2: {{if .Player2}}Connected{{else}}No data{{end}}</li>
</ul>
<p><strong>Note:</strong> This server uses binary protocol instead of JSON for faster data transfer.</p>
</body>
</html>
`
tmpl := template.Must(template.New("status").Parse(statusTemplate))
data := struct {
Player1 bool
Player2 bool
}{
Player1: player1Connected,
Player2: player2Connected,
}
tmpl.Execute(w, data)
}
// handleStatus provides server status information
func (s *AvatarSyncServer) handleStatus(w http.ResponseWriter, r *http.Request) {
setCORSHeaders(w)
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
w.Header().Set("Content-Type", "text/plain")
s.mutex.RLock()
defer s.mutex.RUnlock()
currentTime := time.Now().Unix()
status := fmt.Sprintf("Avatar Sync Server (Go Binary)\n")
status += fmt.Sprintf("Server Time: %d\n", currentTime)
status += fmt.Sprintf("Protocol: Binary (Little Endian)\n")
status += fmt.Sprintf("Player 1: %s\n", func() string {
if s.playerData["player1"] != nil {
return fmt.Sprintf("Connected (last update: %.2f)", s.playerData["player1"].Timestamp)
}
return "No data"
}())
status += fmt.Sprintf("Player 2: %s\n", func() string {
if s.playerData["player2"] != nil {
return fmt.Sprintf("Connected (last update: %.2f)", s.playerData["player2"].Timestamp)
}
return "No data"
}())
w.Write([]byte(status))
}
// handlePlayer handles both GET and POST requests for player data
func (s *AvatarSyncServer) handlePlayer(w http.ResponseWriter, r *http.Request, playerID string) {
setCORSHeaders(w)
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
switch r.Method {
case "GET":
s.mutex.RLock()
playerData := s.playerData[playerID]
s.mutex.RUnlock()
w.Header().Set("Content-Type", "application/octet-stream")
if playerData != nil {
// Update server timestamp before sending
playerData.ServerTime = float64(time.Now().UnixNano()) / 1e9
data, err := playerData.SerializeAvatarData()
if err != nil {
http.Error(w, "Failed to serialize data", http.StatusInternalServerError)
return
}
w.Write(data)
} else {
// Return empty avatar data structure
emptyAvatar := &AvatarData{
RootTransform: Transform{
WorldPosition: Vector3{X: 0.0, Y: 0.0, Z: 0.0},
WorldRotation: Quaternion{X: 0.0, Y: 0.0, Z: 0.0, W: 1.0},
LocalScale: Vector3{X: 1.0, Y: 1.0, Z: 1.0},
},
Timestamp: 0.0,
BoneCount: 0,
Bones: []BoneData{},
BlendCount: 0,
BlendShapes: []BlendShapeData{},
ServerTime: float64(time.Now().UnixNano()) / 1e9,
}
data, err := emptyAvatar.SerializeAvatarData()
if err != nil {
http.Error(w, "Failed to serialize empty data", http.StatusInternalServerError)
return
}
w.Write(data)
}
case "POST":
// Read binary data from request body
buf := new(bytes.Buffer)
_, err := buf.ReadFrom(r.Body)
if err != nil {
http.Error(w, "Failed to read request body", http.StatusBadRequest)
return
}
// Deserialize avatar data
avatarData, err := DeserializeAvatarData(buf.Bytes())
if err != nil {
http.Error(w, "Failed to deserialize avatar data", http.StatusBadRequest)
return
}
// Store the avatar data
avatarData.ServerTime = float64(time.Now().UnixNano()) / 1e9
s.mutex.Lock()
s.playerData[playerID] = avatarData
s.mutex.Unlock()
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
w.Write([]byte(fmt.Sprintf("Data updated for %s at %.2f", playerID, avatarData.ServerTime)))
log.Printf("Updated data for %s at %s", playerID, time.Now().Format("15:04:05"))
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
// getLocalIP returns the local IP address
func getLocalIP() string {
conn, err := net.Dial("udp", "8.8.8.8:80")
if err != nil {
return "127.0.0.1"
}
defer conn.Close()
localAddr := conn.LocalAddr().(*net.UDPAddr)
return localAddr.IP.String()
}
func main() {
server := NewAvatarSyncServer()
// Set up routes
http.HandleFunc("/", server.handleRoot)
http.HandleFunc("/status", server.handleStatus)
http.HandleFunc("/player1", func(w http.ResponseWriter, r *http.Request) {
server.handlePlayer(w, r, "player1")
})
http.HandleFunc("/player2", func(w http.ResponseWriter, r *http.Request) {
server.handlePlayer(w, r, "player2")
})
port := ":8080"
localIP := getLocalIP()
fmt.Println("Avatar Sync Server (Go Binary) starting...")
fmt.Printf("Server running on:\n")
fmt.Printf(" Local: http://127.0.0.1%s\n", port)
fmt.Printf(" Network: http://%s%s\n", localIP, port)
fmt.Printf(" External: http://0.0.0.0%s\n", port)
fmt.Println()
fmt.Println("Available endpoints:")
fmt.Println(" GET /player1 - Get Player 1 avatar data (binary)")
fmt.Println(" GET /player2 - Get Player 2 avatar data (binary)")
fmt.Println(" POST /player1 - Update Player 1 avatar data (binary)")
fmt.Println(" POST /player2 - Update Player 2 avatar data (binary)")
fmt.Println(" GET /status - Server status")
fmt.Println()
fmt.Println("Protocol: Binary (Little Endian) for improved performance")
fmt.Println("Press Ctrl+C to stop the server...")
log.Fatal(http.ListenAndServe(port, nil))
}