Skip to content

Commit 73494bd

Browse files
authored
Merge pull request #203 from markus-wa/steamid-improvements
SteamID Improvements
2 parents c4fa251 + 8e37a8d commit 73494bd

File tree

10 files changed

+107
-34
lines changed

10 files changed

+107
-34
lines changed

pkg/demoinfocs/common/common.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package common
33

44
import (
55
"math/rand"
6+
"strconv"
7+
"strings"
68
"time"
79

810
"github.com/golang/geo/r3"
@@ -205,3 +207,35 @@ func NewTeamState(team Team, membersCallback func(Team) []*Player) TeamState {
205207
membersCallback: membersCallback,
206208
}
207209
}
210+
211+
// ConvertSteamIDTxtTo32 converts a Steam-ID in text format to a 32-bit variant.
212+
// See https://developer.valvesoftware.com/wiki/SteamID
213+
func ConvertSteamIDTxtTo32(steamID string) (uint32, error) {
214+
arr := strings.Split(steamID, ":")
215+
216+
Y, err := strconv.ParseUint(arr[1], 10, 32)
217+
if err != nil {
218+
return 0, err
219+
}
220+
221+
Z, err := strconv.ParseUint(arr[2], 10, 32)
222+
if err != nil {
223+
return 0, err
224+
}
225+
226+
return uint32((Z << 1) + Y), nil
227+
}
228+
229+
const steamID64IndividualIdentifier = 0x0110000100000000
230+
231+
// ConvertSteamID32To64 converts a Steam-ID in 32-bit format to a 64-bit variant.
232+
// See https://developer.valvesoftware.com/wiki/SteamID
233+
func ConvertSteamID32To64(steamID32 uint32) uint64 {
234+
return steamID64IndividualIdentifier + uint64(steamID32)
235+
}
236+
237+
// ConvertSteamID64To32 converts a Steam-ID in 64-bit format to a 32-bit variant.
238+
// See https://developer.valvesoftware.com/wiki/SteamID
239+
func ConvertSteamID64To32(steamID64 uint64) uint32 {
240+
return uint32(steamID64 - steamID64IndividualIdentifier)
241+
}

pkg/demoinfocs/common/common_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,37 @@ func TestTeamState_MoneySpentTotal(t *testing.T) {
113113
assert.Equal(t, 300, state.MoneySpentTotal())
114114
}
115115

