aboutsummaryrefslogtreecommitdiff
path: root/lib/MasterServer/UDP/UDPTicker.pm
diff options
context:
space:
mode:
authorDarkelarious <darkelarious@333networks.com>2016-11-19 20:56:04 +0100
committerDarkelarious <darkelarious@333networks.com>2016-11-19 20:56:04 +0100
commitc3f8d65a4fb1f5674557ee67cf7f74369df86ad1 (patch)
tree92aab2a394bda28da0ed7c7c75e633fdf386fc71 /lib/MasterServer/UDP/UDPTicker.pm
parent1de3da4b8027508a91144639455c934fd6ccb9b7 (diff)
downloadMasterServer-Perl-c3f8d65a4fb1f5674557ee67cf7f74369df86ad1.tar.gz
MasterServer-Perl-c3f8d65a4fb1f5674557ee67cf7f74369df86ad1.zip
Massive improvements on efficiency, robustness, security, reliability and more
Diffstat (limited to 'lib/MasterServer/UDP/UDPTicker.pm')
-rwxr-xr-xlib/MasterServer/UDP/UDPTicker.pm298
1 files changed, 298 insertions, 0 deletions
diff --git a/lib/MasterServer/UDP/UDPTicker.pm b/lib/MasterServer/UDP/UDPTicker.pm
new file mode 100755
index 0000000..5a34a8f
--- /dev/null
+++ b/lib/MasterServer/UDP/UDPTicker.pm
@@ -0,0 +1,298 @@
+
+package MasterServer::UDP::UDPTicker;
+
+use strict;
+use warnings;
+use AnyEvent::Handle::UDP;
+use Exporter 'import';
+
+our @EXPORT = qw| udp_ticker |;
+
+################################################################################
+## When addresses are stored in the 'pending' list, they are supposed to be
+## queried immediately with the secure/validate challenge to testify that
+## the server is genuine and alive.
+##
+## Some servers do not support the secure-challenge on the Uplink port. These
+## servers are verified with a secure-challenge on their heartbeat ports,
+## which are designed to respond to secure queries, as well as status queries.
+##
+## Addresses collected by other scripts, whether from the UCC applet or manual
+## input via the website, are added to the pending list. It is more
+## important to verify pending beacons and new server addresses, than to
+## update the status of existing addresses. Therefore, pending addresses are
+## prioritized.
+##
+## Another function required for 333networks is the "server info" part of the
+## site. UT servers are queried and stored in the database. This is the lowest
+## priority for the masterserver and is therefore performed last.
+##
+################################################################################
+sub udp_ticker {
+ my $self = shift;
+
+ # inform that we are running
+ $self->log("info", "UDP Ticker is loaded.");
+
+ # queue -- which address is next in line?
+ my %reset = (start => time, id => 0);
+ my %pending = (%reset, c => 0, limit => 900); # 900s ~ 15m
+ my %updater = (%reset, c => 0, limit => 1800); # 1800s ~ 30m
+ my %ut_serv = (%reset, c => 0, limit => 300); # 300s ~ 5m
+ my %oldserv = (%reset, c => 0, limit => 86400); # 86400s ~ 24h
+
+ my $debug_counter = 0;
+
+ # go through all servers that need querying
+ my $server_info = AnyEvent->timer (
+ after => 75, # first give beacons a chance to uplink
+ interval => 0.2, # 5 addresses per second is fast enough
+ cb => sub {
+
+ # after the first full run was completed, reset the counters when loop time expires
+ if (defined $self->{firstrun}) {
+ # reset timer
+ %reset = (start => time, id => 0, c => 0);
+
+ #
+ # it can happen that a run takes more than the allowed time
+ # in that case, allow more time
+ #
+
+ # pending
+ if (time - $pending{start} > $pending{limit}) {
+ if ($pending{c} > 0) {
+ # done within defined time, reset
+ %pending = (%pending, %reset);
+ }
+ }
+
+ # ut servers
+ if (time - $ut_serv{start} > $ut_serv{limit}) {
+ if ($ut_serv{c} > 0) {
+ # done within defined time, reset
+ %ut_serv = (%ut_serv, %reset)
+ }
+ }
+
+ # updater
+ if (time - $updater{start} > $updater{limit}) {
+ if ($updater{c} > 0) {
+ # done within defined time, reset
+ %updater = (%updater, %reset);
+ }
+ }
+
+ # old servers
+ if (time - $oldserv{start} > $oldserv{limit}) {
+ if ($oldserv{c} > 0) {
+ %oldserv = (%oldserv, %reset);
+ }
+ }
+
+ #
+ # else { print "Making overtime!" }
+
+=pod
+ # FIXME remove this if above works
+
+ # debug: detect premature resets
+ if (time - $pending{start} > $pending{limit}) {
+ if ($pending{c} == 0) {
+ print "Premature pending reset\n" ;
+ }
+ else{$pending{c} = 0;}
+ }
+
+ if (time - $updater{start} > $updater{limit}) {
+ if ($updater{c} == 0) {
+ print "Premature updater reset\n" ;
+ }
+ else{$updater{c} = 0;}
+ }
+
+ if (time - $ut_serv{start} > $ut_serv{limit}) {
+ if ($ut_serv{c} == 0) {
+ print "Premature ut_serv reset\n" ;
+ }
+ else{$ut_serv{c} = 0;}
+ }
+
+ if (time - $oldserv{start} > $oldserv{limit}) {
+ if ($oldserv{c} == 0) {
+ print "Premature oldserv reset\n" ;
+ }
+ else{$oldserv{c} = 0;}
+ }
+
+ # are we making overtime on any of the counters yet?
+ %pending = (%pending, %reset) if (time - $pending{start} > $pending{limit});
+ %updater = (%updater, %reset) if (time - $updater{start} > $updater{limit});
+ %ut_serv = (%ut_serv, %reset) if (time - $ut_serv{start} > $ut_serv{limit});
+ %oldserv = (%oldserv, %reset) if (time - $oldserv{start} > $oldserv{limit});
+=cut
+ }
+
+ #
+ # Check pending beacons
+ #
+
+ # pending beacons/servers (15 seconds grace time)
+ my $n = $self->get_pending(
+ next_id => $pending{id},
+ added => 15,
+ sort => "id",
+ limit => 1
+ )->[0] if $self->{beacon_checker_enabled};
+
+ # if next pending server/address exists:
+ if ( $n ) {
+ # next pending id will be > $n
+ $pending{id} = $n->{id};
+
+ # query the server using the heartbeat port provided in the beacon/manual add
+ $self->query_udp_server(
+ $n->{id},
+ $n->{ip},
+ $n->{heartbeat},
+ $n->{secure}, # secure string necessary!
+ 1, # request secure challenge
+ );
+
+ # our work is done for this cycle.
+ return;
+ }
+
+ # pending are done and is allowed to reset at a later stadium
+ $pending{c}++;
+
+
+ #
+ # Query Unreal Tournament 99 (demo) servers for serverstats
+ #
+
+ # next server in line
+ $n = $self->get_server(
+ next_id => $ut_serv{id},
+ updated => 3600,
+ gamename => "ut",
+ sort => "id",
+ limit => 1,
+ )->[0] if $self->{utserver_query_enabled};
+
+ # if next server/address exists:
+ if ( $n ) {
+ #next pending id will be > $n
+ $ut_serv{id} = $n->{id};
+
+ # query the server (no secure string)
+ $self->query_udp_server(
+ $n->{id},
+ $n->{ip},
+ $n->{port},
+ "", # no secure string necessary
+ 2, # request full status info
+ );
+
+ # our work is done for this cycle.
+ return;
+ }
+
+ # ut servers are done and is allowed to reset at a later stadium
+ $ut_serv{c}++;
+
+ #
+ # update existing servers (both ut/non-ut)
+ #
+
+ # next server in line
+ $n = $self->get_server(
+ next_id => $updater{id},
+ updated => 7200,
+ sort => "id",
+ limit => 1,
+ )->[0] if $self->{beacon_checker_enabled};
+
+ # if next server/address exists:
+ if ( $n ) {
+ #next pending id will be > $n
+ $updater{id} = $n->{id};
+
+ # query the server (no secure string)
+ $self->query_udp_server(
+ $n->{id},
+ $n->{ip},
+ $n->{port},
+ "", # no secure string necessary
+ 0, # request info
+ );
+
+ # our work is done for this cycle.
+ return;
+ }
+
+ # updating servers is done and is allowed to reset at a later stadium
+ $updater{c}++;
+
+ #
+ # Query servers older than 2 hours
+ #
+
+ # next server in line
+ $n = $self->get_server(
+ next_id => $oldserv{id},
+ before => 7200,
+ sort => "id",
+ limit => 1,
+ )->[0] if $self->{beacon_checker_enabled};
+
+ # if next server/address exists:
+ if ( $n ) {
+ #next old server id will be > $n
+ $oldserv{id} = $n->{id};
+
+ # query the server (no secure string)
+ $self->query_udp_server(
+ $n->{id},
+ $n->{ip},
+ $n->{port},
+ "", # no secure string necessary
+ 0, # request info
+ );
+
+ # our work is done for this cycle.
+ return;
+ }
+
+ # old servers are done and is allowed to reset at a later stadium
+ $oldserv{c}++;
+
+ # and notify about first run being completed
+ if (!defined $self->{firstrun}) {
+ # inform that first run is completed
+ my $t = time-$self->{firstruntime};
+ my $t_readable = ($t > 60) ? (($t/60). ":". ($t%60). "minutes") : ($t. "seconds");
+
+ $self->log("info", "First run completed after $t_readable.");
+ $self->{firstrun} = 0;
+
+ # reset all counters and follow procedure
+ %reset = (start => time, id => 0, c => 0);
+ %pending = (%pending, %reset);
+ %updater = (%updater, %reset);
+ %ut_serv = (%ut_serv, %reset);
+ %oldserv = (%oldserv, %reset);
+ }
+
+ # At this point, we are out of server entries. From here on, just count
+ # down until the cycle is complete and handle new entries while they are
+ # added to the list.
+
+ }
+ );
+
+ # return the timer object to keep it alive outside of this scope
+ return $server_info;
+}
+
+1;