297 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			297 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package handlers
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"gralias/models"
 | |
| 	"html/template"
 | |
| 	"net/http"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| func HandleCreateRoom(w http.ResponseWriter, r *http.Request) {
 | |
| 	// parse payload
 | |
| 	payload := &models.RoomReq{
 | |
| 		RoomPass: r.PostFormValue("room_pass"),
 | |
| 		RoomName: r.PostFormValue("room_name"),
 | |
| 	}
 | |
| 	// create a room
 | |
| 	room, err := createRoom(r.Context(), payload)
 | |
| 	if err != nil {
 | |
| 		msg := "failed to create a room"
 | |
| 		log.Error(msg, "error", err)
 | |
| 		abortWithError(w, msg)
 | |
| 		return
 | |
| 	}
 | |
| 	ctx := context.WithValue(r.Context(), "current_room", room.ID)
 | |
| 	fi, err := getFullInfoByCtx(ctx)
 | |
| 	if err != nil {
 | |
| 		msg := "failed to get full info from ctx"
 | |
| 		log.Error(msg, "error", err)
 | |
| 		abortWithError(w, msg)
 | |
| 		return
 | |
| 	}
 | |
| 	fi.State.RoomID = room.ID
 | |
| 	fi.Room = room
 | |
| 	fi.Room.IsPublic = true // hardcode for local test; move to form
 | |
| 	if err := saveFullInfo(fi); err != nil {
 | |
| 		msg := "failed to set current room to session"
 | |
| 		log.Error(msg, "error", err)
 | |
| 		abortWithError(w, msg)
 | |
| 		return
 | |
| 	}
 | |
| 	notify(models.NotifyRoomListUpdate, "")
 | |
| 	tmpl, err := template.ParseGlob("components/*.html")
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	if err := tmpl.ExecuteTemplate(w, "base", fi); err != nil {
 | |
| 		log.Error("failed to execute base template", "error", err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func HandleJoinTeam(w http.ResponseWriter, r *http.Request) {
 | |
| 	if err := r.ParseForm(); err != nil {
 | |
| 		log.Error("failed to parse form", "error", err)
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	team := r.PostFormValue("team")
 | |
| 	role := r.PostFormValue("role")
 | |
| 	if team == "" || role == "" {
 | |
| 		msg := "missing team or role"
 | |
| 		log.Error(msg)
 | |
| 		abortWithError(w, msg)
 | |
| 		return
 | |
| 	}
 | |
| 	// get username
 | |
| 	fi, err := getFullInfoByCtx(r.Context())
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	if fi.Room.IsRunning && role == "mime" {
 | |
| 		err = errors.New("cannot join as mime when game is running")
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	fi, err = joinTeam(r.Context(), role, team)
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	// reveal all cards
 | |
| 	if fi.State.Role == "mime" {
 | |
| 		fi.Room.MimeView() // there must be a better way
 | |
| 	} else {
 | |
| 		fi.Room.GuesserView()
 | |
| 	}
 | |
| 	// return html
 | |
| 	tmpl, err := template.ParseGlob("components/*.html")
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	notify(models.NotifyRoomUpdatePrefix+fi.Room.ID, "")
 | |
| 	if err := tmpl.ExecuteTemplate(w, "base", fi); err != nil {
 | |
| 		log.Error("failed to execute base template", "error", err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func HandleEndTurn(w http.ResponseWriter, r *http.Request) {
 | |
| 	// get username
 | |
| 	fi, err := getFullInfoByCtx(r.Context())
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	// check if one who pressed it is from the team who has the turn
 | |
| 	if fi.Room.TeamTurn != fi.State.Team {
 | |
| 		msg := fmt.Sprintln("unexpected team turn:" + fi.Room.TeamTurn)
 | |
| 		abortWithError(w, msg)
 | |
| 		return
 | |
| 	}
 | |
| 	fi.Room.ChangeTurn()
 | |
| 	fi.Room.MimeDone = false
 | |
| 	if err := saveFullInfo(fi); err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	// return html
 | |
| 	tmpl, err := template.ParseGlob("components/*.html")
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	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)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func HandleStartGame(w http.ResponseWriter, r *http.Request) {
 | |
| 	fi, err := getFullInfoByCtx(r.Context())
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	// check if enough players
 | |
| 	if err := fi.Room.CanStart(); err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	fi.Room.IsRunning = true
 | |
| 	fi.Room.IsOver = false
 | |
| 	fi.Room.TeamTurn = "blue"
 | |
| 	fi.Room.OpenedThisTurn = 0
 | |
| 	fi.Room.ThisTurnLimit = 0
 | |
| 	loadCards(fi.Room)
 | |
| 	fi.Room.UpdateCounter()
 | |
| 	fi.Room.TeamWon = ""
 | |
| 	action := models.Action{
 | |
| 		Actor:      fi.State.Username,
 | |
| 		ActorColor: string(fi.State.Team),
 | |
| 		WordColor:  string(fi.State.Team),
 | |
| 		Action:     models.ActionTypeGameStarted,
 | |
| 	}
 | |
| 	fi.Room.ActionHistory = append(fi.Room.ActionHistory, action)
 | |
| 	if err := saveFullInfo(fi); err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	// reveal all cards
 | |
| 	if fi.State.Role == "mime" {
 | |
| 		fi.Room.MimeView()
 | |
| 	} else {
 | |
| 		fi.Room.GuesserView()
 | |
| 	}
 | |
| 	// return html
 | |
| 	tmpl, err := template.ParseGlob("components/*.html")
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	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")
 | |
| 	if err := tmpl.ExecuteTemplate(w, "room", fi); err != nil {
 | |
| 		log.Error("failed to execute room template", "error", err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func HandleJoinRoom(w http.ResponseWriter, r *http.Request) {
 | |
| 	roomID := r.URL.Query().Get("id")
 | |
| 	room, err := getRoomByID(roomID)
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	tmpl, err := template.ParseGlob("components/*.html")
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	fi, err := getFullInfoByCtx(r.Context())
 | |
| 	if err != nil {
 | |
| 		// INFO: if non-loggined user join: prompt to login
 | |
| 		fi = &models.FullInfo{}
 | |
| 		fi.LinkLogin = roomID
 | |
| 		if err := tmpl.ExecuteTemplate(w, "base", fi); err != nil {
 | |
| 			log.Error("failed to execute base template", "error", err)
 | |
| 		}
 | |
| 		// abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	// room.PlayerList = append(room.PlayerList, fi.State.Username)
 | |
| 	fi.State.RoomID = room.ID
 | |
| 	fi.Room = room
 | |
| 	fi.List = nil
 | |
| 	if err := saveFullInfo(fi); err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	if err := tmpl.ExecuteTemplate(w, "room", fi); err != nil {
 | |
| 		log.Error("failed to execute room template", "error", err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func HandleGiveClue(w http.ResponseWriter, r *http.Request) {
 | |
| 	if err := r.ParseForm(); err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	clue := r.PostFormValue("clue")
 | |
| 	num := r.PostFormValue("number")
 | |
| 	fi, err := getFullInfoByCtx(r.Context())
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	guessLimitU64, err := strconv.ParseUint(num, 10, 8)
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	// validations ===
 | |
| 	if fi.State.Team != models.UserTeam(fi.Room.TeamTurn) {
 | |
| 		err = errors.New("not your team's move")
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	if fi.State.Role != "mime" {
 | |
| 		err = errors.New("need to be mime to open the card")
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	if fi.Room.MimeDone {
 | |
| 		// team already have a clue
 | |
| 		abortWithError(w, "your team already has a clue")
 | |
| 		return
 | |
| 	}
 | |
| 	// check if the clue is the same as one of the existing words
 | |
| 	for _, card := range fi.Room.Cards {
 | |
| 		if strings.EqualFold(card.Word, clue) {
 | |
| 			msg := fmt.Sprintf("cannot use existing word (%s) as a clue", clue)
 | |
| 			abortWithError(w, msg)
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 	// ===
 | |
| 	action := models.Action{
 | |
| 		Actor:      fi.State.Username,
 | |
| 		ActorColor: string(fi.State.Team),
 | |
| 		WordColor:  string(fi.State.Team),
 | |
| 		Action:     models.ActionTypeClue,
 | |
| 		Word:       clue,
 | |
| 		Number:     num,
 | |
| 	}
 | |
| 	fi.Room.ActionHistory = append(fi.Room.ActionHistory, action)
 | |
| 	fi.Room.MimeDone = true
 | |
| 	fi.Room.ThisTurnLimit = uint8(guessLimitU64) + 1
 | |
| 	if guessLimitU64 == 0 {
 | |
| 		fi.Room.ThisTurnLimit = 9
 | |
| 	}
 | |
| 	fi.Room.OpenedThisTurn = 0
 | |
| 	log.Debug("given clue", "clue", clue, "limit", fi.Room.ThisTurnLimit)
 | |
| 	notify(models.NotifyBacklogPrefix+fi.Room.ID, clue+num)
 | |
| 	notifyBotIfNeeded(fi)
 | |
| 	if err := saveFullInfo(fi); err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func HandleRenotifyBot(w http.ResponseWriter, r *http.Request) {
 | |
| 	fi, err := getFullInfoByCtx(r.Context())
 | |
| 	if err != nil {
 | |
| 		abortWithError(w, err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	notifyBotIfNeeded(fi)
 | |
| }
 | 
