diff options
author | Emile <git@emile.space> | 2024-08-16 19:50:26 +0200 |
---|---|---|
committer | Emile <git@emile.space> | 2024-08-16 19:50:26 +0200 |
commit | 1a57267a17c2fc17fb6e104846fabc3e363c326c (patch) | |
tree | 1e574e3a80622086dc3c81ff9cba65ef7049b1a9 /src/battle.go |
initial commit
Diffstat (limited to 'src/battle.go')
-rw-r--r-- | src/battle.go | 846 |
1 files changed, 846 insertions, 0 deletions
diff --git a/src/battle.go b/src/battle.go new file mode 100644 index 0000000..3d418f2 --- /dev/null +++ b/src/battle.go @@ -0,0 +1,846 @@ +package main + +import ( + "fmt" + "html/template" + "log" + "net/http" + "strconv" + "strings" + "time" + + "github.com/gorilla/mux" +) + +type Battle struct { + ID int + Name string + Bots []Bot + Owners []User + Public bool + Archs []Arch + Bits []Bit +} + +////////////////////////////////////////////////////////////////////////////// +// GENERAL PURPOSE + +func BattleGetAll() ([]Battle, error) { + return globalState.GetAllBattles() +} + +func BattleCreate(name string, public bool) (int, error) { + return globalState.InsertBattle(Battle{Name: name, Public: public}) +} + +func BattleLinkBot(botid int, battleid int) error { + return globalState.LinkBotBattle(botid, battleid) +} + +func BattleGetByIdDeep(id int) (Battle, error) { + return globalState.GetBattleByIdDeep(id) +} + +func BattleUpdate(battle Battle) error { + return globalState.UpdateBattle(battle) +} + +func BattleLinkArchIDs(battleid int, archIDs []int) error { + return globalState.LinkArchIDsToBattle(battleid, archIDs) +} + +func BattleLinkBitIDs(battleid int, bitIDs []int) error { + return globalState.LinkBitIDsToBattle(battleid, bitIDs) +} + +////////////////////////////////////////////////////////////////////////////// +// DATABASE + +func (s *State) InsertBattle(battle Battle) (int, error) { + res, err := s.db.Exec("INSERT INTO battles VALUES(NULL,?,?,?);", time.Now(), battle.Name, battle.Public) + if err != nil { + return -1, err + } + + var id int64 + if id, err = res.LastInsertId(); err != nil { + return -1, err + } + return int(id), nil +} + +func (s *State) UpdateBattle(battle Battle) error { + _, err := s.db.Exec("UPDATE battles SET name=?, public=? WHERE id=?", battle.Name, battle.Public, battle.ID) + if err != nil { + return err + } + return nil +} + +func (s *State) LinkBotBattle(botid int, battleid int) error { + _, err := s.db.Exec("INSERT INTO bot_battle_rel VALUES (?, ?)", botid, battleid) + if err != nil { + log.Println("Error linking bot to battle: ", err) + return err + } else { + return nil + } +} + +func (s *State) LinkArchIDsToBattle(battleid int, archIDs []int) error { + // delete preexisting links + _, err := s.db.Exec("DELETE FROM arch_battle_rel WHERE battle_id=?;", battleid) + if err != nil { + return err + } + + // yes, we're building this by hand, but as we only insert int's I'm just confident that whoever + // gets some sqli here just deserves it :D + query := "INSERT INTO arch_battle_rel (arch_id, battle_id) VALUES" + for idx, id := range archIDs { + query += fmt.Sprintf("(%d, %d)", id, battleid) + if idx != len(archIDs)-1 { + query += ", " + } + } + query += ";" + log.Println(query) + + _, err = s.db.Exec(query) + if err != nil { + log.Println("LinkArchIDsToBattle err: ", err) + return err + } else { + return nil + } +} + +func (s *State) LinkBitIDsToBattle(battleid int, bitIDs []int) error { + // delete preexisting links + _, err := s.db.Exec("DELETE FROM bit_battle_rel WHERE battle_id=?;", battleid) + if err != nil { + return err + } + + // yes, we're building this by hand, but as we only insert int's I'm just confident that whoever + // gets some sqli here just deserves it :D + query := "INSERT INTO bit_battle_rel (bit_id, battle_id) VALUES" + for idx, id := range bitIDs { + query += fmt.Sprintf("(%d, %d)", id, battleid) + if idx != len(bitIDs)-1 { + query += ", " + } + } + query += ";" + log.Println(query) + + _, err = s.db.Exec(query) + if err != nil { + log.Println("LinkBitIDsToBattle err: ", err) + return err + } else { + return nil + } +} + +func (s *State) GetAllBattles() ([]Battle, error) { + rows, err := s.db.Query("SELECT id, name FROM battles;") + defer rows.Close() + if err != nil { + return nil, err + } + + var battles []Battle + for rows.Next() { + var battle Battle + if err := rows.Scan(&battle.ID, &battle.Name); err != nil { + log.Println(err) + return battles, err + } + battles = append(battles, battle) + } + if err = rows.Err(); err != nil { + log.Println(err) + return battles, err + } + return battles, nil +} + +func (s *State) GetBattleByIdDeep(id int) (Battle, error) { + var battleid int + var battlename string + var battlepublic bool + + var botids string + var botnames string + + var userids string + var usernames string + + var archids string + var archnames string + + var bitids string + var bitnames string + + // battles have associated bots and users, we're fetching 'em all! + + // This fetches the battles and relates the associated bots, users, archs and bits + + err := s.db.QueryRow(` + SELECT DISTINCT + ba.id, ba.name, ba.public, + COALESCE(group_concat(DISTINCT bb.bot_id), ""), + COALESCE(group_concat(DISTINCT bo.name), ""), + COALESCE(group_concat(DISTINCT ub.user_id), ""), + COALESCE(group_concat(DISTINCT us.name), ""), + COALESCE(group_concat(DISTINCT ab.arch_id), ""), + COALESCE(group_concat(DISTINCT ar.name), ""), + COALESCE(group_concat(DISTINCT bitbat.bit_id), ""), + COALESCE(group_concat(DISTINCT bi.name), "") + FROM battles ba + + LEFT JOIN bot_battle_rel bb ON bb.battle_id = ba.id + LEFT JOIN bots bo ON bo.id = bb.bot_id + + LEFT JOIN user_battle_rel ub ON ub.battle_id = ba.id + LEFT JOIN users us ON us.id = ub.user_id + + LEFT JOIN arch_battle_rel ab ON ab.battle_id = ba.id + LEFT JOIN archs ar ON ar.id = ab.arch_id + + LEFT JOIN bit_battle_rel bitbat ON bitbat.battle_id = ba.id + LEFT JOIN bits bi ON bi.id = bitbat.bit_id + + WHERE ba.id=? + GROUP BY ba.id; + `, id).Scan(&battleid, &battlename, &battlepublic, &botids, &botnames, &userids, &usernames, &archids, &archnames, &bitids, &bitnames) + if err != nil { + log.Println("Err making GetBattleByID query: ", err) + return Battle{}, err + } + + log.Println("battleid: ", battleid) + log.Println("battlename: ", battlename) + log.Println("battlepublic: ", battlepublic) + log.Println("botids: ", botids) + log.Println("botnames: ", botnames) + log.Println("userids: ", userids) + log.Println("usernames: ", usernames) + log.Println("archids: ", archids) + log.Println("archnames: ", archnames) + log.Println("bitids: ", bitids) + log.Println("bitnames: ", bitnames) + + // The below is a wonderful examle of how golang could profit from macros + // I should just have done this all in common lisp tbh. + + // assemble the bots + botIDList := strings.Split(botids, ",") + botNameList := strings.Split(botnames, ",") + + // Using strings.Split on an empty string returns a list containing + // nothing with a length of one + // https://go.dev/play/p/N1D-OcwiVAs + + var bots []Bot + if botIDList[0] != "" { + for i, _ := range botIDList { + id, err := strconv.Atoi(botIDList[i]) + if err != nil { + log.Println("Err handling bots: ", err) + return Battle{}, err + } + bots = append(bots, Bot{id, botNameList[i], "", []User{}, []Arch{}, []Bit{}}) + } + } else { + bots = []Bot{} + } + + // assemble the users + userIDList := strings.Split(userids, ",") + userNameList := strings.Split(usernames, ",") + + var users []User + if userIDList[0] != "" { + for i, _ := range userIDList { + id, err := strconv.Atoi(userIDList[i]) + if err != nil { + log.Println("Err handling users: ", err) + return Battle{}, err + } + users = append(users, User{id, userNameList[i], []byte{}}) + } + } else { + users = []User{} + } + + // assemble the archs + archIDList := strings.Split(archids, ",") + archNameList := strings.Split(archnames, ",") + + var archs []Arch + if archIDList[0] != "" { + for i, _ := range archIDList { + id, err := strconv.Atoi(archIDList[i]) + if err != nil { + log.Println("Err handling archs: ", err) + return Battle{}, err + } + archs = append(archs, Arch{id, archNameList[i], true}) + } + } else { + archs = []Arch{} + } + + // assemble the bits + bitIDList := strings.Split(bitids, ",") + bitNameList := strings.Split(bitnames, ",") + + var bits []Bit + if bitIDList[0] != "" { + for i, _ := range bitIDList { + id, err := strconv.Atoi(bitIDList[i]) + if err != nil { + log.Println("Err handling bits: ", err) + return Battle{}, err + } + bits = append(bits, Bit{id, bitNameList[i], true}) + } + } else { + bits = []Bit{} + } + + // return it all! + switch { + case err != nil: + log.Println("Overall err in the GetBattleByID func: ", err) + return Battle{}, err + default: + return Battle{ + ID: battleid, + Name: battlename, + Bots: bots, + Owners: users, + Public: battlepublic, + Archs: archs, + Bits: bits, + }, nil + } +} + +////////////////////////////////////////////////////////////////////////////// +// HTTP + +func battlesHandler(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case "GET": + // define data + data := map[string]interface{}{} + data["pagelink1"] = Link{Name: "battle", Target: "/battle"} + data["pagelink1options"] = []Link{ + {Name: "bot", Target: "/bot"}, + {Name: "user", Target: "/user"}, + } + data["pagelinknext"] = []Link{ + {Name: "new", Target: "/new"}, + } + + // sessions + session, _ := globalState.sessions.Get(r, "session") + username := session.Values["username"].(string) + + // get the user + user, err := UserGetUserFromUsername(username) + if err != nil { + http.Redirect(w, r, "/login", http.StatusSeeOther) + return + } else { + data["user"] = user + } + + // get all battles + battles, err := BattleGetAll() + data["battles"] = battles + + // get the template + t, err := template.ParseGlob("./templates/*.html") + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte("500 - Error reading template file")) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // exec! + t.ExecuteTemplate(w, "battles", data) + default: + http.Redirect(w, r, "/", http.StatusMethodNotAllowed) + } +} + +func battleNewHandler(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case "GET": + // define data + data := map[string]interface{}{} + + // breadcrumb foo + session, _ := globalState.sessions.Get(r, "session") + username := session.Values["username"].(string) + data["pagelink1"] = Link{Name: "battle", Target: "/battle"} + data["pagelink1options"] = []Link{ + {Name: "user", Target: "/user"}, + {Name: "bot", Target: "/bot"}, + } + data["pagelink2"] = Link{Name: "new", Target: "/new"} + data["pagelink2options"] = []Link{ + {Name: "list", Target: ""}, + } + + // display errors passed via query parameters + queryres := r.URL.Query().Get("err") + if queryres != "" { + data["res"] = queryres + } + + // get data needed + user, err := UserGetUserFromUsername(username) + if err != nil { + data["err"] = "Could not fetch the user" + } else { + data["user"] = user + } + + archs, err := ArchGetAll() + if err != nil { + data["err"] = "Could not fetch the archs" + } else { + data["archs"] = archs + } + + bits, err := BitGetAll() + if err != nil { + data["err"] = "Could not fetch the bits" + } else { + data["bits"] = bits + } + + // get the template + t, err := template.ParseGlob("./templates/*.html") + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte("500 - Error reading template file")) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // exec! + t.ExecuteTemplate(w, "battleNew", data) + + case "POST": + // parse the post parameters + r.ParseForm() + name := r.Form.Get("name") + + var public bool + query_public := r.Form.Get("public") + if query_public == "on" { + public = true + } + + // gather the information from the arch and bit selection + var archIDs []int + var bitIDs []int + + for k, _ := range r.Form { + if strings.HasPrefix(k, "arch-") { + id, err := strconv.Atoi(strings.TrimPrefix(k, "arch-")) + if err != nil { + msg := "ERROR: Invalid arch id" + http.Redirect(w, r, fmt.Sprintf("/battle/new?res=%s", msg), http.StatusSeeOther) + return + } + archIDs = append(archIDs, id) + } + if strings.HasPrefix(k, "bit-") { + id, err := strconv.Atoi(strings.TrimPrefix(k, "bit-")) + if err != nil { + msg := "ERROR: Invalid bit id" + http.Redirect(w, r, fmt.Sprintf("/battle/new?res=%s", msg), http.StatusSeeOther) + return + } + bitIDs = append(bitIDs, id) + } + } + + if name != "" { + // create the battle itself + log.Println("Creating battle") + battleid, err := BattleCreate(name, public) + if err != nil { + log.Println("Error creating the battle using BattleCreate(): ", err) + msg := "ERROR: Could not create due to internal reasons" + http.Redirect(w, r, fmt.Sprintf("/battle/new?res=%s", msg), http.StatusSeeOther) + return + } + + // link archs to battle + err = BattleLinkArchIDs(battleid, archIDs) + if err != nil { + log.Println("Error linking the arch ids to the battle: ", err) + msg := "ERROR: Could not create due to internal reasons" + http.Redirect(w, r, fmt.Sprintf("/battle/new?res=%s", msg), http.StatusSeeOther) + return + } + + // link bits to battle + err = BattleLinkBitIDs(battleid, bitIDs) + if err != nil { + log.Println("Error linking the bit ids to the battle: ", err) + msg := "ERROR: Could not create due to internal reasons" + http.Redirect(w, r, fmt.Sprintf("/battle/new?res=%s", msg), http.StatusSeeOther) + return + } + } else { + msg := "ERROR: Please provide a name" + http.Redirect(w, r, fmt.Sprintf("/battle/new?res=%s", msg), http.StatusSeeOther) + return + } + + http.Redirect(w, r, "/battle", http.StatusSeeOther) + return + default: + http.Redirect(w, r, "/", http.StatusMethodNotAllowed) + } +} + +// TODO(emile): add user creating battle as default owner +// TODO(emile): allow adding other users as owners to battles +// TODO(emile): implement submitting bots +// TODO(emile): implement running the battle +// TODO(emile): add a "start battle now" button +// TODO(emile): add a "battle starts at this time" field into the battle +// TODO(emile): figure out how time is stored and restored with the db +// TODO(emile): do some magic to display the current fight backlog with all info + +func battleSingleHandler(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + battleid, err := strconv.Atoi(vars["id"]) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte("500 - Invalid battle id")) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + switch r.Method { + case "GET": + // define data + data := map[string]interface{}{} + data["pagelink1"] = Link{"battle", "/battle"} + data["pagelink1options"] = []Link{ + {Name: "user", Target: "/user"}, + {Name: "bot", Target: "/bot"}, + } + + // display errors passed via query parameters + queryres := r.URL.Query().Get("res") + if queryres != "" { + data["res"] = queryres + } + + session, _ := globalState.sessions.Get(r, "session") + username := session.Values["username"].(string) + + viewer, err := UserGetUserFromUsername(username) + if err != nil { + data["err"] = "Could not get the id four your username... Please contact an admin" + } + data["user"] = viewer + + // get the battle including it's users, bots, archs, bits + battle, err := BattleGetByIdDeep(int(battleid)) + data["battle"] = battle + data["botAmount"] = len(battle.Bots) + data["battleCount"] = (len(battle.Bots) * len(battle.Bots)) * 2 + + // define the breadcrumbs + data["pagelink2"] = Link{battle.Name, fmt.Sprintf("/%d", battle.ID)} + + allbattleNames, err := BattleGetAll() + var opts []Link + for _, battle := range allbattleNames { + opts = append(opts, Link{Name: battle.Name, Target: fmt.Sprintf("/%d", battle.ID)}) + } + data["pagelink2options"] = opts + + // get the bots of the user viewing the page, as they might want to submit them + myBots, err := UserGetBotsUsingUsername(username) + if err != nil { + log.Println("err: ", err) + http.Redirect(w, r, fmt.Sprintf("/battle/%d", battleid), http.StatusSeeOther) + return + } + data["myBots"] = myBots + + // get all architectures and set the enable flag on the ones that are enabled in the battle + archs, err := ArchGetAll() + if err != nil { + data["err"] = "Could not fetch the archs" + } else { + data["archs"] = archs + } + + for i, a := range archs { + for _, b := range battle.Archs { + if a.ID == b.ID { + archs[i].Enabled = true + } + } + } + + // get all bits and set the enable flag on the ones that are enabled in the battle + bits, err := BitGetAll() + if err != nil { + data["err"] = "Could not fetch the bits" + } else { + data["bits"] = bits + } + + for i, a := range bits { + for _, b := range battle.Bits { + if a.ID == b.ID { + bits[i].Enabled = true + } + } + } + + // check if we're allowed to edit + editable := false + for _, owner := range battle.Owners { + if owner.ID == viewer.ID { + editable = true + } + } + if editable == true { + data["editable"] = true + } + + // get the template + t, err := template.ParseGlob("./templates/*.html") + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte("500 - Error reading template file")) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // exec! + t.ExecuteTemplate(w, "battleSingle", data) + + case "POST": + log.Println("POST!") + // checking if the user submitting the battle information is allowed to do so + // session, _ := globalState.sessions.Get(r, "session") + // username := session.Values["username"].(string) + + // get the user submitting + // log.Println("Getting the user submitting the change request...") + // requesting_user, err := UserGetUserFromUsername(username) + // if err != nil { + // log.Println("err: ", err) + // http.Redirect(w, r, fmt.Sprintf("/battle/%d", battleid), http.StatusSeeOther) + // return + // } + + // get the users the battle belongs to + // log.Println("Getting the user the battle belongs to...") + // orig_battle, err := BattleGetByIdDeep(int(battleid)) + // if err != nil { + // log.Println("err: ", err) + // http.Redirect(w, r, fmt.Sprintf("/battle/%d", battleid), http.StatusSeeOther) + // return + // } + + // check if the user submitting the change request is within the users the battle belongs to + // log.Println("Checking if edit is allowed...") + // allowed_to_edit := false + // for _, user := range orig_battle.Owners { + // if user.ID == requesting_user.ID { + // allowed_to_edit = true + // } + // } + + // if allowed_to_edit == false { + // msg := "ERROR: You aren't allowed to edit this battle!" + // http.Redirect(w, r, fmt.Sprintf("/battle/%d?res=%s", battleid, msg), http.StatusSeeOther) + // return + // } + + // at this point, we're sure the user is allowed to edit the battle + + r.ParseForm() + + log.Println("r.Form: ", r.Form) + form_name := r.Form.Get("name") + + var public bool + if r.Form.Get("public") == "on" { + public = true + } + + // gather the information from the arch and bit selection + var archIDs []int + var bitIDs []int + + for k, _ := range r.Form { + if strings.HasPrefix(k, "arch-") { + id, err := strconv.Atoi(strings.TrimPrefix(k, "arch-")) + if err != nil { + msg := "ERROR: Invalid arch id" + http.Redirect(w, r, fmt.Sprintf("/battle/%d?res=%s#settings", battleid, msg), http.StatusSeeOther) + return + } + archIDs = append(archIDs, id) + } + if strings.HasPrefix(k, "bit-") { + id, err := strconv.Atoi(strings.TrimPrefix(k, "bit-")) + if err != nil { + msg := "ERROR: Invalid bit id" + http.Redirect(w, r, fmt.Sprintf("/battle/%d?res=%s#settings", battleid, msg), http.StatusSeeOther) + return + } + bitIDs = append(bitIDs, id) + } + } + + // link archs to battle + err = BattleLinkArchIDs(battleid, archIDs) + if err != nil { + log.Println("Error linking the arch ids to the battle: ", err) + msg := "ERROR: Could not create due to internal reasons" + http.Redirect(w, r, fmt.Sprintf("/battle/%d?res=%s#settings", battleid, msg), http.StatusSeeOther) + return + } + + // link bits to battle + err = BattleLinkBitIDs(battleid, bitIDs) + if err != nil { + log.Println("Error linking the bit ids to the battle: ", err) + msg := "ERROR: Could not create due to internal reasons" + http.Redirect(w, r, fmt.Sprintf("/battle/%d?res=%s#settings", battleid, msg), http.StatusSeeOther) + return + } + + new_battle := Battle{int(battleid), form_name, []Bot{}, []User{}, public, []Arch{}, []Bit{}} + + log.Println("Updating battle...") + err = BattleUpdate(new_battle) + if err != nil { + log.Println("err: ", err) + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte("500 - Error inserting battle into db")) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + http.Redirect(w, r, fmt.Sprintf("/battle/%d?res=Success!#settings", battleid), http.StatusSeeOther) + + default: + http.Redirect(w, r, "/", http.StatusMethodNotAllowed) + } +} + +func battleSubmitHandler(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + battleid, err := strconv.Atoi(vars["id"]) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte("500 - Invalid battle id")) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + switch r.Method { + case "POST": + r.ParseForm() + + log.Println("Adding bot to battle", battleid) + log.Println(r.Form) + + // get all the form values that contain the bot that shall be submitted + var botIDs []int + for k, _ := range r.Form { + if strings.HasPrefix(k, "bot-") { + id, err := strconv.Atoi(strings.TrimPrefix(k, "bot-")) + if err != nil { + msg := "ERROR: Invalid bot supplied" + http.Redirect(w, r, fmt.Sprintf("/battle/%d?res=%s", battleid, msg), http.StatusSeeOther) + return + } + botIDs = append(botIDs, id) + } + } + + log.Println(botIDs) + + battle, err := BattleGetByIdDeep(battleid) + if err != nil { + msg := "ERROR: Couln't get the battle with the given id" + http.Redirect(w, r, fmt.Sprintf("/battle/%d?res=%s", battleid, msg), http.StatusSeeOther) + return + } + log.Println(battle) + + // for all bots, get their bits and arch and compare them to the one of the battle + for _, id := range botIDs { + bot, err := BotGetById(id) + if err != nil { + msg := fmt.Sprintf("ERROR: Couldn't get bot with id %d", id) + http.Redirect(w, r, fmt.Sprintf("/battle/%d?res=%s", battleid, msg), http.StatusSeeOther) + return + } + + var archValid bool = false + for _, battle_arch := range battle.Archs { + for _, bot_arch := range bot.Archs { + if battle_arch.ID == bot_arch.ID { + archValid = true + } + } + } + + var bitValid bool = false + for _, battle_bit := range battle.Bits { + for _, bot_bit := range bot.Bits { + if battle_bit.ID == bot_bit.ID { + bitValid = true + } + } + } + + if archValid && bitValid { + log.Printf("arch and bit valid, adding bot with id %d to battle with id %d\n", id, battleid) + BattleLinkBot(id, battleid) + } else { + if archValid == false { + msg := "Bot has an invalid architecture!" + http.Redirect(w, r, fmt.Sprintf("/battle/%d?res=%s", battleid, msg), http.StatusSeeOther) + return + } + if bitValid == false { + msg := "Bot has an invalid 'bit-ness'!" + http.Redirect(w, r, fmt.Sprintf("/battle/%d?res=%s", battleid, msg), http.StatusSeeOther) + return + } + } + + log.Println(bot) + } + msg := "Success!" + http.Redirect(w, r, fmt.Sprintf("/battle/%d?res=%s", battleid, msg), http.StatusSeeOther) + default: + http.Redirect(w, r, "/", http.StatusMethodNotAllowed) + } +} |