Include player rating in match listing
This commit is contained in:
parent
3b818ab272
commit
eda9754b06
8 changed files with 137 additions and 36 deletions
1
event.go
1
event.go
|
@ -50,6 +50,7 @@ type GameListing struct {
|
|||
Password bool
|
||||
Points int8
|
||||
Players int8
|
||||
Rating int
|
||||
Name string
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package server
|
||||
|
||||
type account struct {
|
||||
id int
|
||||
email []byte
|
||||
username []byte
|
||||
password []byte
|
||||
id int
|
||||
email []byte
|
||||
username []byte
|
||||
password []byte
|
||||
|
||||
autoplay bool
|
||||
highlight bool
|
||||
pips bool
|
||||
|
@ -12,4 +13,7 @@ type account struct {
|
|||
flip bool
|
||||
advanced bool
|
||||
speed int8
|
||||
|
||||
casual *clientRating
|
||||
competitive *clientRating
|
||||
}
|
||||
|
|
|
@ -11,11 +11,68 @@ import (
|
|||
"code.rocket9labs.com/tslocum/bgammon"
|
||||
)
|
||||
|
||||
type clientRating struct {
|
||||
backgammonSingle int
|
||||
backgammonMulti int
|
||||
aceySingle int
|
||||
aceyMulti int
|
||||
tabulaSingle int
|
||||
tabulaMulti int
|
||||
}
|
||||
|
||||
func (r *clientRating) getRating(variant int8, multiPoint bool) int {
|
||||
switch variant {
|
||||
case bgammon.VariantBackgammon:
|
||||
if !multiPoint {
|
||||
return r.backgammonSingle
|
||||
}
|
||||
return r.backgammonMulti
|
||||
case bgammon.VariantAceyDeucey:
|
||||
if !multiPoint {
|
||||
return r.aceySingle
|
||||
}
|
||||
return r.aceyMulti
|
||||
case bgammon.VariantTabula:
|
||||
if !multiPoint {
|
||||
return r.tabulaSingle
|
||||
}
|
||||
return r.tabulaMulti
|
||||
default:
|
||||
log.Panicf("unknown variant: %d", variant)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (r *clientRating) setRating(variant int8, multiPoint bool, rating int) {
|
||||
switch variant {
|
||||
case bgammon.VariantBackgammon:
|
||||
if !multiPoint {
|
||||
r.backgammonSingle = rating
|
||||
return
|
||||
}
|
||||
r.backgammonMulti = rating
|
||||
case bgammon.VariantAceyDeucey:
|
||||
if !multiPoint {
|
||||
r.aceySingle = rating
|
||||
return
|
||||
}
|
||||
r.aceyMulti = rating
|
||||
case bgammon.VariantTabula:
|
||||
if !multiPoint {
|
||||
r.tabulaSingle = rating
|
||||
}
|
||||
r.tabulaMulti = rating
|
||||
default:
|
||||
log.Panicf("unknown variant: %d", variant)
|
||||
}
|
||||
}
|
||||
|
||||
type serverClient struct {
|
||||
id int
|
||||
json bool
|
||||
name []byte
|
||||
account int
|
||||
account *account
|
||||
accountID int
|
||||
connected int64
|
||||
active int64
|
||||
lastPing int64
|
||||
|
|
|
@ -343,9 +343,12 @@ func loginAccount(passwordSalt string, username []byte, password []byte) (*accou
|
|||
}
|
||||
defer tx.Commit(context.Background())
|
||||
|
||||
a := &account{}
|
||||
a := &account{
|
||||
casual: &clientRating{},
|
||||
competitive: &clientRating{},
|
||||
}
|
||||
var autoplay, highlight, pips, moves, flip, advanced int
|
||||
err = tx.QueryRow(context.Background(), "SELECT id, email, username, password, autoplay, highlight, pips, moves, flip, advanced, speed FROM account WHERE username = $1 OR email = $2", bytes.ToLower(bytes.TrimSpace(username)), bytes.ToLower(bytes.TrimSpace(username))).Scan(&a.id, &a.email, &a.username, &a.password, &autoplay, &highlight, &pips, &moves, &flip, &advanced, &a.speed)
|
||||
err = tx.QueryRow(context.Background(), "SELECT id, email, username, password, autoplay, highlight, pips, moves, flip, advanced, speed, casual_backgammon_single, casual_backgammon_multi, casual_acey_single, casual_acey_multi, casual_tabula_single, casual_tabula_multi, rated_backgammon_single, rated_backgammon_multi, rated_acey_single, rated_acey_multi, rated_tabula_single, rated_tabula_multi FROM account WHERE username = $1 OR email = $2", bytes.ToLower(bytes.TrimSpace(username)), bytes.ToLower(bytes.TrimSpace(username))).Scan(&a.id, &a.email, &a.username, &a.password, &autoplay, &highlight, &pips, &moves, &flip, &advanced, &a.speed, &a.casual.backgammonSingle, &a.casual.backgammonMulti, &a.casual.aceySingle, &a.casual.aceyMulti, &a.casual.tabulaSingle, &a.casual.tabulaMulti, &a.competitive.backgammonSingle, &a.competitive.backgammonMulti, &a.competitive.aceySingle, &a.competitive.aceyMulti, &a.competitive.tabulaSingle, &a.competitive.tabulaMulti)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
} else if len(a.password) == 0 {
|
||||
|
@ -435,7 +438,7 @@ func setAccountSetting(id int, name string, value int) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func recordGameResult(g *bgammon.Game, winType int8, account1 int, account2 int, replay [][]byte) error {
|
||||
func recordGameResult(g *serverGame, winType int8, replay [][]byte) error {
|
||||
dbLock.Lock()
|
||||
defer dbLock.Unlock()
|
||||
|
||||
|
@ -454,19 +457,19 @@ func recordGameResult(g *bgammon.Game, winType int8, account1 int, account2 int,
|
|||
}
|
||||
defer tx.Commit(context.Background())
|
||||
|
||||
_, err = tx.Exec(context.Background(), "INSERT INTO game (variant, started, ended, player1, account1, player2, account2, points, winner, wintype, replay) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)", g.Variant, g.Started.Unix(), ended.Unix(), g.Player1.Name, account1, g.Player2.Name, account2, g.Points, g.Winner, winType, bytes.Join(replay, []byte("\n")))
|
||||
_, err = tx.Exec(context.Background(), "INSERT INTO game (variant, started, ended, player1, account1, player2, account2, points, winner, wintype, replay) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)", g.Variant, g.Started.Unix(), ended.Unix(), g.Player1.Name, g.account1, g.Player2.Name, g.account2, g.Points, g.Winner, winType, bytes.Join(replay, []byte("\n")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if account1 != 0 {
|
||||
_, err = tx.Exec(context.Background(), "UPDATE account SET active = $1 WHERE id = $2", time.Now().Unix(), account1)
|
||||
if g.account1 != 0 {
|
||||
_, err = tx.Exec(context.Background(), "UPDATE account SET active = $1 WHERE id = $2", time.Now().Unix(), g.account1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if account2 != 0 {
|
||||
_, err = tx.Exec(context.Background(), "UPDATE account SET active = $1 WHERE id = $2", time.Now().Unix(), account2)
|
||||
if g.account2 != 0 {
|
||||
_, err = tx.Exec(context.Background(), "UPDATE account SET active = $1 WHERE id = $2", time.Now().Unix(), g.account2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -474,11 +477,11 @@ func recordGameResult(g *bgammon.Game, winType int8, account1 int, account2 int,
|
|||
return nil
|
||||
}
|
||||
|
||||
func recordMatchResult(g *bgammon.Game, matchType int, account1 int, account2 int) error {
|
||||
func recordMatchResult(g *serverGame, matchType int) error {
|
||||
dbLock.Lock()
|
||||
defer dbLock.Unlock()
|
||||
|
||||
if db == nil || g.Started.IsZero() || g.Winner == 0 || account1 == 0 || account2 == 0 || account1 == account2 {
|
||||
if db == nil || g.Started.IsZero() || g.Winner == 0 || g.account1 == 0 || g.account2 == 0 || g.account1 == g.account2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -491,14 +494,14 @@ func recordMatchResult(g *bgammon.Game, matchType int, account1 int, account2 in
|
|||
columnName := ratingColumn(matchType, g.Variant, g.Points != 1)
|
||||
|
||||
var rating1i int
|
||||
err = tx.QueryRow(context.Background(), "SELECT "+columnName+" FROM account WHERE id = $1", account1).Scan(&rating1i)
|
||||
err = tx.QueryRow(context.Background(), "SELECT "+columnName+" FROM account WHERE id = $1", g.account1).Scan(&rating1i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rating1 := float64(rating1i) / 100
|
||||
|
||||
var rating2i int
|
||||
err = tx.QueryRow(context.Background(), "SELECT "+columnName+" FROM account WHERE id = $1", account2).Scan(&rating2i)
|
||||
err = tx.QueryRow(context.Background(), "SELECT "+columnName+" FROM account WHERE id = $1", g.account2).Scan(&rating2i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -512,12 +515,30 @@ func recordMatchResult(g *bgammon.Game, matchType int, account1 int, account2 in
|
|||
rating2New, _, _ := glicko2.Rank(rating2, 50, 0.06, []glicko2.Opponent{ratingPlayer{rating1, 30, 0.06, outcome2}}, 0.6)
|
||||
|
||||
active := time.Now().Unix()
|
||||
_, err = tx.Exec(context.Background(), "UPDATE account SET "+columnName+" = $1, active = $2 WHERE id = $3", int(rating1New*100), active, account1)
|
||||
_, err = tx.Exec(context.Background(), "UPDATE account SET "+columnName+" = $1, active = $2 WHERE id = $3", int(rating1New*100), active, g.account1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.Exec(context.Background(), "UPDATE account SET "+columnName+" = $1, active = $2 WHERE id = $3", int(rating2New*100), active, account2)
|
||||
return err
|
||||
_, err = tx.Exec(context.Background(), "UPDATE account SET "+columnName+" = $1, active = $2 WHERE id = $3", int(rating2New*100), active, g.account2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if g.client1 != nil && g.client1.account != nil {
|
||||
if matchType == matchTypeCasual {
|
||||
g.client1.account.casual.setRating(g.Variant, g.Points > 1, int(rating1New*100))
|
||||
} else {
|
||||
g.client1.account.competitive.setRating(g.Variant, g.Points > 1, int(rating1New*100))
|
||||
}
|
||||
}
|
||||
if g.client2 != nil && g.client2.account != nil {
|
||||
if matchType == matchTypeCasual {
|
||||
g.client2.account.casual.setRating(g.Variant, g.Points > 1, int(rating2New*100))
|
||||
} else {
|
||||
g.client2.account.competitive.setRating(g.Variant, g.Points > 1, int(rating2New*100))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func matchInfo(id int) (timestamp int64, player1 string, player2 string, replay []byte, err error) {
|
||||
|
|
|
@ -51,11 +51,11 @@ func replayByID(id int) ([]byte, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func recordGameResult(g *bgammon.Game, winType int8, account1 int, account2 int, replay [][]byte) error {
|
||||
func recordGameResult(g *serverGame, winType int8, replay [][]byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func recordMatchResult(g *bgammon.Game, matchType int, account1 int, account2 int) error {
|
||||
func recordMatchResult(g *serverGame, matchType int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -159,7 +159,12 @@ func (g *serverGame) roll(player int8) bool {
|
|||
// Store account IDs.
|
||||
if g.Started.IsZero() && g.Roll1 != 0 && g.Roll2 != 0 {
|
||||
g.Started = time.Now()
|
||||
g.account1, g.account2 = g.client1.account, g.client2.account
|
||||
if g.client1.account != nil {
|
||||
g.account1 = g.client1.account.id
|
||||
}
|
||||
if g.client2.account != nil {
|
||||
g.account2 = g.client2.account.id
|
||||
}
|
||||
}
|
||||
return true
|
||||
} else if player != g.Turn || g.Roll1 != 0 || g.Roll2 != 0 {
|
||||
|
@ -468,6 +473,17 @@ func (g *serverGame) listing(playerName []byte) *bgammon.GameListing {
|
|||
playerCount = g.playerCount()
|
||||
}
|
||||
|
||||
var rating int
|
||||
if g.client1 != nil && g.client1.account != nil {
|
||||
rating = g.client1.account.casual.getRating(g.Variant, g.Points > 1)
|
||||
}
|
||||
if g.client2 != nil && g.client2.account != nil {
|
||||
r := g.client2.account.casual.getRating(g.Variant, g.Points > 1)
|
||||
if r > rating {
|
||||
rating = r
|
||||
}
|
||||
}
|
||||
|
||||
name := string(g.name)
|
||||
switch g.Variant {
|
||||
case bgammon.VariantAceyDeucey:
|
||||
|
@ -481,6 +497,7 @@ func (g *serverGame) listing(playerName []byte) *bgammon.GameListing {
|
|||
Points: g.Points,
|
||||
Password: len(g.password) != 0,
|
||||
Players: playerCount,
|
||||
Rating: rating / 100,
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
|
@ -655,13 +672,13 @@ func (g *serverGame) handleWin() bool {
|
|||
if g.Variant != bgammon.VariantBackgammon {
|
||||
winType = 1
|
||||
}
|
||||
err := recordGameResult(g.Game, winType, g.client1.account, g.client2.account, g.replay)
|
||||
err := recordGameResult(g, winType, g.replay)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to record game result: %s", err)
|
||||
}
|
||||
|
||||
if !reset {
|
||||
err := recordMatchResult(g.Game, matchTypeCasual, g.client1.account, g.client2.account)
|
||||
err := recordMatchResult(g, matchTypeCasual)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to record match result: %s", err)
|
||||
}
|
||||
|
|
|
@ -192,7 +192,7 @@ func (s *server) handleWebSocket(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
c := &serverClient{
|
||||
id: <-s.newClientIDs,
|
||||
account: -1,
|
||||
accountID: -1,
|
||||
connected: now,
|
||||
active: now,
|
||||
commands: commands,
|
||||
|
@ -257,7 +257,7 @@ func (s *server) handleTerminatedGames() {
|
|||
if g.forefeit == 1 {
|
||||
g.Winner = 2
|
||||
}
|
||||
err := recordMatchResult(g.Game, matchTypeCasual, g.account1, g.account2)
|
||||
err := recordMatchResult(g, matchTypeCasual)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to record match result: %s", err)
|
||||
}
|
||||
|
@ -297,7 +297,7 @@ func (s *server) handleConnection(conn net.Conn) {
|
|||
|
||||
c := &serverClient{
|
||||
id: <-s.newClientIDs,
|
||||
account: -1,
|
||||
accountID: -1,
|
||||
connected: now,
|
||||
active: now,
|
||||
commands: commands,
|
||||
|
|
|
@ -40,7 +40,7 @@ COMMANDS:
|
|||
params := bytes.Fields(cmd.command[startParameters:])
|
||||
|
||||
// Require users to send login command first.
|
||||
if cmd.client.account == -1 {
|
||||
if cmd.client.accountID == -1 {
|
||||
resetCommand := keyword == bgammon.CommandResetPassword
|
||||
if resetCommand {
|
||||
if len(params) > 0 {
|
||||
|
@ -165,7 +165,8 @@ COMMANDS:
|
|||
continue
|
||||
}
|
||||
|
||||
cmd.client.account = a.id
|
||||
cmd.client.account = a
|
||||
cmd.client.accountID = a.id
|
||||
cmd.client.name = name
|
||||
cmd.client.autoplay = a.autoplay
|
||||
cmd.client.sendEvent(&bgammon.EventSettings{
|
||||
|
@ -178,7 +179,7 @@ COMMANDS:
|
|||
Speed: a.speed,
|
||||
})
|
||||
} else {
|
||||
cmd.client.account = 0
|
||||
cmd.client.accountID = 0
|
||||
if !randomUsername && !bytes.HasPrefix(username, []byte("BOT_")) && !bytes.HasPrefix(username, []byte("Guest_")) {
|
||||
username = append([]byte("Guest_"), username...)
|
||||
}
|
||||
|
@ -588,13 +589,13 @@ COMMANDS:
|
|||
winEvent.Player = clientGame.Player2.Name
|
||||
}
|
||||
|
||||
err := recordGameResult(clientGame.Game, 4, clientGame.client1.account, clientGame.client2.account, clientGame.replay)
|
||||
err := recordGameResult(clientGame, 4, clientGame.replay)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to record game result: %s", err)
|
||||
}
|
||||
|
||||
if !reset {
|
||||
err := recordMatchResult(clientGame.Game, matchTypeCasual, clientGame.client1.account, clientGame.client2.account)
|
||||
err := recordMatchResult(clientGame, matchTypeCasual)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to record match result: %s", err)
|
||||
}
|
||||
|
@ -1068,7 +1069,7 @@ COMMANDS:
|
|||
|
||||
clientGame.sendBoard(cmd.client, false)
|
||||
case bgammon.CommandPassword:
|
||||
if cmd.client.account == 0 {
|
||||
if cmd.client.account == nil {
|
||||
cmd.client.sendNotice("Failed to change password: you are logged in as a guest.")
|
||||
continue
|
||||
} else if len(params) < 2 {
|
||||
|
@ -1118,10 +1119,10 @@ COMMANDS:
|
|||
cmd.client.autoplay = value == 1
|
||||
}
|
||||
|
||||
if cmd.client.account == 0 {
|
||||
if cmd.client.account == nil {
|
||||
continue
|
||||
}
|
||||
_ = setAccountSetting(cmd.client.account, name, value)
|
||||
_ = setAccountSetting(cmd.client.account.id, name, value)
|
||||
case bgammon.CommandReplay:
|
||||
var (
|
||||
id int
|
||||
|
|
Loading…
Reference in a new issue