itSimplify interface and quit on error - scoreboard - Interactive scoreboard for CTF-like games Err z3bra.org 70 hgit clone git://git.z3bra.org/scoreboard.git URL:git://git.z3bra.org/scoreboard.git z3bra.org 70 1Log /scm/scoreboard/log.gph z3bra.org 70 1Files /scm/scoreboard/files.gph z3bra.org 70 1Refs /scm/scoreboard/refs.gph z3bra.org 70 i--- Err z3bra.org 70 1commit 955d6f3ab6a51e2bb139d30614ed7e5a932e14b6 /scm/scoreboard/commit/955d6f3ab6a51e2bb139d30614ed7e5a932e14b6.gph z3bra.org 70 1parent 2dcf67e2ab0f8c6918133d3a5bcb3c1dee86db98 /scm/scoreboard/commit/2dcf67e2ab0f8c6918133d3a5bcb3c1dee86db98.gph z3bra.org 70 hAuthor: Willy Goiffon URL:mailto:contact@z3bra.org z3bra.org 70 iDate: Tue, 6 Dec 2022 13:52:28 +0100 Err z3bra.org 70 i Err z3bra.org 70 iSimplify interface and quit on error Err z3bra.org 70 i Err z3bra.org 70 iDiffstat: Err z3bra.org 70 i M main.go | 77 ++++++++----------------------- Err z3bra.org 70 i A player.go | 144 +++++++++++++++++++++++++++++++ Err z3bra.org 70 i M playerbox.go | 4 ++-- Err z3bra.org 70 i M ui.go | 34 +++++++++++++++++++++++++++++-- Err z3bra.org 70 i Err z3bra.org 70 i4 files changed, 196 insertions(+), 63 deletions(-) Err z3bra.org 70 i--- Err z3bra.org 70 1diff --git a/main.go b/main.go /scm/scoreboard/file/main.go.gph z3bra.org 70 it@@ -23,7 +23,6 @@ import ( Err z3bra.org 70 i "database/sql" Err z3bra.org 70 i "github.com/gdamore/tcell/v2" Err z3bra.org 70 i "github.com/rivo/tview" Err z3bra.org 70 i- //"golang.org/x/crypto/argon2" Err z3bra.org 70 i Err z3bra.org 70 i _ "modernc.org/sqlite" Err z3bra.org 70 i ) Err z3bra.org 70 it@@ -69,29 +68,6 @@ func usage() { Err z3bra.org 70 i os.Exit(0) Err z3bra.org 70 i } Err z3bra.org 70 i Err z3bra.org 70 i-func pageFlag() tview.Primitive { Err z3bra.org 70 i- input := tview.NewInputField(). Err z3bra.org 70 i- SetLabel("SHA256(flag) "). Err z3bra.org 70 i- SetPlaceholder(" eg. 01BA4719…"). Err z3bra.org 70 i- SetFieldStyle(tcell.StyleDefault.Reverse(true)). Err z3bra.org 70 i- SetFieldWidth(28) Err z3bra.org 70 i- Err z3bra.org 70 i- input.SetAcceptanceFunc(func(text string, ch rune) bool { Err z3bra.org 70 i- matched, err := regexp.Match(`^[a-fA-F0-9]+$`, []byte(text)) Err z3bra.org 70 i- if err != nil { Err z3bra.org 70 i- panic(err) Err z3bra.org 70 i- } Err z3bra.org 70 i- return matched Err z3bra.org 70 i- }) Err z3bra.org 70 i- input.SetDoneFunc(func(key tcell.Key) { Err z3bra.org 70 i- if cyboard.flag == 1 { Err z3bra.org 70 i- cyboard.pages.SwitchToPage("score") Err z3bra.org 70 i- } Err z3bra.org 70 i- }) Err z3bra.org 70 i- Err z3bra.org 70 i- return center(40, 1, input) Err z3bra.org 70 i-} Err z3bra.org 70 i- Err z3bra.org 70 i func pageToken() tview.Primitive { Err z3bra.org 70 i input := tview.NewInputField(). Err z3bra.org 70 i SetLabel("TOKEN "). Err z3bra.org 70 it@@ -107,27 +83,17 @@ func pageToken() tview.Primitive { Err z3bra.org 70 i return matched Err z3bra.org 70 i }) Err z3bra.org 70 i input.SetDoneFunc(func(key tcell.Key) { Err z3bra.org 70 i- _, err := cyboard.player.FromToken(input.GetText()) Err z3bra.org 70 i+ err := cyboard.player.FromToken(input.GetText()) Err z3bra.org 70 i if err != nil { Err z3bra.org 70 i- cyboard.Popup("Error", "Invalid Token") Err z3bra.org 70 i+ cyboard.Fatal(err) Err z3bra.org 70 i return Err z3bra.org 70 i } Err z3bra.org 70 i Err z3bra.org 70 i- if cyboard.player.flag >= cyboard.flag { Err z3bra.org 70 i- cyboard.Popup("Error", "Flag already submitted") Err z3bra.org 70 i+ err = cyboard.player.Submit(cyboard.flag) Err z3bra.org 70 i+ if err != nil { Err z3bra.org 70 i+ cyboard.Fatal(err) Err z3bra.org 70 i return Err z3bra.org 70 i } Err z3bra.org 70 i- Err z3bra.org 70 i- cyboard.player.ts = time.Now().Unix() Err z3bra.org 70 i- cyboard.player.flag = cyboard.flag Err z3bra.org 70 i- cyboard.player.score += 100 Err z3bra.org 70 i- Err z3bra.org 70 i- n := cyboard.player.FlagRank() Err z3bra.org 70 i- if n < 10 { Err z3bra.org 70 i- cyboard.player.score += 10 - n Err z3bra.org 70 i- } Err z3bra.org 70 i- Err z3bra.org 70 i- cyboard.player.Update() Err z3bra.org 70 i cyboard.HighlightBoard(cyboard.player.ScoreRank()) Err z3bra.org 70 i cyboard.pages.SwitchToPage("board") Err z3bra.org 70 i }) Err z3bra.org 70 it@@ -144,13 +110,6 @@ func pageBoard() tview.Primitive { Err z3bra.org 70 i Err z3bra.org 70 i func main() { Err z3bra.org 70 i var err error Err z3bra.org 70 i- cmd := "board" Err z3bra.org 70 i- Err z3bra.org 70 i- flag.Parse() Err z3bra.org 70 i- args := flag.Args() Err z3bra.org 70 i- if len(args) > 0 { Err z3bra.org 70 i- cmd = args[0] Err z3bra.org 70 i- } Err z3bra.org 70 i Err z3bra.org 70 i // Override default borders Err z3bra.org 70 i tview.Borders.HorizontalFocus = tview.BoxDrawingsLightHorizontal Err z3bra.org 70 it@@ -176,25 +135,24 @@ func main() { Err z3bra.org 70 i Err z3bra.org 70 i cyboard.pages.SetBackgroundColor(tcell.ColorDefault) Err z3bra.org 70 i Err z3bra.org 70 i- cyboard.pages.AddPage("flag", pageFlag(), true, false) Err z3bra.org 70 i cyboard.pages.AddPage("token", pageToken(), true, false) Err z3bra.org 70 i cyboard.pages.AddPage("board", pageBoard(), true, false) Err z3bra.org 70 i Err z3bra.org 70 i- switch cmd { Err z3bra.org 70 i- case "flag": Err z3bra.org 70 i- if (len(args) < 2) { Err z3bra.org 70 i- usage() Err z3bra.org 70 i- } Err z3bra.org 70 i- switch cyboard.flag = flagid(args[1]) + 1; cyboard.flag { Err z3bra.org 70 i+ flag.Parse() Err z3bra.org 70 i+ args := flag.Args() Err z3bra.org 70 i+ Err z3bra.org 70 i+ if len(args) > 1 { Err z3bra.org 70 i+ usage() Err z3bra.org 70 i+ } else if len(args) == 1 { Err z3bra.org 70 i+ switch cyboard.flag = flagid(args[0]) + 1; cyboard.flag { Err z3bra.org 70 i case 1: Err z3bra.org 70 i cyboard.player.flag = cyboard.flag Err z3bra.org 70 i cyboard.player.score = 100 Err z3bra.org 70 i cyboard.player.ts = time.Now().Unix() Err z3bra.org 70 i Err z3bra.org 70 i- /* Bonus points for the first 10 players to get first flag */ Err z3bra.org 70 i- n := cyboard.player.FlagRank() Err z3bra.org 70 i- if n < 10 { Err z3bra.org 70 i- cyboard.player.score += 10 - n Err z3bra.org 70 i+ // Bonus points for the first player to submit a flag Err z3bra.org 70 i+ if cyboard.player.FlagRank() == 0 { Err z3bra.org 70 i+ cyboard.player.score += cyboard.flag * 10 Err z3bra.org 70 i } Err z3bra.org 70 i Err z3bra.org 70 i rank := cyboard.player.ScoreRank() + 1 Err z3bra.org 70 it@@ -205,10 +163,11 @@ func main() { Err z3bra.org 70 i cyboard.pages.ShowPage("token") Err z3bra.org 70 i default: Err z3bra.org 70 i fmt.Println("Incorrect flag") Err z3bra.org 70 i+ return Err z3bra.org 70 i } Err z3bra.org 70 i- default: Err z3bra.org 70 i- cyboard.DrawBoard() Err z3bra.org 70 i+ } else { Err z3bra.org 70 i cyboard.pages.SwitchToPage("board") Err z3bra.org 70 i+ cyboard.DrawBoard() Err z3bra.org 70 i } Err z3bra.org 70 i Err z3bra.org 70 i if err := cyboard.app.SetRoot(cyboard.pages, true).EnableMouse(true).Run(); err != nil { Err z3bra.org 70 1diff --git a/player.go b/player.go /scm/scoreboard/file/player.go.gph z3bra.org 70 it@@ -0,0 +1,144 @@ Err z3bra.org 70 i+// Copyright 2016 The Tcell Authors Err z3bra.org 70 i+// Err z3bra.org 70 i+// Licensed under the Apache License, Version 2.0 (the "License"); Err z3bra.org 70 i+// you may not use file except in compliance with the License. Err z3bra.org 70 i+// You may obtain a copy of the license at Err z3bra.org 70 i+// Err z3bra.org 70 i+// http://www.apache.org/licenses/LICENSE-2.0 Err z3bra.org 70 i+// Err z3bra.org 70 i+// Unless required by applicable law or agreed to in writing, software Err z3bra.org 70 i+// distributed under the License is distributed on an "AS IS" BASIS, Err z3bra.org 70 i+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Err z3bra.org 70 i+// See the License for the specific language governing permissions and Err z3bra.org 70 i+// limitations under the License. Err z3bra.org 70 i+ Err z3bra.org 70 i+package main Err z3bra.org 70 i+ Err z3bra.org 70 i+import ( Err z3bra.org 70 i+ "errors" Err z3bra.org 70 i+ "fmt" Err z3bra.org 70 i+ "time" Err z3bra.org 70 i+ "database/sql" Err z3bra.org 70 i+ //"golang.org/x/crypto/argon2" Err z3bra.org 70 i+ "github.com/dustin/go-humanize" Err z3bra.org 70 i+ Err z3bra.org 70 i+ _ "modernc.org/sqlite" Err z3bra.org 70 i+) Err z3bra.org 70 i+ Err z3bra.org 70 i+type Player struct { Err z3bra.org 70 i+ db *sql.DB Err z3bra.org 70 i+ id int Err z3bra.org 70 i+ name string Err z3bra.org 70 i+ token string Err z3bra.org 70 i+ flag int Err z3bra.org 70 i+ score int Err z3bra.org 70 i+ ts int64 Err z3bra.org 70 i+} Err z3bra.org 70 i+ Err z3bra.org 70 i+func (p *Player) Register() int64 { Err z3bra.org 70 i+ query := `INSERT INTO score(name,token,flag,score,ts) VALUES(?,?,?,?,?);` Err z3bra.org 70 i+ r, err := p.db.Exec(query, p.name, p.token, p.flag, p.score, p.ts) Err z3bra.org 70 i+ if err != nil { Err z3bra.org 70 i+ panic(err) Err z3bra.org 70 i+ } Err z3bra.org 70 i+ Err z3bra.org 70 i+ id, _ := r.LastInsertId() Err z3bra.org 70 i+ Err z3bra.org 70 i+ return id Err z3bra.org 70 i+} Err z3bra.org 70 i+ Err z3bra.org 70 i+func (p *Player) Update() int64 { Err z3bra.org 70 i+ query := `UPDATE score SET flag = ?, score = ?, ts = ? WHERE id = ?;` Err z3bra.org 70 i+ r, err := p.db.Exec(query, p.flag, p.score, p.ts, p.id) Err z3bra.org 70 i+ if err != nil { Err z3bra.org 70 i+ panic(err) Err z3bra.org 70 i+ } Err z3bra.org 70 i+ Err z3bra.org 70 i+ id, _ := r.LastInsertId() Err z3bra.org 70 i+ Err z3bra.org 70 i+ return id Err z3bra.org 70 i+} Err z3bra.org 70 i+ Err z3bra.org 70 i+func (p *Player) ScoreRank() int { Err z3bra.org 70 i+ var count int Err z3bra.org 70 i+ query := `SELECT Err z3bra.org 70 i+ count(id) Err z3bra.org 70 i+ FROM score Err z3bra.org 70 i+ WHERE Err z3bra.org 70 i+ score >= ? AND Err z3bra.org 70 i+ ts <= ? Err z3bra.org 70 i+ ;` Err z3bra.org 70 i+ Err z3bra.org 70 i+ row := p.db.QueryRow(query, p.score, p.ts) Err z3bra.org 70 i+ row.Scan(&count) Err z3bra.org 70 i+ return count Err z3bra.org 70 i+} Err z3bra.org 70 i+ Err z3bra.org 70 i+func (p *Player) FlagRank() int { Err z3bra.org 70 i+ var count int Err z3bra.org 70 i+ query := `SELECT Err z3bra.org 70 i+ count(id) Err z3bra.org 70 i+ FROM score Err z3bra.org 70 i+ WHERE Err z3bra.org 70 i+ flag >= ? Err z3bra.org 70 i+ ;` Err z3bra.org 70 i+ Err z3bra.org 70 i+ row := p.db.QueryRow(query, p.flag) Err z3bra.org 70 i+ row.Scan(&count) Err z3bra.org 70 i+ return count Err z3bra.org 70 i+} Err z3bra.org 70 i+ Err z3bra.org 70 i+func (p *Player) Exists() bool { Err z3bra.org 70 i+ forbidden := []string{"KKK", "WGS"} Err z3bra.org 70 i+ for i := 0; i 0) Err z3bra.org 70 i+} Err z3bra.org 70 i+ Err z3bra.org 70 i+func (p *Player) Submit(flag int) error { Err z3bra.org 70 i+ if flag <= p.flag { Err z3bra.org 70 i+ return errors.New("Flag already submitted") Err z3bra.org 70 i+ } Err z3bra.org 70 i+ Err z3bra.org 70 i+ if flag != p.flag + 1 { Err z3bra.org 70 i+ return errors.New(fmt.Sprintf("Missing %s flag", humanize.Ordinal(p.flag + 1))) Err z3bra.org 70 i+ } Err z3bra.org 70 i+ Err z3bra.org 70 i+ p.ts = time.Now().Unix() Err z3bra.org 70 i+ p.flag = flag Err z3bra.org 70 i+ p.score += 100 Err z3bra.org 70 i+ Err z3bra.org 70 i+ if p.FlagRank() == 0 { Err z3bra.org 70 i+ p.score += 10 * flag Err z3bra.org 70 i+ } Err z3bra.org 70 i+ Err z3bra.org 70 i+ p.Update() Err z3bra.org 70 i+ Err z3bra.org 70 i+ return nil Err z3bra.org 70 i+} Err z3bra.org 70 i+ Err z3bra.org 70 i+func (p *Player) FromToken(token string) error { Err z3bra.org 70 i+ query := `SELECT id,name,token,flag,score,ts FROM score WHERE token = ?` Err z3bra.org 70 i+ Err z3bra.org 70 i+ row := p.db.QueryRow(query, token) Err z3bra.org 70 i+ err := row.Scan(&p.id, &p.name, &p.token, &p.flag, &p.score, &p.ts) Err z3bra.org 70 i+ if err == sql.ErrNoRows { Err z3bra.org 70 i+ return errors.New("Unmatched token") Err z3bra.org 70 i+ } Err z3bra.org 70 i+ Err z3bra.org 70 i+ return nil Err z3bra.org 70 i+} Err z3bra.org 70 1diff --git a/playerbox.go b/playerbox.go /scm/scoreboard/file/playerbox.go.gph z3bra.org 70 it@@ -16,7 +16,7 @@ type PlayerBox struct { Err z3bra.org 70 i score *tview.TextView Err z3bra.org 70 i } Err z3bra.org 70 i Err z3bra.org 70 i-var charlist = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 _.-!" Err z3bra.org 70 i+var charlist = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.-!" Err z3bra.org 70 i var playerbox = PlayerBox {name: []byte("AAA"), char: []int{0,0,0}, cur: 0} Err z3bra.org 70 i Err z3bra.org 70 i func boxtext (b PlayerBox) string { Err z3bra.org 70 it@@ -74,7 +74,7 @@ func PlayerBoxName(p Player) *tview.TextView { Err z3bra.org 70 i p.Register() Err z3bra.org 70 i cyboard.DrawBoard() Err z3bra.org 70 i } else { Err z3bra.org 70 i- cyboard.Popup("Error", "Name already registered\nPlease pick another one") Err z3bra.org 70 i+ cyboard.Popup("Error", "Player name unavailable\nPlease pick another one") Err z3bra.org 70 i } Err z3bra.org 70 i } Err z3bra.org 70 i }) Err z3bra.org 70 1diff --git a/ui.go b/ui.go /scm/scoreboard/file/ui.go.gph z3bra.org 70 it@@ -76,8 +76,18 @@ func (a *Application) HighlightBoard(line int) { Err z3bra.org 70 i } Err z3bra.org 70 i Err z3bra.org 70 i func (a *Application) NewPlayer(rank int) { Err z3bra.org 70 i- t1 := RankTable(0, BOARD_HEIGHT, 0, false).SetSelectable(false, false) Err z3bra.org 70 i- t2 := RankTable(rank - 1, BOARD_HEIGHT, rank, true).SetSelectable(false, false) Err z3bra.org 70 i+ var offset, t1_sz, t2_sz int Err z3bra.org 70 i+ if rank > BOARD_HEIGHT { Err z3bra.org 70 i+ offset = rank - BOARD_HEIGHT Err z3bra.org 70 i+ t1_sz = BOARD_HEIGHT - 1 Err z3bra.org 70 i+ t2_sz = 0 Err z3bra.org 70 i+ } else { Err z3bra.org 70 i+ offset = 0 Err z3bra.org 70 i+ t1_sz = rank - 1 Err z3bra.org 70 i+ t2_sz = BOARD_HEIGHT - rank - 1 Err z3bra.org 70 i+ } Err z3bra.org 70 i+ t1 := RankTable(offset, BOARD_HEIGHT, t1_sz, false).SetSelectable(false, false) Err z3bra.org 70 i+ t2 := RankTable(rank - 1, BOARD_HEIGHT, t2_sz, true).SetSelectable(false, false) Err z3bra.org 70 i box := PlayerBoxGrid(cyboard.player, rank) Err z3bra.org 70 i Err z3bra.org 70 i cyboard.board.Clear(). Err z3bra.org 70 it@@ -106,3 +116,23 @@ func (a *Application) Popup(title, text string) { Err z3bra.org 70 i Err z3bra.org 70 i a.pages.AddAndSwitchToPage("popup", c, true) Err z3bra.org 70 i } Err z3bra.org 70 i+ Err z3bra.org 70 i+func (a *Application) Fatal(err error) { Err z3bra.org 70 i+ p := tview.NewTextView(). Err z3bra.org 70 i+ SetDynamicColors(true). Err z3bra.org 70 i+ SetTextAlign(tview.AlignCenter). Err z3bra.org 70 i+ SetText(fmt.Sprintf("[::b]%s", err)). Err z3bra.org 70 i+ SetDoneFunc(func(key tcell.Key) { Err z3bra.org 70 i+ a.app.Stop() Err z3bra.org 70 i+ }) Err z3bra.org 70 i+ Err z3bra.org 70 i+ frame := tview.NewFrame(p). Err z3bra.org 70 i+ SetBorders(1, 0, 2, 2, 1, 1). Err z3bra.org 70 i+ AddText("Press RET", false, tview.AlignRight, tcell.ColorGray) Err z3bra.org 70 i+ Err z3bra.org 70 i+ frame.SetBorder(true).SetTitle(" ERROR ") Err z3bra.org 70 i+ Err z3bra.org 70 i+ c := center(30, 8, frame) Err z3bra.org 70 i+ Err z3bra.org 70 i+ a.pages.AddAndSwitchToPage("fatal", c, true) Err z3bra.org 70 i+} Err z3bra.org 70 .