wfc-server/common/gamespy_message.go
Palapeli f8339dbf87
Some checks are pending
Build CI / build (push) Waiting to run
GameStats: Support public data for getpd and setpd
2025-03-03 09:34:21 -05:00

121 lines
2.5 KiB
Go

package common
import (
"errors"
"fmt"
"strconv"
"strings"
)
type GameSpyCommand struct {
Command string
CommandValue string
OtherValues map[string]string
}
var (
ErrInvalidGameSpyCommand = errors.New("invalid GameSpy command received")
ErrNoGameStatsDataLength = errors.New("no data length found in GameStats message")
)
func parseGameSpyMessage(msg string, gameStats bool) ([]GameSpyCommand, error) {
if !strings.Contains(msg, `\final\`) {
return nil, ErrInvalidGameSpyCommand
}
var commands []GameSpyCommand
for len(msg) > 0 && string(msg[0]) == `\` && strings.Contains(msg, `\final\`) {
foundCommand := false
g := GameSpyCommand{
OtherValues: map[string]string{},
}
for len(msg) > 0 && string(msg[0]) == `\` {
keyEnd := strings.Index(msg[1:], `\`) + 1
if keyEnd < 2 {
return nil, ErrInvalidGameSpyCommand
}
key := msg[1:keyEnd]
value := ""
msg = msg[keyEnd+1:]
if key == "final" {
// We are done.
break
}
if gameStats && key == "data" {
if g.OtherValues["length"] == "" {
return nil, ErrNoGameStatsDataLength
}
dataLength, err := strconv.Atoi(g.OtherValues["length"])
if err != nil {
return nil, err
}
if len(msg) < dataLength+1 {
return nil, ErrInvalidGameSpyCommand
}
value = msg[:dataLength]
msg = msg[dataLength:]
if msg[0] == '\\' {
msg = msg[1:]
}
} else if strings.Contains(msg, `\`) {
if msg[0] != '\\' {
valueEnd := strings.Index(msg[1:], `\`)
value = msg[:valueEnd+1]
msg = msg[valueEnd+1:]
}
} else {
// We have most likely reached the end of the line.
// However, we do not want to exit out without parsing the final key.
value = msg
}
if !foundCommand {
g.Command = key
g.CommandValue = value
foundCommand = true
} else {
g.OtherValues[key] = value
}
}
commands = append(commands, g)
}
return commands, nil
}
func ParseGameSpyMessage(msg string) ([]GameSpyCommand, error) {
return parseGameSpyMessage(msg, false)
}
func ParseGameStatsMessage(msg string) ([]GameSpyCommand, error) {
return parseGameSpyMessage(msg, true)
}
func CreateGameSpyMessage(command GameSpyCommand) string {
query := ""
endQuery := ""
for k, v := range command.OtherValues {
if command.Command == "getpdr" && k == "data" {
endQuery += fmt.Sprintf(`\%s\%s`, k, v)
} else {
query += fmt.Sprintf(`\%s\%s`, k, v)
}
}
query += endQuery
if command.Command != "" {
query = fmt.Sprintf(`\%s\%s%s`, command.Command, command.CommandValue, query)
}
return query + `\final\`
}