diff --git a/handlers/actions.go b/handlers/actions.go index 17149b9..ba49f60 100644 --- a/handlers/actions.go +++ b/handlers/actions.go @@ -33,8 +33,7 @@ func saveRoom(room *models.Room) error { if err != nil { return err } - memcache.Set(models.CacheRoomPrefix+room.ID, data) - log.Debug("saved room", "room", room, "key", key) + memcache.Set(key, data) return nil } @@ -84,6 +83,21 @@ func saveFullInfo(fi *models.FullInfo) error { return nil } +func notifyBotIfNeeded(fi *models.FullInfo) { + if botName := fi.Room.WhichBotToMove(); botName != "" { + // // get bot from memcache + // bot, err := loadBot(botName, fi.Room.ID) + // if err != nil { + // log.Error("failed to load bot", "bot_name", botName, "room_id", fi.Room.ID) + // // abortWithError(w, err.Error()) + // // return + // } + // send signal to bot + llmapi.SignalChanMap[botName] <- true + } + log.Debug("no bot", "room_id", fi.Room.ID) +} + // cache func saveState(username string, state *models.UserState) error { @@ -110,7 +124,16 @@ func loadState(username string) (*models.UserState, error) { } func loadBot(botName, roomID string) (*llmapi.Bot, error) { - return nil, nil + key := "botkey_" + roomID + botName + data, err := memcache.Get(key) + if err != nil { + return nil, err + } + resp := &llmapi.Bot{} + if err := json.Unmarshal(data, &resp); err != nil { + return nil, err + } + return resp, nil } func getAllNames() []string { diff --git a/handlers/elements.go b/handlers/elements.go index 3d0cc14..137ce78 100644 --- a/handlers/elements.go +++ b/handlers/elements.go @@ -92,6 +92,7 @@ func HandleShowColor(w http.ResponseWriter, r *http.Request) { case "white", string(oppositeColor): // end turn fi.Room.TeamTurn = oppositeColor + fi.Room.MimeDone = false } // check if no cards left => game over if fi.Room.BlueCounter == 0 { @@ -110,6 +111,8 @@ func HandleShowColor(w http.ResponseWriter, r *http.Request) { abortWithError(w, err.Error()) return } + // get mime bot for opp team and notify it + notifyBotIfNeeded(fi) notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "") if err := tmpl.ExecuteTemplate(w, "cardword", cardword); err != nil { log.Error("failed to execute cardword template", "error", err) @@ -136,16 +139,19 @@ func HandleAddBot(w http.ResponseWriter, r *http.Request) { // get team; // get role; make up a name team := r.URL.Query().Get("team") role := r.URL.Query().Get("role") + log.Debug("got add-bot request", "team", team, "role", role) fi, err := getFullInfoByCtx(r.Context()) if err != nil { abortWithError(w, err.Error()) return } + // TODO: what if bot exists already? + // control number and names of bots bot, err := llmapi.NewBot(role, team, "bot1", fi.Room.ID, cfg) if err != nil { abortWithError(w, err.Error()) return } - bot.StartBot() + go bot.StartBot() notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "") } diff --git a/handlers/game.go b/handlers/game.go index 169fb8f..1832f1a 100644 --- a/handlers/game.go +++ b/handlers/game.go @@ -51,53 +51,6 @@ func HandleCreateRoom(w http.ResponseWriter, r *http.Request) { } } -// DEPRACATED: duplication of HandleJoinRoom -// func HandleRoomEnter(w http.ResponseWriter, r *http.Request) { -// // parse payload -// roomID := r.URL.Query().Get("id") -// if roomID == "" { -// msg := "room id not provided" -// log.Error(msg) -// abortWithError(w, msg) -// return -// } -// tmpl, err := template.ParseGlob("components/*.html") -// if err != nil { -// abortWithError(w, err.Error()) -// return -// } -// // create a room -// room, err := getRoomByID(roomID) -// if err != nil { -// msg := "failed to find the room" -// log.Error(msg, "error", err, "room_id", roomID) -// abortWithError(w, msg) -// return -// } -// state, err := getStateByCtx(r.Context()) -// // INFO: if non-loggined user join: prompt to login -// if err != nil { -// log.Error("failed to get state", "error", err) -// // abortWithError(w, err.Error()) -// tmpl.ExecuteTemplate(w, "login", nil) -// return -// } -// state.RoomID = room.ID -// // update state -// if err := saveStateByCtx(r.Context(), state); err != nil { -// log.Error("failed to update state", "error", err) -// abortWithError(w, err.Error()) -// return -// } -// // send msg of created room -// // h.Broker.Notifier <- broker.NotificationEvent{ -// // EventName: models.MsgRoomListUpdate, -// // Payload: fmt.Sprintf("%s created a room named %s", r.CreatorName, r.RoomName), -// // } -// // return html -// tmpl.ExecuteTemplate(w, "base", room) -// } - func HandleJoinTeam(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { log.Error("failed to parse form", "error", err) @@ -158,6 +111,7 @@ func HandleEndTurn(w http.ResponseWriter, r *http.Request) { return } fi.Room.ChangeTurn() + fi.Room.MimeDone = false if err := saveFullInfo(fi); err != nil { abortWithError(w, err.Error()) return @@ -168,17 +122,7 @@ func HandleEndTurn(w http.ResponseWriter, r *http.Request) { abortWithError(w, err.Error()) return } - if botName := fi.Room.WhichBotToMove(); botName != "" { - // get bot from memcache - bot, err := loadBot(botName, fi.Room.ID) - if err != nil { - log.Error("failed to load bot", "bot_name", botName, "room_id", fi.Room.ID) - abortWithError(w, err.Error()) - return - } - // send signal to bot - bot.SignalsCh <- true - } + notifyBotIfNeeded(fi) notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "") if err := tmpl.ExecuteTemplate(w, "base", fi); err != nil { log.Error("failed to execute base template", "error", err) @@ -222,17 +166,7 @@ func HandleStartGame(w http.ResponseWriter, r *http.Request) { abortWithError(w, err.Error()) return } - if botName := fi.Room.WhichBotToMove(); botName != "" { - // get bot from memcache - bot, err := loadBot(botName, fi.Room.ID) - if err != nil { - log.Error("failed to load bot", "bot_name", botName, "room_id", fi.Room.ID) - abortWithError(w, err.Error()) - return - } - // send signal to bot - bot.SignalsCh <- true - } + notifyBotIfNeeded(fi) // to update only the room that should be updated notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "") // notify(models.NotifyBacklogPrefix+fi.Room.ID, "game started") diff --git a/handlers/middleware.go b/handlers/middleware.go index 1da5b12..d374c84 100644 --- a/handlers/middleware.go +++ b/handlers/middleware.go @@ -5,24 +5,22 @@ import ( "crypto/hmac" "crypto/sha256" "encoding/base64" - "errors" "golias/models" "net/http" - "time" ) // LogRequests logs all HTTP requests with method, path and duration func LogRequests(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - start := time.Now() + // start := time.Now() // Wrap response writer to capture status code next.ServeHTTP(w, r) - duration := time.Since(start) - log.Debug("request completed", - "method", r.Method, - "path", r.URL.RequestURI(), - "duration", duration.String(), - ) + // duration := time.Since(start) + // log.Debug("request completed", + // "method", r.Method, + // "path", r.URL.RequestURI(), + // "duration", duration.String(), + // ) }) } @@ -65,11 +63,11 @@ func GetSession(next http.Handler) http.Handler { return } userSession, err := cacheGetSession(sessionToken) - log.Debug("userSession from cache", "us", userSession) + // log.Debug("userSession from cache", "us", userSession) if err != nil { - msg := "auth failed; session does not exists" - err = errors.New(msg) - log.Debug(msg, "error", err) + // msg := "auth failed; session does not exists" + // err = errors.New(msg) + // log.Debug(msg, "error", err) next.ServeHTTP(w, r) return } diff --git a/llmapi/main.go b/llmapi/main.go index 4b9ef7a..adaaa84 100644 --- a/llmapi/main.go +++ b/llmapi/main.go @@ -10,6 +10,7 @@ import ( "io" "log/slog" "net/http" + "os" "strings" ) @@ -18,6 +19,14 @@ import ( // MIME: llm needs to know all the cards, colors and previous actions // GUESSER: llm needs to know all the cards and previous actions +// channels are not serializable + +var ( + // botname -> channel + SignalChanMap = make(map[string]chan bool) + DoneChanMap = make(map[string]chan bool) +) + type Bot struct { Role string // gueeser | mime Team string @@ -26,15 +35,17 @@ type Bot struct { BotName string log *slog.Logger // channels for communicaton - SignalsCh chan bool - DoneCh chan bool + // channels are not serializable + // SignalsCh chan bool + // DoneCh chan bool } // StartBot func (b *Bot) StartBot() { for { select { - case <-b.SignalsCh: + case <-SignalChanMap[b.BotName]: + b.log.Debug("got signal", "bot-team", b.Team, "bot-role", b.Role) // get room cards and actions room, err := getRoomByID(b.RoomID) if err != nil { @@ -53,7 +64,7 @@ func (b *Bot) StartBot() { // if mime -> give clue // if guesser -> open card (does opening one card prompting new loop?) // send notification to sse broker - case <-b.DoneCh: + case <-DoneChanMap[b.BotName]: b.log.Debug("got done signal", "bot-name", b.BotName) return } @@ -69,6 +80,10 @@ func NewBot(role, team, name, roomID string, cfg *config.Config) (*Bot, error) { BotName: name, Team: team, cfg: cfg, + log: slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{ + Level: slog.LevelDebug, + AddSource: true, + })), } // add to room room, err := getRoomByID(bot.RoomID) @@ -108,10 +123,25 @@ func NewBot(role, team, name, roomID string, cfg *config.Config) (*Bot, error) { if err := saveRoom(room); err != nil { return nil, err } + if err := saveBot(bot); err != nil { + return nil, err + } + SignalChanMap[bot.BotName] = make(chan bool) + DoneChanMap[bot.BotName] = make(chan bool) go bot.StartBot() // run bot routine return bot, nil } +func saveBot(bot *Bot) error { + key := "botkey_" + bot.RoomID + bot.BotName + data, err := json.Marshal(bot) + if err != nil { + return err + } + cache.MemCache.Set(key, data) + return nil +} + func getRoomByID(roomID string) (*models.Room, error) { roomBytes, err := cache.MemCache.Get(models.CacheRoomPrefix + roomID) if err != nil { diff --git a/models/main.go b/models/main.go index cd09bf5..7fc81db 100644 --- a/models/main.go +++ b/models/main.go @@ -1,6 +1,7 @@ package models import ( + "fmt" "golias/utils" "time" @@ -84,6 +85,7 @@ type Room struct { // WhichBotToMove returns bot name that have to move or empty string func (r *Room) WhichBotToMove() string { + fmt.Println("looking for bot to move", "team-turn", r.TeamTurn, "mime-done", r.MimeDone, "bot-map", r.BotMap, "is_running", r.IsRunning) if !r.IsRunning { return "" } diff --git a/todos.md b/todos.md index 093de79..dcfd802 100644 --- a/todos.md +++ b/todos.md @@ -7,6 +7,7 @@ - login with invite link; - add html icons of whos turn it is (like an image of big ? when mime is thinking); - there two places for bot to check if its its move: start-game; end-turn; +- remove bot button (if game is not running); #### sse points - clue sse update;