server.go
1 package main 2 3 import ( 4 "context" 5 "encoding/json" 6 "log" 7 "net/http" 8 "sync" 9 "time" 10 11 "creature/src/systems" 12 13 "github.com/gorilla/websocket" 14 ) 15 16 var upgrader = websocket.Upgrader{ 17 CheckOrigin: func(r *http.Request) bool { 18 return true 19 }, 20 } 21 22 type HeartbeatData struct { 23 CellStates []map[string]interface{} `json:"cell_states"` 24 TotalEnergy float64 `json:"total_energy"` 25 TotalThoughts int `json:"total_thoughts"` 26 TotalPlans int `json:"total_plans"` 27 CellsCount int `json:"cells_count"` 28 MutationRate float64 `json:"mutation_rate"` 29 ClusterCount int `json:"cluster_count"` 30 GridDepth int `json:"grid_depth"` 31 } 32 33 func startServer(ctx context.Context, colony *systems.Colony, colonyMutex *sync.Mutex) { 34 shutdownChan := make(chan struct{}) 35 36 // Listen for context cancellation 37 go func() { 38 <-ctx.Done() 39 close(shutdownChan) 40 }() 41 http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { 42 conn, err := upgrader.Upgrade(w, r, nil) 43 if err != nil { 44 log.Println("WebSocket upgrade error:", err) 45 return 46 } 47 defer conn.Close() 48 49 go handleConnection(conn, colony, colonyMutex) 50 }) 51 52 server := &http.Server{ 53 Addr: "127.0.0.1:3030", 54 } 55 56 go func() { 57 if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { 58 log.Fatalf("Server error: %v", err) 59 } 60 }() 61 62 <-shutdownChan 63 log.Println("Shutting down WebSocket server...") 64 server.Close() 65 } 66 67 func prepareHeartbeatData(colony *systems.Colony, colonyMutex *sync.Mutex) HeartbeatData { 68 colonyMutex.Lock() 69 defer colonyMutex.Unlock() 70 71 cellStates := make([]map[string]interface{}, 0) 72 totalEnergy := 0.0 73 totalThoughts := 0 74 totalPlans := 0 75 76 for id, cell := range colony.Cells { 77 totalEnergy += cell.Energy 78 totalThoughts += len(cell.Thoughts) 79 if cell.CurrentPlan != nil { 80 totalPlans++ 81 } 82 83 cellState := map[string]interface{}{ 84 "id": id, 85 "energy": cell.Energy, 86 "position": map[string]float64{ 87 "x": cell.Position.X, 88 "y": cell.Position.Y, 89 "z": cell.Position.Z, 90 "heat": cell.Position.Heat, 91 }, 92 "dimensions": map[string]float64{ 93 "emergence": cell.DimensionalPosition.Emergence, 94 "coherence": cell.DimensionalPosition.Coherence, 95 "resilience": cell.DimensionalPosition.Resilience, 96 "intelligence": cell.DimensionalPosition.Intelligence, 97 "efficiency": cell.DimensionalPosition.Efficiency, 98 "integration": cell.DimensionalPosition.Integration, 99 }, 100 "thoughts": len(cell.Thoughts), 101 "has_plan": cell.CurrentPlan != nil, 102 "dopamine": cell.Dopamine, 103 "neighbors": len(cell.Neighbors), 104 } 105 cellStates = append(cellStates, cellState) 106 } 107 108 return HeartbeatData{ 109 CellStates: cellStates, 110 TotalEnergy: totalEnergy, 111 TotalThoughts: totalThoughts, 112 TotalPlans: totalPlans, 113 CellsCount: len(colony.Cells), 114 MutationRate: colony.GetMutationRate(), 115 ClusterCount: colony.GetClusterCount(), 116 GridDepth: colony.GetMaxDepth(), 117 } 118 } 119 120 func generateHeartbeatFromData(data HeartbeatData) ([]byte, error) { 121 avgEnergy := 0.0 122 if data.CellsCount > 0 { 123 avgEnergy = data.TotalEnergy / float64(data.CellsCount) 124 } 125 126 heartbeat := map[string]interface{}{ 127 "type": "heartbeat", 128 "timestamp": time.Now().Unix(), 129 "colony_stats": map[string]interface{}{ 130 "total_cells": data.CellsCount, 131 "total_thoughts": data.TotalThoughts, 132 "total_plans": data.TotalPlans, 133 "average_energy": avgEnergy, 134 "mutation_rate": data.MutationRate, 135 "cluster_count": data.ClusterCount, 136 }, 137 "platform_stats": map[string]interface{}{ 138 "memory_usage": data.TotalThoughts * 64, // Approximate size of a Thought 139 "cells_per_cluster": float64(data.CellsCount) / float64(data.ClusterCount), 140 "grid_depth": data.GridDepth, 141 "processing_load": float64(data.TotalThoughts) / float64(data.CellsCount), 142 }, 143 "cells": data.CellStates, 144 } 145 146 return json.Marshal(heartbeat) 147 } 148 149 func handleConnection(conn *websocket.Conn, colony *systems.Colony, colonyMutex *sync.Mutex) { 150 snapshotTicker := time.NewTicker(5 * time.Second) 151 updateTicker := time.NewTicker(2 * time.Second) 152 heartbeatTicker := time.NewTicker(500 * time.Millisecond) 153 154 defer func() { 155 snapshotTicker.Stop() 156 updateTicker.Stop() 157 heartbeatTicker.Stop() 158 }() 159 160 // Send initial snapshot 161 initialSnapshot := func() ([]byte, error) { 162 colonyMutex.Lock() 163 defer colonyMutex.Unlock() 164 165 snapshot := map[string]interface{}{ 166 "type": "snapshot", 167 "timestamp": time.Now().Unix(), 168 "colony_stats": map[string]interface{}{ 169 "total_cells": len(colony.Cells), 170 "average_energy": colony.GetAverageEnergy(), 171 "total_thoughts": colony.GetTotalThoughts(), 172 "total_plans": colony.GetTotalPlans(), 173 "mutation_rate": colony.GetMutationRate(), 174 "cluster_count": colony.GetClusterCount(), 175 }, 176 "cells": colony.Cells, 177 } 178 return json.Marshal(snapshot) 179 } 180 181 snapshot, err := initialSnapshot() 182 if err != nil { 183 log.Println("Error creating initial snapshot:", err) 184 return 185 } 186 187 if err := conn.WriteMessage(websocket.TextMessage, snapshot); err != nil { 188 log.Println("Error sending initial snapshot:", err) 189 return 190 } 191 192 for { 193 select { 194 case <-snapshotTicker.C: 195 snapshot, err := initialSnapshot() 196 if err != nil { 197 log.Println("Error creating snapshot:", err) 198 return 199 } 200 201 if err := conn.WriteMessage(websocket.TextMessage, snapshot); err != nil { 202 log.Println("Error sending snapshot:", err) 203 return 204 } 205 206 case <-updateTicker.C: 207 update := func() ([]byte, error) { 208 colonyMutex.Lock() 209 defer colonyMutex.Unlock() 210 211 updateData := map[string]interface{}{ 212 "type": "update", 213 "timestamp": time.Now().Unix(), 214 "colony_stats": map[string]interface{}{ 215 "total_cells": len(colony.Cells), 216 "average_energy": colony.GetAverageEnergy(), 217 "total_thoughts": colony.GetTotalThoughts(), 218 "total_plans": colony.GetTotalPlans(), 219 "mutation_rate": colony.GetMutationRate(), 220 "cluster_count": colony.GetClusterCount(), 221 }, 222 "cells": colony.Cells, 223 } 224 return json.Marshal(updateData) 225 } 226 227 updateData, err := update() 228 if err != nil { 229 log.Println("Error creating update:", err) 230 return 231 } 232 233 if err := conn.WriteMessage(websocket.TextMessage, updateData); err != nil { 234 log.Println("Error sending update:", err) 235 return 236 } 237 238 case <-heartbeatTicker.C: 239 heartbeatData := prepareHeartbeatData(colony, colonyMutex) 240 heartbeat, err := generateHeartbeatFromData(heartbeatData) 241 if err != nil { 242 log.Println("Error creating heartbeat:", err) 243 return 244 } 245 246 if err := conn.WriteMessage(websocket.TextMessage, heartbeat); err != nil { 247 log.Println("Error sending heartbeat:", err) 248 return 249 } 250 } 251 } 252 }