xonotic/misc/tools/weapon-profiler.pl
2010-03-18 14:22:15 +01:00

181 lines
4.3 KiB
Perl
Executable File

#!/usr/bin/perl
# no warranty for this script
# and no documentation
# take it or leave it
use strict;
use warnings;
use FindBin; use lib $FindBin::Bin;
use IO::Socket;
use Socket;
use sigtrap qw(die normal-signals);
use WeaponEncounterProfile;
my ($statsfile) = @ARGV;
my $password = $ENV{rcon_password};
my $server = $ENV{rcon_address};
my $bind = $ENV{rcon_bindaddress};
my $stats;
sub AddKill($$$$$)
{
my ($addr, $map, $attackerweapon, $targweapon, $type) = @_;
$stats->event($addr, $map, $attackerweapon, $targweapon, $type);
}
sub StoreData()
{
$stats->save();
}
sub LoadData()
{
$stats = WeaponEncounterProfile->new($statsfile);
}
$SIG{ALRM} = sub
{
print STDERR "Operation timed out.\n";
exit 1;
};
our @discosockets = ();
sub LogDestUDP($)
{
# connects to a DP server using rcon with log_dest_udp
my ($sock) = @_;
my $value = sprintf "%s:%d", $sock->sockhost(), $sock->sockport();
$sock->send("\377\377\377\377rcon $password log_dest_udp", 0)
or die "send rcon: $!";
alarm 15;
for(;;)
{
$sock->recv(my $response, 2048, 0)
or die "recv: $!";
if($response =~ /^\377\377\377\377n"log_dest_udp" is "(.*)" \[".*"\]\n$/s)
{
alarm 0;
my @dests = split /\s+/, $1;
return
if grep { $_ eq $value } @dests;
push @dests, $value;
$sock->send("\377\377\377\377rcon $password log_dest_udp \"@dests\"");
last;
}
}
alarm 0;
push @discosockets, [$sock, $value];
END
{
for(@discosockets)
{
my ($s, $v) = @$_;
# disconnects (makes the server stop send the data to us)
$s->send("\377\377\377\377rcon $password log_dest_udp", 0)
or die "send rcon: $!";
alarm 15;
for(;;)
{
$s->recv(my $response, 2048, 0)
or die "recv: $!";
if($response =~ /^\377\377\377\377n"log_dest_udp" is "(.*)" \[".*"\]\n$/s)
{
alarm 0;
my @dests = split /\s+/, $1;
return
if not grep { $_ eq $v } @dests;
@dests = grep { $_ ne $v } @dests;
$s->send("\377\377\377\377rcon $password log_dest_udp \"@dests\"");
last;
}
}
alarm 0;
}
}
}
sub sockaddr_readable($)
{
my ($binary) = @_;
my ($port, $addr) = sockaddr_in $binary;
return sprintf "%s:%d", inet_ntoa($addr), $port;
}
my $sock;
if(defined $bind)
{
# bind to a port and wait for any packets
$sock = IO::Socket::INET->new(Proto => 'udp', LocalAddr => $bind, LocalPort => 26000)
or die "socket: $!";
}
else
{
# connect to a DP server
$sock = IO::Socket::INET->new(Proto => 'udp', PeerAddr => $server, PeerPort => 26000)
or die "socket: $!";
LogDestUDP $sock;
}
my %currentmap = ();
my %bots = ();
LoadData();
while(my $addr = sockaddr_readable $sock->recv($_, 2048, 0))
{
$addr = $server
if not defined $bind;
s/^\377\377\377\377n//
or next;
for(split /\r?\n/, $_)
{
if(/^:gamestart:([^:]+):/)
{
StoreData();
$currentmap{$addr} = $1;
$bots{$addr} = {};
print "($addr) switching to $1\n";
next;
}
next
unless defined $currentmap{$addr};
if(/^:join:(\d+):bot:/)
{
$bots{$addr}{$1} = 1;
}
elsif(/^:kill:frag:(\d+):(\d+):type=(\d+):items=(\d+)([A-Z]*)(?:|(\d+)):victimitems=(\d+)([A-Z]*)(?:|(\d+))$/)
{
my ($a, $b, $type, $killweapon, $killflags, $killrunes, $victimweapon, $victimflags, $victimrules) = ($1, $2, $3, $4, $5, $6, $7, $8, $9);
next
if exists $bots{$addr}{$a} or exists $bots{$addr}{$b}; # only count REAL kills
$type &= 0xFF
if $type < 10000;
$killweapon = $type
if $stats->weaponid_valid($type); # if $type is not a weapon deathtype, count the weapon of the killer
$killweapon = 0
if not $stats->weaponid_valid($killweapon); # invalid weapon? that's 0 then
$victimweapon = 0
if not $stats->weaponid_valid($victimweapon); # dito
next
if $killflags =~ /S|I/ or $victimflags =~ /T/; # no strength, shield or typekills (these skew the statistics)
AddKill($addr, $currentmap{$addr}, $killweapon, $victimweapon, +1);
}
elsif(/^:kill:suicide:\d+:\d+:type=(\d+):items=(\d+)([A-Z]*)(?:|(\d+))$/)
{
my ($type, $killweapon, $killflags, $killrunes) = ($1, $2, $3, $4, $5, $6, $7);
$type &= 0xFF
if $type < 10000;
$killweapon = $type
if $stats->weaponid_valid($type);
$killweapon = 0
if not $stats->weaponid_valid($killweapon);
next
if $killflags =~ /S/; # no strength suicides (happen too easily accidentally)
AddKill($addr, $currentmap{$addr}, $killweapon, $killweapon, +1);
}
}
}