Evan S Kaufman

NAME

Games::FEAR::Log - Log analysis tool for F.E.A.R. dedicated servers

VERSION

Version 0.02

SYNOPSIS

    use Games::FEAR::Log;
    
    # instantiate new object, passing a hash reference of options
    my $log_obj = Games::FEAR::Log->new( {
      # database information: a dsn, username, and password
      -dbi => [ 
                'DBI:mysql:database=scoreboard;host=localhost;port=3306',
                'scoreboard_admin',
                'scoreboard_password'
              ],
      # table to store info
      -table => 'deathmatch1',
      # create table if it doesnt exist
      -create => ,
      # full path to logfile
      -logfile => '/var/log/FEAR/mp_scores.log',
      # empty the source logfile after reading it
      -truncate => 1,
      # delete any records older than 30 days
      -history => '30d'
    } );
    
    # process log file, importing new entries
    $log_obj->process() or die 'processing failed';
    
    # get ID of first user
    my @uids = $log_obj->get_uids();
    
    # get playernames this user goes by
    my @names = $log_obj->get_playernames( $uid[0] );
    
    # get stats for this user
    my $stats = $log_obj->get_stats( $uid[0] );
    
    # get history for this user
    my $history = $log_obj->get_history( $uid[0] );
    my @gametimes = keys %{$history};
    
    # get information for a game played by said user
    my $game = $log_obj->get_game( $gametimes[0] );
    
    # get scoreboard-structured informatuon
    my @scores = build_scoreboard('player', 'asc');

DESCRIPTION

This module allows the parsing of a F.E.A.R. multiplayer server log into a manageable database format, and provides an easy to use object-oriented interface to access that information. This information could then be used to create a CGI scoreboard application, such as the one included in the /examples directory.

The underlying system uses a SQL relational database to store and retrieve game information. Initially, this implimentation is built to use a MySQL or PostgreSQL database, but I can add support for other database systems if there is a demand.

Ideally, there could be two different 'pieces' to an application using this module, an administrative interface to import new log entries into the database, and a public interface to display and/or cross-reference already imported information.

If performance is not a concern, however, it could be a one-piece application where new entries are checked for and added every time the interface is viewed.

METHODS

new()

Creates and returns a new object. Takes a single argument, a hash reference containing configuration options. The available options are as follows:

  • -dbi

    An anonymous array of a DSN (data source name), username, and password for connecting to the database. See the DBI docs for an explanation and syntax of a DSN. An error will be thrown if this option is not found or invalid.

        [ 'DBI:mysql:database=test;host=localhost', 'devuser', 'devpass' ]
  • -table

    Name of the database table to use for this set of statistics. If stats are being kept for multiple game servers, each one should have its own seperate table. An error will be thrown if this option is not found or invalid.

  • -create

    Indicate whether the given table should be created if it does not already exist. A true value creates the table if necessary, while a false value throws an error if the table doesnt exist. The default is to create the table.

  • -logfile

    Source of the log entries. This is not a required parameter unless you plan on calling the process method. If a scalar value is passed, it is assumed to be a filename. If a scalar reference is passed, it is assumed to be the contents of the log, and will be dereferenced and processed. If a glob reference is passed, it is assumed to be an open filehandle to the logfile (note that it should be opened for read and write operations).

  • -history

    Length of time to keep records, specified in a format similar to that used by the CGI module, with a numeric quantity followed by a one-letter unit indicator:

        86400s  # 1 day specified in seconds
        1440m   # 1 day specified in minutes
        24h     # 1 day specified in hours
        90d     # 3 months specified in days
        12M     # 1 year specified in months
        2y      # 2 years specified in years

    The default is to keep them forever, and this can be specified by passing an empty or undefined scalar, or a value of zero.

  • -truncate

    Indicate whether the source log should be truncated to zero bytes (and in effect emptied). This is useful if you are reading from a live log file and don't want to waste resources reprocessing old log entries. Note, of course, that if a logfile is already locked by the server process, any attempted writes to it will fail. A non-zero value turns on log truncating, and a zero value turns it off. The default is off.

process()

Truncates log file if the truncate option is set to true, deletes expired records if history option is specified, and processes any new entries. Returns 1 on success. If a logfile option is not specified, an error will be thrown.

    $log_obj->process();

get_uids()

Returns an array of all unique UIDs found in the current database table. See the JARGON section for an explanation of UIDs in the FEAR server logs.

    @uids = $log_obj->get_uids();

