3 # no warranty for this script
9 use FindBin; use lib $FindBin::Bin;
12 use sigtrap qw(die normal-signals);
13 use WeaponEncounterProfile;
15 my ($statsfile) = @ARGV;
16 my $password = $ENV{rcon_password};
17 my $server = $ENV{rcon_address};
18 my $bind = $ENV{rcon_bindaddress};
24 my ($addr, $map, $attackerweapon, $targweapon, $type) = @_;
25 $stats->event($addr, $map, $attackerweapon, $targweapon, $type);
35 $stats = WeaponEncounterProfile->new($statsfile);
40 print STDERR "Operation timed out.\n";
44 our @discosockets = ();
47 # connects to a DP server using rcon with log_dest_udp
49 my $value = sprintf "%s:%d", $sock->sockhost(), $sock->sockport();
50 $sock->send("\377\377\377\377rcon $password log_dest_udp", 0)
51 or die "send rcon: $!";
55 $sock->recv(my $response, 2048, 0)
57 if($response =~ /^\377\377\377\377n"log_dest_udp" is "(.*)" \[".*"\]\n$/s)
60 my @dests = split /\s+/, $1;
62 if grep { $_ eq $value } @dests;
64 $sock->send("\377\377\377\377rcon $password log_dest_udp \"@dests\"");
69 push @discosockets, [$sock, $value];
76 # disconnects (makes the server stop send the data to us)
77 $s->send("\377\377\377\377rcon $password log_dest_udp", 0)
78 or die "send rcon: $!";
82 $s->recv(my $response, 2048, 0)
84 if($response =~ /^\377\377\377\377n"log_dest_udp" is "(.*)" \[".*"\]\n$/s)
87 my @dests = split /\s+/, $1;
89 if not grep { $_ eq $v } @dests;
90 @dests = grep { $_ ne $v } @dests;
91 $s->send("\377\377\377\377rcon $password log_dest_udp \"@dests\"");
100 sub sockaddr_readable($)
103 my ($port, $addr) = sockaddr_in $binary;
104 return sprintf "%s:%d", inet_ntoa($addr), $port;
110 # bind to a port and wait for any packets
111 $sock = IO::Socket::INET->new(Proto => 'udp', LocalAddr => $bind, LocalPort => 26000)
116 # connect to a DP server
117 $sock = IO::Socket::INET->new(Proto => 'udp', PeerAddr => $server, PeerPort => 26000)
126 while(my $addr = sockaddr_readable $sock->recv($_, 2048, 0))
129 if not defined $bind;
130 s/^\377\377\377\377n//
132 for(split /\r?\n/, $_)
134 if(/^:gamestart:([^:]+):/)
137 $currentmap{$addr} = $1;
139 print "($addr) switching to $1\n";
144 unless defined $currentmap{$addr};
145 if(/^:join:(\d+):bot:/)
147 $bots{$addr}{$1} = 1;
149 elsif(/^:kill:frag:(\d+):(\d+):type=(\d+):items=(\d+)([A-Z]*)(?:|(\d+)):victimitems=(\d+)([A-Z]*)(?:|(\d+))$/)
151 my ($a, $b, $type, $killweapon, $killflags, $killrunes, $victimweapon, $victimflags, $victimrules) = ($1, $2, $3, $4, $5, $6, $7, $8, $9);
153 if exists $bots{$addr}{$a} or exists $bots{$addr}{$b}; # only count REAL kills
157 if $stats->weaponid_valid($type); # if $type is not a weapon deathtype, count the weapon of the killer
159 if not $stats->weaponid_valid($killweapon); # invalid weapon? that's 0 then
161 if not $stats->weaponid_valid($victimweapon); # dito
163 if $killflags =~ /S|I/ or $victimflags =~ /T/; # no strength, shield or typekills (these skew the statistics)
164 AddKill($addr, $currentmap{$addr}, $killweapon, $victimweapon, +1);
166 elsif(/^:kill:suicide:\d+:\d+:type=(\d+):items=(\d+)([A-Z]*)(?:|(\d+))$/)
168 my ($type, $killweapon, $killflags, $killrunes) = ($1, $2, $3, $4, $5, $6, $7);
172 if $stats->weaponid_valid($type);
174 if not $stats->weaponid_valid($killweapon);
176 if $killflags =~ /S/; # no strength suicides (happen too easily accidentally)
177 AddKill($addr, $currentmap{$addr}, $killweapon, $killweapon, +1);