Files
blackroad-apps/roadworld/main.go
2026-03-06 02:28:24 +00:00

380 lines
7.9 KiB
Go

package main
import (
"log"
"net/http"
"os"
"sync"
"time"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
)
type World struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
CreatedBy string `json:"created_by"`
Players int `json:"players"`
MaxPlayers int `json:"max_players"`
IsPublic bool `json:"is_public"`
CreatedAt time.Time `json:"created_at"`
}
type Player struct {
ID string `json:"id"`
Name string `json:"name"`
WorldID string `json:"world_id"`
X float64 `json:"x"`
Y float64 `json:"y"`
Z float64 `json:"z"`
Rotation float64 `json:"rotation"`
Avatar string `json:"avatar"`
}
type GameObject struct {
ID string `json:"id"`
WorldID string `json:"world_id"`
Type string `json:"type"`
Name string `json:"name"`
X float64 `json:"x"`
Y float64 `json:"y"`
Z float64 `json:"z"`
Scale float64 `json:"scale"`
Color string `json:"color"`
}
var (
worlds = make(map[string]*World)
players = make(map[string]*Player)
gameObjects = make(map[string]*GameObject)
mu sync.RWMutex
upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { return true },
}
)
func init() {
worlds["earth-genesis"] = &World{
ID: "earth-genesis",
Name: "Earth - Beginning",
Description: "Experience Earth from the very beginning",
Type: "open-world",
CreatedBy: "system",
Players: 0,
MaxPlayers: 1000,
IsPublic: true,
CreatedAt: time.Now(),
}
worlds["metaverse-hub"] = &World{
ID: "metaverse-hub",
Name: "Metaverse Hub",
Description: "Central hub for virtual companies and games",
Type: "hub",
CreatedBy: "system",
Players: 0,
MaxPlayers: 5000,
IsPublic: true,
CreatedAt: time.Now(),
}
}
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
r := gin.Default()
r.Use(func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
})
r.GET("/health", healthCheck)
r.GET("/", root)
api := r.Group("/api")
{
api.GET("/health", healthCheck)
api.GET("/worlds", getWorlds)
api.GET("/worlds/:id", getWorld)
api.POST("/worlds", createWorld)
api.GET("/players", getPlayers)
api.POST("/players", createPlayer)
api.PUT("/players/:id", updatePlayer)
api.GET("/objects", getGameObjects)
api.POST("/objects", createGameObject)
api.GET("/stats", getStats)
}
r.GET("/ws", handleWebSocket)
log.Printf("🌍 RoadWorld running on port %s", port)
log.Printf(" Health: http://localhost:%s/health", port)
log.Printf(" API: http://localhost:%s/api", port)
log.Printf(" WebSocket: ws://localhost:%s/ws", port)
if err := r.Run(":" + port); err != nil {
log.Fatal(err)
}
}
func healthCheck(c *gin.Context) {
c.JSON(200, gin.H{
"status": "healthy",
"service": "RoadWorld API",
"version": "1.0.0",
"timestamp": time.Now().Format(time.RFC3339),
"stats": gin.H{
"total_worlds": len(worlds),
"active_players": len(players),
"total_objects": len(gameObjects),
},
})
}
func root(c *gin.Context) {
c.JSON(200, gin.H{
"name": "RoadWorld",
"description": "Metaverse & Game Creation Platform",
"version": "1.0.0",
"features": []string{
"Open world Earth simulation",
"Virtual company headquarters",
"Game creation tools",
"Real-time multiplayer",
"WebGL rendering",
"3D asset management",
},
"endpoints": gin.H{
"worlds": "/api/worlds",
"players": "/api/players",
"objects": "/api/objects",
"stats": "/api/stats",
"ws": "/ws",
},
})
}
func getWorlds(c *gin.Context) {
mu.RLock()
defer mu.RUnlock()
worldList := make([]*World, 0, len(worlds))
for _, w := range worlds {
worldList = append(worldList, w)
}
c.JSON(200, gin.H{
"success": true,
"count": len(worldList),
"data": worldList,
})
}
func getWorld(c *gin.Context) {
id := c.Param("id")
mu.RLock()
world, exists := worlds[id]
mu.RUnlock()
if !exists {
c.JSON(404, gin.H{"success": false, "error": "World not found"})
return
}
c.JSON(200, gin.H{"success": true, "data": world})
}
func createWorld(c *gin.Context) {
var world World
if err := c.ShouldBindJSON(&world); err != nil {
c.JSON(400, gin.H{"success": false, "error": err.Error()})
return
}
world.CreatedAt = time.Now()
world.Players = 0
mu.Lock()
worlds[world.ID] = &world
mu.Unlock()
c.JSON(201, gin.H{"success": true, "data": world})
}
func getPlayers(c *gin.Context) {
worldID := c.Query("world_id")
mu.RLock()
defer mu.RUnlock()
playerList := make([]*Player, 0)
for _, p := range players {
if worldID == "" || p.WorldID == worldID {
playerList = append(playerList, p)
}
}
c.JSON(200, gin.H{
"success": true,
"count": len(playerList),
"data": playerList,
})
}
func createPlayer(c *gin.Context) {
var player Player
if err := c.ShouldBindJSON(&player); err != nil {
c.JSON(400, gin.H{"success": false, "error": err.Error()})
return
}
mu.Lock()
players[player.ID] = &player
if world, exists := worlds[player.WorldID]; exists {
world.Players++
}
mu.Unlock()
c.JSON(201, gin.H{"success": true, "data": player})
}
func updatePlayer(c *gin.Context) {
id := c.Param("id")
var updates Player
if err := c.ShouldBindJSON(&updates); err != nil {
c.JSON(400, gin.H{"success": false, "error": err.Error()})
return
}
mu.Lock()
if player, exists := players[id]; exists {
player.X = updates.X
player.Y = updates.Y
player.Z = updates.Z
player.Rotation = updates.Rotation
mu.Unlock()
c.JSON(200, gin.H{"success": true, "data": player})
} else {
mu.Unlock()
c.JSON(404, gin.H{"success": false, "error": "Player not found"})
}
}
func getGameObjects(c *gin.Context) {
worldID := c.Query("world_id")
mu.RLock()
defer mu.RUnlock()
objectList := make([]*GameObject, 0)
for _, obj := range gameObjects {
if worldID == "" || obj.WorldID == worldID {
objectList = append(objectList, obj)
}
}
c.JSON(200, gin.H{
"success": true,
"count": len(objectList),
"data": objectList,
})
}
func createGameObject(c *gin.Context) {
var obj GameObject
if err := c.ShouldBindJSON(&obj); err != nil {
c.JSON(400, gin.H{"success": false, "error": err.Error()})
return
}
mu.Lock()
gameObjects[obj.ID] = &obj
mu.Unlock()
c.JSON(201, gin.H{"success": true, "data": obj})
}
func getStats(c *gin.Context) {
mu.RLock()
defer mu.RUnlock()
c.JSON(200, gin.H{
"success": true,
"data": gin.H{
"worlds": gin.H{
"total": len(worlds),
"public": countPublicWorlds(),
"with_players": countWorldsWithPlayers(),
},
"players": gin.H{
"total": len(players),
"online": len(players),
},
"objects": gin.H{
"total": len(gameObjects),
},
},
})
}
func countPublicWorlds() int {
count := 0
for _, w := range worlds {
if w.IsPublic {
count++
}
}
return count
}
func countWorldsWithPlayers() int {
count := 0
for _, w := range worlds {
if w.Players > 0 {
count++
}
}
return count
}
func handleWebSocket(c *gin.Context) {
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
log.Println("WebSocket upgrade error:", err)
return
}
defer conn.Close()
log.Println("New WebSocket connection established")
for {
messageType, message, err := conn.ReadMessage()
if err != nil {
log.Println("WebSocket read error:", err)
break
}
log.Printf("Received: %s", message)
if err := conn.WriteMessage(messageType, message); err != nil {
log.Println("WebSocket write error:", err)
break
}
}
}