Files
golias/handlers/actions.go
2025-05-09 11:24:33 +03:00

211 lines
5.1 KiB
Go

package handlers
import (
"context"
"encoding/json"
"errors"
"golias/models"
"golias/utils"
)
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)
if err := saveRoom(room); err != nil {
return nil, err
}
return room, nil
}
func saveRoom(room *models.Room) error {
data, err := json.Marshal(room)
if err != nil {
return err
}
memcache.Set(models.CacheRoomPrefix+room.ID, data)
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
}
// context
func getRoomIDFromCtx(ctx context.Context) string {
id, _ := ctx.Value(models.CtxRoomIDKey).(string)
return id
}
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 saveStateByCtx(ctx context.Context, state *models.UserState) error {
username, ok := ctx.Value(models.CtxUsernameKey).(string)
if !ok {
log.Debug("no username in ctx")
return errors.New("no username in ctx")
}
return saveState(username, state)
}
func saveFullInfo(fi *models.FullInfo) error {
// INFO: unfortunately working no transactions; so case are possible where first object is updated but the second is not
if err := saveState(fi.State.Username, fi.State); err != nil {
return err
}
// can room be nil?
// if fi.Room == nil {
// return nil
// }
if err := saveRoom(fi.Room); err != nil {
return err
}
return nil
}
// 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 loadState(username string) (*models.UserState, error) {
key := models.CacheStatePrefix + username
data, err := memcache.Get(key)
if err != nil {
return nil, err
}
resp := &models.UserState{}
if err := json.Unmarshal(data, &resp); err != nil {
return nil, err
}
return resp, nil
}
func getAllNames() []string {
names := []string{}
// will not scale
wholeMemStore := memcache.GetAll()
session := &models.Session{}
// filter by key size only sessions
for k, v := 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
}
resp.State = state
if state.RoomID == "" {
return resp, nil
}
room, err := getRoomByID(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 joinTeam(ctx context.Context, role, team string) (*models.FullInfo, error) {
// get username
fi, _ := getFullInfoByCtx(ctx)
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)
// 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.State.Team = "blue"
fi.State.Role = "mime"
if fi.Room.RedTeam.Mime == fi.State.Username {
fi.Room.RedTeam.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.State.Team = "red"
fi.State.Role = "mime"
if fi.Room.BlueTeam.Mime == fi.State.Username {
fi.Room.BlueTeam.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.State.Team = "blue"
fi.State.Role = "guesser"
} else if team == "red" {
fi.Room.RedTeam.Guessers = append(fi.Room.RedTeam.Guessers, fi.State.Username)
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
}