package boxes import ( "fmt" "strconv" "strings" "sync" "github.com/labstack/echo/v4" "github.com/labstack/gommon/log" "golang.org/x/net/websocket" ) var ( wsConnections = make(map[*websocket.Conn]bool) wsMutex sync.Mutex ) type Box struct { Id int Value bool } func deserializeBox(msg string) (Box, error) { parts := strings.Split(msg, ":") index, err := strconv.Atoi(parts[1]) if err != nil { return Box{}, err } checked := parts[2] == "+" return Box{index, checked}, nil } func (box Box) serialize() string { message := "b:" + strconv.Itoa(box.Id) if box.Value { message += ":+" } else { message += ":-" } return message + "\n" } func handleWsError(err error, conn *websocket.Conn) { if err != nil { log.Errorf("Failed to handle websocket: %v", err) conn.Close() wsMutex.Lock() defer wsMutex.Unlock() delete(wsConnections, conn) } } func broadcastMessage(message string) { wsMutex.Lock() defer wsMutex.Unlock() for conn := range wsConnections { err := websocket.Message.Send(conn, message) handleWsError(err, conn) } } func randomizeBoxes(count int) []Box { boxes := make([]Box, count) for i := 0; i < count; i++ { index := random.Int() % 1000 box := getBox(index) box.Value = !box.Value box.persist() boxes[i] = box } return boxes } func handleMessage(msg string, ws *websocket.Conn) error { if msg == "gol" { matrix := make(map[string]Box) boxes := GetBoxes() index := 0 for i := 0; i < 32; i++ { for j := 0; j < 32; j++ { matrix[fmt.Sprintf("%d-%d", i, j)] = boxes[index] index++ } } nextGen := make([]Box, 0) for id, item := range matrix { nextItem := shouldBeAlive(matrix, id) if nextItem.Value != item.Value { nextGen = append(nextGen, shouldBeAlive(matrix, id)) } } nextMessage := "" for _, item := range nextGen { item.persist() nextMessage += item.serialize() } broadcastMessage(nextMessage) } else if strings.Contains(msg, "r:") { count, err := strconv.Atoi(strings.Split(msg, ":")[1]) if err != nil { log.Errorf("Failed to parse randomize count: %v", err) } boxes := randomizeBoxes(count) message := "" for _, box := range boxes { message += box.serialize() } broadcastMessage(message) } else if strings.Contains(msg, "b:") { box, err := deserializeBox(msg) if err != nil { return err } broadcastMessage(msg) box.persist() } else if strings.Contains(msg, "ping") { len := len(wsConnections) websocket.Message.Send(ws, "pong-"+strconv.Itoa(len)) } return nil } func InitWs(c echo.Context) error { websocket.Handler(func(ws *websocket.Conn) { wsMutex.Lock() wsConnections[ws] = true wsMutex.Unlock() boxes := GetBoxes() msg := "" for _, box := range boxes { msg += box.serialize() } err := websocket.Message.Send(ws, msg) handleWsError(err, ws) for { var msg string err := websocket.Message.Receive(ws, &msg) handleWsError(err, ws) if err != nil { break } handleMessage(msg, ws) log.Infof("Received message: %s", msg) } }).ServeHTTP(c.Response(), c.Request()) return nil }