File Coverage

blib/lib/Games/Quakeworld/Query.pm
Criterion Covered Total %
statement 9 61 14.7
branch 0 18 0.0
condition 0 5 0.0
subroutine 3 10 30.0
pod 6 6 100.0
total 18 100 18.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2              
3             # Quakeworld Server Query 0.0.1
4             #
5             # A simple class for querying quakeworld servers.
6             # Quite simple to use; see the perlpod documentation for more info.
7              
8             # Copyright (c) 2003 Antoine Kalmbach. All rights reserved.
9             # This program is free software; you can redistribute it and/or modify it
10             # under the same terms as Perl itself.
11             # (Licensed under the Perl Artistic License, if you didn't get it)
12              
13             package Games::Quakeworld::Query;
14              
15             require 5.001;
16              
17 1     1   9142 use strict;
  1         2  
  1         49  
18 1     1   1382 use IO::Socket;
  1         39185  
  1         5  
19              
20 1     1   721 use vars qw($VERSION);
  1         8  
  1         954  
21              
22             $VERSION = "0.35";
23              
24             # Here's the different values for the server. These should be quite up to date, but who knows
25             # if the server software changes.
26              
27             our @vars = qw(teamplay map maxclients hostname admin spawn maxvip_spectators *version
28             *qwe_version watervis samelevel deathmatch url *gamedir timelimit maxspectators *progs);
29              
30             # Command to be sent to the server
31             my $cmd = "\377\377\377\377status\x00";
32              
33             # Game maps. Use if you want cool map names :)
34             my %maps = ( start => "Introduction",
35             e1m1 => "The Slipgate Complex",
36             e1m2 => "Castle of the Damned",
37             e1m3 => "The Necropolis",
38             e1m4 => "The Grisly Grotto",
39             e1m5 => "Gloom Keep",
40             e1m6 => "The Door to Cthon",
41             cthon => "The House of Cthon", # boss of episode one
42             e2m1 => "The Installation",
43             e2m2 => "The Ogre Citadel",
44             e2m3 => "Crypt of Decacy",
45             e2m4 => "The Ebon Fortress",
46             e2m5 => "The Wizards Manse",
47             e2m6 => "The Dismal Oubliette",
48             e3m1 => "Termination Central",
49             e3m2 => "The Vaults of Zin",
50             e3m3 => "The Tomb of Terror",
51             e3m4 => "Satan's Dark Delight",
52             e3m5 => "Wind tunnels",
53             e3m6 => "Chambers of Torment",
54             e4m1 => "The Sewage System",
55             e4m2 => "The Tower of Despair",
56             e4m3 => "The Elder god's Shrine",
57             e4m4 => "The Palace of Hate",
58             e4m5 => "Hell's Atrium",
59             e4m6 => "The Pain Maze",
60             e4m7 => "Azure Agony",
61             end => "Shub-Niggurath's Pit", # the final boss
62             # Some deathmatch maps
63             dm1 => "The Place of Two Deaths",
64             dm2 => "Claustrophobopolis",
65             dm3 => "The Abandoned Place",
66             dm4 => "The Bad Place", # my favourite :)
67             dm5 => "The Cistern",
68             dm6 => "The Dark Zone",
69             ztndm1 => "Smile, it get's worse",
70             ztndm2 => "Show No Mercy",
71             ztndm3 => "Blood Run",
72             ztndm4 => "The Steeler",
73             ztndm5 => "Painkiller",
74             ztndm6 => "The Vomitorium",
75             endif => "#endif",
76             );
77              
78             ######################################################################## CONSTRUCTOR #####
79             # Returns a hash with the server info.
80             # Somekind of a usage: my $QWS = ...->new(...); %info = $QWS->getinfo(); print %info{map}.
81             # Look for the values in the list above.
82              
83             sub new {
84 0     0 1   my $proto = shift;
85 0   0       my $class = ref($proto) || $proto;
86 0           my $self = {};
87 0           $self->{server} = shift;
88 0   0       $self->{port} = shift || 27500;
89 0           $self->{info} = {};
90 0           $self->{players} = undef;
91 0           $self->{failed} = undef;
92 0           bless $self, $class;
93 0           $self->_init($self->{server}, $self->{port});
94 0 0         if ($self->{failed}) {
95 0           return undef;
96             }
97 0           return $self;
98             }
99              
100              
101             # Looks up the server and puts the values in the hash info.
102             sub _init {
103 0     0     my $self = shift;
104 0           my $server = shift;
105 0           my $port = shift;
106              
107 0           my ($buffer, $recvd, $players, $info, @data, $key);
108              
109             # Create the socket
110 0 0         my $sock = IO::Socket::INET->new(Proto => "udp",
111             PeerAddr => $server,
112             PeerPort => $port,
113             Timeout => 5,)
114             or $self->{failed} = 1;
115            
116             # oops!
117 0 0         return undef if $self->{failed};
118              
119 0           $sock->autoflush(1);
120              
121             # send some stuff
122 0 0         $sock->syswrite($cmd, length($cmd)) or $self->{failed} = 1;
123 0 0         $recvd = $sock->sysread($buffer, 9000) or $self->{failed} = 1;
124 0           $buffer =~ s/\337//g; # strip the weird charachters
125              
126 0           my ($sinfo, @players) = split("\n", $buffer); # from \n starts the players. we don't care about that now.
127 0           @data = split("\\\\", $sinfo); # \\ is the delimiter
128 0           shift(@data); # shift some crap from the beginning
129              
130             # players
131 0           $self->{players} = @players;
132              
133             # set up the info
134 0           $key = 0;
135 0           foreach my $value (@data) {
136 0           foreach my $param (@vars) {
137 0 0         if ($value eq $param) {
138 0           $self->{info}{$param} = $data[$key+1];
139             }
140             }
141 0           $key++;
142             }
143             }
144              
145             # Returns the info, in a hash. OBSOLETED!
146             sub getinfo {
147 0     0 1   my $self = shift;
148 0 0         if (defined($self->{info})) {
149 0           return $self->{info};
150             }
151             else {
152 0           return undef;
153             }
154             }
155            
156              
157             # Just print the values
158             sub dumpinfo {
159 0     0 1   my $self = shift;
160 0           while (my ($p, $v) = each(%{$self->{info}})) {
  0            
161 0           print $p." => ".$v."\n";
162             }
163             }
164              
165             # Returns %info{shift}
166             sub get {
167 0     0 1   my $self = shift;
168 0           my $what = shift;
169 0           return $self->{info}{$what};
170             }
171              
172             # Returns the long name of a map
173             sub map_long {
174 0     0 1   my $self = shift;
175 0 0         return undef if !defined($self->{info}{map});
176 0           foreach my $map (keys %maps) {
177 0 0         if ($map eq $self->{info}{map}) {
178 0           return $maps{$map};
179             }
180             }
181 0           return undef;
182             }
183              
184              
185             sub players {
186 0     0 1   my $self = shift;
187 0           return $self->{players} - 1;
188             }
189              
190             __END__