/ src / server.go
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  }