BZFlag-Info

 view release on metacpan or  search on metacpan

Info.pm  view on Meta::CPAN

package BZFlag::Info;

use 5.006001;
use strict;
use warnings;

use LWP::UserAgent;
use Socket;

our $VERSION = '1.9.2';

sub new {
    my $self = { };
    bless $self, "BZFlag::Info";
    return $self;
}

sub serverlist(%) {
    my $self = shift;
    
    my %options;
    while (my @option = splice(@_, 0, 2)) {
	$options{$option[0]} = $option[1];
    }
    
    my $proxy = $options{Proxy};
    my $response;
    my $ua = new LWP::UserAgent;
    $ua->proxy('http', $proxy) if defined($proxy);
    
    $ua->timeout(10);
    
    my $req = HTTP::Request->new('GET', ($options{Server} ? $options{Server} : $self->listserver));
    my $res = $ua->request($req);
    my $totalServers = 0;
    my $totalPlayers = 0;
    for my $line (split("\n",$res->content)) {
	my ($serverport, $version, $flags, $ip, $description) = split(" ",$line,5);
	
	my @fields = ('style','maxShots','shakeWins','shakeTimeout','maxPlayerScore',
	    'maxTeamScore','maxTime','maxPlayers','rogueSize','rogueMax','redSize',
	    'redMax','greenSize','greenMax','blueSize','blueMax','purpleSize',
	    'purpleMax','observerSize','observerMax'
	    );

	my @info = unpack("(A4)7 (A2)13", $flags);

	my $counter = 0;
	my %info;

	foreach (@fields) {
	    $info{$_} = oct('0x'.$info[$counter]);
	    $counter++;
	}
	
	my $playerSize = $info{rogueSize} + $info{redSize} + $info{greenSize}
	    + $info{blueSize} + $info{purpleSize} + $info{observerSize};
	
	unless ($serverport =~ m/.*:\d+/) {
	    $serverport = "$serverport:5154";
	}
	
	$response->{servers}->{$serverport}->{version}     = $version;
	$response->{servers}->{$serverport}->{ip}          = $ip;
	$response->{servers}->{$serverport}->{description} = $description;
	
	$response->{servers}->{$serverport}->{numplayers}   = $playerSize;
	$response->{servers}->{$serverport}->{roguesize}    = $info{rogueSize};
	$response->{servers}->{$serverport}->{redsize}      = $info{redSize};
	$response->{servers}->{$serverport}->{greensize}    = $info{greenSize};
	$response->{servers}->{$serverport}->{bluesize}     = $info{blueSize};
	$response->{servers}->{$serverport}->{purplesize}   = $info{purpleSize};
	$response->{servers}->{$serverport}->{observersize} = $info{observerSize};

	$response->{servers}->{$serverport}->{serverconfig}->{style}          = $self->parsestyle($info{style});
	$response->{servers}->{$serverport}->{serverconfig}->{maxshots}       = $info{maxShots};
	$response->{servers}->{$serverport}->{serverconfig}->{shakewins}      = $info{shakeWins};
	$response->{servers}->{$serverport}->{serverconfig}->{shaketimeout}   = $info{shakeTimeout} / 10;
	$response->{servers}->{$serverport}->{serverconfig}->{maxplayerscore} = $info{maxPlayerScore};
	$response->{servers}->{$serverport}->{serverconfig}->{maxteamscore}   = $info{maxTeamScore};
	$response->{servers}->{$serverport}->{serverconfig}->{maxtime}        = $info{maxTime};
	$response->{servers}->{$serverport}->{serverconfig}->{maxplayers}     = $info{maxPlayers};
	$response->{servers}->{$serverport}->{serverconfig}->{roguemax}       = $info{rogueMax};
	$response->{servers}->{$serverport}->{serverconfig}->{redmax}         = $info{redMax};
	$response->{servers}->{$serverport}->{serverconfig}->{greenmax}       = $info{greenMax};
	$response->{servers}->{$serverport}->{serverconfig}->{bluemax}        = $info{blueMax};
	$response->{servers}->{$serverport}->{serverconfig}->{purplemax}      = $info{purpleMax};
	$response->{servers}->{$serverport}->{serverconfig}->{observermax}    = $info{observerMax};

	$totalServers += 1;
	$totalPlayers += $playerSize;
    }
    $response->{totalservers} = $totalServers;
    $response->{totalplayers} = $totalPlayers;

    return ($response);

}    

