diff options
| author | Darkelarious <darkelarious@333networks.com> | 2017-08-22 11:00:13 +0200 |
|---|---|---|
| committer | Darkelarious <darkelarious@333networks.com> | 2017-08-22 11:00:13 +0200 |
| commit | c06322da38b4cb76b2036af1a5448083adb8ff20 (patch) | |
| tree | 189c9f0fec3325be927f763aba23cf18aa68cfe4 /lib/MasterServer/Database | |
| parent | e0d727670cbeda0db0812c5c9efc503d75f8d0a4 (diff) | |
| download | MasterServer-Perl-c06322da38b4cb76b2036af1a5448083adb8ff20.tar.gz MasterServer-Perl-c06322da38b4cb76b2036af1a5448083adb8ff20.zip | |
new server checking mechanism, complete recode of major functionsv2.4.0
Diffstat (limited to 'lib/MasterServer/Database')
| -rwxr-xr-x | lib/MasterServer/Database/Pg/dbAddServers.pm | 167 | ||||
| -rwxr-xr-x | lib/MasterServer/Database/Pg/dbAppletActions.pm | 51 | ||||
| -rwxr-xr-x | lib/MasterServer/Database/Pg/dbCiphers.pm | 29 | ||||
| -rwxr-xr-x | lib/MasterServer/Database/Pg/dbCore.pm | 38 | ||||
| -rwxr-xr-x | lib/MasterServer/Database/Pg/dbExtendedInfo.pm | 88 | ||||
| -rwxr-xr-x | lib/MasterServer/Database/Pg/dbGetServers.pm | 58 | ||||
| -rwxr-xr-x | lib/MasterServer/Database/Pg/dbMaintenance.pm | 19 | ||||
| -rwxr-xr-x | lib/MasterServer/Database/Pg/dbStats.pm | 83 | ||||
| -rwxr-xr-x | lib/MasterServer/Database/Pg/dbUTServerInfo.pm | 237 | ||||
| -rwxr-xr-x | lib/MasterServer/Database/SQLite/dbAddServers.pm | 168 | ||||
| -rwxr-xr-x | lib/MasterServer/Database/SQLite/dbAppletActions.pm | 51 | ||||
| -rwxr-xr-x | lib/MasterServer/Database/SQLite/dbCiphers.pm | 29 | ||||
| -rwxr-xr-x | lib/MasterServer/Database/SQLite/dbCore.pm | 43 | ||||
| -rwxr-xr-x | lib/MasterServer/Database/SQLite/dbGetServers.pm | 58 | ||||
| -rwxr-xr-x | lib/MasterServer/Database/SQLite/dbMaintenance.pm | 23 | ||||
| -rwxr-xr-x | lib/MasterServer/Database/SQLite/dbStats.pm | 83 | ||||
| -rwxr-xr-x | lib/MasterServer/Database/SQLite/dbUTServerInfo.pm | 117 |
17 files changed, 398 insertions, 944 deletions
diff --git a/lib/MasterServer/Database/Pg/dbAddServers.pm b/lib/MasterServer/Database/Pg/dbAddServers.pm index 31deb08..6befd83 100755 --- a/lib/MasterServer/Database/Pg/dbAddServers.pm +++ b/lib/MasterServer/Database/Pg/dbAddServers.pm @@ -4,159 +4,78 @@ use strict; use warnings; use MasterServer::Core::Util 'sqlprint'; use Exporter 'import'; - -our @EXPORT = qw| add_server_new - add_server_list - update_server_list - syncer_add |; +our @EXPORT = qw| insert_server + update_server + insert_pending |; ################################################################################ -## Update an existing address or add a new address to the pending list. -## opts: direct beacon, set update, game +## Insert minimalistic game data into serverlist +## params: ip, port, hostport ################################################################################ -sub add_server_new { +sub insert_server { my $self = shift; - my %o = ( - updated => time, - @_); - - # try updating it in serverlist - my %H = ( - $o{direct} ? ( 'b333ms = CAST(? AS BOOLEAN)' => $o{direct}) : (), - $o{updated} ? ( 'updated = to_timestamp(?)' => $o{updated}) : (), - $o{beacon} ? ( 'beacon = to_timestamp(?)' => $o{beacon}) : (), - - # some applets have incorrect gamename lists, let udpticker update this - # entry instead. this way, applets don't overwrite with incorrect data - #$o{gamename} ? ('gamename = ?' => lc $o{gamename}) : (), - ); - - my($q, @p) = sqlprint("UPDATE serverlist !H - WHERE ip = ? AND port = ?", \%H, $o{ip}, $o{heartbeat}); - - my $n = $self->{dbh}->do($q, undef, @p); - - # if serverlist was updated - return 0 if ($n > 0); - - # try updating it in pending - %H = ( - $o{added} ? ( 'added = ?' => $o{added}) : (), - $o{secure} ? ( 'secure = ?' => $o{secure}) : (), - $o{beaconport} ? ('beaconport = ?' => $o{beaconport}) : (), - ); - - ($q, @p) = sqlprint("UPDATE pending !H - WHERE ip = ? AND heartbeat = ?", \%H, $o{ip}, $o{heartbeat}); - - # exec query - $n = $self->{dbh}->do($q, undef, @p); + my %o = (@_); - # if beacon was updated - return 1 if ($n > 0); - # if not found at all, add to pending - $n = $self->{dbh}->do( - "INSERT INTO pending ( - ip, - beaconport, - heartbeat, - gamename, - secure) - SELECT ?, ?, ?, ?, ?", - undef, $o{ip}, $o{beaconport}, $o{heartbeat}, lc $o{gamename}, $o{secure}); - - # it was added to pending - return 2 if ($n > 0); + return $self->{dbh}->do( + "INSERT INTO serverlist (ip, port, hostport, country) VALUES (?, ?, ?, ?)", + undef, $o{ip}, $o{port}, $o{hostport}, $self->ip2country($o{ip}) ); } ################################################################################ ## Update the server info in the serverlist +## required: id or ip + port/hostport ################################################################################ -sub update_server_list { +sub update_server { my $self = shift; - my %o = ( - updated => time, - @_); + my %o = (updated => time, @_); + + # either id, ip+port or ip+hostport are provided. + my %W = ( + $o{id} ? ( 'id = ?' => $o{id}) : (), + $o{ip} ? ( 'ip = ?' => $o{ip}) : (), + $o{port} ? ( 'port = ?' => $o{port}) : (), + $o{hostport} ? ('hostport = ?' => $o{hostport}) : (), + ); - # try updating it in serverlist + # update where possible my %H = ( $o{gamename} ? ('gamename = ?' => lc $o{gamename}) : (), $o{gamever} ? ( 'gamever = ?' => $o{gamever}) : (), $o{hostname} ? ('hostname = ?' => $o{hostname}) : (), $o{hostport} ? ('hostport = ?' => $o{hostport}) : (), - $o{updated} ? ( 'updated = to_timestamp(?)' => $o{updated}) : (), + $o{direct} ? ( 'b333ms = CAST(? AS BOOLEAN)' => $o{direct}) : (), + $o{direct} ? ( 'beacon = to_timestamp(?)' => $o{updated}) : (), + $o{updated} ? ( 'updated = to_timestamp(?)' => $o{updated}) : (), ); - my($q, @p) = sqlprint("UPDATE serverlist !H - WHERE ip = ? AND port = ?", \%H, $o{ip}, $o{port}); - + my($q, @p) = sqlprint("UPDATE serverlist !H !W", \%H, \%W); return $self->{dbh}->do($q, undef, @p); } ################################################################################ -## beacon was verified or otherwise accepted and will now be added to the -## serverlist. +## check if an ip, port/hostport combination is recent in the serverlist. +## if not, add the address to the pending list ################################################################################ -sub add_server_list { +sub insert_pending { my $self = shift; - my %o = @_; + my %o = (updated => 3600, @_ ); - # insert basic data - return $self->{dbh}->do("INSERT INTO serverlist (ip, port, gamename, country) - SELECT ?, ?, ?, ?", undef, - $o{ip}, $o{port}, lc $o{gamename}, $self->ip2country($o{ip})); -} - -################################################################################ -## add new addresses to the pending list, but do not update timestamps. masters -## that sync with each other would otherwise update the timestamp for a server -## which is no longer online. -################################################################################ -sub syncer_add { - my ($self, $ip, $port, $gamename, $secure) = @_; - - # if address is in the list AND up to date, - # acknowledge its existance but don't do anything with it - my $u = $self->{dbh}->do( - "SELECT count(*) FROM serverlist - WHERE ip = ? - AND port = ? - AND updated > to_timestamp(?)", - undef, $ip, $port, time-7200); - - # if found, return 0 - return 0 if ($u > 0); + # selection criteria + my %W = ( + $o{ip} ? ( 'ip = ?' => $o{ip}) : (), + $o{port} ? ( 'port = ?' => $o{port}) : (), + $o{updated} ? ('updated > to_timestamp(?)' => (time-$o{updated})) : (), + ); - # if it is already in the pending list, update it with a new challenge - $u = $self->{dbh}->do( - "UPDATE pending - SET secure = ? - WHERE ip = ? - AND heartbeat = ?", - undef, $secure, $ip, $port); + # determine if it already exsits + my($q, @p) = sqlprint("SELECT id FROM serverlist !W", \%W); + my $u = $self->{dbh}->do($q, undef, @p); + return 0 if int($u); - # notify (debug) - #$self->log("update","$ip:$port was updated by syncer") if ($u > 0); - - # return 1 if found - return 1 if ($u > 0); - - # if not found or out of date, add it to pending to be checked again - $u = $self->{dbh}->do( - "INSERT INTO pending (ip, heartbeat, gamename, secure) - SELECT ?, ?, ?, ?", - undef, $ip, $port, lc $gamename, $secure); - - # notify (debug) - #$self->log("add","beacon: $ip:$port was added for $gamename after sync") if ($u > 0); - - # return 2 if added new - return 2 if ($u > 0); - - # or else report error - $self->log("error", "an error occurred adding $ip:$port after sync"); - return -1; + # else, insert in pending (duplicates may exist -- see remove_pending) + return $self->{dbh}->do("INSERT INTO pending (ip, heartbeat) VALUES (?, ?)", + undef, $o{ip}, $o{port} ); } 1; diff --git a/lib/MasterServer/Database/Pg/dbAppletActions.pm b/lib/MasterServer/Database/Pg/dbAppletActions.pm index dc7d941..9dcb3e7 100755 --- a/lib/MasterServer/Database/Pg/dbAppletActions.pm +++ b/lib/MasterServer/Database/Pg/dbAppletActions.pm @@ -3,7 +3,6 @@ package MasterServer::Database::Pg::dbAppletActions; use strict; use warnings; use Exporter 'import'; - our @EXPORT = qw| add_master_applet update_master_applet reset_master_applets @@ -18,19 +17,20 @@ sub add_master_applet { my %o = @_; my $u = $self->{dbh}->do( - "SELECT * FROM appletlist - WHERE ip = ? - AND port = ? + "SELECT * FROM appletlist + WHERE ip = ? + AND hostport = ? AND gamename = ?", - undef, $o{ip}, $o{port}, lc $o{gamename}); + undef, $o{ip}, $o{hostport}, lc $o{gamename}); # return if found return if ($u > 0); # insert applet data - return $self->{dbh}->do("INSERT INTO appletlist (ip, port, gamename) - SELECT ?, ?, ?", undef, - $o{ip}, $o{port}, lc $o{gamename}); + return $self->{dbh}->do( + "INSERT INTO appletlist (ip, hostport, gamename) + SELECT ?, ?, ?", + undef, $o{ip}, $o{hostport}, lc $o{gamename}); } ################################################################################ @@ -38,10 +38,11 @@ sub add_master_applet { ################################################################################ sub reset_master_applets { my $self = shift; - return $self->{dbh}->do("UPDATE appletlist - SET added = to_timestamp(?), - updated = to_timestamp(?)", - undef, time, time); + return $self->{dbh}->do( + "UPDATE appletlist + SET added = to_timestamp(?), + updated = to_timestamp(?)", + undef, time, time); } ################################################################################ @@ -50,12 +51,13 @@ sub reset_master_applets { sub update_master_applet { my ($self, %o) = @_; - return $self->{dbh}->do("UPDATE appletlist - SET updated = to_timestamp(?) - WHERE ip = ? - AND port = ? + return $self->{dbh}->do( + "UPDATE appletlist + SET updated = to_timestamp(?) + WHERE ip = ? + AND hostport = ? AND gamename = ?", - undef, time, $o{ip}, $o{port}, lc $o{gamename}); + undef, time, $o{ip}, $o{hostport}, lc $o{gamename}); } ################################################################################ @@ -66,10 +68,10 @@ sub get_masterserver_applets { my $self = shift; return $self->db_all( - "SELECT * - FROM appletlist - WHERE updated > to_timestamp(?)", - time-604800); + "SELECT * + FROM appletlist + WHERE updated > to_timestamp(?)", + time-604800); } ################################################################################ @@ -82,11 +84,12 @@ sub remove_unresponsive_applets { # remove entries my $u = $self->{dbh}->do( - "DELETE FROM appletlist - WHERE updated < to_timestamp(?)", undef, time-604800); + "DELETE FROM appletlist + WHERE updated < to_timestamp(?)", + undef, time-604800); # notify - $self->log("delete", "Removed $u entries from applet list.") if ($u > 0); + $self->log("delete", "Removed $u entries from applet list") if ($u > 0); } 1; diff --git a/lib/MasterServer/Database/Pg/dbCiphers.pm b/lib/MasterServer/Database/Pg/dbCiphers.pm index 5343065..6dde097 100755 --- a/lib/MasterServer/Database/Pg/dbCiphers.pm +++ b/lib/MasterServer/Database/Pg/dbCiphers.pm @@ -3,19 +3,17 @@ package MasterServer::Database::Pg::dbCiphers; use strict; use warnings; use Exporter 'import'; - our @EXPORT = qw| check_cipher_count clear_ciphers insert_cipher - get_game_props - get_gamenames |; + get_game_props |; ################################################################################ ## Check if ciphers exist ################################################################################ sub check_cipher_count { my $self = shift; - return $self->db_all('SELECT count(*) as num from games')->[0]->{num}; + return $self->db_all('SELECT count(gamename) as num from games')->[0]->{num}; } ################################################################################ @@ -23,9 +21,7 @@ sub check_cipher_count { ################################################################################ sub clear_ciphers { my $self = shift; - - # delete ALL entries - my $u = $self->{dbh}->do("DELETE FROM games"); + $self->{dbh}->do("DELETE FROM games"); } ################################################################################ @@ -56,10 +52,7 @@ sub insert_cipher { ################################################################################ sub get_game_props { my $s = shift; - my %o = ( - sort => '', - @_ - ); + my %o = (sort => '', @_); my %where = ( $o{gamename} ? ('gamename = ?' => lc $o{gamename}) : (), @@ -90,18 +83,4 @@ sub get_game_props { ); } - -################################################################################ -## get a list of distinct gamenames currently in the database. it does not -## matter whether they are recent or old, as long as the game is currently in -## the database. -################################################################################ -sub get_gamenames { - my $self = shift; - - return $self->{dbh}->selectall_arrayref( - "SELECT distinct gamename - FROM serverlist"); -} - 1; diff --git a/lib/MasterServer/Database/Pg/dbCore.pm b/lib/MasterServer/Database/Pg/dbCore.pm index 0891012..4b2d16f 100755 --- a/lib/MasterServer/Database/Pg/dbCore.pm +++ b/lib/MasterServer/Database/Pg/dbCore.pm @@ -4,50 +4,30 @@ use strict; use warnings; use POSIX qw/strftime/; use Exporter 'import'; - our @EXPORT = qw| database_login dump_database |; ################################################################################ ## login to the database with credentials provided in the config file. ## returns dbh object or quits application on error. -## -## Recommended database types: Postgresql, MySQL or SQLite. Warranty void if -## other database types are used. Use at your own risk. ################################################################################ sub database_login { my $self = shift; - - # get db info my @db_type = split(':', $self->{dblogin}->[0]); - - # inform what db we try to load - $self->log("info","Database: $db_type[1], $db_type[2]"); - - # create the dbi object + + # connect to Pg database my $dbh = DBI->connect(@{$self->{dblogin}}, {PrintError => 1}); # verify that the database connected if (defined $dbh) { - - # log the event - $self->log("info","Connected to the $db_type[1] database."); - - # turn on error printing + $self->log("info","Connected to $db_type[1] database $db_type[2]"); $dbh->{printerror} = 1; - - # return the dbi object for further use return $dbh; } else { # fatal error $self->log("fatal", "$DBI::errstr!"); - - # end program $self->halt(); } - - # in case of any other error, return undef. - return undef; } ################################################################################ @@ -56,23 +36,15 @@ sub database_login { ################################################################################ sub dump_database { my $self = shift; - - # filename / time my $time = strftime('%Y-%m-%d-%H-%M',localtime); - - # FIXME - # separate absolute path and relative path, - # split database filename for dump filename. - + # read db credentials from db login my @db_type = split(':', $self->{dblogin}->[0]); $db_type[2] =~ s/dbname=//; # use pg_dump to dump Postgresql databases system("pg_dump $db_type[2] -U $self->{dblogin}->[1] > $self->{root}/data/dumps/Pg-$time-$db_type[2].db"); - - # log - $self->log("dump", "Dumping database to /data/dumps/$db_type[1]-$time.db"); + $self->log("dump", "Dumping database to /data/dumps/Pg-$time-$db_type[2].db"); } diff --git a/lib/MasterServer/Database/Pg/dbExtendedInfo.pm b/lib/MasterServer/Database/Pg/dbExtendedInfo.pm new file mode 100755 index 0000000..8ecc624 --- /dev/null +++ b/lib/MasterServer/Database/Pg/dbExtendedInfo.pm @@ -0,0 +1,88 @@ +package MasterServer::Database::Pg::dbExtendedInfo; + +use strict; +use warnings; +use MasterServer::Core::Util 'sqlprint'; +use Exporter 'import'; +our @EXPORT = qw| insert_extended + update_extended + delete_players + insert_players |; + +################################################################################ +## Add extended server information for a new server. +## opts: ipm hostport +################################################################################ +sub insert_extended { + my $self = shift; + my %o = ( @_); + return $self->{dbh}->do( + "INSERT INTO extended_info (server_id) + SELECT (SELECT id FROM serverlist WHERE ip = ? AND hostport = ?)", + undef, $o{ip}, $o{hostport}); +} + +################################################################################ +## Update serverinfo for an existing address to the utserver list. +## opts: all server info data fields. +################################################################################ +sub update_extended { + my $self = shift; + my %o = (updated => time, @_); + + # try updating it in serverlist + my %H = ( + $o{minnetver} ? ( 'minnetver = ?' => $o{minnetver} ) : (), + $o{location} ? ( 'location = ?' => $o{location} ) : (), + $o{listenserver} ? ( 'listenserver = ?' => $o{listenserver}) : (), + $o{AdminName} ? ( 'adminname = ?' => $o{AdminName}) : (), + $o{AdminEMail} ? ( 'adminemail = ?' => $o{AdminEMail}) : (), + $o{password} ? ( 'password = ?' => $o{password}) : (), + $o{gametype} ? ( 'gametype = ?' => $o{gametype}) : (), + $o{gamestyle} ? ( 'gamestyle = ?' => $o{gamestyle}) : (), + $o{changelevels} ? ( 'changelevels = ?' => $o{changelevels}) : (), + $o{maptitle} ? ( 'maptitle = ?' => $o{maptitle}) : (), + $o{mapname} ? ( 'mapname = ?' => $o{mapname}) : (), + $o{numplayers} ? ( 'numplayers = ?' => $o{numplayers}) : ('numplayers = ?' => 0), + $o{maxplayers} ? ( 'maxplayers = ?' => $o{maxplayers}) : ('maxplayers = ?' => 0), + $o{minplayers} ? ( 'minplayers = ?' => $o{minplayers}) : ('minplayers = ?' => 0), + $o{botskill} ? ( 'botskill = ?' => $o{botskill}) : (), + $o{balanceteams} ? ( 'balanceteams = ?' => $o{balanceteams} ) : (), + $o{playersbalanceteams} ? ( 'playersbalanceteams = ?' => $o{playersbalanceteams}) : (), + $o{friendlyfire} ? ( 'friendlyfire = ?' => $o{friendlyfire}) : (), + $o{maxteams} ? ( 'maxteams = ?' => $o{maxteams}) : (), + $o{timelimit} ? ( 'timelimit = ?' => $o{timelimit}) : (), + $o{goalteamscore} ? ( 'goalteamscore = ?' => $o{goalteamscore}) : (), + $o{fraglimit} ? ( 'fraglimit = ?' => $o{fraglimit}) : (), + $o{mutators} ? ( 'mutators = ?' => $o{mutators}) : ('mutators = ?' => "None"), + $o{updated} ? ('updated = to_timestamp(?)' => $o{updated}) : (), + ); + + my($q, @p) = sqlprint("UPDATE extended_info !H WHERE server_id = ?", \%H, $o{sid}); + return $self->{dbh}->do($q, undef, @p); +} + +################################################################################ +## Delete all players from a certain server ID +## opts: server id +################################################################################ +sub delete_players { + my ($self, $sid) = @_; + + # delete players with server_id + return $self->{dbh}->do( + "DELETE FROM player_info WHERE server_id = ?", + undef, $sid); +} + +################################################################################ +## Insert player info for a single player in server sid +## opts: server id, player info +################################################################################ +sub insert_players { + my ($self, @pl) = @_; + my($q, @p) = sqlprint("INSERT INTO player_info (server_id, player, team, frags, mesh, skin, face, ping, ngsecret) VALUES (!l)", \@pl); + return $self->{dbh}->do($q, undef, @p); +} + +1; diff --git a/lib/MasterServer/Database/Pg/dbGetServers.pm b/lib/MasterServer/Database/Pg/dbGetServers.pm index e9bfaec..827443a 100755 --- a/lib/MasterServer/Database/Pg/dbGetServers.pm +++ b/lib/MasterServer/Database/Pg/dbGetServers.pm @@ -3,37 +3,35 @@ package MasterServer::Database::Pg::dbGetServers; use strict; use warnings; use Exporter 'import'; - our @EXPORT = qw| get_server get_pending |; ################################################################################ ## get server details for one or multiple servers -## opts: limit, see $order +## opts: limit, see %where or $order ################################################################################ sub get_server { my $s = shift; - my %o = ( - sort => '', - @_ - ); + my %o = (sort => '', blacklisted => 0, @_); my %where = ( - $o{next_id} ? ( 'id > ?' => $o{next_id}) : (), - $o{id} ? ( 'id = ?' => $o{id}) : (), - $o{ip} ? ( 'ip = ?' => $o{ip}) : (), - $o{port} ? ( 'port = ?' => $o{port}) : (), - $o{gamename} ? ( 'gamename = ?' => lc $o{gamename}) : (), - $o{gamever} ? ( 'gamever = ?' => $o{gamever}) : (), - $o{hostname} ? ( 'hostname = ?' => $o{hostname}) : (), - $o{hostport} ? ( 'hostport = ?' => $o{hostport}) : (), - $o{country} ? ( 'country = ?' => $o{country}) : (), - $o{b333ms} ? ( 'b333ms = ?' => $o{b333ms}) : (), - $o{blacklisted} ? ('blacklisted = ?' => $o{blacklisted}) : (), + $o{next_id} ? ( 'id > ?' => $o{next_id}) : (), + $o{id} ? ( 'id = ?' => $o{id}) : (), + $o{ip} ? ( 'ip = ?' => $o{ip}) : (), + $o{port} ? ( 'port = ?' => $o{port}) : (), + $o{gamename} ? ('gamename = ?' => lc $o{gamename}) : (), + $o{gamever} ? ( 'gamever = ?' => $o{gamever}) : (), + $o{hostname} ? ('hostname = ?' => $o{hostname}) : (), + $o{hostport} ? ('hostport = ?' => $o{hostport}) : (), + $o{country} ? ( 'country = ?' => $o{country}) : (), + $o{b333ms} ? ( 'b333ms = ?' => $o{b333ms}) : (), $o{added} ? ( 'added < to_timestamp(?)' => (time-$o{added})) : (), $o{beacon} ? ( 'beacon > to_timestamp(?)' => (time-$o{beacon})) : (), $o{updated} ? ('updated > to_timestamp(?)' => (time-$o{updated})) : (), $o{before} ? ('updated < to_timestamp(?)' => (time-$o{before})) : (), + + # never process blacklisted servers, unless explicitly specified + ('blacklisted = CAST(? AS BOOLEAN)' => $o{blacklisted}), ); my @select = ( qw| @@ -79,45 +77,29 @@ sub get_server { ################################################################################ ## get server details for one or multiple pending servers -## opts: limit, next_id, beaconport, heartbeat, gamename, secure, enctype, added +## opts: limit, next_id, ip, heartbeat port ################################################################################ sub get_pending { my $s = shift; - my %o = ( - sort => '', - @_ - ); + my %o = (sort => '', @_); my %where = ( $o{next_id} ? ( 'id > ?' => $o{next_id}) : (), $o{id} ? ( 'id = ?' => $o{id}) : (), $o{ip} ? ( 'ip = ?' => $o{ip}) : (), - $o{beaconport} ? ('beaconport = ?' => $o{beaconport}) : (), $o{heartbeat} ? ( 'heartbeat = ?' => $o{heartbeat}) : (), - $o{gamename} ? ( 'gamename = ?' => lc $o{gamename}) : (), - $o{secure} ? ( 'secure = ?' => $o{secure}) : (), - $o{enctype} ? ( 'enctype = ?' => $o{enctype}) : (), - $o{added} ? ('added < to_timestamp(?)' => (time-$o{added})) : (), - $o{after} ? ('added > to_timestamp(?)' => (time-$o{after})) : (), ); - my @select = ( qw| id ip beaconport heartbeat gamename secure enctype added |,); + my @select = ( qw| id ip heartbeat added |,); my $order = sprintf { id => 'id %s', ip => 'ip %s', - beaconport => 'beaconport %s', heartbeat => 'heartbeat %s', - gamename => 'gamename %s', - secure => 'secure %s', - enctype => 'enctype %s', added => 'added %s', }->{ $o{sort}||'id' }, $o{reverse} ? 'DESC' : 'ASC'; - return $s->db_all( q| - SELECT !s FROM pending - !W - ORDER BY !s| - .($o{limit} ? " LIMIT ?" : ""), + return $s->db_all( q| SELECT !s FROM pending !W ORDER BY !s| + .($o{limit} ? " LIMIT ?" : ""), join(', ', @select), \%where, $order, ($o{limit} ? $o{limit} : ()), ); } diff --git a/lib/MasterServer/Database/Pg/dbMaintenance.pm b/lib/MasterServer/Database/Pg/dbMaintenance.pm index 7a4fc23..f6c39b7 100755 --- a/lib/MasterServer/Database/Pg/dbMaintenance.pm +++ b/lib/MasterServer/Database/Pg/dbMaintenance.pm @@ -3,7 +3,6 @@ package MasterServer::Database::Pg::dbMaintenance; use strict; use warnings; use Exporter 'import'; - our @EXPORT = qw| delete_old_pending remove_pending |; @@ -12,14 +11,10 @@ our @EXPORT = qw| delete_old_pending ## where the server is unresponsive for more than 1 hour ################################################################################ sub delete_old_pending { - my ($self) = shift; - - # remove servers + my $self = shift; my $u = $self->{dbh}->do( "DELETE FROM pending WHERE added < to_timestamp(?)", undef, time-3600); - - # notify $self->log("delete", "Removed $u entries from pending.") if ($u > 0); } @@ -28,13 +23,11 @@ sub delete_old_pending { ## of error(s). ################################################################################ sub remove_pending { - my ($self, $id) = @_; - - # if address is in list, update the timestamp - my $u = $self->{dbh}->do("DELETE FROM pending WHERE id = ?", undef, $id); - - # notify - $self->log("delete", "removed pending id $id from pending") if ($u > 0); + my $self = shift; + my %o = ( @_); + my $u = $self->{dbh}->do("DELETE FROM pending WHERE ip = ? AND heartbeat = ?", + undef, $o{ip}, $o{port}); + $self->log("delete", "removed $o{ip}, $o{port} from pending (".$u."x)") if ($u > 0); } 1; diff --git a/lib/MasterServer/Database/Pg/dbStats.pm b/lib/MasterServer/Database/Pg/dbStats.pm index 6e92ab6..cfd61a4 100755 --- a/lib/MasterServer/Database/Pg/dbStats.pm +++ b/lib/MasterServer/Database/Pg/dbStats.pm @@ -3,41 +3,47 @@ package MasterServer::Database::Pg::dbStats; use strict; use warnings; use Exporter 'import'; - -our @EXPORT = qw| get_gamelist_stats - write_direct_beacons +our @EXPORT = qw| get_gamenames + get_gamestats + get_listedstats write_stat + write_direct_beacons write_kfstats |; ################################################################################ -# calculate stats for all individual games +## get a list of distinct gamenames currently in the server list ################################################################################ -sub get_gamelist_stats { +sub get_gamenames { my $self = shift; - return $self->{dbh}->selectall_arrayref( - "SELECT DISTINCT gamename AS gamename, - COUNT(NULLIF(b333ms AND updated > to_timestamp(?), FALSE)) AS numdirect, - COUNT(NULLIF(updated > to_timestamp(?), FALSE)) AS numtotal - FROM serverlist - GROUP BY gamename", undef, time-7200, time-7200); + "SELECT distinct gamename + FROM serverlist"); } ################################################################################ -# Determine from the last beacon whether the server is still uplinking -# directly to us, or whether it stopped uplinking and is now artificially -# kept in the database. +## get statistics (num_direct, num_total) per gamename ################################################################################ -sub write_direct_beacons { +sub get_gamestats { + my ($self, $gn) = @_; + return $self->db_all( + "SELECT COUNT(CASE WHEN b333ms THEN 1 END) as num_uplink, count(*) as num_total + FROM serverlist + WHERE gamename = ? AND updated > to_timestamp(?)", + lc $gn, time-7200); +} + +################################################################################ +## get a list of distinct gamenames currently in the database. it does not +## matter whether they are recent or old, as long as the game is currently in +## the database. +################################################################################ +sub get_listedstats { my $self = shift; - my $u = $self->{dbh}->do( - "UPDATE serverlist - SET b333ms = CAST(0 AS BOOLEAN) - WHERE beacon < to_timestamp(?) AND b333ms", - undef, time-3600); - - # notify - $self->log("unset", "Lost $u direct beacons.") if ($u > 0); + return $self->{dbh}->selectall_arrayref( + "SELECT gamename + FROM games + WHERE num_uplink > 0 + OR num_total > 0"); } ################################################################################ @@ -45,19 +51,28 @@ sub write_direct_beacons { # A stat can not exist without existing gamename. Was inserted by cipher loader. ################################################################################ sub write_stat { - my ($self, %opt) = @_; - - # if it is already in the pending list, update it with a new challenge - my $u = $self->{dbh}->do( + my ($self, %o) = @_; + return $self->{dbh}->do( "UPDATE games SET num_uplink = ?, num_total = ? WHERE gamename = ?", - undef, $opt{num_uplink}, $opt{num_total}, lc $opt{gamename}); - - # notify - $self->log("update", "Updated stats for $opt{gamename}.") if ($u > 0); + undef, $o{num_uplink}, $o{num_total}, lc $o{gamename}); +} +################################################################################ +# Determine from the last beacon whether the server is still uplinking +# directly to us, or whether it stopped uplinking and is now artificially +# kept in the database. +################################################################################ +sub write_direct_beacons { + my $self = shift; + my $u = $self->{dbh}->do( + "UPDATE serverlist + SET b333ms = CAST(0 AS BOOLEAN) + WHERE beacon < to_timestamp(?) AND b333ms", + undef, time-3600); + $self->log("unset", "Lost $u direct beacons.") if ($u > 0); } ################################################################################ @@ -66,7 +81,7 @@ sub write_stat { sub write_kfstats { my ($self, $h) = @_; - # check if entry already excists. + # check if entry already exists. my $u = $self->{dbh}->selectall_arrayref( "SELECT * FROM kfstats WHERE UTkey = ? ", undef, $h->{UTkey}); @@ -74,13 +89,11 @@ sub write_kfstats { $u = $self->{dbh}->do( "INSERT INTO kfstats (UTkey, Username) VALUES (?,?)", undef, $h->{UTkey}, $h->{Username}); - - # notify $self->log("kfnew", "New KF Player $h->{Username} added"); } # update existing information - $u = $self->{dbh}->do("UPDATE kfstats SET + $self->{dbh}->do("UPDATE kfstats SET Username = ?, CurrentVeterancy = ?, TotalKills = ?, diff --git a/lib/MasterServer/Database/Pg/dbUTServerInfo.pm b/lib/MasterServer/Database/Pg/dbUTServerInfo.pm deleted file mode 100755 index 0bf005e..0000000 --- a/lib/MasterServer/Database/Pg/dbUTServerInfo.pm +++ /dev/null @@ -1,237 +0,0 @@ -package MasterServer::Database::Pg::dbUTServerInfo; - -use strict; -use warnings; -use MasterServer::Core::Util 'sqlprint'; -use Exporter 'import'; - -our @EXPORT = qw| get_utserver - add_utserver - update_utserver - delete_utplayers - insert_utplayer |; - -################################################################################ -## get server details for one or multiple UT servers -## opts: limit, see $order -################################################################################ -sub get_utserver { - my $s = shift; - my %o = ( - sort => '', - @_ - ); - - my %where = ( - $o{id} ? ('server_id = ?' => $o{id}) : (), - $o{minnetver} ? ('minnetver = ?' => $o{minnetver}) : (), - $o{gamever} ? ('gamever = ?' => $o{gamever}) : (), - $o{location} ? ('location = ?' => $o{location}) : (), - $o{listenserver} ? ('listenserver = ?' => $o{listenserver}) : (), - $o{hostport} ? ('hostport = ?' => $o{hostport}) : (), - $o{hostname} ? ('hostname = ?' => $o{hostname}) : (), - $o{adminname} ? ('adminname = ?' => $o{adminname}) : (), - $o{adminemail} ? ('adminemail = ?' => $o{adminemail}) : (), - $o{password} ? ('password = ?' => $o{password}) : (), - $o{gametype} ? ('gametype = ?' => $o{gametype}) : (), - $o{gamestyle} ? ('gamestyle = ?' => $o{gamestyle}) : (), - $o{changelevels} ? ('changelevels = ?' => $o{changelevels}) : (), - $o{maptitle} ? ('maptitle = ?' => $o{maptitle}) : (), - $o{mapname} ? ('mapname = ?' => $o{mapname}) : (), - $o{numplayers} ? ('numplayers = ?' => $o{numplayers}) : (), - $o{maxplayers} ? ('maxplayers = ?' => $o{maxplayers}) : (), - $o{minplayers} ? ('minplayers = ?' => $o{minplayers}) : (), - $o{botskill} ? ('botskill = ?' => $o{botskill}) : (), - $o{balanceteams} ? ('balanceteams = ?' => $o{balanceteams}) : (), - $o{playersbalanceteams} ? ('playersbalanceteams = ?' => $o{playersbalanceteams}) : (), - $o{friendlyfire} ? ('friendlyfire = ?' => $o{friendlyfire}) : (), - $o{maxteams} ? ('maxteams = ?' => $o{maxteams}) : (), - $o{timelimit} ? ('timelimit = ?' => $o{timelimit}) : (), - $o{goalteamscore} ? ('goalteamscore = ?' => $o{goalteamscore}) : (), - $o{fraglimit} ? ('fraglimit = ?' => $o{fraglimit}) : (), - $o{mutators} ? ('hostname ILIKE ?' => "%$o{mutators}%") : (), - $o{updated} ? ('updated > to_timestamp(?)'=> (time-$o{updated})) : (), - ); - - my @select = ( qw| - server_id - minnetver - gamever - location - listenserver - hostport - hostname - adminname - adminemail - password - gametype - gamestyle - changelevels - maptitle - mapname - numplayers - maxplayers - minplayers - botskill - balanceteams - playersbalanceteams - friendlyfire - maxteams - timelimit - goalteamscore - fraglimit - mutators - updated - |); - - my $order = sprintf { - server_id => 'server_id %s', - minnetver => 'minnetver %s', - gamever => 'gamever %s', - location => 'location %s', - listenserver => 'listenserver %s', - hostport => 'hostport %s', - hostname => 'hostname %s', - adminname => 'adminname %s', - adminemail => 'adminemail %s', - password => 'password %s', - gametype => 'gametype %s', - gamestyle => 'gamestyle %s', - changelevels => 'changelevels %s', - maptitle => 'maptitle %s', - mapname => 'mapname %s', - numplayers => 'numplayers %s', - maxplayers => 'maxplayers %s', - minplayers => 'minplayers %s', - botskill => 'botskill %s', - balanceteams => 'balanceteams %s', - playersbalanceteams => 'playersbalanceteams %s', - friendlyfire => 'friendlyfire %s', - maxteams => 'maxteams %s', - timelimit => 'timelimit %s', - goalteamscore => 'goalteamscore %s', - fraglimit => 'fraglimit %s', - mutators => 'mutators %s', - updated => 'updated %s', - }->{ $o{sort}||'server_id' }, $o{reverse} ? 'DESC' : 'ASC'; - - return $s->db_all( q| - SELECT !s FROM utserver_info - !W - ORDER BY !s| - .($o{limit} ? " LIMIT ?" : ""), - join(', ', @select), \%where, $order, ($o{limit} ? $o{limit} : ()), - ); -} - - -################################################################################ -## Update serverinfo for an existing address to the utserver list. -## opts: all server info data fields. -################################################################################ -sub update_utserver { - my $self = shift; - my $id = shift; - my %s = ( - # defaults - updated => time, - @_); - - # try updating it in serverlist - my %H = ( - $s{minnetver} ? ( 'minnetver = ?' => $s{minnetver} ) : (), - $s{gamever} ? ( 'gamever = ?' => int( $s{gamever}) ) : (), - $s{location} ? ( 'location = ?' => $s{location} ) : (), - $s{listenserver} ? ( 'listenserver = ?' => ( $s{listenserver} ? 1 : 0) ) : (), - $s{hostport} ? ( 'hostport = ?' => $s{hostport}) : (), - $s{hostname} ? ( 'hostname = ?' => $s{hostname}) : (), - $s{AdminName} ? ( 'adminname = ?' => $s{AdminName}) : (), - $s{AdminEMail} ? ( 'adminemail = ?' => $s{AdminEMail}) : (), - $s{password} ? ( 'password = ?' => ( $s{password} ? 1 : 0) ) : (), - $s{gametype} ? ( 'gametype = ?' => $s{gametype}) : (), - $s{gamestyle} ? ( 'gamestyle = ?' => $s{gamestyle}) : (), - $s{changelevels} ? ( 'changelevels = ?' => ( $s{changelevels} ? 1 : 0) ) : (), - $s{maptitle} ? ( 'maptitle = ?' => $s{maptitle}) : (), - $s{mapname} ? ( 'mapname = ?' => $s{mapname}) : (), - $s{numplayers} ? ( 'numplayers = ?' => $s{numplayers}) : ('numplayers = ?' => 0), - $s{maxplayers} ? ( 'maxplayers = ?' => $s{maxplayers}) : ('maxplayers = ?' => 0), - $s{minplayers} ? ( 'minplayers = ?' => $s{minplayers}) : ('minplayers = ?' => 0), - $s{botskill} ? ( 'botskill = ?' => $s{botskill}) : (), - $s{balanceteams} ? ( 'balanceteams = ?' => ( $s{balanceteams} ? 1 : 0) ) : (), - $s{playersbalanceteams} ? ( 'playersbalanceteams = ?' => ( $s{playersbalanceteams} ? 1 : 0) ) : (), - $s{friendlyfire} ? ( 'friendlyfire = ?' => $s{friendlyfire}) : (), - $s{maxteams} ? ( 'maxteams = ?' => $s{maxteams}) : (), - $s{timelimit} ? ( 'timelimit = ?' => $s{timelimit}) : (), - $s{goalteamscore} ? ( 'goalteamscore = ?' => int( $s{goalteamscore}) ) : (), - $s{fraglimit} ? ( 'fraglimit = ?' => int( $s{fraglimit}) ) : (), - $s{mutators} ? ( 'mutators = ?' => $s{mutators}) : ('mutators = ?' => "None"), - $s{updated} ? ('updated = to_timestamp(?)' => $s{updated}) : (), - ); - - my($q, @p) = sqlprint("UPDATE utserver_info !H WHERE server_id = ?", \%H, $id); - return $self->{dbh}->do($q, undef, @p); -} - - -################################################################################ -## Add a new utserver and trigger the update routine above. -## opts: id, server info data -################################################################################ -sub add_utserver { - my ($self, $ip, $port) = @_; - - # create new entry - return $self->{dbh}->do( - "INSERT INTO utserver_info (server_id) - SELECT (SELECT id FROM serverlist WHERE ip = ? AND port = ?)", - undef, $ip, $port); -} - - -################################################################################ -## Delete all players from a certain server ID -## opts: server id -################################################################################ -sub delete_utplayers { - my ($self, $sid) = @_; - - # delete players for server_id - return $self->{dbh}->do( - "DELETE FROM utplayer_info WHERE server_id = ?", - undef, $sid); -} - -################################################################################ -## Insert player info for a single player in server sid -## opts: server id, player info -################################################################################ -sub insert_utplayer { - my $self = shift; - my $sid = shift; - my %s = ( - updated => time, - @_); - - # apparently useless chunk of code - # FIXME move to site part - my %H = ( - $s{server_id} ? ( 'server_id = ?' => $s{server_id}) : (), - $s{player} ? ( 'player = ?' => $s{player}) : (), - $s{team} ? ( 'team = ?' => int( $s{team})) : (), - $s{frags} ? ( 'frags = ?' => int( $s{frags})) : (), - $s{mesh} ? ( 'mesh = ?' => $s{mesh}) : (), - $s{skin} ? ( 'skin = ?' => $s{skin}) : (), - $s{face} ? ( 'face = ?' => $s{face}) : (), - $s{ping} ? ( 'ping = ?' => int( $s{ping})) : (), - $s{ngsecret} ? ( 'ngsecret = ?' => $s{ngsecret}) : (), - $s{updated} ? ('updated = to_timestamp(?)' => $s{updated}) : (), - ); - - # insert - return $self->{dbh}->do( - "INSERT INTO utplayer_info (server_id, player, team, frags, mesh, skin, face, ping, ngsecret) - VALUES (?,?,?,?,?,?,?,?,?)", - undef, $sid, $s{player}, $s{team}, $s{frags}, $s{mesh}, $s{skin}, $s{face}, $s{ping}, $s{ngsecret}); -} - -1; diff --git a/lib/MasterServer/Database/SQLite/dbAddServers.pm b/lib/MasterServer/Database/SQLite/dbAddServers.pm index 592ab7b..88b1bc8 100755 --- a/lib/MasterServer/Database/SQLite/dbAddServers.pm +++ b/lib/MasterServer/Database/SQLite/dbAddServers.pm @@ -1,163 +1,81 @@ - package MasterServer::Database::SQLite::dbAddServers; use strict; use warnings; use MasterServer::Core::Util 'sqlprint'; use Exporter 'import'; - -our @EXPORT = qw| add_server_new - add_server_list - update_server_list - syncer_add |; +our @EXPORT = qw| insert_server + update_server + insert_pending |; ################################################################################ -## Update an existing address or add a new address to the pending list. -## opts: direct beacon, set update, game +## Insert minimalistic game data into serverlist +## params: ip, port, hostport ################################################################################ -sub add_server_new { +sub insert_server { my $self = shift; - my %o = ( - updated => time, - @_); - - # try updating it in serverlist - my %H = ( - $o{direct} ? ( 'b333ms = CAST(? AS BOOLEAN)' => $o{direct}) : (), - $o{updated} ? ( 'updated = datetime(?, \'unixepoch\')' => $o{updated}) : (), - $o{beacon} ? ( 'beacon = datetime(?, \'unixepoch\')' => $o{beacon}) : (), - - # some applets have incorrect gamename lists, let udpticker update this - # entry instead. this way, applets don't overwrite with incorrect data - #$o{gamename} ? ('gamename = ?' => lc $o{gamename}) : (), - ); - - my($q, @p) = sqlprint("UPDATE serverlist !H - WHERE ip = ? AND port = ?", \%H, $o{ip}, $o{heartbeat}); - - my $n = $self->{dbh}->do($q, undef, @p); - - # if serverlist was updated - return 0 if ($n > 0); - - # try updating it in pending - %H = ( - $o{added} ? ( 'added = ?' => $o{added}) : (), - $o{secure} ? ( 'secure = ?' => $o{secure}) : (), - $o{beaconport} ? ('beaconport = ?' => $o{beaconport}) : (), - ); - - ($q, @p) = sqlprint("UPDATE pending !H - WHERE ip = ? AND heartbeat = ?", \%H, $o{ip}, $o{heartbeat}); - - # exec query - $n = $self->{dbh}->do($q, undef, @p); + my %o = (@_); - # if beacon was updated - return 1 if ($n > 0); - # if not found at all, add to pending - $n = $self->{dbh}->do( - "INSERT INTO pending ( - ip, - beaconport, - heartbeat, - gamename, - secure) - SELECT ?, ?, ?, ?, ?", - undef, $o{ip}, $o{beaconport}, $o{heartbeat}, lc $o{gamename}, $o{secure}); - - # it was added to pending - return 2 if ($n > 0); + return $self->{dbh}->do( + "INSERT INTO serverlist (ip, port, hostport, country) VALUES (?, ?, ?, ?)", + undef, $o{ip}, $o{port}, $o{hostport}, $self->ip2country($o{ip}) ); } ################################################################################ ## Update the server info in the serverlist +## required: id or ip + port/hostport ################################################################################ -sub update_server_list { +sub update_server { my $self = shift; - my %o = ( - updated => time, - @_); + my %o = (updated => time, @_); + + # either id, ip+port or ip+hostport are provided. + my %W = ( + $o{id} ? ( 'id = ?' => $o{id}) : (), + $o{ip} ? ( 'ip = ?' => $o{ip}) : (), + $o{port} ? ( 'port = ?' => $o{port}) : (), + $o{hostport} ? ('hostport = ?' => $o{hostport}) : (), + ); - # try updating it in serverlist + # update where possible my %H = ( $o{gamename} ? ('gamename = ?' => lc $o{gamename}) : (), $o{gamever} ? ( 'gamever = ?' => $o{gamever}) : (), $o{hostname} ? ('hostname = ?' => $o{hostname}) : (), $o{hostport} ? ('hostport = ?' => $o{hostport}) : (), - $o{updated} ? ( 'updated = datetime(?, \'unixepoch\')' => $o{updated}) : (), + $o{direct} ? ( 'b333ms = CAST(? AS BOOLEAN)' => $o{direct}) : (), + $o{direct} ? ( 'beacon = datetime(?, \'unixepoch\')' => $o{updated}) : (), + $o{updated} ? ( 'updated = datetime(?, \'unixepoch\')' => $o{updated}) : (), ); - my($q, @p) = sqlprint("UPDATE serverlist !H - WHERE ip = ? AND port = ?", \%H, $o{ip}, $o{port}); - + my($q, @p) = sqlprint("UPDATE serverlist !H !W", \%H, \%W); return $self->{dbh}->do($q, undef, @p); } ################################################################################ -## beacon was verified or otherwise accepted and will now be added to the -## serverlist. +## check if an ip, port/hostport combination is recent in the serverlist. +## if not, add the address to the pending list ################################################################################ -sub add_server_list { +sub insert_pending { my $self = shift; - my %o = @_; + my %o = (updated => 3600, @_ ); - # insert basic data - return $self->{dbh}->do("INSERT INTO serverlist (ip, port, gamename, country) - SELECT ?, ?, ?, ?", undef, - $o{ip}, $o{port}, lc $o{gamename}, $self->ip2country($o{ip})); -} - -################################################################################ -## add new addresses to the pending list, but do not update timestamps. masters -## that sync with each other would otherwise update the timestamp for a server -## which is no longer online. -################################################################################ -sub syncer_add { - my ($self, $ip, $port, $gamename, $secure) = @_; - - # if address is in the list AND up to date, - # acknowledge its existance but don't do anything with it - my $u = $self->{dbh}->do( - "SELECT count(*) FROM serverlist - WHERE ip = ? - AND port = ? - AND updated > datetime(?, 'unixepoch')", - undef, $ip, $port, time-7200); - - # if found, return 0 - return 0 if ($u > 0); + # selection criteria + my %W = ( + $o{ip} ? ( 'ip = ?' => $o{ip}) : (), + $o{port} ? ( 'port = ?' => $o{port}) : (), + $o{updated} ? ('updated > datetime(?, \'unixepoch\')' => (time-$o{updated})) : (), + ); - # if it is already in the pending list, update it with a new challenge - $u = $self->{dbh}->do( - "UPDATE pending - SET secure = ? - WHERE ip = ? - AND heartbeat = ?", - undef, $secure, $ip, $port); + # determine if it already exsits + my($q, @p) = sqlprint("SELECT id FROM serverlist !W", \%W); + my $u = $self->{dbh}->do($q, undef, @p); + return 0 if int($u); - # notify - #$self->log("update","$ip:$port was updated by syncer") if ($u > 0); - - # return 1 if found - return 1 if ($u > 0); - - # if not found or out of date, add it to pending to be checked again - $u = $self->{dbh}->do( - "INSERT INTO pending (ip, heartbeat, gamename, secure) - SELECT ?, ?, ?, ?", - undef, $ip, $port, lc $gamename, $secure); - - # notify - #$self->log("add","beacon: $ip:$port was added for $gamename after sync") if ($u > 0); - - # return 2 if added new - return 2 if ($u > 0); - - # or else report error - $self->log("error", "an error occurred adding $ip:$port after sync"); - return -1; + # else, insert in pending (duplicates may exist -- see remove_pending) + return $self->{dbh}->do("INSERT INTO pending (ip, heartbeat) VALUES (?, ?)", + undef, $o{ip}, $o{port} ); } 1; diff --git a/lib/MasterServer/Database/SQLite/dbAppletActions.pm b/lib/MasterServer/Database/SQLite/dbAppletActions.pm index d2421fc..1d9c975 100755 --- a/lib/MasterServer/Database/SQLite/dbAppletActions.pm +++ b/lib/MasterServer/Database/SQLite/dbAppletActions.pm @@ -3,7 +3,6 @@ package MasterServer::Database::SQLite::dbAppletActions; use strict; use warnings; use Exporter 'import'; - our @EXPORT = qw| add_master_applet update_master_applet reset_master_applets @@ -18,19 +17,20 @@ sub add_master_applet { my %o = @_; my $u = $self->{dbh}->do( - "SELECT * FROM appletlist - WHERE ip = ? - AND port = ? + "SELECT * FROM appletlist + WHERE ip = ? + AND hostport = ? AND gamename = ?", - undef, $o{ip}, $o{port}, lc $o{gamename}); + undef, $o{ip}, $o{hostport}, lc $o{gamename}); # return if found return if ($u > 0); # insert applet data - return $self->{dbh}->do("INSERT INTO appletlist (ip, port, gamename) - SELECT ?, ?, ?", undef, - $o{ip}, $o{port}, lc $o{gamename}); + return $self->{dbh}->do( + "INSERT INTO appletlist (ip, hostport, gamename) + SELECT ?, ?, ?", + undef, $o{ip}, $o{hostport}, lc $o{gamename}); } ################################################################################ @@ -38,10 +38,11 @@ sub add_master_applet { ################################################################################ sub reset_master_applets { my $self = shift; - return $self->{dbh}->do("UPDATE appletlist - SET added = datetime(?, \'unixepoch\'), - updated = datetime(?, \'unixepoch\')", - undef, time, time); + return $self->{dbh}->do( + "UPDATE appletlist + SET added = datetime(?, \'unixepoch\'), + updated = datetime(?, \'unixepoch\')", + undef, time, time); } ################################################################################ @@ -50,12 +51,13 @@ sub reset_master_applets { sub update_master_applet { my ($self, %o) = @_; - return $self->{dbh}->do("UPDATE appletlist - SET updated = datetime(?, \'unixepoch\') - WHERE ip = ? - AND port = ? + return $self->{dbh}->do( + "UPDATE appletlist + SET updated = datetime(?, \'unixepoch\') + WHERE ip = ? + AND hostport = ? AND gamename = ?", - undef, time, $o{ip}, $o{port}, lc $o{gamename}); + undef, time, $o{ip}, $o{hostport}, lc $o{gamename}); } ################################################################################ @@ -66,10 +68,10 @@ sub get_masterserver_applets { my $self = shift; return $self->db_all( - "SELECT * - FROM appletlist - WHERE updated > datetime(?, \'unixepoch\')", - time-604800); + "SELECT * + FROM appletlist + WHERE updated > datetime(?, \'unixepoch\')", + time-604800); } ################################################################################ @@ -82,11 +84,12 @@ sub remove_unresponsive_applets { # remove entries my $u = $self->{dbh}->do( - "DELETE FROM appletlist - WHERE updated < datetime(?, \'unixepoch\')", undef, time-604800); + "DELETE FROM appletlist + WHERE updated < datetime(?, \'unixepoch\')", + undef, time-604800); # notify - $self->log("delete", "Removed $u entries from applet list.") if ($u > 0); + $self->log("delete", "Removed $u entries from applet list") if ($u > 0); } 1; diff --git a/lib/MasterServer/Database/SQLite/dbCiphers.pm b/lib/MasterServer/Database/SQLite/dbCiphers.pm index f257b7b..1032b4e 100755 --- a/lib/MasterServer/Database/SQLite/dbCiphers.pm +++ b/lib/MasterServer/Database/SQLite/dbCiphers.pm @@ -3,19 +3,17 @@ package MasterServer::Database::SQLite::dbCiphers; use strict; use warnings; use Exporter 'import'; - our @EXPORT = qw| check_cipher_count clear_ciphers insert_cipher - get_game_props - get_gamenames |; + get_game_props |; ################################################################################ ## Check if ciphers exist ################################################################################ sub check_cipher_count { my $self = shift; - return $self->db_all('SELECT count(*) as num from games')->[0]->{num}; + return $self->db_all('SELECT count(gamename) as num from games')->[0]->{num}; } ################################################################################ @@ -23,9 +21,7 @@ sub check_cipher_count { ################################################################################ sub clear_ciphers { my $self = shift; - - # delete ALL entries - my $u = $self->{dbh}->do("DELETE FROM games"); + $self->{dbh}->do("DELETE FROM games"); } ################################################################################ @@ -56,10 +52,7 @@ sub insert_cipher { ################################################################################ sub get_game_props { my $s = shift; - my %o = ( - sort => '', - @_ - ); + my %o = (sort => '', @_); my %where = ( $o{gamename} ? ('gamename = ?' => lc $o{gamename}) : (), @@ -90,18 +83,4 @@ sub get_game_props { ); } - -################################################################################ -## get a list of distinct gamenames currently in the database. it does not -## matter whether they are recent or old, as long as the game is currently in -## the database. -################################################################################ -sub get_gamenames { - my $self = shift; - - return $self->{dbh}->selectall_arrayref( - "SELECT distinct gamename - FROM serverlist"); -} - 1; diff --git a/lib/MasterServer/Database/SQLite/dbCore.pm b/lib/MasterServer/Database/SQLite/dbCore.pm index 0772a4e..d16cb26 100755 --- a/lib/MasterServer/Database/SQLite/dbCore.pm +++ b/lib/MasterServer/Database/SQLite/dbCore.pm @@ -4,72 +4,47 @@ use strict; use warnings; use POSIX qw/strftime/; use Exporter 'import'; - our @EXPORT = qw| database_login dump_database |; ################################################################################ ## login to the database with credentials provided in the config file. ## returns dbh object or quits application on error. -## -## Recommended database types: Postgresql, MySQL or SQLite. Warranty void if -## other database types are used. Use at your own risk. ################################################################################ sub database_login { my $self = shift; - - # get db info my @db_type = split(':', $self->{dblogin}->[0]); - # inform what db we try to load - $self->log("info","Database: $db_type[1], $db_type[2]"); - # check if database file exists my $db_file = [split(':', $self->{dblogin}->[0])]->[2]; $db_file =~ s/dbname=//i; + # fatal error if database does not exist unless (-e $db_file) { - # fatal error $self->log("fatal", "Database file $db_file does not exist!"); - - # end program $self->halt(); } - # create the dbi object + # connect to SQLite database my $dbh = DBI->connect(@{$self->{dblogin}}, {PrintError => 1}); # verify that the database connected if (defined $dbh) { - - # log the event - $self->log("info","Connected to the $db_type[1] database."); - - # turn on error printing + $self->log("info","Connected to the $db_type[1] database $db_type[2]"); $dbh->{printerror} = 1; - # synchronous read/writing to the SQLite file OFF. That means: when the script - # shuts down unexpectedly, i.e. because of power failure or a crash, changes - # to the database are NOT SAVED. However, if this setting is not turned OFF, - # it takes too long to write to the database, which means that new beacons, - # requests and servers cannot be processed. You don't have a choice, really.. + # synchronous read/writing to the SQLite file OFF. Faster, but risk on data + # loss on crashes, premature exits or power failure. $dbh->do("PRAGMA synchronous = OFF"); # allow the use of foreign keys (referencing) $dbh->do("PRAGMA foreign_keys = ON"); - - # return the dbi object for further use return $dbh; } else { # fatal error $self->log("fatal", "$DBI::errstr!"); - - # end program $self->halt(); } - - # in case of any other error, return undef. - return undef; } ################################################################################ @@ -78,21 +53,15 @@ sub database_login { ################################################################################ sub dump_database { my $self = shift; - - # filename / time my $time = strftime('%Y-%m-%d-%H-%M',localtime); # read db credentials from db login my @db_type = split ':', $self->{dblogin}->[0]; $db_type[2] =~ s/dbname=//; - - # split db path my @db_path = split '/', $db_type[2]; - # use pg_dump to dump Postgresql databases + # make a copy of the database file system("cp $db_type[2] $self->{root}/data/dumps/SQLite-$time-$db_path[-1]"); - - # log $self->log("dump", "Dumping database to /data/dumps/SQLite-$time-$db_path[-1]"); } diff --git a/lib/MasterServer/Database/SQLite/dbGetServers.pm b/lib/MasterServer/Database/SQLite/dbGetServers.pm index 719e00a..7ddce2b 100755 --- a/lib/MasterServer/Database/SQLite/dbGetServers.pm +++ b/lib/MasterServer/Database/SQLite/dbGetServers.pm @@ -3,37 +3,35 @@ package MasterServer::Database::SQLite::dbGetServers; use strict; use warnings; use Exporter 'import'; - our @EXPORT = qw| get_server get_pending |; ################################################################################ ## get server details for one or multiple servers -## opts: limit, see $order +## opts: limit, see %where or $order ################################################################################ sub get_server { my $s = shift; - my %o = ( - sort => '', - @_ - ); + my %o = (sort => '', blacklisted => 0, @_); my %where = ( - $o{next_id} ? ( 'id > ?' => $o{next_id}) : (), - $o{id} ? ( 'id = ?' => $o{id}) : (), - $o{ip} ? ( 'ip = ?' => $o{ip}) : (), - $o{port} ? ( 'port = ?' => $o{port}) : (), - $o{gamename} ? ( 'gamename = ?' => lc $o{gamename}) : (), - $o{gamever} ? ( 'gamever = ?' => $o{gamever}) : (), - $o{hostname} ? ( 'hostname = ?' => $o{hostname}) : (), - $o{hostport} ? ( 'hostport = ?' => $o{hostport}) : (), - $o{country} ? ( 'country = ?' => $o{country}) : (), - $o{b333ms} ? ( 'b333ms = ?' => $o{b333ms}) : (), - $o{blacklisted} ? ('blacklisted = ?' => $o{blacklisted}) : (), + $o{next_id} ? ( 'id > ?' => $o{next_id}) : (), + $o{id} ? ( 'id = ?' => $o{id}) : (), + $o{ip} ? ( 'ip = ?' => $o{ip}) : (), + $o{port} ? ( 'port = ?' => $o{port}) : (), + $o{gamename} ? ('gamename = ?' => lc $o{gamename}) : (), + $o{gamever} ? ( 'gamever = ?' => $o{gamever}) : (), + $o{hostname} ? ('hostname = ?' => $o{hostname}) : (), + $o{hostport} ? ('hostport = ?' => $o{hostport}) : (), + $o{country} ? ( 'country = ?' => $o{country}) : (), + $o{b333ms} ? ( 'b333ms = ?' => $o{b333ms}) : (), $o{added} ? ( 'added < datetime(?, \'unixepoch\')' => (time-$o{added})) : (), $o{beacon} ? ( 'beacon > datetime(?, \'unixepoch\')' => (time-$o{beacon})) : (), $o{updated} ? ('updated > datetime(?, \'unixepoch\')' => (time-$o{updated})) : (), $o{before} ? ('updated < datetime(?, \'unixepoch\')' => (time-$o{before})) : (), + + # never process blacklisted servers, unless explicitly specified + ('blacklisted = CAST(? AS BOOLEAN)' => $o{blacklisted}), ); my @select = ( qw| @@ -79,45 +77,29 @@ sub get_server { ################################################################################ ## get server details for one or multiple pending servers -## opts: limit, next_id, beaconport, heartbeat, gamename, secure, enctype, added +## opts: limit, next_id, ip, heartbeat port ################################################################################ sub get_pending { my $s = shift; - my %o = ( - sort => '', - @_ - ); + my %o = (sort => '', @_); my %where = ( $o{next_id} ? ( 'id > ?' => $o{next_id}) : (), $o{id} ? ( 'id = ?' => $o{id}) : (), $o{ip} ? ( 'ip = ?' => $o{ip}) : (), - $o{beaconport} ? ('beaconport = ?' => $o{beaconport}) : (), $o{heartbeat} ? ( 'heartbeat = ?' => $o{heartbeat}) : (), - $o{gamename} ? ( 'gamename = ?' => lc $o{gamename}) : (), - $o{secure} ? ( 'secure = ?' => $o{secure}) : (), - $o{enctype} ? ( 'enctype = ?' => $o{enctype}) : (), - $o{added} ? ('added < datetime(?, \'unixepoch\')' => (time-$o{added})) : (), - $o{after} ? ('added > datetime(?, \'unixepoch\')' => (time-$o{after})) : (), ); - my @select = ( qw| id ip beaconport heartbeat gamename secure enctype added |,); + my @select = ( qw| id ip heartbeat added |,); my $order = sprintf { id => 'id %s', ip => 'ip %s', - beaconport => 'beaconport %s', heartbeat => 'heartbeat %s', - gamename => 'gamename %s', - secure => 'secure %s', - enctype => 'enctype %s', added => 'added %s', }->{ $o{sort}||'id' }, $o{reverse} ? 'DESC' : 'ASC'; - return $s->db_all( q| - SELECT !s FROM pending - !W - ORDER BY !s| - .($o{limit} ? " LIMIT ?" : ""), + return $s->db_all( q| SELECT !s FROM pending !W ORDER BY !s| + .($o{limit} ? " LIMIT ?" : ""), join(', ', @select), \%where, $order, ($o{limit} ? $o{limit} : ()), ); } diff --git a/lib/MasterServer/Database/SQLite/dbMaintenance.pm b/lib/MasterServer/Database/SQLite/dbMaintenance.pm index 019f31f..ec9b5cc 100755 --- a/lib/MasterServer/Database/SQLite/dbMaintenance.pm +++ b/lib/MasterServer/Database/SQLite/dbMaintenance.pm @@ -1,9 +1,8 @@ -package MasterServer::Database::SQLite::dbMaintenance; +package MasterServer::Database::Pg::dbMaintenance; use strict; use warnings; use Exporter 'import'; - our @EXPORT = qw| delete_old_pending remove_pending |; @@ -12,14 +11,10 @@ our @EXPORT = qw| delete_old_pending ## where the server is unresponsive for more than 1 hour ################################################################################ sub delete_old_pending { - my ($self) = shift; - - # remove servers + my $self = shift; my $u = $self->{dbh}->do( "DELETE FROM pending - WHERE added < datetime(?, 'unixepoch')", undef, time-3600); - - # notify + WHERE added < datetime(?, \'unixepoch\')", undef, time-3600); $self->log("delete", "Removed $u entries from pending.") if ($u > 0); } @@ -28,13 +23,11 @@ sub delete_old_pending { ## of error(s). ################################################################################ sub remove_pending { - my ($self, $id) = @_; - - # if address is in list, update the timestamp - my $u = $self->{dbh}->do("DELETE FROM pending WHERE id = ?", undef, $id); - - # notify - $self->log("delete", "removed pending id $id from pending") if ($u > 0); + my $self = shift; + my %o = ( @_); + my $u = $self->{dbh}->do("DELETE FROM pending WHERE ip = ? AND heartbeat = ?", + undef, $o{ip}, $o{port}); + $self->log("delete", "removed $o{ip}, $o{port} from pending (".$u."x)") if ($u > 0); } 1; diff --git a/lib/MasterServer/Database/SQLite/dbStats.pm b/lib/MasterServer/Database/SQLite/dbStats.pm index 1cf94e6..57d3100 100755 --- a/lib/MasterServer/Database/SQLite/dbStats.pm +++ b/lib/MasterServer/Database/SQLite/dbStats.pm @@ -3,41 +3,47 @@ package MasterServer::Database::SQLite::dbStats; use strict; use warnings; use Exporter 'import'; - -our @EXPORT = qw| get_gamelist_stats - write_direct_beacons +our @EXPORT = qw| get_gamenames + get_gamestats + get_listedstats write_stat + write_direct_beacons write_kfstats |; ################################################################################ -# calculate stats for all individual games +## get a list of distinct gamenames currently in the server list ################################################################################ -sub get_gamelist_stats { +sub get_gamenames { my $self = shift; - return $self->{dbh}->selectall_arrayref( - "SELECT DISTINCT gamename AS gamename, - COUNT(NULLIF(b333ms AND updated > datetime(?, 'unixepoch'), 0)) AS numdirect, - COUNT(NULLIF(updated > datetime(?, 'unixepoch'), 0)) AS numtotal - FROM serverlist - GROUP BY gamename", undef, time-7200, time-7200); + "SELECT distinct gamename + FROM serverlist"); } ################################################################################ -# Determine from the last beacon whether the server is still uplinking -# directly to us, or whether it stopped uplinking and is now artificially -# kept in the database. +## get statistics (num_direct, num_total) per gamename ################################################################################ -sub write_direct_beacons { +sub get_gamestats { + my ($self, $gn) = @_; + return $self->db_all( + "SELECT COUNT(CASE WHEN b333ms THEN 1 END) as num_uplink, count(*) as num_total + FROM serverlist + WHERE gamename = ? AND updated > datetime(?, \'unixepoch\')", + lc $gn, time-7200); +} + +################################################################################ +## get a list of distinct gamenames currently in the database. it does not +## matter whether they are recent or old, as long as the game is currently in +## the database. +################################################################################ +sub get_listedstats { my $self = shift; - my $u = $self->{dbh}->do( - "UPDATE serverlist - SET b333ms = 0 - WHERE beacon < datetime(?, 'unixepoch') AND b333ms", - undef, time-3600); - - # notify - $self->log("unset", "Lost $u direct beacons.") if ($u > 0); + return $self->{dbh}->selectall_arrayref( + "SELECT gamename + FROM games + WHERE num_uplink > 0 + OR num_total > 0"); } ################################################################################ @@ -45,19 +51,28 @@ sub write_direct_beacons { # A stat can not exist without existing gamename. Was inserted by cipher loader. ################################################################################ sub write_stat { - my ($self, %opt) = @_; - - # if it is already in the pending list, update it with a new challenge - my $u = $self->{dbh}->do( + my ($self, %o) = @_; + return $self->{dbh}->do( "UPDATE games SET num_uplink = ?, num_total = ? WHERE gamename = ?", - undef, $opt{num_uplink}, $opt{num_total}, lc $opt{gamename}); - - # notify - $self->log("update", "Updated stats for $opt{gamename}.") if ($u > 0); + undef, $o{num_uplink}, $o{num_total}, lc $o{gamename}); +} +################################################################################ +# Determine from the last beacon whether the server is still uplinking +# directly to us, or whether it stopped uplinking and is now artificially +# kept in the database. +################################################################################ +sub write_direct_beacons { + my $self = shift; + my $u = $self->{dbh}->do( + "UPDATE serverlist + SET b333ms = CAST(0 AS BOOLEAN) + WHERE beacon < datetime(?, \'unixepoch\') AND b333ms", + undef, time-3600); + $self->log("unset", "Lost $u direct beacons.") if ($u > 0); } ################################################################################ @@ -66,7 +81,7 @@ sub write_stat { sub write_kfstats { my ($self, $h) = @_; - # check if entry already excists. + # check if entry already exists. my $u = $self->{dbh}->selectall_arrayref( "SELECT * FROM kfstats WHERE UTkey = ? ", undef, $h->{UTkey}); @@ -74,13 +89,11 @@ sub write_kfstats { $u = $self->{dbh}->do( "INSERT INTO kfstats (UTkey, Username) VALUES (?,?)", undef, $h->{UTkey}, $h->{Username}); - - # notify $self->log("kfnew", "New KF Player $h->{Username} added"); } # update existing information - $u = $self->{dbh}->do("UPDATE kfstats SET + $self->{dbh}->do("UPDATE kfstats SET Username = ?, CurrentVeterancy = ?, TotalKills = ?, diff --git a/lib/MasterServer/Database/SQLite/dbUTServerInfo.pm b/lib/MasterServer/Database/SQLite/dbUTServerInfo.pm index 119900b..5a579ac 100755 --- a/lib/MasterServer/Database/SQLite/dbUTServerInfo.pm +++ b/lib/MasterServer/Database/SQLite/dbUTServerInfo.pm @@ -5,127 +5,12 @@ use warnings; use MasterServer::Core::Util 'sqlprint'; use Exporter 'import'; -our @EXPORT = qw| get_utserver - add_utserver +our @EXPORT = qw| add_utserver update_utserver delete_utplayers insert_utplayer |; ################################################################################ -## get server details for one or multiple UT servers -## opts: limit, see $order -################################################################################ -sub get_utserver { - my $s = shift; - my %o = ( - sort => '', - @_ - ); - - my %where = ( - $o{id} ? ('server_id = ?' => $o{id}) : (), - $o{minnetver} ? ('minnetver = ?' => $o{minnetver}) : (), - $o{gamever} ? ('gamever = ?' => $o{gamever}) : (), - $o{location} ? ('location = ?' => $o{location}) : (), - $o{listenserver} ? ('listenserver = ?' => $o{listenserver}) : (), - $o{hostport} ? ('hostport = ?' => $o{hostport}) : (), - $o{hostname} ? ('hostname = ?' => $o{hostname}) : (), - $o{adminname} ? ('adminname = ?' => $o{adminname}) : (), - $o{adminemail} ? ('adminemail = ?' => $o{adminemail}) : (), - $o{password} ? ('password = ?' => $o{password}) : (), - $o{gametype} ? ('gametype = ?' => $o{gametype}) : (), - $o{gamestyle} ? ('gamestyle = ?' => $o{gamestyle}) : (), - $o{changelevels} ? ('changelevels = ?' => $o{changelevels}) : (), - $o{maptitle} ? ('maptitle = ?' => $o{maptitle}) : (), - $o{mapname} ? ('mapname = ?' => $o{mapname}) : (), - $o{numplayers} ? ('numplayers = ?' => $o{numplayers}) : (), - $o{maxplayers} ? ('maxplayers = ?' => $o{maxplayers}) : (), - $o{minplayers} ? ('minplayers = ?' => $o{minplayers}) : (), - $o{botskill} ? ('botskill = ?' => $o{botskill}) : (), - $o{balanceteams} ? ('balanceteams = ?' => $o{balanceteams}) : (), - $o{playersbalanceteams} ? ('playersbalanceteams = ?' => $o{playersbalanceteams}) : (), - $o{friendlyfire} ? ('friendlyfire = ?' => $o{friendlyfire}) : (), - $o{maxteams} ? ('maxteams = ?' => $o{maxteams}) : (), - $o{timelimit} ? ('timelimit = ?' => $o{timelimit}) : (), - $o{goalteamscore} ? ('goalteamscore = ?' => $o{goalteamscore}) : (), - $o{fraglimit} ? ('fraglimit = ?' => $o{fraglimit}) : (), - $o{mutators} ? ('hostname ILIKE ?' => "%$o{mutators}%") : (), - $o{updated} ? ('updated > datetime(?, \'unixepoch\')'=> (time-$o{updated})) : (), - ); - - my @select = ( qw| - server_id - minnetver - gamever - location - listenserver - hostport - hostname - adminname - adminemail - password - gametype - gamestyle - changelevels - maptitle - mapname - numplayers - maxplayers - minplayers - botskill - balanceteams - playersbalanceteams - friendlyfire - maxteams - timelimit - goalteamscore - fraglimit - mutators - updated - |); - - my $order = sprintf { - server_id => 'server_id %s', - minnetver => 'minnetver %s', - gamever => 'gamever %s', - location => 'location %s', - listenserver => 'listenserver %s', - hostport => 'hostport %s', - hostname => 'hostname %s', - adminname => 'adminname %s', - adminemail => 'adminemail %s', - password => 'password %s', - gametype => 'gametype %s', - gamestyle => 'gamestyle %s', - changelevels => 'changelevels %s', - maptitle => 'maptitle %s', - mapname => 'mapname %s', - numplayers => 'numplayers %s', - maxplayers => 'maxplayers %s', - minplayers => 'minplayers %s', - botskill => 'botskill %s', - balanceteams => 'balanceteams %s', - playersbalanceteams => 'playersbalanceteams %s', - friendlyfire => 'friendlyfire %s', - maxteams => 'maxteams %s', - timelimit => 'timelimit %s', - goalteamscore => 'goalteamscore %s', - fraglimit => 'fraglimit %s', - mutators => 'mutators %s', - updated => 'updated %s', - }->{ $o{sort}||'server_id' }, $o{reverse} ? 'DESC' : 'ASC'; - - return $s->db_all( q| - SELECT !s FROM utserver_info - !W - ORDER BY !s| - .($o{limit} ? " LIMIT ?" : ""), - join(', ', @select), \%where, $order, ($o{limit} ? $o{limit} : ()), - ); -} - - -################################################################################ ## Update serverinfo for an existing address to the utserver list. ## opts: all server info data fields. ################################################################################ |
