File Coverage

blib/lib/WWW/BF2Player.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package WWW::BF2Player;
2              
3             our $VERSION = '0.01';
4              
5 1     1   24904 use XML::Simple;
  0            
  0            
6             use Data::Dumper;
7             use LWP::Simple;
8             use Hey::Common;
9             use Hey::Cache;
10              
11             =cut
12              
13             =head1 NAME
14              
15             WWW::BF2Player - Fetch information about game servers from BF2Player.com
16              
17             =head1 SYNOPSIS
18              
19             # example 1
20             use WWW::BF2Player;
21             my $bfp = WWW::BF2Player->new; # omitted UserId, must set it per-request
22             my $player = $bfp->getPlayer( UserId => '5307', PlayerId => '64246757' ); # userId specified per-request
23            
24             # example 2
25             use WWW::BF2Player;
26             my $bfp = WWW::BF2Player->new( UserId => '5307' ); # set a default UserId, can omit UserId per-request
27             my $player = $bfp->getPlayer( PlayerId => '64246757' ); # use the default UserId
28              
29             =head1 DESCRIPTION
30              
31             First, you must have an account (free) at BF2Player.com to make use of this module. Second, you must create and populate a buddy list on their site. You can only use this module to ask for information about players in your buddy list. This is a restriction on their part to prevent you from asking information for too many players. I guess to prevent you from competing and wasting their resources. Understandable, I suppose.
32              
33             =head2 new
34              
35             my $gm = WWW::BF2Player->new; # no options or defaults specified
36            
37             my $gm = WWW::BF2Player->new( UserId => '5307' ); # default to a certain UserId
38              
39             You can specify several options in the constructor.
40              
41             my $gm = WWW::BF2Player->new(
42             Expires => 300,
43             UserId => '5307',
44             CacheFile => 'my_player_cache.xml',
45             DebugLog => 'my_debug_log.txt',
46             DebugLevel => 3,
47             );
48              
49             =head3 Expires [optional]
50              
51             Sets the data cache freshness in seconds. If the cache has data older than this number of seconds, it is no longer valid. It's best that
52             you set this value to something higher than 1 minute and would be even better if you were satisfied with setting it around 5 minutes. If
53             the cache is fresh enough, it won't even ask the Game-Monitor.com server for any information. Keep in mind that Game-Monitor doesn't update
54             their information more than once every several minutes. It won't be useful for you to set the Expires value too low.
55              
56             =head3 UserId [optional]
57              
58             Sets the default UserId use. If you don't specify a UserId when asking for data, it will use this value instead. You have to specify it somewhere (here or per-request) or it won't work.
59              
60             =head3 CacheFile [optional]
61              
62             Sets the path and filename for the data cache. This is "bf2PlayerCache.xml" by default.
63              
64             =head3 DebugLog [optional]
65              
66             Sets the path and filename for the debug log. This is "bf2PlayerDebug.log" by default. To enable logging, you'll have to choose a DebugLevel
67             greater than zero (zero is default).
68              
69             =head3 DebugLevel [optional]
70              
71             Sets the level of debugging. The larger the number, the more verbose the logging. This is zero by default, which means no logging at all.
72              
73             =cut
74              
75             sub new {
76             my $class = shift;
77             my %options = @_;
78             my $self = {};
79             bless($self, $class); # class-ify it.
80              
81             $self->{fxn} = Hey::Common->new;
82              
83             $self->{debugLog} = $options{DebugLog} || 'bf2PlayerDebug.log';
84             $self->{debugLevel} = $options{DebugLevel} || 0;
85              
86             $self->{cache} = Hey::Cache->new(
87             Namespace => $options{Namespace} || $options{NameSpace} || 'WWW::BF2Player',
88             CacheFile => $options{CacheFile} || $options{StoreFile} || 'bf2PlayerCache.xml',
89             Expires => $options{Expires} || $options{Fresh} || 600,
90             );
91              
92             $self->{userId} = $options{UserId} || undef;
93              
94             $self->__debug(7, 'Object Attributes:', Dumper($self));
95              
96             return $self;
97             }
98              
99             sub __debug {
100             my $self = shift || return undef;
101             return undef unless $self->{debugLog}; # skip unless log file is defined
102             my $level = int(shift);
103             return undef unless $self->{debugLevel} >= $level; # skip unless log level is as high as this item
104             if (open(BF2PLAYERDEBUG, ">>$self->{debugLog}")) {
105             my $time = localtime();
106             foreach my $group (@_) { # roll through many items if they are passed in as an array
107             foreach my $line (split(/\r?\n/, $group)) { # roll through items that are multiline, converting to multiple separate lines
108             print BF2PLAYERDEBUG "[$time] $line\n";
109             }
110             }
111             close(BF2PLAYERDEBUG);
112             }
113             return undef;
114             }
115              
116             sub __fetchPlayerInfo {
117             my $self = shift || return undef;
118             my %options = @_;
119             my $userId = $options{UserId} || $self->{userId} || return undef; # if the UserId isn't defined, fail
120             my $playerId = $options{PlayerId} || return undef; # if the PlayerId isn't defined, fail
121              
122             my $cache = $self->{cache}->get( Name => $playerId ); # get data from cache
123             if ($cache) { # cache data exists for this host/port
124             $self->__debug(3, 'Cache data is fresh.');
125             return $cache if ($VERSION eq $cache->{client_version}); ## check the client version against the cache, in case the client (this code) has been upgraded, which might break the cache
126             }
127             else {
128             $self->__debug(2, 'Cache is not fresh or no data. Fetching from source.');
129             }
130              
131             my $url = qq(http://www.bf2player.com/index.php?page=xml&id=$userId&pid=$playerId); # format the url for the source
132             my $response = get($url); # fetch the info from the source
133             unless ($response) { # it failed (rejected, bad connection, etc)
134             $self->__debug(2, 'Could not fetch data from source.');
135             if ($store) {
136             $self->__debug(2, 'Going to provide stale store data instead of failing.');
137             return $self->{cache}->get( Name => $playerId, Expires => 99999999 ); # get data from cache with no expiration
138             }
139             else { # there is nothing to send back, fail
140             $self->__debug(3, 'There is no store data to return.');
141             return undef;
142             }
143             }
144             my $data = XMLin($response, KeyAttr => undef); # parse the xml into hashref
145              
146             $data->{client_version} = $VERSION;
147              
148             $self->{cache}->set( Name => $playerId, Value => $data ); # store it, baby!
149              
150             return $data;
151             }
152              
153             =cut
154              
155             =head2 getPlayerRaw
156              
157             my $player = $gm->getPlayerRaw( PlayerId => '64246757' ); # omitted UserId, use the UserId specified in the constructor
158             my $player = $gm->getPlayerRaw( UserId => '5307', PlayerId => '64246757' ); # specified UserId, use a different UserId
159              
160             This fetches the player data from the BF2Player.com server. It's returned as-is from the server, so the data isn't pretty, but it's technically accurate. If you just want the raw data, this is the function for you. If you want it prettified a bit, getPlayer might be a better fit. See also getPlayer.
161              
162             =head3 UserId [optional]
163              
164             If you specify it here, it overrides what was set in the constructor. If you didn't specify it in the constructor, it is required here.
165              
166             =head3 PlayerId [required]
167              
168             Which PlayerId to ask about. This is their official Battlefield 2 PID, not their username.
169              
170             =cut
171              
172             sub getPlayerRaw {
173             my $self = shift || return undef;
174             my %options = @_;
175             my $userId = $options{UserId} || $self->{userId} || return undef; # if the UserId isn't defined, get the default or fail
176             my $playerId = $options{PlayerId} || return undef; # if the PlayerId isn't defined, fail
177             my $data = $self->__fetchPlayerInfo( UserId => $userId, PlayerId => $playerId ); # fetch it!
178             return $data; # return the post-processed player info
179             }
180              
181             =cut
182              
183             =head2 getPlayer
184              
185             my $player = $gm->getPlayer( PlayerId => '64246757' ); # omitted UserId, use the UserId specified in the constructor
186             my $player = $gm->getPlayer( UserId => '5307', PlayerId => '64246757' ); # specified UserId, use a different UserId
187              
188             This is the same thing as getPlayerRaw, except it prettifies the returned data. See also getPlayerRaw.
189              
190             =head3 UserId [optional]
191              
192             If you specify it here, it overrides what was set in the constructor. If you didn't specify it in the constructor, it is required here.
193              
194             =head3 PlayerId [required]
195              
196             Which PlayerId to ask about. This is their official Battlefield 2 PID, not their username.
197              
198             =cut
199              
200             sub getPlayer {
201             my $self = shift || return undef;
202             my $data = $self->getPlayerRaw(@_); # fetch the data first
203              
204             my $key = {
205             army => {
206             0 => "USMC",
207             1 => "MEC",
208             2 => "China",
209             3 => "US Navy SEAL",
210             4 => "British SAS",
211             5 => "Russian Spetsnaz",
212             6 => "MEC SF",
213             7 => "Rebels",
214             8 => "Insurgent",
215             9 => "European Union",
216             avg => "Average",
217             total => "Total",
218             },
219             equipment => {
220             1 => "C4",
221             2 => "Claymore",
222             3 => "Hand Grenade",
223             5 => "AT Mine",
224             avgexp => "Avg Explosive",
225             totalexp => "Total Explosive",
226             6 => "Flashbang / Tear Gas",
227             7 => "Grappling Hook",
228             8 => "Zip Line",
229             avgtac => "Avg Tactical",
230             totaltac => "Total Tactical ",
231             0 => "Knife",
232             4 => "Defibrillator",
233             avgwea => "Avg Weapons",
234             totalwea => "Total Weapons",
235             },
236             expansion => {
237             0 => "Original BF2",
238             1 => "Special Forces",
239             2 => "European Union",
240             },
241             kit => {
242             0 => "Anti-tank",
243             1 => "Assault",
244             2 => "Engineer",
245             3 => "Medic",
246             6 => "Sniper",
247             4 => "Spec-Ops",
248             5 => "Support",
249             avg => "Average",
250             total => "Total",
251             },
252             map => {
253             101 => "Dalian Plant",
254             100 => "Daqing Oilfields",
255             102 => "Dragon Valley",
256             103 => "FuShe Pass",
257             6 => "Gulf of Oman",
258             0 => "Kubra Dam",
259             1 => "Mashtuur City",
260             2 => "Operation Clean Sweep",
261             5 => "Sharqi Peninsula",
262             105 => "Songhua Stalemate",
263             4 => "Strike at Karkand",
264             3 => "Zatar Wetlands",
265             601 => "Wake Island",
266             300 => "Devils Perch",
267             307 => "Ghost Town",
268             304 => "Leviathan",
269             305 => "Mass Destruction",
270             302 => "Night Flight",
271             306 => "Surge",
272             301 => "The Iron Gator",
273             303 => "Warlord",
274             110 => "Great Wall",
275             10 => "Operation Smoke Screen",
276             11 => "Taraba Quarry",
277             avg => "Average",
278             total => "Total",
279             },
280             rank => {
281             0 => "Private",
282             1 => "Private First Class",
283             2 => "Lance Corporal",
284             3 => "Corporal",
285             4 => "Sergeant",
286             5 => "Staff Sergeant",
287             6 => "Gunnery Sergeant",
288             7 => "Master Sergeant",
289             8 => "First Sergeant",
290             9 => "Master Gunnery Sergeant",
291             10 => "Sergeant Major",
292             11 => "Sergeant Major of the Corps",
293             12 => "Second Lieutenant",
294             13 => "First Lieutenant",
295             14 => "Captain",
296             15 => "Major",
297             16 => "Lieutenant Colonel",
298             17 => "Colonel",
299             18 => "Brigidier General",
300             19 => "Major General",
301             20 => "Lieutenant General",
302             21 => "General",
303             },
304             theater => {
305             0 => "USMC",
306             1 => "MEC",
307             2 => "China",
308             3 => "US Navy SEAL",
309             4 => "British SAS",
310             5 => "Russian Spetsnaz",
311             6 => "MEC SF",
312             7 => "Rebels",
313             8 => "Insurgent",
314             9 => "European Union",
315             },
316             unlock => {
317             11 => "DAO-12",
318             22 => "G3",
319             33 => "Jackhammer",
320             44 => "L85A1",
321             55 => "G36C",
322             66 => "PKM",
323             77 => "M95",
324             88 => "F2000",
325             99 => "MP7",
326             111 => "G36E",
327             222 => "SCAR-L",
328             333 => "MG36",
329             444 => "P90",
330             555 => "L96A1",
331             },
332             vehicle => {
333             0 => "Armor",
334             1 => "Aviator",
335             2 => "Air Defense",
336             6 => "Ground Defense",
337             3 => "Helicopter",
338             4 => "Transport",
339             avg => "Average",
340             total => "Total",
341             },
342             weapon => {
343             0 => "Assault Rifle",
344             6 => "AT/AA",
345             2 => "Carbines",
346             10 => "Defibrillator",
347             11 => "Explosives",
348             12 => "Grenade",
349             1 => "Grenade Launcher",
350             9 => "Knife",
351             3 => "Lt Machine Gun",
352             5 => "Pistol",
353             8 => "Shotgun",
354             4 => "Sniper Rifle",
355             7 => "Submachine Gun",
356             13 => "Zip Line",
357             avg => "Average",
358             total => "Total",
359             },
360             badge => {
361             1031120 => "Anti-Tank Combat",
362             1031119 => "Assault Combat",
363             1031105 => "Engineer Combat",
364             1031113 => "Medic Combat",
365             1031109 => "Sniper Combat",
366             1031115 => "Spec-Ops Combat",
367             1031121 => "Support Combat",
368             1032415 => "Explosives Ordinance",
369             1031406 => "Knife Combat",
370             1031619 => "Pistol Combat",
371             1190304 => "Command",
372             1190507 => "Engineer",
373             1190601 => "First Aid",
374             1191819 => "Resupply",
375             1220104 => "Air Defense",
376             1220118 => "Armor",
377             1220122 => "Aviator",
378             1031923 => "Ground Defense",
379             1220803 => "Helicopter",
380             1222016 => "Transport",
381             1261120 => "Anti-Tank Specialist",
382             1261119 => "Assault Specialist",
383             1261105 => "Engineer Specialist",
384             1261113 => "Medic Specialist",
385             1261109 => "Sniper Specialist",
386             1261115 => "Spec-Ops Specialist",
387             1261121 => "Support Specialist",
388             1260708 => "Grappling Hook",
389             1260602 => "Tactical Support Weaponry",
390             1262612 => "Zip Line Specialist",
391             },
392             medal => {
393             2051907 => "Gold Star",
394             2051919 => "Silver Star",
395             2051902 => "Bronze Star",
396             2191608 => "Purple Heart",
397             2020903 => "Combat Infantry",
398             2020913 => "Marksman Infantry",
399             2020919 => "Sharpshooter Infantry",
400             2021403 => "Navy Cross",
401             2020719 => "Golden Scimitar",
402             2021613 => "Peoples Medallion",
403             2190309 => "Air Combat",
404             2190318 => "Armor Combat",
405             2190303 => "Combat Action",
406             2020419 => "Distinguished Service",
407             2190703 => "Good Conduct",
408             2190308 => "Helicopter Combat",
409             2021322 => "Medal of Valor",
410             2191319 => "Meritorious Service",
411             2261919 => "British SAS Special Service",
412             2260914 => "Insurgent Forces Special Service",
413             2261303 => "MEC SF Special Service",
414             2261802 => "Rebels Special Service",
415             2261613 => "Russian Spetsnaz Special Service",
416             2261913 => "U.S. Navy SEAL Special Service",
417             2270521 => "European Union Special Service",
418             },
419             ribbon => {
420             3190105 => "Aerial Service",
421             3040109 => "Air Defense",
422             3240102 => "Airborne",
423             3190118 => "Armored Service",
424             3240301 => "Combat Action",
425             3190318 => "Crew Service",
426             3190409 => "Distinguished Service",
427             3190605 => "Far East Service",
428             3240703 => "Good Conduct",
429             3040718 => "Ground Defense",
430             3190803 => "Helicopter Service",
431             3150914 => "Infantry Officer",
432             3241213 => "Legion of Merit",
433             3211305 => "Meritorious Unit",
434             3191305 => "Mid-East Service",
435             3151920 => "Staff Officer",
436             3212201 => "Valorous Unit",
437             3242303 => "War College",
438             3260105 => "Aerial Specialist",
439             3260118 => "Armored Specialist",
440             3261901 => "British SAS Service",
441             3260318 => "Crew Specialist",
442             3260803 => "Helicopter Specialist",
443             3260914 => "Insurgent Forces Service",
444             3261319 => "MEC Special Forces Service",
445             3261805 => "Rebels Service",
446             3261819 => "Russian Spetsnaz Service",
447             3261919 => "U.S. Navy SEAL Service",
448             3270519 => "European Union Service",
449             },
450             };
451              
452             my $stats = {};
453             foreach my $stat (sort(keys(%{$data->{stats}}))) {
454             my $target = $stats;
455             my @split = split(/_/, $stat);
456             my $category = $split[0];
457             while (my $split = shift(@split)) {
458             $target->{$split} = $target->{$split} || {};
459             if (!$split[0] && $split[0] ne '0') {
460             $target->{$split} = $data->{stats}->{$stat};
461             }
462             if ($split =~ m|^\d+$|) {
463             if ($key->{$category}->{$split}) {
464             $target->{$split}->{name} = $key->{$category}->{$split};
465             }
466             }
467             $target = $target->{$split};
468             }
469             }
470              
471             $data->{stats} = $stats; # overwrite the ugly with the pretty
472              
473             return $data; # return the post-processed player info
474             }
475              
476             =cut
477              
478             =head1 AUTHOR
479              
480             Dusty Wilson, Ewww-bf2player-module@dusty.hey.nuE
481              
482             =head1 COPYRIGHT AND LICENSE
483              
484             Copyright (C) 2006 by Dusty Wilson Ehttp://dusty.hey.nu/E
485              
486             This library is free software; you can redistribute it and/or modify
487             it under the same terms as Perl itself, either Perl version 5.8.8 or,
488             at your option, any later version of Perl 5 you may have available.
489              
490             =cut
491              
492             1;