116+
func TestConvertSteamIDTxtTo32(t *testing.T) {
117+
id, err := ConvertSteamIDTxtTo32("STEAM_0:1:26343269")
118+
119+
assert.Nil(t, err)
120+
assert.Equal(t, uint32(52686539), id)
121+
}
122+
123+
func TestConvertSteamIDTxtTo32_Error(t *testing.T) {
124+
id, err := ConvertSteamIDTxtTo32("STEAM_0:1:a")
125+
126+
assert.Equal(t, uint32(0), id)
127+
assert.NotNil(t, err)
128+
129+
id, err = ConvertSteamIDTxtTo32("STEAM_0:b:21643603")
130+
131+
assert.Equal(t, uint32(0), id)
132+
assert.NotNil(t, err)
133+
}
134+
135+
func TestConvertSteamID32To64(t *testing.T) {
136+
id := ConvertSteamID32To64(52686539)
137+
138+
assert.Equal(t, uint64(76561198012952267), id)
139+
}
140+
141+
func TestConvertSteamID64To32(t *testing.T) {
142+
id := ConvertSteamID64To32(76561198012952267)
143+
144+
assert.Equal(t, uint32(52686539), id)
145+
}
146+
116147
type demoInfoProviderMock struct {
117148
tickRate float64
118149
ingameTick int

pkg/demoinfocs/common/player.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const (
2121
type Player struct {
2222
demoInfoProvider demoInfoProvider // provider for demo info such as tick-rate or current tick
2323

24-
SteamID int64 // int64 representation of the User's Steam ID
24+
SteamID64 uint64 // 64-bit representation of the user's Steam ID. See https://developer.valvesoftware.com/wiki/SteamID
2525
LastAlivePosition r3.Vector // The location where the player was last alive. Should be equal to Position if the player is still alive.
2626
UserID int // Mostly used in game-events to address this player
2727
Name string // Steam / in-game user name
@@ -51,6 +51,12 @@ func (p *Player) String() string {
5151
return p.Name
5252
}
5353

54+
// SteamID32 converts SteamID64 to the 32-bit SteamID variant and returns the result.
55+
// See https://developer.valvesoftware.com/wiki/SteamID
56+
func (p *Player) SteamID32() uint32 {
57+
return ConvertSteamID64To32(p.SteamID64)
58+
}
59+
5460
// IsAlive returns true if the Hp of the player are > 0.
5561
func (p *Player) IsAlive() bool {
5662
return p.Health() > 0

pkg/demoinfocs/common/player_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,12 @@ func TestPlayer_ControlledBot(t *testing.T) {
250250
assert.Same(t, dave, pl.ControlledBot())
251251
}
252252

253+
func TestPlayer_SteamID32(t *testing.T) {
254+
pl := &Player{SteamID64: 76561198012952267}
255+
256+
assert.Equal(t, uint32(52686539), pl.SteamID32())
257+
}
258+
253259
func newPlayer(tick int) *Player {
254260
return NewPlayer(mockDemoInfoProvider(128, tick))
255261
}

pkg/demoinfocs/datatables.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ func (p *parser) getOrCreatePlayer(entityID int, rp *playerInfo) (isNew bool, pl
191191

192192
player = common.NewPlayer(p.demoInfoProvider)
193193
player.Name = rp.name
194-
player.SteamID = rp.xuid
194+
player.SteamID64 = rp.xuid
195195
player.IsBot = rp.isFakePlayer || rp.guid == "BOT"
196196
player.UserID = rp.userID
197197
}
@@ -284,7 +284,7 @@ func (p *parser) bindNewPlayer(playerEntity st.Entity) {
284284
playerEntity.Property("m_bSpottedByMask.001").OnUpdate(spottersChanged)
285285
}
286286

287-
if isNew && pl.SteamID != 0 {
287+
if isNew && pl.SteamID64 != 0 {
288288
p.eventDispatcher.Dispatch(events.PlayerConnect{Player: pl})
289289
}
290290
}

pkg/demoinfocs/events/events.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,13 +410,19 @@ type ChatMessage struct {
410410
// RankUpdate signals the new rank. Not sure if this
411411
// only occurs if the rank changed.
412412
type RankUpdate struct {
413-
SteamID32 int32
413+
SteamID32 int32 // 32-bit variant of the SteamID. See https://developer.valvesoftware.com/wiki/SteamID
414414
RankChange float32
415415
RankOld int
416416
RankNew int
417417
WinCount int
418418
}
419419

420+
// SteamID64 converts SteamID32 to the 64-bit SteamID variant and returns the result.
421+
// See https://developer.valvesoftware.com/wiki/SteamID
422+
func (ru RankUpdate) SteamID64() uint64 {
423+
return common.ConvertSteamID32To64(uint32(ru.SteamID32))
424+
}
425+
420426
// ItemEquip signals an item was equipped.
421427
// This event is not available in all demos.
422428
type ItemEquip struct {

pkg/demoinfocs/events/events_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ func TestBombEvents(t *testing.T) {
4343
}
4444
}
4545

46+
func TestRankUpdate_SteamID64(t *testing.T) {
47+
event := RankUpdate{SteamID32: 52686539}
48+
49+
assert.Equal(t, uint64(76561198012952267), event.SteamID64())
50+
}
51+
4652
type demoInfoProviderMock struct {
4753
}
4854

pkg/demoinfocs/game_events.go

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package demoinfocs
33
import (
44
"errors"
55
"fmt"
6-
"strconv"
7-
86
"github.com/golang/geo/r3"
97
"github.com/markus-wa/go-unassert"
108

@@ -427,7 +425,7 @@ func (geh gameEventHandler) playerConnect(data map[string]*msg.CSVCMsg_GameEvent
427425
}
428426

429427
var err error
430-
pl.xuid, err = getCommunityID(pl.guid)
428+
pl.xuid, err = guidToSteamID64(pl.guid)
431429

432430
if err != nil {
433431
geh.parser.setError(fmt.Errorf("failed to parse player XUID: %v", err.Error()))
@@ -734,24 +732,15 @@ func mapGameEventData(d *msg.CSVCMsg_GameEventListDescriptorT, e *msg.CSVCMsg_Ga
734732
return data
735733
}
736734

737-
// We're all better off not asking questions
738-
const valveMagicNumber = 76561197960265728
739-
740-
func getCommunityID(guid string) (int64, error) {
735+
func guidToSteamID64(guid string) (uint64, error) {
741736
if guid == "BOT" {
742737
return 0, nil
743738
}
744739

745-
authSrv, errSrv := strconv.ParseInt(guid[8:9], 10, 64)
746-
if errSrv != nil {
747-
return 0, errSrv
748-
}
749-
750-
authID, errID := strconv.ParseInt(guid[10:], 10, 64)
751-
if errID != nil {
752-
return 0, errID
740+
steamID32, err := common.ConvertSteamIDTxtTo32(guid)
741+
if err != nil {
742+
return 0, err
753743
}
754744

755-
// WTF are we doing here?
756-
return valveMagicNumber + authID*2 + authSrv, nil
745+
return common.ConvertSteamID32To64(steamID32), nil
757746
}

pkg/demoinfocs/game_events_test.go

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -216,24 +216,19 @@ func TestGetEquipmentInstance_Grenade_Thrown(t *testing.T) {
216216
}
217217

218218
func TestGetCommunityId(t *testing.T) {
219-
xuid, err := getCommunityID("abcdefgh1:3")
219+
xuid, err := guidToSteamID64("STEAM_0:1:26343269")
220220
assert.Nil(t, err)
221-
expected := int64(76561197960265728 + 6 + 1)
222-
assert.Equal(t, expected, xuid)
221+
assert.Equal(t, uint64(76561198012952267), xuid)
223222
}
224223

225224
func TestGetCommunityId_BOT(t *testing.T) {
226-
xuid, err := getCommunityID("BOT")
225+
xuid, err := guidToSteamID64("BOT")
227226
assert.Zero(t, xuid)
228227
assert.Nil(t, err)
229228
}
230229

231-
func TestGetCommunityId_Errors(t *testing.T) {
232-
_, err := getCommunityID("12345678a90123")
230+
func TestGetCommunityId_Error(t *testing.T) {
231+
_, err := guidToSteamID64("STEAM_0:1:abc")
233232
assert.NotNil(t, err)
234-
assert.Equal(t, "strconv.ParseInt: parsing \"a\": invalid syntax", err.Error())
235-
236-
_, err = getCommunityID("1234567890abc")
237-
assert.NotNil(t, err)
238-
assert.Equal(t, "strconv.ParseInt: parsing \"abc\": invalid syntax", err.Error())
233+
assert.Equal(t, "strconv.ParseUint: parsing \"abc\": invalid syntax", err.Error())
239234
}

pkg/demoinfocs/stringtables.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const (
2121

2222
type playerInfo struct {
2323
version int64
24-
xuid int64
24+
xuid uint64
2525
name string
2626
userID int
2727
guid string
@@ -247,7 +247,7 @@ func parsePlayerInfo(reader io.Reader) *playerInfo {
247247

248248
res := &playerInfo{
249249
version: int64(binary.BigEndian.Uint64(br.ReadBytes(8))),
250-
xuid: int64(binary.BigEndian.Uint64(br.ReadBytes(8))),
250+
xuid: binary.BigEndian.Uint64(br.ReadBytes(8)),
251251
name: br.ReadCString(playerNameMaxLength),
252252
userID: int(int32(binary.BigEndian.Uint32(br.ReadBytes(4)))),
253253
guid: br.ReadCString(guidLength),

0 commit comments

Comments
 (0)