craft-bot/playersBoard.go
Timofey.Kovalev 12fce31ed8
All checks were successful
continuous-integration/drone/pr Build is passing
small refactoring message cleaning
2021-06-25 22:31:29 +03:00

322 lines
6.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"context"
"fmt"
"strings"
"sync"
"time"
"github.com/hako/durafmt"
)
type achievement uint8
const (
_ achievement = iota
achievementBestFeeder
achievementVeryLongOnline
achievementDeathless
achievementPeaceable
achievementMaxFrags
achievementMaxLevel
)
func getEmojiByAchievement(ach achievement) string {
switch ach {
case achievementBestFeeder:
return "\xF0\x9F\x91\xBB"
case achievementVeryLongOnline:
return "\xF0\x9F\xA4\xAA"
case achievementDeathless:
return "\xF0\x9F\x98\x8E"
case achievementPeaceable:
return "\xF0\x9F\x98\x87"
case achievementMaxFrags:
return "\xF0\x9F\x98\x88"
case achievementMaxLevel:
return "\xF0\x9F\xA7\x99\xE2\x80\x8D\xE2\x99\x82\xEF\xB8\x8F"
default:
return ""
}
}
var allAchievement = []achievement{
achievementBestFeeder,
achievementVeryLongOnline,
achievementDeathless,
achievementPeaceable,
achievementMaxFrags,
achievementMaxLevel,
}
type playerInfo struct {
playerID string
isOnline bool
achievements []achievement
messageText string
}
func (p *playerInfo) setOnline(v bool) *playerInfo {
p.isOnline = v
return p
}
const (
emojiUp = "\xE2\xAC\x86"
emojiDeaths = "\xF0\x9F\x92\x80"
emojiTime = "\xF0\x9F\x95\x90"
emojiGun = "\xF0\x9F\x94\xAB"
emojiCheck = "\xE2\x9C\x85"
)
func (p *playerInfo) updatePlayerInfo(ctx context.Context) error {
db := getDB(ctx)
lines := make([]string, 0, 10)
player, err := db.getPlayerByID(ctx, p.playerID)
if err != nil {
return err
}
kills, err := db.getKillsByPlayerID(ctx, player.id)
if err != nil {
return err
}
access := "\xF0\x9F\x94\xB4 *offLine*"
if p.isOnline {
access = "\xF0\x9F\x9F\xA2 *onLine*"
}
level := "-"
if player.level >= 0 {
level = fmt.Sprintf("%d", player.level)
}
d := durafmt.Parse(player.onlineDuration).LimitFirstN(3)
units, err := durafmt.DefaultUnitsCoder.Decode("мес.:мес.,нед.:нед.,дн.:дн.,чс.:чс.,мин.:мин.,cек.:cек.,-:-,-:-")
if err != nil {
panic(err)
}
lines = append(lines, fmt.Sprintf("*%s* | %s", player.name, access))
lines = append(lines, fmt.Sprintf("%s Уровень: *%s*", emojiUp, level))
lines = append(lines, fmt.Sprintf("%s Смертей: *%d*", emojiDeaths, player.deaths))
lines = append(lines, fmt.Sprintf("%s Фрагов: *%d*", emojiGun, kills))
lines = append(lines, fmt.Sprintf("%s Время в игре: *%s*", emojiTime, d.Format(units)))
if !player.lastLogout.IsZero() && !p.isOnline {
lines = append(lines, fmt.Sprintf("%s Был в сети: *%s*", emojiCheck, player.lastLogout.Format("02 Jan 15:04")))
}
if len(p.achievements) > 0 {
lines = append(lines, "-----")
emojiAch := make([]string, 0, len(p.achievements))
for _, a := range p.achievements {
emojiAch = append(emojiAch, getEmojiByAchievement(a))
}
lines = append(lines, strings.Join(emojiAch, " "))
}
text := strings.Join(lines, "\n")
if text != p.messageText {
messStorage := getMessageStorage(ctx)
err := messStorage.apply(ctx, fmt.Sprintf("playerBoard:%s", p.playerID), 0, func(interface{}) (string, interface{}) {
return text, nil
})
if err != nil {
return err
}
p.messageText = text
}
return nil
}
func (p *playerInfo) setAchievement(ach achievement) {
for _, a := range p.achievements {
if a == ach {
return
}
}
p.achievements = append(p.achievements, ach)
}
func (p *playerInfo) unsetAchievement(ach achievement) {
for i, a := range p.achievements {
if a == ach {
p.achievements = append(p.achievements[:i], p.achievements[i+1:]...)
}
}
}
type playersBoard struct {
mx sync.Mutex
players []*playerInfo
}
func newPlayersBoard() *playersBoard {
return &playersBoard{
players: make([]*playerInfo, 0),
}
}
func (pb *playersBoard) load(ctx context.Context) error {
pb.mx.Lock()
defer pb.mx.Unlock()
db := getDB(ctx)
players, err := db.getPlayers(ctx)
if err != nil {
return err
}
pb.players = make([]*playerInfo, 0, len(players))
for _, p := range players {
pi := &playerInfo{
playerID: p.id,
}
pb.players = append(pb.players, pi)
}
for _, a := range allAchievement {
err := pb.updateAchievements(ctx, a)
if err != nil {
return err
}
}
for _, p := range pb.players {
err := p.updatePlayerInfo(ctx)
if err != nil {
return err
}
}
return nil
}
func (pb *playersBoard) getPlayerBoard(playerID string) *playerInfo {
for i := range pb.players {
if pb.players[i].playerID == playerID {
return pb.players[i]
}
}
pb.mx.Lock()
defer pb.mx.Unlock()
pi := &playerInfo{
playerID: playerID,
}
pb.players = append(pb.players, pi)
return pi
}
func (pb *playersBoard) updateAchievements(ctx context.Context, ach achievement) error {
db := getDB(ctx)
var value int
playerIDs := make([]string, 0, len(pb.players))
setValue := func(v int, playerID string) {
if value < v {
value = v
playerIDs = []string{playerID}
} else if value == v {
playerIDs = append(playerIDs, playerID)
}
}
for _, p := range pb.players {
player, err := db.getPlayerByID(ctx, p.playerID)
if err != nil {
return err
}
switch ach {
case achievementBestFeeder:
setValue(player.deaths, p.playerID)
case achievementVeryLongOnline:
setValue(int(player.onlineDuration/time.Second), p.playerID)
case achievementDeathless:
if player.deaths == 0 {
playerIDs = append(playerIDs, p.playerID)
}
case achievementPeaceable:
kills, err := db.getKillsByPlayerID(ctx, p.playerID)
if err != nil {
return err
}
setValue(int(^uint(0)>>1)-kills, p.playerID)
case achievementMaxFrags:
kills, err := db.getKillsByPlayerID(ctx, p.playerID)
if err != nil {
return err
}
setValue(kills, p.playerID)
case achievementMaxLevel:
setValue(player.level, p.playerID)
}
}
mainLoop:
for _, p := range pb.players {
for _, pid := range playerIDs {
if p.playerID == pid {
p.setAchievement(ach)
continue mainLoop
}
}
p.unsetAchievement(ach)
}
return nil
}
func (pb *playersBoard) update(ctx context.Context, as ...achievement) error {
pb.mx.Lock()
defer pb.mx.Unlock()
for _, a := range as {
err := pb.updateAchievements(ctx, a)
if err != nil {
return err
}
}
for _, p := range pb.players {
err := p.updatePlayerInfo(ctx)
if err != nil {
return err
}
}
return nil
}