-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathplayer.go
183 lines (141 loc) · 3.63 KB
/
player.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package a2s
import (
"encoding/binary"
"errors"
)
const (
A2S_PLAYER_REQUEST = 0x55
A2S_PLAYER_RESPONSE = 0x44 // Source & up
)
var (
ErrBadPlayerReply = errors.New("Bad player reply")
)
type PlayerInfo struct {
// Number of players whose information was gathered.
Count uint8 `json:"Count"`
// Slice of pointers to each Player
Players []*Player `json:"Players"`
}
type Player struct {
/*
Index of player chunk starting from 0.
This seems to be always 0?
*/
Index uint8 `json:"Index"`
// Name of the player.
Name string `json:"Name"`
// Player's score (usually "frags" or "kills".)
Score uint32 `json:"Score"`
// Time (in seconds) player has been connected to the server.
Duration float32 `json:"Duration"`
// The Ship additional player info
TheShip *TheShipPlayer `json:"TheShip,omitempty"`
}
type TheShipPlayer struct {
// Player's deaths
Deaths uint32 `json:"Deaths"`
// Player's money
Money uint32 `json:"Money"`
}
func (c *Client) QueryPlayer() (*PlayerInfo, error) {
/*
A2S_PLAYER
Request Format
Header byte 'U' (0x55)
Challenge int Challenge number, or -1 (0xFFFFFFFF) to receive a challenge number.
FF FF FF FF 55 FF FF FF FF ÿÿÿÿUÿÿÿÿ"
Example A2S_PLAYER request with the received challenge number:
FF FF FF FF 55 4B A1 D5 22 ÿÿÿÿUÿÿÿÿ"
*/
playerRequest := []byte{0xFF, 0xFF, 0xFF, 0xFF, A2S_PLAYER_REQUEST, 0xFF, 0xFF, 0xFF, 0xFF}
data, immediate, err := c.getChallenge(playerRequest, A2S_PLAYER_RESPONSE)
if err != nil {
return nil, err
}
if !immediate {
if err := c.send([]byte{
0xff, 0xff, 0xff, 0xff,
A2S_PLAYER_REQUEST,
data[0], data[1], data[2], data[3],
}); err != nil {
return nil, err
}
data, err = c.receive()
if err != nil {
return nil, err
}
}
// Read header (long 4 bytes)
switch int32(binary.LittleEndian.Uint32(data)) {
case -1:
return c.parsePlayerInfo(data)
case -2:
data, err = c.collectMultiplePacketResponse(data)
if err != nil {
return nil, err
}
return c.parsePlayerInfo(data)
}
return nil, ErrBadPacketHeader
}
func (c *Client) parsePlayerInfo(data []byte) (*PlayerInfo, error) {
reader := NewPacketReader(data)
// Simple response now
_, ok := reader.TryReadInt32()
if !ok {
return nil, ErrBadPlayerReply
}
headerByte, ok := reader.TryReadUint8()
if !ok || headerByte != A2S_PLAYER_RESPONSE {
return nil, ErrBadPlayerReply
}
info := &PlayerInfo{}
count, hasCount := reader.TryReadUint8()
if !hasCount {
return nil, ErrBadPlayerReply
}
info.Count = count
var player *Player
for i := 0; i < int(info.Count); i++ {
player = &Player{}
index, hasIndex := reader.TryReadUint8()
if !hasIndex {
return nil, ErrBadPlayerReply
}
player.Index = index
name, hasName := reader.TryReadString()
if !hasName {
return nil, ErrBadPlayerReply
}
player.Name = name
score, hasScore := reader.TryReadUint32()
if !hasScore {
return nil, ErrBadPlayerReply
}
player.Score = score
duration, hasDuration := reader.TryReadFloat32()
if !hasDuration {
return nil, ErrBadPlayerReply
}
player.Duration = duration
/*
The Ship additional player info
Only if client AppID is set to 2400
*/
if c.appid == App_TheShip {
player.TheShip = &TheShipPlayer{}
shipDeaths, hasShipDeaths := reader.TryReadUint32()
if !hasShipDeaths {
return nil, ErrBadPlayerReply
}
player.TheShip.Deaths = shipDeaths
shipMoney, hasShipMoney := reader.TryReadUint32()
if !hasShipMoney {
return nil, ErrBadPlayerReply
}
player.TheShip.Money = shipMoney
}
info.Players = append(info.Players, player)
}
return info, nil
}