get_playernames(UID)

Returns an array of all unique playernames found for the given UID. They are ordered by frequency of use.

    @names = $log_obj->get_playernames($uid);

get_stats(UID)

Returns a hash reference containing averaged and totalled stats for the given UID. The data structure returned is as follows:

    {
      game_count    => $game_count,
      tot_score     => $tot_score,
      avg_score     => $avg_score,
      tot_kills     => $tot_kills,
      avg_kills     => $avg_kills,
      tot_deaths    => $tot_deaths,
      avg_deaths    => $avg_deaths,
      tot_suicides  => $tot_suicides,
      avg_suicides  => $avg_suicides,
      tot_teamkills => $tot_teamkills,
      avg_teamkills => $avg_teamkills,
      tot_objective => $tot_objective,
      avg_objective => $avg_objective,
    }

get_history(UID)

Returns a hashref of hashrefs of games played by the given UID, each keyed to the game time. Note that gametime is a unix timestamp as would be returned by the time() builtin. The data structure returned is as follows:

    {
      $gametime => {
        gametime  => $gametime,
        team      => $team,
        player    => $player,
        score     => $score,
        kills     => $kills,
        deaths    => $deaths,
        teamkills => $teamkills,
        suicides  => $suicides,
        objective => $objective,
      },
    }

get_game(GAMETIME)

Returns a hashref of hashrefs of players in the game at the given game time, each keyed to a UID. Note that gametime is a unix timestamp as would be returned by the time() builtin. The data structure returned is as follows:

    {
      $uid => {
        uid       => $uid,
        team      => $team,
        player    => $player,
        score     => $score,
        kills     => $kills,
        deaths    => $deaths,
        teamkills => $teamkills,
        suicides  => $suicides,
        objective => $objective,
      }
    }

build_scoreboard(OFFSET,LENGTH)

Returns an array of hashrefs ideal for displaying a summary scoreboard. Takes two optional arguments:

  • OFFSET

    How many records into the start of a resultset to begin retrieving results, akin to a SQL OFFSET clause. The default is 0.

  • LENGTH

    How many records to retrieve from a resultset, akin to a SQL LIMIT clause. The default is 0 which is interpreted as 'no limit'.

The data structure returned is as follows:

    (
      {
        uid           => $uid,
        player        => $player,
        avg_score     => $avg_score,
        avg_kills     => $avg_kills,
        avg_deaths    => $avg_deaths,
        avg_suicides  => $avg_suicides,
        avg_teamkills => $avg_teamkills,
        avg_objective => $avg_objective,
        tot_score     => $tot_score,
        tot_kills     => $tot_kills,
        tot_deaths    => $tot_deaths,
        tot_suicides  => $tot_suicides,
        tot_teamkills => $tot_teamkills,
        tot_objective => $tot_objective,
      }
    )

DESTROY()

The class destructor that, when called, closes the database connection and any open filehandles, and destroys the object.

FUNCTIONS

supported_dbds()

Returns a list of the currently supported DBI drivers. This function can be called from an instantiated object, or directly.

    # called from an object
    @drivers = $log_object->supported_dbds();
    
    # called directly from the module namespace
    @drivers = Games::FEAR::Log::supported_dbds();

JARGON

Here, a few of the terms used throughout this documentation are briefly defined.

UID

The UID found in the FEAR multiplayer log is used to uniquely identify a user. It is calculated as a hexadecimal MD5 hash of their CD key. For example, the UID for a CD key of ABCD-EFGH-IJKL-MNOP-QRST would be f00eeddcb4a079de173b673a3d45fcfc.

Player Name

The player name is, as it suggests, a name picked by the user and is how they appear in-game. It is not suitable for tracking statistics since it can be changed at the user's discretion, so we use the UID for that purpose.

Game Time

The game time is a timestamp of precisely when a specific game ended. By matching up different players with the same game times, you can determine the participants of any specific game.

DEPENDANCIES

Test::More - Used by the test suite during make test

DBI - Used for database connectivity

File::Copy - Used to copy log during processing

File::Temp - Used to create temp file during processing

AUTHOR

Evan Kaufman, <evank at cpan.org>

BUGS

Please report any bugs or feature requests to bug-games-fear-log at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Games-FEAR-Log. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

ACKNOWLEDGEMENTS

The fine folks at PerlMonks.org, always willing to lend a helping hand to a struggling programmer.

COPYRIGHT

Copyright 2007 Evan Kaufman, all rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.