-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgetDysModStats.tcl
312 lines (257 loc) · 8.37 KB
/
getDysModStats.tcl
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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
#!/usr/bin/tclsh
package require udp
encoding system ascii
# oh noes static ports! - too lazy to change right now, make sure they are not in use
set udp_port_mastah 47890
set udp_port_servq 55670
set state waiting
set stats_servers 0
set stats_clients 0
set stats_spaceclients 0
set stats_bots 0
set stats_passw 0
set stats_vac 0
#
# whatever we do with stats, we do it here
# -> extern
#
proc submitStats { } {
global stats_servers stats_clients stats_spaceclients stats_bots stats_passw stats_vac
# debug
puts "servers: $stats_servers"
puts "clients: $stats_clients"
puts "spaceclients: $stats_spaceclients"
puts "bots: $stats_bots"
puts "passw: $stats_passw"
puts "vac: $stats_vac"
}
#
# write stats to chache file
#
proc printToCache { cache } {
global stats_servers stats_clients stats_spaceclients stats_bots stats_passw stats_vac
set filez [open $cache w]
puts $filez "$stats_servers $stats_clients $stats_spaceclients $stats_bots $stats_passw $stats_vac"
close $filez
}
#
# proc to update rrd tool with our new values
#
proc updateRrdfile { rrdfile } {
global stats_servers stats_clients stats_spaceclients stats_bots stats_passw stats_vac
exec /usr/bin/rrdtool update ${rrdfile} N:$stats_clients:$stats_passw:$stats_vac:$stats_servers:$stats_spaceclients:$stats_bots
set filez [open debug.txt w]
puts $filez "update ${rrdfile} N:$stats_clients:$stats_passw:$stats_vac:$stats_servers:$stats_spaceclients:$stats_bots"
close $filez
}
#
# handler is calling this func
# parse all including servs and request a new set if query didnt include all servs
#
proc parseServers { servers } {
global sock_mastah stats_servers sock_servq
# ignore some chars at beginning cause they are crap
set start 6
set end [expr $start + 6]
set servs 0
#
# parse through all servs and catch them,
# we are doing that by cutting the long bytestream into small pieces until theres nothing left
#
for { set i 0 } { $end <= [string bytelength $servers] } { incr i } {
# thats a piece!
set token [string range $servers $start [expr $end-1]]
# if we found something, append it to our list
if {[string bytelength $token] > 0} {
lappend servs $token
# debug
#puts "add: [string range $servers $start $end]"
#puts "added: $test -- [llength $servs] --- [string bytelength $token]"
#binary scan [string range $servers $start [expr $end -1]] H* test
}
# set pos information for next element
set start $end
set end [expr $start + 6]
}
#
# loop our list and extract serv data
#
for { set i 1 } { $i < [llength $servs] } { incr i } {
set token [lindex $servs $i]
set port 0
set ip0 0
set ip1 0
set ip2 0
set ip3 0
# we dont want empty entries
if {[string bytelength $token] <= 0} {
continue
}
# extract info from servtoken
binary scan $token ccccS ip0 ip1 ip2 ip3 port
# debug
binary scan $token H* test2
# usigned plz
set port [expr { $port & 0xffff }]
set ip0 [expr { $ip0 & 0xff }]
set ip1 [expr { $ip1 & 0xff }]
set ip2 [expr { $ip2 & 0xff }]
set ip3 [expr { $ip3 & 0xff }]
#
# get more information about that serv, we want to make stats!
#
queryServer $sock_servq "$ip0.$ip1.$ip2.$ip3" $port
# debug
#puts "$i: $ip0.$ip1.$ip2.$ip3:$port"
#puts "$i: $test2"
}
# gimme the last entry again
binary scan [lindex $servs end] ccccS ip0 ip1 ip2 ip3 port
set port [expr { $port & 0xffff }]
set ip0 [expr { $ip0 & 0xff }]
set ip1 [expr { $ip1 & 0xff }]
set ip2 [expr { $ip2 & 0xff }]
set ip3 [expr { $ip3 & 0xff }]
# if its 0 we got all we want, if not we have to request moar
if {$port != 0} {
set stats_servers [expr $stats_servers - 1]
requestNext $sock_servq $ip0 $ip1 $ip2 $ip3 $port
} else {
# debug
#puts "COMPLETE - GOT: $counter"
}
}
#
# that func will send out an udp packet to an gameserv to get further information about it
# AS_INFO
#
proc queryServer {sock ip port} {
# reconfig that socket for teh target machine
udp_conf $sock $ip $port
# static bytecode to get AS_INFO from serv
puts -nonewline $sock "\xFF\xFF\xFF\xFF\x54\x53\x6F\x75\x72\x63\x65\x20\x45\x6E\x67\x69\x6E\x65\x20\x51\x75\x65\x72\x79\x00"
}
#
# our event handler forwards incomming messages to the right func
#
proc udpEventHandler {sock} {
global udp_port_servq udp_port_mastah
set pkt [read $sock]
set peer [fconfigure $sock -peer]
set myport [udp_conf $sock -myport]
# debug
#puts "$peer: [string length $pkt] {$pkt}"
if { $myport == $udp_port_mastah } {
parseServers $pkt
} elseif { $myport == $udp_port_servq } {
parseServInfo $pkt
} else {
puts stderr "ZOMG, somthing went wrong in eventhandler, got packet but no recipient for it"
}
return
}
#
# listen for udp replies
#
proc udp_listen {port} {
set srv [udp_open $port]
fconfigure $srv -encoding binary -buffering none -translation binary
fileevent $srv readable [list ::udpEventHandler $srv]
# debug
#puts "Listening on udp port: [fconfigure $srv -myport]"
return $srv
}
#
# parse teh serverquery packet we receive due to our request and parse information from it
#
proc parseServInfo { token } {
global stats_clients stats_spaceclients stats_bots stats_passw stats_vac stats_servers
#
# we are only using the last 15 chars of the reply,
# some dirty string range stuff here, i heard we should not do that - but it worx!
#
set testtoken [string range $token [expr [string length $token] - 15] end]
set cplayers 0
set maxplayers 0
set bots 0
set os 0
set vac 0
# debug stuff
binary scan $testtoken h* dbg
#
# get all infos from that little string rangi
#
binary scan $testtoken cccaacc cplayers maxplayers bots dedicated os passw vac
# make sure they are unsigned
set cplayers [expr { $cplayers & 0xff }]
set maxplayers [expr { $maxplayers & 0xff }]
set bots [expr { $bots & 0xff }]
set vac [expr { $vac & 0xff }]
set passw [expr { $passw & 0xff }]
# add those to our global stats
set stats_clients [expr $stats_clients + $cplayers]
set stats_spaceclients [expr $stats_spaceclients + $maxplayers]
set stats_bots [expr $stats_bots + $bots]
set stats_passw [expr $stats_passw + $passw]
set stats_vac [expr $stats_vac + $vac]
if {$maxplayers > 0} {
# yay, server++
incr stats_servers
}
#
# debug stuff
#
#puts "DBG: $dbg"
#puts "DBG: cplayers: $cplayers -- maxplayers: $maxplayers -- bots: $bots -- os: $os -- vac: $vac -- passw: $passw"
}
#
# request next result set of servers from mastah serv,
# give him the last entry of the prior result or 0 0 0 0 0 to start from beginning
#
proc requestNext {sock lastip0 lastip1 lastip2 lastip3 port} {
# bytecode for ypsilon - country filter / all
set a "\xFF"
# bytecode for "\" char
set b "\x5C"
# just \x00
set c "\x00"
#
# get servs from beginning or submit "salt" of the last res?
#
if {$port == 0} {
puts -nonewline $sock "1${a}0.0.0.0:0${c}${b}gamedir${b}dystopia_v1${b}napp${b}50"
# Debug stuff
#puts -nonewline $sock "1${a}0.0.0.0:0${c}${b}gamedir${b}dystopia_v1${b}empty${b}1"
#puts $sock "\x31\xff\x30\x2e\x30\x2e\x30\x2e\x30\x3a\x30\x00\x5C\x67\x61\x6D\x65\x64\x69\x72\x5C\x64\x79\x73\x74\x6F\x70\x69\x61\x5F\x76\x31\x5C\x65\x6D\x70\x74\x79\x5C\x31"
} else {
puts -nonewline $sock "1${a}${lastip0}.${lastip1}.${lastip2}.${lastip3}:${port}${c}${b}gamedir${b}dystopia_v1${b}napp${b}50"
# Debug stuff
#puts -nonewline $sock "1${a}${lastip0}.${lastip1}.${lastip2}.${lastip3}:${port}${C}${B}gamedir${b}dystopia_v1${b}empty${b}1"
#puts "1${a}${lastip0}.${lastip1}.${lastip2}.${lastip3}:${port}${c}${b}gamedir${b}tf{b}empty${b}1"
}
}
proc retry { } {
global stats_servers sock_mastah
if { $stats_servers == 0 } {
requestNext $sock_mastah 0 0 0 0 0
#puts "[clock seconds] retrying..."
}
}
set sock_servq [udp_listen $udp_port_servq]
set sock_mastah [udp_listen $udp_port_mastah]
after 20000 retry
after 40000 retry
after 55000 set state plzexit
# sock for querying valve mastah serv
#udp_conf $sock_mastah "hl2master.steampowered.com" "27011"
udp_conf $sock_mastah "72.165.61.189" "27011"
#udp_conf $sock_mastah "69.28.140.246" "27011"
#udp_conf $sock_mastah "68.142.72.250" "27010"
requestNext $sock_mastah 0 0 0 0 0
vwait state
#submitStats
printToCache "dysplayers.cache"
#update rrd
updateRrdfile "./rrd/dysstats.rrd"
close $sock_servq
close $sock_mastah