sub queryserver(%) {
    my $self = shift;

    my %options;
    while (my @option = splice(@_, 0, 2)) {
	$options{$option[0]} = $option[1];
    }

    my $hostandport = $options{Server};
    my $timeout = $options{Timeout};

    my @teamName = ("X", "R", "G", "B", "P", "O", "H");
#    my @teamName     = ("X", "R", "G", "B", "P");
    my ($message, $server);
    my $response;
    my ($servername, $port) = split(/:/, $hostandport);
    $port = 5154 unless $port;
    
    # socket define
    my $sockaddr = 'S n a4 x8';
    
    # port to port number
    my ($name,$aliases,$proto) = getprotobyname('tcp');
    ($name,$aliases,$port)  = getservbyname($port,'tcp') unless $port =~ /^\d+$/;
    
    # get server address
    my ($type,$len,$serveraddr);
    ($name,$aliases,$type,$len,$serveraddr) = gethostbyname($servername);
    $server = pack($sockaddr, AF_INET, $port, $serveraddr);
    
    # connect
    unless (socket(S, AF_INET, SOCK_STREAM, $proto)) {
	$self->{error} = 'errSocketError';
	return undef;
    }

    unless (connect(S, $server)) {
	$self->{error} = "errCouldNotConnect: $servername:$port";
	return undef;
    }
    
    # don't buffer
    select(S); $| = 1; select(STDOUT);
    
    # get hello
    my $buffer;
    unless (read(S, $buffer, 9) == 9) {
	$self->{error} = 'errReadError';
	return undef;
    }

    # parse reply
    my ($magic, $version, $id) = unpack("a4 a4 C1", $buffer);
    
    # quit if version isn't valid
    if ($magic ne "BZFS") {
	$self->{error} = 'errNotABzflagServer';
	return undef;
    }

    # try incompatible for 1.7, etc.
    if ($version != '1910') {
	$self->{error} = 'errIncompatibleVersion';
	return undef;
    }
    
    # send game request
    print S pack("n2", 0, 0x7167);
    
    # get reply
    unless (read(S, $buffer, 40) == 40) {
	$self->{error} = 'errServerReadError';
	return undef;
    }

    my ($infolen,$infocode,$style,$maxPlayers,$maxShots,
	$rogueSize,$redSize,$greenSize,$blueSize,$purpleSize,
	$rogueMax,$redMax,$greenMax,$blueMax,$purpleMax,
	$shakeWins,$shakeTimeout,
	$maxPlayerScore,$maxTeamScore,$maxTime) = unpack("n20", $buffer);

    unless ($infocode == 0x7167) {
	$self->{error} = 'errBadServerData';
	return undef;
    }

    $response->{serverconfig}->{style} = $self->parsestyle($style);

    $response->{serverconfig}->{maxplayers} = $maxPlayers;
    $response->{serverconfig}->{maxshots} = $maxShots;
    $response->{serverconfig}->{roguemax} = $rogueMax;
    $response->{serverconfig}->{redmax} = $redMax;
    $response->{serverconfig}->{greenmax} = $greenMax;
    $response->{serverconfig}->{bluemax} = $blueMax;
    $response->{serverconfig}->{purplemax} = $purpleMax;
    $response->{serverconfig}->{shakewins} = $shakeWins;
    $response->{serverconfig}->{shaketimeout} = $shakeTimeout;
    $response->{serverconfig}->{maxplayerscore} = $maxPlayerScore;
    $response->{serverconfig}->{maxteamscore} = $maxTeamScore;
    $response->{serverconfig}->{maxtime} = $maxTime;

    # send players request
    print S pack("n2", 0, 0x7170);
    
    # get number of teams and players we'll be receiving
    unless (read(S, $buffer, 8) == 8) {
	$self->{error} = 'errCountReadError';
	return undef;
    }

    my ($countlen,$countcode,$numTeams,$numPlayers) = unpack("n4", $buffer);
    unless ($countcode == 0x7170) {
	$self->{error} = 'errBadCountData';
	return undef;
    }

    $response->{numplayers} = $numPlayers;

    unless (read(S, $buffer, 5) == 5) {
	$self->{error} = 'errCountReadError';
	return undef;
    }

    my ($countlen2, $countcode2, $numTeams2) = unpack("n2 C", $buffer);
    unless ($countcode2 == 0x7475) {
	$self->{error} = 'errBadCountData';
        return undef;
    }

    $response->{numteams} = $numTeams2;

    # get the teams
    for (1..$numTeams2) {
	unless (read(S, $buffer, 8) == 8) {
	    $self->{error} = 'errTeamReadError';
	    return undef;
	}

	my ($team, $size, $wins, $losses) = unpack("n4", $buffer);

	my $score = $wins - $losses;

	$response->{teams}->{$teamName[$team]}->{size}   = $size;
	$response->{teams}->{$teamName[$team]}->{score}  = $score;
	$response->{teams}->{$teamName[$team]}->{wins}   = $wins;
	$response->{teams}->{$teamName[$team]}->{losses} = $losses;
	
    }
    
    # get the players
    for (1..$numPlayers) {
	next unless (read(S, $buffer, 175) == 175);
	my ($len, $code, $pID, $type, $team, $wins, $losses, $tks, $callsign, $email) = 
	    unpack("n2 C n5 A32 A128", $buffer);

	unless ($code == 0x6170) {
	    $self->{error} = 'errBadPlayerData';



( run in 2.912 seconds using v1.01-cache-2.11-cpan-c966e8aa7e8 )