362 lines
9.5 KiB
Go
362 lines
9.5 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"gralias/broker"
|
|
"gralias/llmapi"
|
|
"gralias/models"
|
|
"gralias/wordloader"
|
|
)
|
|
|
|
func createRoom(ctx context.Context, req *models.RoomReq) (*models.Room, error) {
|
|
creator, ok := ctx.Value(models.CtxUsernameKey).(string)
|
|
if !ok {
|
|
err := errors.New("failed to extract user from ctx")
|
|
return nil, err
|
|
}
|
|
room := req.CreateRoom(creator)
|
|
room.RoomLink = cfg.BaseURL + "/room-join?id=" + room.ID
|
|
if err := repo.RoomCreate(ctx, room); err != nil {
|
|
return nil, err
|
|
}
|
|
return room, nil
|
|
}
|
|
|
|
// // DEPRECATED
|
|
// func saveRoom(room *models.Room) error {
|
|
// key := models.CacheRoomPrefix + room.ID
|
|
// data, err := json.Marshal(room)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// memcache.Set(key, data)
|
|
// // do I need last action here? since room save is kind of an action on itself
|
|
// // time.Now().Add(time.Hour).Sub(room.LastActionTS)
|
|
// anHour := int64(216000) // 60 * 60 * 60
|
|
// memcache.Expire(key, anHour)
|
|
// return nil
|
|
// }
|
|
|
|
// func getRoomByID(roomID string) (*models.Room, error) {
|
|
// roomBytes, err := memcache.Get(models.CacheRoomPrefix + roomID)
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
// resp := &models.Room{}
|
|
// if err := json.Unmarshal(roomBytes, &resp); err != nil {
|
|
// return nil, err
|
|
// }
|
|
// return resp, nil
|
|
// }
|
|
|
|
// func removeRoom(roomID string) {
|
|
// key := models.CacheRoomPrefix + roomID
|
|
// memcache.RemoveKey(key)
|
|
// }
|
|
|
|
// context
|
|
|
|
// func getStateByCtx(ctx context.Context) (*models.UserState, error) {
|
|
// username, ok := ctx.Value(models.CtxUsernameKey).(string)
|
|
// if !ok {
|
|
// log.Debug("no username in ctx")
|
|
// return &models.UserState{}, errors.New("no username in ctx")
|
|
// }
|
|
// us, err := loadState(username)
|
|
// if err != nil {
|
|
// return &models.UserState{}, err
|
|
// }
|
|
// return us, nil
|
|
// }
|
|
|
|
// func dbCreate(fi *models.FullInfo) error{
|
|
// repo.CreateRoom()
|
|
// }
|
|
|
|
func saveFullInfo(fi *models.FullInfo) error {
|
|
// INFO: no transactions; so case is possible where first object is updated but the second is not
|
|
if err := repo.PlayerUpdate(fi.State); err != nil {
|
|
return err
|
|
}
|
|
log.Debug("saved user state", "state", fi.State)
|
|
if err := repo.RoomUpdate(context.Background(), fi.Room); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func notifyBotIfNeeded(room *models.Room) {
|
|
if botName := room.WhichBotToMove(); botName != "" {
|
|
log.Debug("got botname", "name", botName, "channel_len", len(llmapi.SignalChanMap[botName]))
|
|
llmapi.SignalChanMap[botName] <- true
|
|
log.Debug("after sending signal", "name", botName)
|
|
}
|
|
}
|
|
|
|
// cache
|
|
|
|
// func saveState(username string, state *models.UserState) error {
|
|
// key := models.CacheStatePrefix + username
|
|
// data, err := json.Marshal(state)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// memcache.Set(key, data)
|
|
// return nil
|
|
// }
|
|
|
|
// func getAllNames() []string {
|
|
// names := []string{}
|
|
// // will not scale
|
|
// session := &models.Session{}
|
|
// // filter by key size only sessions
|
|
// for _, name := range wholeMemStore {
|
|
// // xid is 20 in len
|
|
// if len(k) != 20 {
|
|
// continue
|
|
// }
|
|
// if err := json.Unmarshal(v, &session); err != nil {
|
|
// log.Error("failed to unmarshal", "error", err)
|
|
// continue
|
|
// }
|
|
// names = append(names, session.Username)
|
|
// }
|
|
// return names
|
|
// }
|
|
|
|
// can room exists without state? I think no
|
|
func getFullInfoByCtx(ctx context.Context) (*models.FullInfo, error) {
|
|
resp := &models.FullInfo{}
|
|
// state, err := getStateByCtx(ctx)
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
state, err := getPlayerByCtx(ctx)
|
|
resp.State = state
|
|
if state.RoomID == "" {
|
|
return resp, nil
|
|
}
|
|
// room, err := getRoomByID(state.RoomID)
|
|
room, err := repo.RoomGetByID(ctx, state.RoomID)
|
|
if err != nil {
|
|
log.Warn("failed to find room despite knowing room_id;",
|
|
"room_id", state.RoomID)
|
|
return nil, err
|
|
}
|
|
resp.Room = room
|
|
return resp, nil
|
|
}
|
|
|
|
func getPlayerByCtx(ctx context.Context) (*models.Player, error) {
|
|
username, ok := ctx.Value(models.CtxUsernameKey).(string)
|
|
if !ok {
|
|
log.Debug("no username in ctx")
|
|
return &models.Player{}, errors.New("no username in ctx")
|
|
}
|
|
return repo.PlayerGetByName(username)
|
|
}
|
|
|
|
// // DEPRECATED
|
|
// func leaveRole(fi *models.FullInfo) {
|
|
// fi.Room.RedTeam.Guessers = utils.RemoveFromSlice(fi.State.Username, fi.Room.RedTeam.Guessers)
|
|
// fi.Room.BlueTeam.Guessers = utils.RemoveFromSlice(fi.State.Username, fi.Room.BlueTeam.Guessers)
|
|
// if fi.Room.RedTeam.Mime == fi.State.Username {
|
|
// fi.Room.RedTeam.Mime = ""
|
|
// }
|
|
// if fi.Room.BlueTeam.Mime == fi.State.Username {
|
|
// fi.Room.BlueTeam.Mime = ""
|
|
// }
|
|
// }
|
|
|
|
func joinTeam(ctx context.Context, role, team string) (*models.FullInfo, error) {
|
|
// get username
|
|
fi, _ := getFullInfoByCtx(ctx)
|
|
fi.Room.RemovePlayer(fi.State.Username)
|
|
// get room
|
|
if role == "mime" {
|
|
if team == "blue" {
|
|
if fi.Room.BlueTeam.Mime != "" {
|
|
// error: alredy taken
|
|
err := errors.New("Mime role already taken!")
|
|
return fi, err
|
|
}
|
|
fi.Room.BlueTeam.Mime = fi.State.Username
|
|
fi.Room.BlueTeam.Color = "blue"
|
|
fi.State.Team = "blue"
|
|
fi.State.Role = "mime"
|
|
} else if team == "red" {
|
|
if fi.Room.RedTeam.Mime != "" {
|
|
// error: alredy taken
|
|
err := errors.New("Mime role already taken!")
|
|
return fi, err
|
|
}
|
|
fi.Room.RedTeam.Mime = fi.State.Username
|
|
fi.Room.RedTeam.Color = "red"
|
|
fi.State.Team = "red"
|
|
fi.State.Role = "mime"
|
|
} else {
|
|
err := errors.New("uknown team:" + team)
|
|
return nil, err
|
|
}
|
|
} else if role == "guesser" {
|
|
if team == "blue" {
|
|
fi.Room.BlueTeam.Guessers = append(fi.Room.BlueTeam.Guessers, fi.State.Username)
|
|
fi.Room.BlueTeam.Color = "blue"
|
|
fi.State.Team = "blue"
|
|
fi.State.Role = "guesser"
|
|
} else if team == "red" {
|
|
fi.Room.RedTeam.Guessers = append(fi.Room.RedTeam.Guessers, fi.State.Username)
|
|
fi.Room.RedTeam.Color = "red"
|
|
fi.State.Team = "red"
|
|
fi.State.Role = "guesser"
|
|
} else {
|
|
err := errors.New("uknown team:" + team)
|
|
return nil, err
|
|
}
|
|
} else {
|
|
err := errors.New("uknown role:" + role)
|
|
return nil, err
|
|
}
|
|
if err := saveFullInfo(fi); err != nil {
|
|
return nil, err
|
|
}
|
|
return fi, nil
|
|
}
|
|
|
|
// get all rooms
|
|
// func listRooms(allRooms bool) []*models.Room {
|
|
// cacheMap := memcache.GetAll()
|
|
// publicRooms := []*models.Room{}
|
|
// // no way to know if room is public until unmarshal -_-;
|
|
// for key, value := range cacheMap {
|
|
// if strings.HasPrefix(key, models.CacheRoomPrefix) {
|
|
// room := &models.Room{}
|
|
// if err := json.Unmarshal(value, &room); err != nil {
|
|
// log.Warn("failed to unmarshal room", "error", err)
|
|
// continue
|
|
// }
|
|
// if room.IsPublic || allRooms {
|
|
// publicRooms = append(publicRooms, room)
|
|
// }
|
|
// }
|
|
// }
|
|
// return publicRooms
|
|
// }
|
|
|
|
// get bots
|
|
func listBots() []models.Player {
|
|
bots, err := repo.PlayerList(true)
|
|
if err != nil {
|
|
log.Error("failed to fetch bots from db", "error", err)
|
|
}
|
|
return bots
|
|
}
|
|
|
|
// get players
|
|
func notify(event, msg string) {
|
|
Notifier.Notifier <- broker.NotificationEvent{
|
|
EventName: event,
|
|
Payload: msg,
|
|
}
|
|
}
|
|
|
|
func loadCards(room *models.Room) {
|
|
// store it somewhere
|
|
wordMap := map[string]string{
|
|
"en": "assets/words/en_nouns.txt",
|
|
"ru": "assets/words/ru_nouns.txt",
|
|
}
|
|
wl := wordloader.InitDefaultLoader(wordMap[room.Settings.Language])
|
|
cards, err := wl.Load()
|
|
if err != nil {
|
|
// no logger
|
|
fmt.Println("failed to load cards", "error", err)
|
|
}
|
|
room.Cards = cards
|
|
room.WCMap = make(map[string]models.WordColor)
|
|
for _, card := range room.Cards {
|
|
room.WCMap[card.Word] = card.Color
|
|
}
|
|
}
|
|
|
|
func recoverBots() {
|
|
bots := listBots()
|
|
for _, bot := range bots {
|
|
if err := recoverBot(bot); err != nil {
|
|
log.Warn("failed to recover bot", "botName", bot.Username, "error", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func recoverBot(bm models.Player) error {
|
|
// check if room still exists
|
|
if _, err := repo.RoomGetByID(context.Background(), bm.RoomID); err != nil {
|
|
return fmt.Errorf("no such room: %s; err: %w", bm.RoomID, err)
|
|
}
|
|
log.Debug("recovering bot", "bot", bm)
|
|
_, err := llmapi.NewBot(string(bm.Role), string(bm.Team), bm.Username, bm.RoomID, cfg, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// func recoverPlayers() {
|
|
// players := listPlayers()
|
|
// for playerName, playerMap := range players {
|
|
// if err := recoverPlayer(playerMap); err != nil {
|
|
// log.Warn("failed to recover player", "playerName", playerName, "error", err)
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// func recoverPlayer(pm map[string]string) error {
|
|
// // check if room still exists
|
|
// room, err := repo.RoomGetByID(context.Background(), pm["RoomID"])
|
|
// if err != nil {
|
|
// return fmt.Errorf("no such room: %s; err: %w", pm["RoomID"], err)
|
|
// }
|
|
// log.Debug("recovering player", "player", pm)
|
|
// role, team, ok := room.GetPlayerByName(pm["Username"])
|
|
// if !ok {
|
|
// return fmt.Errorf("failed to find player %s in the room %v", pm["Username"], room)
|
|
// }
|
|
// us := &models.Player{
|
|
// Username: pm["Username"],
|
|
// RoomID: pm["RoomID"],
|
|
// Team: team,
|
|
// Role: role,
|
|
// }
|
|
// return saveState(pm["Username"], us)
|
|
// }
|
|
|
|
// validateMove checks if it is players turn
|
|
func validateMove(fi *models.FullInfo, ur models.UserRole) error {
|
|
if fi.State.Role != ur {
|
|
err := fmt.Errorf("need to be %s to make that action", ur)
|
|
return err
|
|
}
|
|
// whos move it is?
|
|
if fi.State.Team != models.UserTeam(fi.Room.TeamTurn) {
|
|
err := errors.New("not your team's move")
|
|
return err
|
|
}
|
|
switch ur {
|
|
case models.UserRoleGuesser:
|
|
if !fi.Room.MimeDone {
|
|
err := errors.New("wait for the mime to give a clue")
|
|
return err
|
|
}
|
|
case models.UserRoleMime:
|
|
if fi.Room.MimeDone {
|
|
err := errors.New("clue was already given")
|
|
return err
|
|
}
|
|
default:
|
|
return fmt.Errorf("uknown user role: %s", ur)
|
|
}
|
|
return nil
|
|
}
|