// NGnius 2020-01-30 package main // leadercraft-server import ( "flag" "fmt" "os" "os/signal" ) const ( // Version the current version Version = "0.1" // Name the program name Name = "leadercraft-s" ) var ( isClosing bool printVersionAndExit bool ) func init() { initArgs() } func main() { parseArgs() sqlInitErr := sqlInit() if sqlInitErr != nil { fmt.Printf("Failed to initialise SQL connection: %s\n", sqlInitErr) os.Exit(1) } // handle interrupt (terminate) signal signalChan := make(chan os.Signal) signal.Notify(signalChan, os.Interrupt) go func() { s := <-signalChan fmt.Println("Received terminate signal " + s.String()) isClosing = true sqlClose() }() // get new rankings rows, err := db.Query("SELECT * FROM Entries WHERE rank=-1 ORDER BY time ASC;") var players []int64 if err == nil { // update rank for boards var entries []*Entry count := 0 for rows.Next() { entries = append(entries, &Entry{}) scanErr := rows.Scan(entries[count].Intake()...) if scanErr == nil { //fmt.Printf("Updating rank for entry %d in board %d from player %d\n", entries[count].ID, entries[count].Board, entries[count].Player) if entries[count].Board > 2 { // ignore special boards players = append(players, entries[count].Player) } updateBoardEntries(entries[count]) count++ } else { fmt.Println(scanErr) } } // update special boards for _, elem := range players { p := &Player{ID: elem} err := p.Load() if err == nil { //fmt.Printf("Updating high score for player %d\n", p.ID) updateMainRank(p) } else { fmt.Println(err) } } } else { fmt.Println(err) } } func initArgs() { flag.BoolVar(&printVersionAndExit, "version", false, "Print version and exit") flag.StringVar(&sqlConnection, "conn", sqlConnectionDefault, "Database connection string") flag.StringVar(&sqlServer, "sql", sqlServerDefault, "SQL Database type") } func parseArgs() { flag.Parse() if printVersionAndExit { fmt.Println(Name + " v" + Version) os.Exit(0) } } func updateBoardEntries(entry *Entry) { // get nearest entry that's lower and steal it's rank nearestEntry := &Entry{} scanErr := db.QueryRow("SELECT * FROM Entries WHERE score < $1 AND rank!= -1 AND board=$2 ORDER BY score DESC LIMIT 1;", entry.Score, entry.Board).Scan(nearestEntry.Intake()...) if scanErr == nil { entry.Rank = nearestEntry.Rank entry.Commit() // update all ranks lower than itself tx, _ := db.Begin() stmt, _ := tx.Prepare("UPDATE Entries SET rank=rank+1 WHERE rank >=$1 AND id!=$2 AND board=$3;") _, err := stmt.Exec(entry.Rank, entry.ID, entry.Board) if err != nil { tx.Rollback() fmt.Println(err) } else { tx.Commit() } } else { // nothing to beat scanErr = db.QueryRow("SELECT * FROM Entries WHERE score >= $1 AND rank!= -1 AND board=$2 ORDER BY score ASC LIMIT 1;", entry.Score, entry.Board).Scan(nearestEntry.Intake()...) if scanErr == nil { entry.Rank = nearestEntry.Rank + 1 entry.Commit() } else { // no other entries entry.Rank = 1 entry.Commit() } } } func updateMainRank(p *Player) { rows, err := db.Query("SELECT * FROM Entries WHERE player = $1 AND board > 2 ORDER BY score DESC LIMIT $2;", p.ID, 100) var count int64 = 0 var sum int64 = 0 if err == nil { for rows.Next() { entry := &Entry{} scanErr := rows.Scan(entry.Intake()...) if scanErr == nil { count++ sum += entry.Score } else { fmt.Println(scanErr) return } } if count > 0 && sum > 0 { mainEntry := &Entry{ Player: p.ID, Board: 1, } average := sum / count //fmt.Printf("Average of %d/%d is %d\n", sum, count, average) dne := db.QueryRow("SELECT * FROM Entries WHERE board=$1 AND player=$2", 1, p.ID).Scan(mainEntry.Intake()...) if dne == nil { tx, _ := db.Begin() stmt, _ := tx.Prepare("UPDATE Entries SET rank=rank-1 WHERE rank >$1 AND id!=$2 AND board=$3;") _, err := stmt.Exec(mainEntry.Rank, mainEntry.ID, 1) if err != nil { tx.Rollback() fmt.Println(err) return // abort } else { tx.Commit() } mainEntry.Score = average mainEntry.Rank = -1 mainEntry.Commit() } else { createErr := newEntrySql(average, p.ID, 1) if createErr == nil { fmt.Println(createErr) return } } } } else { fmt.Println(err) return } }