aboutsummaryrefslogtreecommitdiff
path: root/lib/MasterServer/Core
diff options
context:
space:
mode:
Diffstat (limited to 'lib/MasterServer/Core')
-rwxr-xr-xlib/MasterServer/Core/Core.pm120
-rwxr-xr-xlib/MasterServer/Core/LoadConfig.pm108
-rwxr-xr-xlib/MasterServer/Core/Logging.pm51
-rwxr-xr-xlib/MasterServer/Core/Schedulers.pm65
-rwxr-xr-xlib/MasterServer/Core/Secure.pm72
-rwxr-xr-xlib/MasterServer/Core/Stats.pm48
-rwxr-xr-xlib/MasterServer/Core/Util.pm20
-rwxr-xr-xlib/MasterServer/Core/Version.pm6
8 files changed, 187 insertions, 303 deletions
diff --git a/lib/MasterServer/Core/Core.pm b/lib/MasterServer/Core/Core.pm
index 4d1e47b..db04344 100755
--- a/lib/MasterServer/Core/Core.pm
+++ b/lib/MasterServer/Core/Core.pm
@@ -5,32 +5,20 @@ use warnings;
use AnyEvent;
use Exporter 'import';
use DBI;
-$|++;
-
our @EXPORT = qw | halt select_database_type main |;
################################################################################
## Handle shutting down the program in case a fatal error occurs.
+## clear all other timers, network servers, etc
################################################################################
sub halt {
my $self = shift;
-
- # log shutdown
- $self->log("stop", "Stopping the masterserver.");
-
- # clear all other timers, network servers, etc
+ $self->log("stop", "stopping the masterserver!");
$self->{dbh}->disconnect() if (defined $self->{dbh});
$self->{dbh} = undef;
$self->{scope} = undef;
-
- # and send signal to condition var to let the loops end
$self->{must_halt}->send;
-
- # log halt
- $self->log("stop", "Shutting down NOW!");
-
- # time for a beer.
- exit;
+ exit; # time for a beer.
}
################################################################################
@@ -39,28 +27,17 @@ sub halt {
################################################################################
sub select_database_type {
my $self = shift;
-
- # read from login
- my @db_type = split(':', $self->{dblogin}->[0]);
+ my @db_type = split(':', $self->{dblogin}->[0]); # from config file
# format supported?
if ( "Pg SQLite mysql" =~ m/$db_type[1]/i) {
-
- # inform us what DB we try to load
- $self->log("debug","Loading $db_type[1] database module.");
-
- # load dbd and tables/queries for this db type
+ # load database for this type
MasterServer::load_recursive("MasterServer::Database::$db_type[1]");
-
- # Connect to database
$self->{dbh} = $self->database_login();
-
- # and test whether we succeeded.
$self->halt() unless (defined $self->{dbh});
}
- else {
- # raise error and halt
- $self->log("fatal", "The masterserver could not determine the chosen database type.");
+ else { # we can not continue without database
+ $self->log("fatal", "the masterserver could not determine the chosen database type");
$self->halt();
}
}
@@ -70,88 +47,47 @@ sub select_database_type {
################################################################################
sub main {
my $self = shift;
-
- # condition var prevents or allows the program from ending
$self->{must_halt} = AnyEvent->condvar;
-
- # load version info
$self->version();
- # print startup
+ # startup
print "Running 333networks Master Server Application...\n";
-
- # keep several objects alive outside their original scope
- $self->{scope} = ();
-
- # startup procedure information
- $self->log("info", "");
- $self->log("info", "");
$self->log("info", "333networks Master Server Application.");
- $self->log("info", "Hostname: $self->{masterserver_hostname}");
- $self->log("info", "Build: $self->{build_type}");
- $self->log("info", "Version: $self->{build_version}");
- $self->log("info", "Author: $self->{build_author}");
- $self->log("info", "Logs: $self->{log_dir}");
+ $self->log("info", "hostname: $self->{masterserver_hostname}");
+ $self->log("info", "build: $self->{build_type}");
+ $self->log("info", "version: $self->{build_version}");
+ $self->log("info", "author: $self->{build_author}");
+ $self->log("info", "logs: $self->{log_dir}");
- # determine the type of database and load the appropriate module
+ # load database and set up scope for timers/network
$self->select_database_type();
-
- ###
- #
- # execute necessary tasks for running the masterserver
- #
- ###
-
+ $self->{scope} = ();
+
# load the list with ciphers from the config file if no ciphers were detected
- # update manually with util/tools/db_load_ciphers.pl
- # then unload the game variables from masterserver memory
$self->load_ciphers() unless $self->check_cipher_count();
$self->{game} = undef;
- # (re)load the list with masterservers and master applets from config
- # does not clear out old entries, but resets "last_updated" to now
+ # reload the list with masterservers and master applets from config
$self->load_sync_masters();
$self->load_applet_masters();
- # set first run flag to avoid ignoring/deleting servers after downtime
+ # first run flag for all startup actions
$self->{firstrun} = undef;
$self->{firstruntime} = time;
- ###
- #
- # activate all schedulers and functions
- #
- ###
-
- #
- # Timers
- #
- # tasks that are executed once or twice per hour
- $self->{scope}->{long_periodic_tasks} = $self->long_periodic_tasks();
- #
- # tasks that are executed every few minutes
- $self->{scope}->{short_periodic_tasks} = $self->short_periodic_tasks();
- #
- # tasks that are executed every few milliseconds
- $self->{scope}->{udp_ticker} = $self->udp_ticker();
-
- #
- # Network listeners
- #
- # start the listening service (listen for UDP beacons)
+ # beacons and serverlists (listen for UDP beacons / TCP requests)
$self->{scope}->{beacon_catcher} = $self->beacon_catcher();
- #
- # provide server lists to clients with the browser host server
$self->{scope}->{browser_host} = $self->browser_host();
-
- ###
- #
+
+ # recurring tasks (sync and updates)
+ $self->{scope}->{long_periodic_tasks} = $self->long_periodic_tasks();
+ $self->{scope}->{short_periodic_tasks} = $self->short_periodic_tasks();
+
+ # verify and update server status
+ $self->{scope}->{udp_ticker} = $self->udp_ticker() if $self->{beacon_checker_enabled};
+
# all modules loaded. Running...
- #
- ###
- $self->log("info", "All modules loaded. Masterserver is now running.");
-
- # prevent main program from ending as long as no fatal errors occur
+ $self->log("info", "all modules loaded. Masterserver is now running!");
$self->{must_halt}->recv;
}
diff --git a/lib/MasterServer/Core/LoadConfig.pm b/lib/MasterServer/Core/LoadConfig.pm
index e209848..f0d0671 100755
--- a/lib/MasterServer/Core/LoadConfig.pm
+++ b/lib/MasterServer/Core/LoadConfig.pm
@@ -2,11 +2,9 @@ package MasterServer::Core::LoadConfig;
use strict;
use warnings;
-use AnyEvent;
+use DBI;
use POSIX qw/strftime/;
use Exporter 'import';
-use DBI;
-
our @EXPORT = qw | load_applet_masters
load_sync_masters
add_sync_master |;
@@ -17,53 +15,33 @@ our @EXPORT = qw | load_applet_masters
sub load_applet_masters {
my $self = shift;
- # loop through config entries
+ # iterate through all games per entry
foreach my $master_applet (@{$self->{master_applet}}) {
- # master_applet contains
- # address --> domain
- # port --> tcp port
- # games --> array of gamenames
-
- # iterate through all games per entry
for my $gamename (@{$master_applet->{games}}) {
# resolve domain names
my $applet_ip = $self->host2ip($master_applet->{address});
# check if all credentials are valid
- if ($applet_ip &&
- $master_applet->{port} &&
- $gamename)
- {
- # add to database
+ if ($applet_ip && $master_applet->{port} && $gamename) {
$self->add_master_applet(
ip => $applet_ip,
- port => $master_applet->{port},
+ hostport => $master_applet->{port},
gamename => $gamename,
);
-
- #log
$self->log("add", "added applet $master_applet->{address}:$master_applet->{port} for $gamename");
-
} # else: insufficient info available
- else {
- $self->log("fail", "Could not add master applet: ".
- ($applet_ip || "unknown ip"). ", ".
- ($master_applet->{port} || "0"). ", ".
- ($gamename || "game"). "."
- );
- }
+ else { $self->log("fail", "could not add master applet: ".
+ ($applet_ip || "unknown ip"). ", ".
+ ($master_applet->{port} || "0"). ", ".
+ ($gamename || "game"));}
} # end gamename
} # end master_applet
- # reset added/updated time to last current time
- $self->reset_master_applets();
-
- # clear out the original variable, we don't use it anymore
+ # reset added/updated time clear the applet list from memory
+ $self->reset_master_applets;
$self->{master_applet} = ();
-
- # report
- $self->log("info", "Applet database successfully updated!");
+ $self->log("info", "applet database successfully updated");
}
@@ -76,18 +54,13 @@ sub load_applet_masters {
sub load_sync_masters {
my $self = shift;
- # loop through config entries
+ # add config entries to database
foreach my $sync_host (@{$self->{sync_masters}}) {
+ $self->add_sync_master($sync_host);}
- # add them to database
- $self->add_sync_master($sync_host);
- }
-
- # clear out the original variable, we don't use it anymore
+ # clear list from memory
$self->{sync_masters} = ();
-
- # report
- $self->log("info", "Sync server database successfully updated!");
+ $self->log("info", "sync server database successfully updated");
}
@@ -97,53 +70,18 @@ sub load_sync_masters {
################################################################################
sub add_sync_master {
my ($self, $sync_host) = @_;
-
- # sync_host contains
- # address --> domain
- # port --> tcp port
- # beacon --> udp port
-
- # resolve domain names
my $sync_ip = $self->host2ip($sync_host->{address});
# check if all credentials are valid
- if ($sync_ip &&
- $sync_host->{beacon} &&
- $sync_host->{port})
- {
- # select sync master from serverlist
- my $entry = $self->get_server(ip => $sync_ip,
- port => $sync_host->{beacon})->[0];
-
- # was found, update the entry
- if (defined $entry) {
- # update the serverlist with
- my $sa = $self->update_server_list(
- ip => $sync_ip,
- port => $sync_host->{beacon},
- hostport => $sync_host->{port},
- gamename => "333networks",
- );
- }
- # was not found, insert clean entry
- else {
- my $sa = $self->add_server_list(
- ip => $sync_ip,
- port => $sync_host->{beacon},
- hostport => $sync_host->{port},
- gamename => "333networks",
- );
-
- #log
- $self->log("add", "added sync $sync_host->{address}:$sync_host->{port},$sync_host->{beacon}");
- }
+ if ($sync_ip && $sync_host->{beacon}) {
+ # add it to the pending list so it gets picked up with the "normal" status update
+ $self->insert_pending(ip => $sync_ip, port => $sync_host->{beacon});
+ $self->log("add", "added sync $sync_host->{address}:$sync_host->{beacon}");
} # else: insufficient info available
- else {
- $self->log("fail", "Could not add sync master: ".
- ($sync_ip || "ip"). ", ".
- ($sync_host->{beacon} || "0"). ", ".
- ($sync_host->{port} || "0"). "."
- );
+ else { $self->log("fail", "failed to add sync master: ".
+ ($sync_host->{address}|| "domain"). ", ".
+ ($sync_ip || "invalid ip"). ", ".
+ ($sync_host->{beacon} || "invalid beacon port") );
}
}
diff --git a/lib/MasterServer/Core/Logging.pm b/lib/MasterServer/Core/Logging.pm
index 416a97f..d2094ed 100755
--- a/lib/MasterServer/Core/Logging.pm
+++ b/lib/MasterServer/Core/Logging.pm
@@ -5,9 +5,8 @@ use warnings;
use Switch;
use POSIX qw/strftime/;
use Exporter 'import';
-$|++;
-
our @EXPORT = qw| log error |;
+$|++;
################################################################################
## Split up errors in multiple log types for suppressing
@@ -16,33 +15,18 @@ our @EXPORT = qw| log error |;
sub error {
my ($self, $error, $instigator) = @_;
- # which error?
+ # which one?
switch ($error) {
-
# connection timed out
- case m/Connection timed out/i {
- $self->log("timeout", "on $instigator.");
- }
-
+ case m/Connection timed out/i {$self->log("timeout", "on $instigator.");}
# connection reset by peer
- case m/Connection reset by peer/i {
- $self->log("reset", "on $instigator.");
- }
-
+ case m/Connection reset by peer/i {$self->log("reset", "on $instigator.");}
# connection refused
- case m/Connection refused/i {
- $self->log("refused", "on $instigator.");
- }
-
+ case m/Connection refused/i {$self->log("refused", "on $instigator.");}
# no such device or address
- case m/No such device or address/i {
- $self->log("nodevice", "on $instigator.");
- }
-
+ case m/No such device or address/i {$self->log("nodevice", "on $instigator.");}
# if all else fails
- else {
- $self->log("error", "$error on $instigator.");
- }
+ else {$self->log("error", "$error on $instigator.");}
}
}
@@ -54,7 +38,8 @@ sub log {
my ($self, $type, $msg) = @_;
# is the message suppressed in config?
- return if (defined $type && $self->{suppress} =~ m/$type/i);
+ return if ($self->{suppress} =~ m/$type/i);
+ $type = "unknown" unless $type;
# parse time of log entry and prep for rotating log
my $time = strftime('%Y-%m-%d %H:%M:%S',localtime);
@@ -71,11 +56,8 @@ sub log {
# put log filename together
my $logfile = $self->{log_dir}.((substr($self->{log_dir},-1) eq "/")?"":"/").$f;
-
- # print to stdout if enabled
- print "[$time]\t[$type]\t$msg\n" if $self->{printlog};
- # temporarily disable the warnings-to-log, to avoid infinite recursion if
+ # temporarily disable the warnings-to-log, to avoid recursion if
# this function throws a warning.
my $old = $SIG{__WARN__};
$SIG{__WARN__} = undef;
@@ -90,6 +72,19 @@ sub log {
close $F;
}
$SIG{__WARN__} = $old;
+
+ # color codes for fancy terminal output (not to file)
+ $type = "\e[1m\e[91m$type\e[0m" if ($type =~ m/(fatal|fail|error|stop)/i);# bold red
+ $type = "\e[91m$type\e[0m" if ($type =~ m/(refused|nodevice|timeout)/i); # red
+ $type = "\e[93m$type\e[0m" if ($type =~ m/(reset|warning|secure|unset)/i);# yellow
+ $type = "\e[95m$type\e[0m" if ($type =~ m/(add|update|delete)/i); # magenta
+ $type = "\e[96m$type\e[0m" if ($type =~ m/(list|uplink)/i); # cyan
+ $type = "\e[94m$type\e[0m" if ($type =~ m/(beacon|syncer)/i); # blue
+ $type = "\e[92m$type\e[0m" if ($type =~ m/(stat|kfnew)/i); # green
+ $type = "\e[1m\e[92m$type\e[0m" if ($type =~ m/(info|debug)/i); # bold green
+
+ # print to stdout if enabled
+ print "[$time]\t[$type]\t$msg\n" if $self->{printlog};
}
1;
diff --git a/lib/MasterServer/Core/Schedulers.pm b/lib/MasterServer/Core/Schedulers.pm
index cee4e5c..230a423 100755
--- a/lib/MasterServer/Core/Schedulers.pm
+++ b/lib/MasterServer/Core/Schedulers.pm
@@ -6,11 +6,8 @@ use AnyEvent;
use POSIX qw/strftime/;
use Exporter 'import';
use DBI;
-
-our @EXPORT = qw |
- long_periodic_tasks
- short_periodic_tasks
-|;
+our @EXPORT = qw | long_periodic_tasks
+ short_periodic_tasks |;
################################################################################
## tasks that are executed only once or twice per hour
@@ -20,20 +17,18 @@ sub long_periodic_tasks {
my $prev = 0;
return AnyEvent->timer (
- after => 30, # 30 seconds grace time
- interval => 3600, # execute every hour
+ after => 90, # grace time receiving beacons
+ interval => 3600,
cb => sub {
# update Killing Floor stats
- $self->read_kfstats() if $self->{kfstats_enabled};
+ $self->read_kfstats if $self->{kfstats_enabled};
# delete old masterserver applets that have been unresponsive for a while now
- $self->remove_unresponsive_applets() if (defined $self->{firstrun});
+ $self->remove_unresponsive_applets if (defined $self->{firstrun});
- # time spacer
- my $t = 0;
-
# clean out handles from the previous round (executed or not)
+ my $t = 0;
$self->{scope}->{sync} = ();
# Synchronize with all other 333networks masterservers that are uplinking,
@@ -42,7 +37,7 @@ sub long_periodic_tasks {
# get serverlist
my $masterserverlist = $self->get_server(
- updated => 3600,
+ updated => 7200,
gamename => "333networks",
);
@@ -50,7 +45,7 @@ sub long_periodic_tasks {
# add 5 second delay to spread network/server load
$self->{scope}->{sync}->{$t} = AnyEvent->timer(
after => 5*$t++,
- cb => sub{$self->sync_with_master($ms)}
+ cb => sub{$self->synchronize($ms, "333nwm")}
) if ($ms->{hostport} > 0);
}
}
@@ -63,22 +58,20 @@ sub long_periodic_tasks {
if ($self->{master_applet_enabled}) {
# get applet list
- my $appletlist = $self->get_masterserver_applets();
+ my $appletlist = $self->get_masterserver_applets;
for my $ms (@{$appletlist}) {
# add 5 second delay to spread network/server load
$self->{scope}->{sync}->{$t} = AnyEvent->timer(
after => 5*$t++,
- cb => sub{$self->query_applet($ms)}
+ cb => sub{$self->synchronize($ms, "applet")}
);
}
}
- #
- # very long-running tasks, like database dumps
+ # very long-running tasks, like database dumps.
# interval from config
- #
my $curr = 0;
$curr = strftime('%d',localtime) if ($self->{dump_db} =~ /^daily$/i );
$curr = strftime('%U',localtime) if ($self->{dump_db} =~ /^weekly$/i );
@@ -87,19 +80,11 @@ sub long_periodic_tasks {
# on change, execute
if ($prev < $curr) {
+ # skip on first run and update timer
+ if ($prev == 0) { $prev = $curr; return; }
- # skip on first run
- if ($prev == 0) {
- # update timer and loop
- $prev = $curr;
- return;
- }
-
- # dump db
- $self->dump_database();
-
- # update timekeeper
- $prev = $curr;
+ # dump db and update timer
+ $self->dump_database; $prev = $curr;
}
},
);
@@ -110,25 +95,21 @@ sub long_periodic_tasks {
################################################################################
sub short_periodic_tasks {
my $self = shift;
-
return AnyEvent->timer (
- after => 10,
+ after => 5,
interval => 120,
cb => sub {
-
# update stats on direct beacons and total number of servers
- $self->update_stats();
+ $self->update_stats;
# determine whether servers are still uplinking to us. If not, toggle.
- $self->write_direct_beacons() if (defined $self->{firstrun});
+ $self->write_direct_beacons if (defined $self->{firstrun});
- # delete old servers from the "pending" list (except for the first run)
- $self->delete_old_pending() if (defined $self->{firstrun});
-
- # uplink to other 333networks masterservers with heartbeats, so other
- # masterservers can find us too
- $self->send_heartbeats();
+ # delete old servers from the "pending" list
+ $self->delete_old_pending;
+ # uplink to other 333networks masterservers so others can find us too
+ $self->send_heartbeats;
},
);
}
diff --git a/lib/MasterServer/Core/Secure.pm b/lib/MasterServer/Core/Secure.pm
index 125e276..b3e401c 100755
--- a/lib/MasterServer/Core/Secure.pm
+++ b/lib/MasterServer/Core/Secure.pm
@@ -4,11 +4,11 @@ use strict;
use warnings;
use POSIX qw/strftime/;
use Exporter 'import';
-
our @EXPORT = qw| load_ciphers
- secure_string
- validate_string
- compare_challenge |;
+ secure_string
+ auth_browser
+ auth_server
+ validate_string |;
################################################################################
## Supported Games list ciphers
@@ -24,13 +24,11 @@ sub load_ciphers {
# first delete the old cipher database
$self->clear_ciphers();
- # start inserting ciphers (use transactions for slow systems)
+ # start inserting ciphers (bulk)
$self->{dbh}->begin_work;
- # iterate through the game list
+ # iterate through the game list and insert entries
for (keys %{$self->{game}}) {
-
- # verify entries
my %opt = ();
$opt{gamename} = lc $_;
$opt{cipher} = $self->{game}->{$_}->{key};
@@ -40,13 +38,14 @@ sub load_ciphers {
# insert the game/cipher in the db or halt on error
if ($self->insert_cipher(%opt) < 0) {
$self->{dbh}->rollback;
+ $self->log("fatal", "could not update cipher database");
$self->halt();
}
}
# commit
$self->{dbh}->commit;
- $self->log("info", "Cipher database successfully updated!");
+ $self->log("info", "cipher database successfully updated");
}
################################################################################
@@ -61,42 +60,51 @@ sub secure_string {
}
################################################################################
-# authenticate the \validate\ response for the \secure\ challenge.
+# authenticate browser response for secure/validate challenge
# returns 1 on valid response, 0 on invalid
################################################################################
-sub compare_challenge {
- my ($self, %o) = @_;
-
- # debugging enabled? Then don't care about validation
+sub auth_browser {
+ my ($self, %o) = @_;
+ # exceptions (debugging, exclusion)
return 1 if ($self->{debug_validate});
-
- # ignore this game if asked to do so
- if ($self->{ignore_browser_key} =~ m/$o{gamename}/i){
- $self->log("ignore", "ignored beacon validation for $o{gamename}");
- return 1;
- }
-
+ return 1 if ($self->{ignore_browser_key} =~ m/$o{gamename}/i);
+
# calculate validate string
my $val = get_validate_string(
$self->get_game_props(gamename => $o{gamename})->[0]->{cipher},
$o{secure},
$o{enctype} || 0
);
-
# return match or no match
- return ($val eq ($o{validate} || ""));
+ return ( $o{validate} && ($val eq $o{validate}) );
}
################################################################################
-# obtain the secure/validate challenge string
+# authenticate server response for secure/validate challenge
+# returns 1 on valid response, 0 on invalid
################################################################################
-sub validate_string {
+sub auth_server {
my ($self, %o) = @_;
+ # exceptions (debugging, exclusion)
+ return 1 if ($self->{debug_validate});
+ return 1 if ($self->{ignore_beacon_key} =~ m/$o{gamename}/i);
- # secure string too long? discard as hack.
- return 0 if (length $o{secure} > 6);
+ # calculate validate string
+ my $val = get_validate_string(
+ $self->get_game_props(gamename => $o{gamename})->[0]->{cipher},
+ $o{secure},
+ $o{enctype} || 0
+ );
+ # return match or no match
+ return ( $o{validate} && ($val eq $o{validate}) );
+}
- # calculate and return validate string
+################################################################################
+# calculate and return validate string
+# requires gamename
+################################################################################
+sub validate_string {
+ my ($self, %o) = @_;
return get_validate_string(
$self->get_game_props(gamename => $o{gamename})->[0]->{cipher},
$o{secure},
@@ -146,8 +154,8 @@ sub get_validate_string {
my ($cipher_string, $secure_string, $enctype) = @_;
# convert to array of characters
- my @cip = split "", $cipher_string;
- my @sec = split "", $secure_string;
+ my @cip = split "", $cipher_string || "";
+ my @sec = split "", $secure_string || "";
# length of strings/arrays which should be 6
my $sec_len = scalar @sec;
@@ -161,8 +169,8 @@ sub get_validate_string {
my ($i,$j,$k,$l,$m,$n,$p);
# too short or too long -- return empty string
- return "" if ($sec_len <= 0 || $sec_len >= 32);
- return "" if ($cip_len <= 0 || $cip_len >= 32);
+ return "" if ($sec_len <= 0 || $sec_len > 8);
+ return "" if ($cip_len <= 0 || $cip_len > 8);
# temporary array with ascii characters
my @enc;
diff --git a/lib/MasterServer/Core/Stats.pm b/lib/MasterServer/Core/Stats.pm
index 8e9eb95..4f23723 100755
--- a/lib/MasterServer/Core/Stats.pm
+++ b/lib/MasterServer/Core/Stats.pm
@@ -4,7 +4,6 @@ use strict;
use warnings;
use AnyEvent::IO;
use Exporter 'import';
-
our @EXPORT = qw| update_stats |;
################################################################################
@@ -13,25 +12,38 @@ our @EXPORT = qw| update_stats |;
sub update_stats {
my $self = shift;
- # get all gamenames where there is one or more servers online and update the
- # stats per gamename.
- my $games = $self->get_gamelist_stats();
-
- # iterate through available stats
- for my $e (@{$games}) {
+ # find all gamenames with 1 server or more
+ my $in_slist = $self->get_gamenames();
+ my $in_glist = $self->get_listedstats();
+
+ # list of unique gamenames
+ my %games; $games{$_->[0]} = 1 for (@{$in_slist}, @{$in_glist});
+
+ # update stats per gamename
+ for my $gamename (sort keys %games) {
+
+ # get statistics per game
+ my $num = $self->get_gamestats($gamename)->[0];
- # extract gamename, number of direct uplinks and total servers
- my %opt = ();
- $opt{gamename} = $e->[0];
- $opt{num_uplink} = $e->[1];
- $opt{num_total} = $e->[2];
-
- # write to DB
- $self->write_stat(%opt);
+ # update in db
+ my $u = $self->write_stat(
+ gamename => $gamename,
+ num_uplink => $num->{num_uplink} || 0,
+ num_total => $num->{num_total} || 0,
+ );
+
+ # log stats too
+ if ( int($u) > 0) {
+ # log the statistics
+ $self->log("update", "updated stats ($num->{num_uplink}/$num->{num_total}) for $gamename");
+ } else {
+ # report unable to update stats
+ $self->log("error", "can not update stats for $gamename");
+ }
}
-
- # done
- $self->log("stat", "Updated all game statistics.");
+
+ # notify
+ $self->log("stat", "updated all game statistics");
}
1;
diff --git a/lib/MasterServer/Core/Util.pm b/lib/MasterServer/Core/Util.pm
index 682335c..5af8264 100755
--- a/lib/MasterServer/Core/Util.pm
+++ b/lib/MasterServer/Core/Util.pm
@@ -2,12 +2,28 @@ package MasterServer::Core::Util;
use strict;
use warnings;
-use IP::Country::Fast;
use Socket;
+use Encode;
+use IP::Country::Fast;
use POSIX qw/strftime/;
use Exporter 'import';
+our @EXPORT = qw| data2hashref
+ ip2country
+ host2ip
+ valid_address
+ db_all
+ sqlprint |;
-our @EXPORT = qw| ip2country host2ip valid_address db_all sqlprint |;
+################################################################################
+## process udp/tcp data strings from \key\value to hash
+################################################################################
+sub data2hashref {
+ my ($self, $str) = @_;
+ my @a = split /\\/, encode('UTF-8', $str||"");
+ shift @a;
+ my %h = (@a, (scalar @a % 2 == 1) ? "dummy" : () );
+ return \%h;
+}
################################################################################
## return the abbreviated country name based on IP
diff --git a/lib/MasterServer/Core/Version.pm b/lib/MasterServer/Core/Version.pm
index 4a49392..0b4d058 100755
--- a/lib/MasterServer/Core/Version.pm
+++ b/lib/MasterServer/Core/Version.pm
@@ -3,10 +3,8 @@ package MasterServer::Core::Version;
use strict;
use warnings;
use Exporter 'import';
-
our @EXPORT = qw| version |;
-
################################################################################
##
## Version information
@@ -29,13 +27,13 @@ sub version {
$self->{build_type} = "333networks Masterserver-Perl Multidb";
# version
- $self->{build_version} = "2.3.1";
+ $self->{build_version} = "2.4.0";
# short version for uplinks
$self->{short_version} = "MS-perl $self->{build_version}";
# date yyyy-mm-dd
- $self->{build_date} = "2017-07-06";
+ $self->{build_date} = "2017-08-22";
#author, email
$self->{build_author} = "Darkelarious, darkelarious\@333networks.com";