Skip to content

Commit

Permalink
Merge pull request #10 from leighmacdonald/steam32
Browse files Browse the repository at this point in the history
Improve steam32 support. Improve steam parser funcs.
  • Loading branch information
leighmacdonald authored Mar 20, 2024
2 parents 0ac4caf + 411e0e3 commit db2c4e7
Show file tree
Hide file tree
Showing 7 changed files with 548 additions and 468 deletions.
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ linters:
#- exhaustive
#- exhaustruct
- exportloopref
- forbidigo
#- forbidigo
- forcetypeassert
#- funlen
#- gci
Expand Down
83 changes: 63 additions & 20 deletions extra/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,22 @@ import (
"errors"
"fmt"
"io"
"strings"
"regexp"
"slices"

"github.com/leighmacdonald/steamid/v4/steamid"
)

var (
ErrIDType = errors.New("invalid sid type")

ErrReadInput = errors.New("error while reading input")

ErrWrite = errors.New("failed to write to output file")

ErrFlush = errors.New("failed to flush contents")
ErrWrite = errors.New("failed to write to output file")
ErrFlush = errors.New("failed to flush contents")
)

// ParseReader attempt to find all types of steam ids in the data stream provided by the
// input reader. It will write the output of what it finds to the output writer applying the
// formatting strings to each value. The formatting string takes the same formatting as the
// standards fmt.SprintF() and expects one %s token.
// standard fmt.SprintF() and expects one %s token.
//
// A formatting example to place each steam id on a newline: "%s\n"
//
Expand All @@ -33,32 +30,24 @@ func ParseReader(input io.Reader, output io.Writer, format string, idType string
switch idType {
case "steam":
case "steam3":
case "steam32":
case "steam64":
default:
return fmt.Errorf("%w: %s", ErrIDType, idType)
}

writer := bufio.NewWriter(output)
reader := bufio.NewScanner(input)

var lines []string

for reader.Scan() {
lines = append(lines, reader.Text())
}

if err := reader.Err(); err != nil {
return errors.Join(err, ErrReadInput)
}

for _, id := range steamid.ParseString(strings.Join(lines, "")) {
for _, id := range FindReaderSteamIDs(input) {
value := ""

switch idType {
case "steam64":
value = id.String()
case "steam3":
value = string(id.Steam3())
case "steam32":
value = fmt.Sprintf("%d", id.AccountID)
case "steam":
value = string(id.Steam(false))
}
Expand All @@ -75,3 +64,57 @@ func ParseReader(input io.Reader, output io.Writer, format string, idType string

return nil
}

// FindReaderSteamIDs attempts to parse any strings of any known format within the body to a common SID64 format.
func FindReaderSteamIDs(reader io.Reader) []steamid.SteamID {
var (
scanner = bufio.NewScanner(reader)
freSID = regexp.MustCompile(`STEAM_0:[01]:[0-9][0-9]{0,8}`)
freSID64 = regexp.MustCompile(`7656119\d{10}`)
freSID3 = regexp.MustCompile(`\[U:1:\d+]`)
// Store only unique entries
found []steamid.SteamID
)

for scanner.Scan() {
line := scanner.Text()
if matches := freSID.FindAllStringSubmatch(line, -1); matches != nil {
for _, i := range matches {
sid := steamid.New(i[0])
if !sid.Valid() {
continue
}
found = append(found, sid)
}
}
if matches := freSID64.FindAllStringSubmatch(line, -1); matches != nil {
for _, i := range matches {
sid := steamid.New(i[0])
if !sid.Valid() {
continue
}
found = append(found, sid)
}
}
if matches := freSID3.FindAllStringSubmatch(line, -1); matches != nil {
for _, i := range matches {
sid := steamid.New(i[0])
if !sid.Valid() {
continue
}
found = append(found, sid)
}
}
}

var uniq []steamid.SteamID
for _, foundID := range found {
if !slices.ContainsFunc(uniq, func(sid steamid.SteamID) bool {
return foundID.Int64() == sid.Int64()
}) {
uniq = append(uniq, foundID)
}
}

return uniq
}
58 changes: 58 additions & 0 deletions extra/file_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package extra_test

import (
"bytes"
"strings"
"testing"

"github.com/leighmacdonald/steamid/v4/extra"

"github.com/stretchr/testify/require"
)

func TestParseInput(t *testing.T) {
t.Parallel()

testBody := `# userid name uniqueid connected ping loss state
# 2 "WolfXine" [U:1:166779318] 15:22 85 0 active
# 3 "mdaniels5746" [U:1:361821288] 15:22 87 0 active
# 28 "KRGonzales" [U:1:875620767] 00:29 76 10 active
# 4 "juan.martinez2009" [U:1:79002518] 15:22 72 0 active
[U:1:172346362]STEAM_0:0:86173182[U:1:172346342]
STEAM_0:0:86173181
76561198132612090
76561198084134025
`

ids := extra.FindReaderSteamIDs(strings.NewReader(testBody))
require.Len(t, ids, 8) // 2 duplicated
}

func TestParseReader(t *testing.T) {
testBody := `# userid name uniqueid connected ping loss state
# 2 "WolfXine" [U:1:166779318] 15:22 85 0 active
# 3 "mdaniels5746" [U:1:361821288] 15:22 87 0 active
# 28 "KRGonzales" [U:1:875620767] 00:29 76 10 active
# 4 "juan.martinez2009" [U:1:79002518] 15:22 72 0 active
[U:1:172346362]STEAM_0:0:86173182[U:1:172346342]
STEAM_0:0:86173181
76561198132612090
76561198084134025
`
for format, expected := range map[string]string{
"steam64": "-76561198127045046-\n-76561198322087016-\n-76561198835886495-\n-76561198039268246-\n" +
"-76561198132612092-\n-76561198132612090-\n-76561198132612070-\n-76561198084134025-\n",
"steam3": "-[U:1:166779318]-\n-[U:1:361821288]-\n-[U:1:875620767]-\n-[U:1:79002518]-\n" +
"-[U:1:172346364]-\n-[U:1:172346362]-\n-[U:1:172346342]-\n-[U:1:123868297]-\n",
"steam": "-STEAM_0:0:83389659-\n-STEAM_0:0:180910644-\n-STEAM_0:1:437810383-\n-STEAM_0:0:39501259-\n" +
"-STEAM_0:0:86173182-\n-STEAM_0:0:86173181-\n-STEAM_0:0:86173171-\n-STEAM_0:1:61934148-\n",
"steam32": "-166779318-\n-361821288-\n-875620767-\n-79002518-\n-172346364-\n-172346362-\n-172346342-\n-123868297-\n",
} {
var buf64 bytes.Buffer
require.NoError(t, extra.ParseReader(strings.NewReader(testBody), &buf64, "-%s-\n", format))
require.Equalf(t, expected, buf64.String(), "Failed to generate: %s", format)
}
}
55 changes: 31 additions & 24 deletions extra/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,33 @@ func SIDSFromStatus(text string) []steamid.SteamID {
return ids
}

func parseMaxPlayers(part string) int {
ps := strings.Split(strings.ReplaceAll(part, "(", ""), " ")

m, errPlayers := strconv.ParseUint(ps[4], 10, 64)
if errPlayers != nil {
return -1
}

return int(m)
}

func parseEdits(part string) []int {
ed := strings.Split(part, " ")

l, errEdictCount := strconv.ParseUint(ed[0], 10, 64)
if errEdictCount != nil {
return []int{-1, -1}
}

m, errEdictTotal := strconv.ParseUint(ed[3], 10, 64)
if errEdictTotal != nil {
return []int{-1, -1}
}

return []int{int(l), int(m)}
}

// ParseStatus will parse a status command output into a struct
// If full is true, it will also parse the address/port of the player.
// This only works for status commands via RCON/CLI.
Expand All @@ -88,40 +115,20 @@ func ParseStatus(status string, full bool) (Status, error) {
switch strings.TrimRight(parts[0], " ") {
case "hostname":
s.ServerName = parts[1]

case "version":
s.Version = parts[1]

case "map":
s.Map = strings.Split(parts[1], " ")[0]

case "tags":
s.Tags = strings.Split(parts[1], ",")

case "players":
ps := strings.Split(strings.ReplaceAll(parts[1], "(", ""), " ")

m, errPlayers := strconv.ParseUint(ps[4], 10, 64)
if errPlayers != nil {
return Status{}, errors.Join(errPlayers, ErrParsePlayers)
if maxPlayers := parseMaxPlayers(parts[1]); maxPlayers > 0 {
s.PlayersMax = maxPlayers
}

s.PlayersMax = int(m)

case "edicts":
ed := strings.Split(parts[1], " ")

l, errEdictCount := strconv.ParseUint(ed[0], 10, 64)
if errEdictCount != nil {
return Status{}, errors.Join(errEdictCount, ErrParseEdict)
if ed := parseEdits(parts[1]); ed[0] > 0 && ed[1] > 0 {
s.Edicts = ed
}

m, errEdictTotal := strconv.ParseUint(ed[3], 10, 64)
if errEdictTotal != nil {
return Status{}, errors.Join(errEdictTotal, ErrParseEdictTotal)
}

s.Edicts = []int{int(l), int(m)}
}

continue
Expand Down
Loading

0 comments on commit db2c4e7

Please sign in to comment.