diff options
Diffstat (limited to 'lib/MasterServer/Core')
| -rwxr-xr-x | lib/MasterServer/Core/Core.pm | 120 | ||||
| -rwxr-xr-x | lib/MasterServer/Core/LoadConfig.pm | 108 | ||||
| -rwxr-xr-x | lib/MasterServer/Core/Logging.pm | 51 | ||||
| -rwxr-xr-x | lib/MasterServer/Core/Schedulers.pm | 65 | ||||
| -rwxr-xr-x | lib/MasterServer/Core/Secure.pm | 72 | ||||
| -rwxr-xr-x | lib/MasterServer/Core/Stats.pm | 48 | ||||
| -rwxr-xr-x | lib/MasterServer/Core/Util.pm | 20 | ||||
| -rwxr-xr-x | lib/MasterServer/Core/Version.pm | 6 |
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"; |
