Comparing sensitive data, confidential files or internal emails?

Most legal and privacy policies prohibit uploading sensitive data online. Diffchecker Desktop ensures your confidential information never leaves your computer. Work offline and compare documents securely.

Its just a little change on the normal in game display behavior

Created Diff never expires
0 removals
939 lines
0 additions
939 lines
#!/usr/bin/perl
#!/usr/bin/perl
# HLstatsX Community Edition - Real-time player and clan rankings and statistics
# HLstatsX Community Edition - Real-time player and clan rankings and statistics
# Copyleft (L) 2008-20XX Nicholas Hastings (nshastings@gmail.com)
# Copyleft (L) 2008-20XX Nicholas Hastings (nshastings@gmail.com)
# http://www.hlxcommunity.com
# http://www.hlxcommunity.com
#
#
# HLstatsX Community Edition is a continuation of
# HLstatsX Community Edition is a continuation of
# ELstatsNEO - Real-time player and clan rankings and statistics
# ELstatsNEO - Real-time player and clan rankings and statistics
# Copyleft (L) 2008-20XX Malte Bayer (steam@neo-soft.org)
# Copyleft (L) 2008-20XX Malte Bayer (steam@neo-soft.org)
# http://ovrsized.neo-soft.org/
# http://ovrsized.neo-soft.org/
#
#
# ELstatsNEO is an very improved & enhanced - so called Ultra-Humongus Edition of HLstatsX
# ELstatsNEO is an very improved & enhanced - so called Ultra-Humongus Edition of HLstatsX
# HLstatsX - Real-time player and clan rankings and statistics for Half-Life 2
# HLstatsX - Real-time player and clan rankings and statistics for Half-Life 2
# http://www.hlstatsx.com/
# http://www.hlstatsx.com/
# Copyright (C) 2005-2007 Tobias Oetzel (Tobi@hlstatsx.com)
# Copyright (C) 2005-2007 Tobias Oetzel (Tobi@hlstatsx.com)
#
#
# HLstatsX is an enhanced version of HLstats made by Simon Garner
# HLstatsX is an enhanced version of HLstats made by Simon Garner
# HLstats - Real-time player and clan rankings and statistics for Half-Life
# HLstats - Real-time player and clan rankings and statistics for Half-Life
# http://sourceforge.net/projects/hlstats/
# http://sourceforge.net/projects/hlstats/
# Copyright (C) 2001 Simon Garner
# Copyright (C) 2001 Simon Garner
#
#
# This program is free software; you can redistribute it and/or
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# of the License, or (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
# For support and installation notes visit http://www.hlxcommunity.com
# For support and installation notes visit http://www.hlxcommunity.com


use strict;
use strict;
no strict 'vars';
no strict 'vars';


$SIG{HUP} = 'HUP_handler';
$SIG{HUP} = 'HUP_handler';
$SIG{INT} = 'INT_handler'; # unix
$SIG{INT} = 'INT_handler'; # unix
$SIG{INT2} = 'INT_handler'; # windows
$SIG{INT2} = 'INT_handler'; # windows


##
##
## Settings
## Settings
##
##


# $opt_configfile - Absolute path and filename of configuration file.
# $opt_configfile - Absolute path and filename of configuration file.
$opt_configfile = "./hlstats.conf";
$opt_configfile = "./hlstats.conf";


# $opt_libdir - Directory to look in for local required files
# $opt_libdir - Directory to look in for local required files
# (our *.plib, *.pm files).
# (our *.plib, *.pm files).
$opt_libdir = "./";
$opt_libdir = "./";




##
##
##
##
################################################################################
################################################################################
## No need to edit below this line
## No need to edit below this line
##
##


use Getopt::Long;
use Getopt::Long;
use Time::Local;
use Time::Local;
use IO::Socket;
use IO::Socket;
use IO::Select;
use IO::Select;
use DBI;
use DBI;
use Digest::MD5;
use Digest::MD5;
use Encode;
use Encode;
use bytes;
use bytes;


require "$opt_libdir/ConfigReaderSimple.pm";
require "$opt_libdir/ConfigReaderSimple.pm";
require "$opt_libdir/TRcon.pm";
require "$opt_libdir/TRcon.pm";
require "$opt_libdir/BASTARDrcon.pm";
require "$opt_libdir/BASTARDrcon.pm";
require "$opt_libdir/HLstats_Server.pm";
require "$opt_libdir/HLstats_Server.pm";
require "$opt_libdir/HLstats_Player.pm";
require "$opt_libdir/HLstats_Player.pm";
require "$opt_libdir/HLstats_Game.pm";
require "$opt_libdir/HLstats_Game.pm";
do "$opt_libdir/HLstats_GameConstants.plib";
do "$opt_libdir/HLstats_GameConstants.plib";
do "$opt_libdir/HLstats.plib";
do "$opt_libdir/HLstats.plib";
do "$opt_libdir/HLstats_EventHandlers.plib";
do "$opt_libdir/HLstats_EventHandlers.plib";


$|=1;
$|=1;
Getopt::Long::Configure ("bundling");
Getopt::Long::Configure ("bundling");


$last_trend_timestamp = 0;
$last_trend_timestamp = 0;


binmode STDIN, ":utf8";
binmode STDIN, ":utf8";
binmode STDOUT, ":utf8";
binmode STDOUT, ":utf8";


##
##
## Functions
## Functions
##
##


sub lookupPlayer
sub lookupPlayer
{
{
my ($saddr, $id, $uniqueid) = @_;
my ($saddr, $id, $uniqueid) = @_;
if (defined($g_servers{$saddr}->{"srv_players"}->{"$id/$uniqueid"}))
if (defined($g_servers{$saddr}->{"srv_players"}->{"$id/$uniqueid"}))
{
{
return $g_servers{$saddr}->{"srv_players"}->{"$id/$uniqueid"};
return $g_servers{$saddr}->{"srv_players"}->{"$id/$uniqueid"};
}
}
return undef;
return undef;
}
}


sub removePlayer
sub removePlayer
{
{
my ($saddr, $id, $uniqueid, $dontUpdateCount) = @_;
my ($saddr, $id, $uniqueid, $dontUpdateCount) = @_;
my $deleteplayer = 0;
my $deleteplayer = 0;
if(defined($g_servers{$saddr}->{"srv_players"}->{"$id/$uniqueid"}))
if(defined($g_servers{$saddr}->{"srv_players"}->{"$id/$uniqueid"}))
{
{
$deleteplayer = 1;
$deleteplayer = 1;
}
}
else
else
{
{
&::printEvent("400", "Bad attempted delete ($saddr) ($id/$uniqueid)");
&::printEvent("400", "Bad attempted delete ($saddr) ($id/$uniqueid)");
}
}


if ($deleteplayer == 1) {
if ($deleteplayer == 1) {
$g_servers{$saddr}->{"srv_players"}->{"$id/$uniqueid"}->playerCleanup();
$g_servers{$saddr}->{"srv_players"}->{"$id/$uniqueid"}->playerCleanup();
delete($g_servers{$saddr}->{"srv_players"}->{"$id/$uniqueid"});
delete($g_servers{$saddr}->{"srv_players"}->{"$id/$uniqueid"});
if (!$dontUpdateCount) # double negative, i know...
if (!$dontUpdateCount) # double negative, i know...
{
{
$g_servers{$saddr}->updatePlayerCount();
$g_servers{$saddr}->updatePlayerCount();
}
}
}
}
}
}


sub checkBonusRound
sub checkBonusRound
{
{
if ($g_servers{$s_addr}->{bonusroundtime} > 0 && ($::ev_remotetime > ($g_servers{$s_addr}->{bonusroundtime_ts} + $g_servers{$s_addr}->{bonusroundtime}))) {
if ($g_servers{$s_addr}->{bonusroundtime} > 0 && ($::ev_remotetime > ($g_servers{$s_addr}->{bonusroundtime_ts} + $g_servers{$s_addr}->{bonusroundtime}))) {
if ($g_servers{$s_addr}->{bonusroundtime_state} == 1) {
if ($g_servers{$s_addr}->{bonusroundtime_state} == 1) {
&printEvent("SERVER", "Bonus Round Expired",1);
&printEvent("SERVER", "Bonus Round Expired",1);
}
}
$g_servers{$s_addr}->set("bonusroundtime_state",0);
$g_servers{$s_addr}->set("bonusroundtime_state",0);
}
}
if($g_servers{$s_addr}->{bonusroundignore} == 1 && $g_servers{$s_addr}->{bonusroundtime_state} == 1) {
if($g_servers{$s_addr}->{bonusroundignore} == 1 && $g_servers{$s_addr}->{bonusroundtime_state} == 1) {
return 1;
return 1;
}
}
return 0;
return 0;
}
}


sub is_number ($) { ( $_[0] ^ $_[0] ) eq '0' }
sub is_number ($) { ( $_[0] ^ $_[0] ) eq '0' }




#
#
# void printNotice (string notice)
# void printNotice (string notice)
#
#
# Prins a debugging notice to stdout.
# Prins a debugging notice to stdout.
#
#


sub printNotice
sub printNotice
{
{
my ($notice) = @_;
my ($notice) = @_;
if ($g_debug > 1) {
if ($g_debug > 1) {
print ">> $notice\n";
print ">> $notice\n";
}
}
}
}


sub track_hlstats_trend
sub track_hlstats_trend
{
{
if ($last_trend_timestamp > 0) {
if ($last_trend_timestamp > 0) {
if ($last_trend_timestamp+299 < $ev_daemontime) {
if ($last_trend_timestamp+299 < $ev_daemontime) {
my $query = "
my $query = "
SELECT
SELECT
COUNT(*),
COUNT(*),
a.game
a.game
FROM
FROM
hlstats_Players a
hlstats_Players a
INNER JOIN
INNER JOIN
(
(
SELECT
SELECT
game
game
FROM
FROM
hlstats_Servers
hlstats_Servers
GROUP BY
GROUP BY
game
game
) AS b
) AS b
ON
ON
a.game = b.game
a.game = b.game
GROUP BY
GROUP BY
a.game
a.game
";
";
my $result = &execCached("get_total_player_counts", $query);
my $result = &execCached("get_total_player_counts", $query);
my $insvalues = "";
my $insvalues = "";
while ( my($total_players, $game) = $result->fetchrow_array) {
while ( my($total_players, $game) = $result->fetchrow_array) {
my $query = "
my $query = "
SELECT
SELECT
SUM(kills),
SUM(kills),
SUM(headshots),
SUM(headshots),
COUNT(serverId),
COUNT(serverId),
SUM(act_players),
SUM(act_players),
SUM(max_players)
SUM(max_players)
FROM
FROM
hlstats_Servers
hlstats_Servers
WHERE
WHERE
game=?
game=?
";
";
my $data = &execCached("get_game_stat_counts", $query, &quoteSQL($game));
my $data = &execCached("get_game_stat_counts", $query, &quoteSQL($game));
my ($total_kills, $total_headshots, $total_servers, $act_slots, $max_slots) = $data->fetchrow_array;
my ($total_kills, $total_headshots, $total_servers, $act_slots, $max_slots) = $data->fetchrow_array;
if ($max_slots > 0) {
if ($max_slots > 0) {
if ($act_slots > $max_slots) {
if ($act_slots > $max_slots) {
$act_slots = $max_slots;
$act_slots = $max_slots;
}
}
}
}
if ($insvalues ne "") {
if ($insvalues ne "") {
$insvalues .= ",";
$insvalues .= ",";
}
}
$insvalues .= "
$insvalues .= "
(
(
$ev_daemontime,
$ev_daemontime,
'".&quoteSQL($game)."',
'".&quoteSQL($game)."',
$total_players,
$total_players,
$total_kills,
$total_kills,
$total_headshots,
$total_headshots,
$total_servers,
$total_servers,
$act_slots,
$act_slots,
$max_slots
$max_slots
)
)
";
";
}
}
if ($insvalues ne "") {
if ($insvalues ne "") {
&execNonQuery("
&execNonQuery("
INSERT INTO
INSERT INTO
hlstats_Trend
hlstats_Trend
(
(
timestamp,
timestamp,
game,
game,
players,
players,
kills,
kills,
headshots,
headshots,
servers,
servers,
act_slots,
act_slots,
max_slots
max_slots
)
)
VALUES $insvalues
VALUES $insvalues
");
");
}
}
$last_trend_timestamp = $ev_daemontime;
$last_trend_timestamp = $ev_daemontime;
&::printEvent("HLSTATSX", "Insert new server trend timestamp", 1);
&::printEvent("HLSTATSX", "Insert new server trend timestamp", 1);
}
}
} else {
} else {
$last_trend_timestamp = $ev_daemontime;
$last_trend_timestamp = $ev_daemontime;
}
}
}
}


sub send_global_chat
sub send_global_chat
{
{
my ($message) = @_;
my ($message) = @_;
while( my($server) = each(%g_servers))
while( my($server) = each(%g_servers))
{
{
if ($server ne $s_addr && $g_servers{$server}->{"srv_players"})
if ($server ne $s_addr && $g_servers{$server}->{"srv_players"})
{
{
my @userlist;
my @userlist;
my %players_temp=%{$g_servers{$server}->{"srv_players"}};
my %players_temp=%{$g_servers{$server}->{"srv_players"}};
my $pcount = scalar keys %players_temp;
my $pcount = scalar keys %players_temp;
if ($pcount > 0) {
if ($pcount > 0) {
while ( my($pl, $b_player) = each(%players_temp) ) {
while ( my($pl, $b_player) = each(%players_temp) ) {
my $b_userid = $b_player->{userid};
my $b_userid = $b_player->{userid};
if ($g_global_chat == 2) {
if ($g_global_chat == 2) {
my $b_steamid = $b_player->{uniqueid};
my $b_steamid = $b_player->{uniqueid};
if ($g_servers{$server}->is_admin($b_steamid) == 1) {
if ($g_servers{$server}->is_admin($b_steamid) == 1) {
if (($b_player->{display_events} == 1) && ($b_player->{display_chat} == 1)) {
if (($b_player->{display_events} == 1) && ($b_player->{display_chat} == 1)) {
push(@userlist, $b_player->{userid});
push(@userlist, $b_player->{userid});
}
}
}
}
} else {
} else {
if (($b_player->{display_events} == 1) && ($b_player->{display_chat} == 1)) {
if (($b_player->{display_events} == 1) && ($b_player->{display_chat} == 1)) {
push(@userlist, $b_player->{userid});
push(@userlist, $b_player->{userid});
}
}
}
}
}
}
$g_servers{$server}->messageMany($message, 0, @userlist);
$g_servers{$server}->messageMany($message, 0, @userlist);
}
}
}
}
}
}
}
}


#
#
# void buildEventInsertData ()
# void buildEventInsertData ()
#
#
# Ran at startup to init event table queues, build initial queries, and set allowed-null columns
# Ran at startup to init event table queues, build initial queries, and set allowed-null columns
#
#


my %g_eventtable_data = ();
my %g_eventtable_data = ();


sub buildEventInsertData
sub buildEventInsertData
{
{
my $insertType = "";
my $insertType = "";
$insertType = " DELAYED" if ($db_lowpriority);
$insertType = " DELAYED" if ($db_lowpriority);
while ( my ($table, $colsref) = each(%g_eventTables) )
while ( my ($table, $colsref) = each(%g_eventTables) )
{
{
$g_eventtable_data{$table}{queue} = [];
$g_eventtable_data{$table}{queue} = [];
$g_eventtable_data{$table}{nullallowed} = 0;
$g_eventtable_data{$table}{nullallowed} = 0;
$g_eventtable_data{$table}{lastflush} = $ev_daemontime;
$g_eventtable_data{$table}{lastflush} = $ev_daemontime;
$g_eventtable_data{$table}{query} = "
$g_eventtable_data{$table}{query} = "
INSERT$insertType INTO
INSERT$insertType INTO
hlstats_Events_$table
hlstats_Events_$table
(
(
eventTime,
eventTime,
serverId,
serverId,
map"
map"
;
;
my $j = 0;
my $j = 0;
foreach $i (@{$colsref})
foreach $i (@{$colsref})
{
{
$g_eventtable_data{$table}{query} .= ",\n$i";
$g_eventtable_data{$table}{query} .= ",\n$i";
if (substr($i, 0, 4) eq 'pos_') {
if (substr($i, 0, 4) eq 'pos_') {
$g_eventtable_data{$table}{nullallowed} |= (1 << $j);
$g_eventtable_data{$table}{nullallowed} |= (1 << $j);
}
}
$j++;
$j++;
}
}
$g_eventtable_data{$table}{query} .= ") VALUES\n";
$g_eventtable_data{$table}{query} .= ") VALUES\n";
}
}
}
}


#
#
# void recordEvent (string table, array cols, bool getid, [mixed eventData ...])
# void recordEvent (string table, array cols, bool getid, [mixed eventData ...])
#
#
# Queues an event for addition to an Events table, flushing when hitting table queue limit.
# Queues an event for addition to an Events table, flushing when hitting table queue limit.
#
#


sub recordEvent
sub recordEvent
{
{
my $table = shift;
my $table = shift;
my $unused = shift;
my $unused = shift;
my @coldata = @_;
my @coldata = @_;
my $value = "(FROM_UNIXTIME($::ev_unixtime),".$g_servers{$s_addr}->{'id'}.",'".quoteSQL($g_servers{$s_addr}->get_map())."'";
my $value = "(FROM_UNIXTIME($::ev_unixtime),".$g_servers{$s_addr}->{'id'}.",'".quoteSQL($g_servers{$s_addr}->get_map())."'";
$j = 0;
$j = 0;
for $i (@coldata) {
for $i (@coldata) {
if ($g_eventtable_data{$table}{nullallowed} & (1 << $j) && (!defined($i) || $i eq "")) {
if ($g_eventtable_data{$table}{nullallowed} & (1 << $j) && (!defined($i) || $i eq "")) {
$value .= ",NULL";
$value .= ",NULL";
} elsif (!defined($i)) {
} elsif (!defined($i)) {
$value .= ",''";
$value .= ",''";
} else {
} else {
$value .= ",'".quoteSQL($i)."'";
$value .= ",'".quoteSQL($i)."'";
}
}
$j++;
$j++;
}
}
$value .= ")";
$value .= ")";
push(@{$g_eventtable_data{$table}{queue}}, $value);
push(@{$g_eventtable_data{$table}{queue}}, $value);
if (scalar(@{$g_eventtable_data{$table}{queue}}) > $g_event_queue_size)
if (scalar(@{$g_eventtable_data{$table}{queue}}) > $g_event_queue_size)
{
{
flushEventTable($table);
flushEventTable($table);
}
}
}
}


sub flushEventTable
sub flushEventTable
{
{
my ($table) = @_;
my ($table) = @_;
if (scalar(@{$g_eventtable_data{$table}{queue}}) == 0)
if (scalar(@{$g_eventtable_data{$table}{queue}}) == 0)
{
{
return;
return;
}
}
my $query = $g_eventtable_data{$table}{query};
my $query = $g_eventtable_data{$table}{query};
foreach (@{$g_eventtable_data{$table}{queue}})
foreach (@{$g_eventtable_data{$table}{queue}})
{
{
$query .= $_.",";
$query .= $_.",";
}
}
$query =~ s/,$//;
$query =~ s/,$//;
execNonQuery($query);
execNonQuery($query);
$g_eventtable_data{$table}{lastflush} = $ev_daemontime;
$g_eventtable_data{$table}{lastflush} = $ev_daemontime;
$g_eventtable_data{$table}{queue} = [];
$g_eventtable_data{$table}{queue} = [];
}
}




#
#
# array calcSkill (int skill_mode, int killerSkill, int killerKills, int victimSkill, int victimKills, string weapon)
# array calcSkill (int skill_mode, int killerSkill, int killerKills, int victimSkill, int victimKills, string weapon)
#
#
# Returns an array, where the first index contains the killer's new skill, and
# Returns an array, where the first index contains the killer's new skill, and
# the second index contains the victim's new skill.
# the second index contains the victim's new skill.
#
#


sub calcSkill
sub calcSkill
{
{
my ($skill_mode, $killerSkill, $killerKills, $victimSkill, $victimKills, $weapon, $killerTeam) = @_;
my ($skill_mode, $killerSkill, $killerKills, $victimSkill, $victimKills, $weapon, $killerTeam) = @_;
my @newSkill;
my @newSkill;
# ignored bots never do a "comeback"
# ignored bots never do a "comeback"
return ($g_skill_minchange, $victimSkill) if ($killerSkill < 1);
return ($g_skill_minchange, $victimSkill) if ($killerSkill < 1);
return ($killerSkill + $g_skill_minchange, $victimSkill) if ($victimSkill < 1);
return ($killerSkill + $g_skill_minchange, $victimSkill) if ($victimSkill < 1);
if ($g_debug > 2) {
if ($g_debug > 2) {
&printNotice("Begin calcSkill: killerSkill=$killerSkill");
&printNotice("Begin calcSkill: killerSkill=$killerSkill");
&printNotice("Begin calcSkill: victimSkill=$victimSkill");
&printNotice("Begin calcSkill: victimSkill=$victimSkill");
}
}


my $modifier = 1.00;
my $modifier = 1.00;
# Look up the weapon's skill modifier
# Look up the weapon's skill modifier
if (defined($g_games{$g_servers{$s_addr}->{game}}{weapons}{$weapon})) {
if (defined($g_games{$g_servers{$s_addr}->{game}}{weapons}{$weapon})) {
$modifier = $g_games{$g_servers{$s_addr}->{game}}{weapons}{$weapon}{modifier};
$modifier = $g_games{$g_servers{$s_addr}->{game}}{weapons}{$weapon}{modifier};
}
}


# Calculate the new skills
# Calculate the new skills
my $killerSkillChange = 0;
my $killerSkillChange = 0;
if ($g_skill_ratio_cap > 0) {
if ($g_skill_ratio_cap > 0) {
# SkillRatioCap, from *XYZ*SaYnt
# SkillRatioCap, from *XYZ*SaYnt
#
#
# dgh...we want to cap the ratio between the victimkill and killerskill. For example, if the number 1 player
# dgh...we want to cap the ratio between the victimkill and killerskill. For example, if the number 1 player
# kills a newbie, he gets 1000/5000 * 5 * 1 = 1 points. If gets killed by the newbie, he gets 5000/1000 * 5 *1
# kills a newbie, he gets 1000/5000 * 5 * 1 = 1 points. If gets killed by the newbie, he gets 5000/1000 * 5 *1
# = -25 points. Not exactly fair. To fix this, I'm going to cap the ratio to 1/2 and 2/1.
# = -25 points. Not exactly fair. To fix this, I'm going to cap the ratio to 1/2 and 2/1.
# these numbers are designed such that an excellent player will have to get about a 2:1 ratio against noobs to
# these numbers are designed such that an excellent player will have to get about a 2:1 ratio against noobs to
# hold steady in points.
# hold steady in points.
my $lowratio = 0.7;
my $lowratio = 0.7;
my $highratio = 1.0 / $lowratio;
my $highratio = 1.0 / $lowratio;
my $ratio = ($victimSkill / $killerSkill);
my $ratio = ($victimSkill / $killerSkill);
if ($ratio < $lowratio) { $ratio = $lowratio; }
if ($ratio < $lowratio) { $ratio = $lowratio; }
if ($ratio > $highratio) { $ratio = $highratio; }
if ($ratio > $highratio) { $ratio = $highratio; }
$killerSkillChange = $ratio * 5 * $modifier;
$killerSkillChange = $ratio * 5 * $modifier;
} else {
} else {
$killerSkillChange = ($victimSkill / $killerSkill) * 5 * $modifier;
$killerSkillChange = ($victimSkill / $killerSkill) * 5 * $modifier;
}
}


if ($killerSkillChange > $g_skill_maxchange) {
if ($killerSkillChange > $g_skill_maxchange) {
&printNotice("Capping killer skill change of $killerSkillChange to $g_skill_maxchange") if ($g_debug > 2);
&printNotice("Capping killer skill change of $killerSkillChange to $g_skill_maxchange") if ($g_debug > 2);
$killerSkillChange = $g_skill_maxchange;
$killerSkillChange = $g_skill_maxchange;
}
}
my $victimSkillChange = $killerSkillChange;
my $victimSkillChange = $killerSkillChange;


if ($skill_mode == 1)
if ($skill_mode == 1)
{
{
$victimSkillChange = $killerSkillChange * 0.75;
$victimSkillChange = $killerSkillChange * 0.75;
}
}
elsif ($skill_mode == 2)
elsif ($skill_mode == 2)
{
{
$victimSkillChange = $killerSkillChange * 0.5;
$victimSkillChange = $killerSkillChange * 0.5;
}
}
elsif ($skill_mode == 3)
elsif ($skill_mode == 3)
{
{
$victimSkillChange = $killerSkillChange * 0.25;
$victimSkillChange = $killerSkillChange * 0.25;
}
}
elsif ($skill_mode == 4)
elsif ($skill_mode == 4)
{
{
$victimSkillChange = 0;
$victimSkillChange = 0;
}
}
elsif ($skill_mode == 5)
elsif ($skill_mode == 5)
{
{
#Zombie Panic: Source only
#Zombie Panic: Source only
#Method suggested by heimer. Survivor's lose half of killer's gain when dying, but Zombie's only lose a quarter.
#Method suggested by heimer. Survivor's lose half of killer's gain when dying, but Zombie's only lose a quarter.
if ($killerTeam eq "Undead")
if ($killerTeam eq "Undead")
{
{
$victimSkillChange = $killerSkillChange * 0.5;
$victimSkillChange = $killerSkillChange * 0.5;
}
}
elsif ($killerTeam eq "Survivor")
elsif ($killerTeam eq "Survivor")
{
{
$victimSkillChange = $killerSkillChange * 0.25;
$victimSkillChange = $killerSkillChange * 0.25;
}
}
}
}
if ($victimSkillChange > $g_skill_maxchange) {
if ($victimSkillChange > $g_skill_maxchange) {
&printNotice("Capping victim skill change of $victimSkillChange to $g_skill_maxchange") if ($g_debug > 2);
&printNotice("Capping victim skill change of $victimSkillChange to $g_skill_maxchange") if ($g_debug > 2);
$victimSkillChange = $g_skill_maxchange;
$victimSkillChange = $g_skill_maxchange;
}
}
if ($g_skill_maxchange >= $g_skill_minchange) {
if ($g_skill_maxchange >= $g_skill_minchange) {
if ($killerSkillChange < $g_skill_minchange) {
if ($killerSkillChange < $g_skill_minchange) {
&printNotice("Capping killer skill change of $killerSkillChange to $g_skill_minchange") if ($g_debug > 2);
&printNotice("Capping killer skill change of $killerSkillChange to $g_skill_minchange") if ($g_debug > 2);
$killerSkillChange = $g_skill_minchange;
$killerSkillChange = $g_skill_minchange;
}
}
if (($victimSkillChange < $g_skill_minchange) && ($skill_mode != 4)) {
if (($victimSkillChange < $g_skill_minchange) && ($skill_mode != 4)) {
&printNotice("Capping victim skill change of $victimSkillChange to $g_skill_minchange") if ($g_debug > 2);
&printNotice("Capping victim skill change of $victimSkillChange to $g_skill_minchange") if ($g_debug > 2);
$victimSkillChange = $g_skill_minchange;
$victimSkillChange = $g_skill_minchange;
}
}
}
}
if (($killerKills < $g_player_minkills ) || ($victimKills < $g_player_minkills )) {
if (($killerKills < $g_player_minkills ) || ($victimKills < $g_player_minkills )) {
$killerSkillChange = $g_skill_minchange;
$killerSkillChange = $g_skill_minchange;
if ($skill_mode != 4) {
if ($skill_mode != 4) {
$victimSkillChange = $g_skill_minchange;
$victimSkillChange = $g_skill_minchange;
} else {
} else {
$victimSkillChange = 0;
$victimSkillChange = 0;
}
}
}
}
$killerSkill += $killerSkillChange;
$killerSkill += $killerSkillChange;
$victimSkill -= $victimSkillChange;
$victimSkill -= $victimSkillChange;
# we want int not float
# we want int not float
$killerSkill = sprintf("%d", $killerSkill + 0.5);
$killerSkill = sprintf("%d", $killerSkill + 0.5);
$victimSkill = sprintf("%d", $victimSkill + 0.5);
$victimSkill = sprintf("%d", $victimSkill + 0.5);
if ($g_debug > 2) {
if ($g_debug > 2) {
&printNotice("End calcSkill: killerSkill=$killerSkill");
&printNotice("End calcSkill: killerSkill=$killerSkill");
&printNotice("End calcSkill: victimSkill=$victimSkill");
&printNotice("End calcSkill: victimSkill=$victimSkill");
}
}


return ($killerSkill, $victimSkill);
return ($killerSkill, $victimSkill);
}
}


sub calcL4DSkill
sub calcL4DSkill
{
{
my ($killerSkill, $weapon, $difficulty) = @_;
my ($killerSkill, $weapon, $difficulty) = @_;
# ignored bots never do a "comeback"
# ignored bots never do a "comeback"
#return ($killerSkill, $victimSkill) if ($killerSkill < 1);
#return ($killerSkill, $victimSkill) if ($killerSkill < 1);
#return ($killerSkill, $victimSkill) if ($victimSkill < 1);
#return ($killerSkill, $victimSkill) if ($victimSkill < 1);
if ($g_debug > 2) {
if ($g_debug > 2) {
&printNotice("Begin calcSkill: killerSkill=$killerSkill");
&printNotice("Begin calcSkill: killerSkill=$killerSkill");
&printNotice("Begin calcSkill: victimSkill=$victimSkill");
&printNotice("Begin calcSkill: victimSkill=$victimSkill");
}
}


my $modifier = 1.00;
my $modifier = 1.00;
# Look up the weapon's skill modifier
# Look up the weapon's skill modifier
if (defined($g_games{$g_servers{$s_addr}->{game}}{weapons}{$weapon})) {
if (defined($g_games{$g_servers{$s_addr}->{game}}{weapons}{$weapon})) {
$modifier = $g_games{$g_servers{$s_addr}->{game}}{weapons}{$weapon}{modifier};
$modifier = $g_games{$g_servers{$s_addr}->{game}}{weapons}{$weapon}{modifier};
}
}
# Calculate the new skills
# Calculate the new skills
$diffweight=0.5;
$diffweight=0.5;
if ($difficulty > 0) {
if ($difficulty > 0) {
$diffweight = $difficulty / 2;
$diffweight = $difficulty / 2;
}
}
my $killerSkillChange = $pointvalue * $diffweight;
my $killerSkillChange = $pointvalue * $diffweight;


if ($killerSkillChange > $g_skill_maxchange) {
if ($killerSkillChange > $g_skill_maxchange) {
&printNotice("Capping killer skill change of $killerSkillChange to $g_skill_maxchange") if ($g_debug > 2);
&printNotice("Capping killer skill change of $killerSkillChange to $g_skill_maxchange") if ($g_debug > 2);
$killerSkillChange = $g_skill_maxchange;
$killerSkillChange = $g_skill_maxchange;
}
}


if ($g_skill_maxchange >= $g_skill_minchange) {
if ($g_skill_maxchange >= $g_skill_minchange) {
if ($killerSkillChange < $g_skill_minchange) {
if ($killerSkillChange < $g_skill_minchange) {
&printNotice("Capping killer skill change of $killerSkillChange to $g_skill_minchange") if ($g_debug > 2);
&printNotice("Capping killer skill change of $killerSkillChange to $g_skill_minchange") if ($g_debug > 2);
$killerSkillChange = $g_skill_minchange;
$killerSkillChange = $g_skill_minchange;
}
}
}
}
$killerSkill += $killerSkillChange;
$killerSkill += $killerSkillChange;
# we want int not float
# we want int not float
$killerSkill = sprintf("%d", $killerSkill + 0.5);
$killerSkill = sprintf("%d", $killerSkill + 0.5);
if ($g_debug > 2) {
if ($g_debug > 2) {
&printNotice("End calcSkill: killerSkill=$killerSkill");
&printNotice("End calcSkill: killerSkill=$killerSkill");
}
}
return $killerSkill;
return $killerSkill;
}
}




# Gives members of 'team' an extra 'reward' skill points. Members of the team
# Gives members of 'team' an extra 'reward' skill points. Members of the team
# who have been inactive (no events) for more than 2 minutes are not rewarded.
# who have been inactive (no events) for more than 2 minutes are not rewarded.
#
#


sub rewardTeam
sub rewardTeam
{
{
my ($team, $reward, $actionid, $actionname, $actioncode) = @_;
my ($team, $reward, $actionid, $actionname, $actioncode) = @_;
$rcmd = $g_servers{$s_addr}->{broadcasting_command};
$rcmd = $g_servers{$s_addr}->{broadcasting_command};
my $player;
my $player;
&printNotice("Rewarding team \"$team\" with \"$reward\" skill for action \"$actionid\" ...");
&printNotice("Rewarding team \"$team\" with \"$reward\" skill for action \"$actionid\" ...");
my @userlist;
my @userlist;
foreach $player (values(%g_players)) {
foreach $player (values(%g_players)) {
my $player_team = $player->{team};
my $player_team = $player->{team};
my $player_timestamp = $player->{timestamp};
my $player_timestamp = $player->{timestamp};
if (($g_servers{$s_addr}->{ignore_bots} == 1) && (($player->{is_bot} == 1) || ($player->{userid} <= 0))) {
if (($g_servers{$s_addr}->{ignore_bots} == 1) && (($player->{is_bot} == 1) || ($player->{userid} <= 0))) {
$desc = "(IGNORED) BOT: ";
$desc = "(IGNORED) BOT: ";
} else {
} else {
if ($player_team eq $team) {
if ($player_team eq $team) {
if ($g_debug > 2) {
if ($g_debug > 2) {
&printNotice("Rewarding " . $player->getInfoString() . " with \"$reward\" skill for action \"$actionid\"");
&printNotice("Rewarding " . $player->getInfoString() . " with \"$reward\" skill for action \"$actionid\"");
}
}
&recordEvent(
&recordEvent(
"TeamBonuses", 0,
"TeamBonuses", 0,
$player->{playerid},
$player->{playerid},
$actionid,
$actionid,
$reward
$reward
);
);
$player->increment("skill", $reward, 1);
$player->increment("skill", $reward, 1);
$player->increment("session_skill", $reward, 1);
$player->increment("session_skill", $reward, 1);
$player->updateDB();
$player->updateDB();
}
}
if ($player->{is_bot} == 0 && $player->{userid} > 0 && $player->{display_events} == 1) {
if ($player->{is_bot} == 0 && $player->{userid} > 0 && $player->{display_events} == 1) {
push(@userlist, $player->{userid});
push(@userlist, $player->{userid});
}
}
}
}
}
}
if (($g_servers{$s_addr}->{broadcasting_events} == 1) && ($g_servers{$s_addr}->{broadcasting_player_actions} == 1)) {
if (($g_servers{$s_addr}->{broadcasting_events} == 1) && ($g_servers{$s_addr}->{broadcasting_player_actions} == 1)) {
my $coloraction = $g_servers{$s_addr}->{format_action};
my $coloraction = $g_servers{$s_addr}->{format_action};
my $verb = "got";
my $verb = "got";
if ($reward < 0) {
if ($reward < 0) {
$verb = "lost";
$verb = "lost";
}
}
my $msg = sprintf("%s %s %s points for %s%s", $team, $verb, abs($reward), $coloraction, $actionname);
my $msg = sprintf("%s %s %s points for %s%s", $team, $verb, abs($reward), $coloraction, $actionname);
$g_servers{$s_addr}->messageMany($msg, 0, @userlist);
$g_servers{$s_addr}->messageMany($msg, 0, @userlist);
}
}
}
}




#
#
# int getPlayerId (uniqueId)
# int getPlayerId (uniqueId)
#
#
# Looks up a player's ID number, from their unique (WON) ID. Returns their PID.
# Looks up a player's ID number, from their unique (WON) ID. Returns their PID.
#
#


sub getPlayerId
sub getPlayerId
{
{
my ($uniqueId) = @_;
my ($uniqueId) = @_;


my $query = "
my $query = "
SELECT
SELECT
playerId
playerId
FROM
FROM
hlstats_PlayerUniqueIds
hlstats_PlayerUniqueIds
WHERE
WHERE
uniqueId='" . &::quoteSQL($uniqueId) . "' AND
uniqueId='" . &::quoteSQL($uniqueId) . "' AND
game='" . $g_servers{$s_addr}->{game} . "'
game='" . $g_servers{$s_addr}->{game} . "'
";
";
my $result = &doQuery($query);
my $result = &doQuery($query);


if ($result->rows > 0) {
if ($result->rows > 0) {
my ($playerId) = $result->fetchrow_array;
my ($playerId) = $result->fetchrow_array;
$result->finish;
$result->finish;
return $playerId;
return $playerId;
} else {
} else {
$result->finish;
$result->finish;
return 0;
return 0;
}
}
}
}




#
#
# int updatePlayerProfile (object player, string field, string value)
# int updatePlayerProfile (object player, string field, string value)
#
#
# Updates a player's profile information in the database.
# Updates a player's profile information in the database.
#
#


sub updatePlayerProfile
sub updatePlayerProfile
{
{
my ($player, $field, $value) = @_;
my ($player, $field, $value) = @_;
$rcmd = $g_servers{$s_addr}->{player_command};
$rcmd = $g_servers{$s_addr}->{player_command};
unless ($player) {
unless ($player) {
&printNotice("updatePlayerInfo: Bad player");
&printNotice("updatePlayerInfo: Bad player");
return 0;
return 0;
}
}


$value = &quoteSQL($value);
$value = &quoteSQL($value);
if ($value eq "none" || $value eq " ") {
if ($value eq "none" || $value eq " ") {
$value = "";
$value = "";
}
}
my $playerName = &abbreviate($player->{name});
my $playerName = &abbreviate($player->{name});
my $playerId = $player->{playerid};
my $playerId = $player->{playerid};


&execNonQuery("
&execNonQuery("
UPDATE
UPDATE
hlstats_Players
hlstats_Players
SET
SET
$field='$value'
$field='$value'
WHERE
WHERE
playerId=$playerId
playerId=$playerId
");
");
if ($g_servers{$s_addr}->{player_events} == 1) {
if ($g_servers{$s_addr}->{player_events} == 1) {
my $p_userid = $g_servers{$s_addr}->format_userid($player->{userid});
my $p_userid = $g_servers{$s_addr}->format_userid($player->{userid});
my $p_is_bot = $player->{is_bot};
my $p_is_bot = $player->{is_bot};
$cmd_str = $rcmd." $p_userid ".$g_servers{$s_addr}->quoteparam("SET command successful for '$playerName'.");
$cmd_str = $rcmd." $p_userid ".$g_servers{$s_addr}->quoteparam("SET command successful for '$playerName'.");
$g_servers{$s_addr}->dorcon($cmd_str);
$g_servers{$s_addr}->dorcon($cmd_str);
}
}
return 1;
return 1;
}
}


#
#
# mixed getClanId (string name)
# mixed getClanId (string name)
#
#
# Looks up a player's clan ID from their name. Compares the player's name to tag
# Looks up a player's clan ID from their name. Compares the player's name to tag
# patterns in hlstats_ClanTags. Patterns look like: [AXXXXX] (matches 1 to 6
# patterns in hlstats_ClanTags. Patterns look like: [AXXXXX] (matches 1 to 6
# letters inside square braces, e.g. [ZOOM]Player) or =\*AAXX\*= (matches
# letters inside square braces, e.g. [ZOOM]Player) or =\*AAXX\*= (matches
# 2 to 4 letters between an equals sign and an asterisk, e.g. =*RAGE*=Player).
# 2 to 4 letters between an equals sign and an asterisk, e.g. =*RAGE*=Player).
#
#
# Special characters in the pattern:
# Special characters in the pattern:
# A matches one character (i.e. a character is required)
# A matches one character (i.e. a character is required)
# X matches zero or one characters (i.e. a character is optional)
# X matches zero or one characters (i.e. a character is optional)
# a matches literal A or a
# a matches literal A or a
# x matches literal X or x
# x matches literal X or x
#
#
# If no clan exists for the tag, it will be created. Returns the clan's ID, or
# If no clan exists for the tag, it will be created. Returns the clan's ID, or
# 0 if the player is not in a clan.
# 0 if the player is not in a clan.
#
#


sub getClanId
sub getClanId
{
{
my ($name) = @_;
my ($name) = @_;
my $clanTag = "";
my $clanTag = "";
my $clanName = "";
my $clanName = "";
my $clanId = 0;
my $clanId = 0;
my $result = &doQuery("
my $result = &doQuery("
SELECT
SELECT
pattern,
pattern,
position,
position,
LENGTH(pattern) AS pattern_length
LENGTH(pattern) AS pattern_length
FROM
FROM
hlstats_ClanTags
hlstats_ClanTags
ORDER BY
ORDER BY
pattern_length DESC,
pattern_length DESC,
id
id
");
");
while ( my($pattern, $position) = $result->fetchrow_array) {
while ( my($pattern, $position) = $result->fetchrow_array) {
my $regpattern = quotemeta($pattern);
my $regpattern = quotemeta($pattern);
$regpattern =~ s/([A-Za-z0-9]+[A-Za-z0-9_-]*)/\($1\)/; # to find clan name from tag
$regpattern =~ s/([A-Za-z0-9]+[A-Za-z0-9_-]*)/\($1\)/; # to find clan name from tag
$regpattern =~ s/A/./g;
$regpattern =~ s/A/./g;
$regpattern =~ s/X/.?/g;
$regpattern =~ s/X/.?/g;
if ($g_debug > 2) {
if ($g_debug > 2) {
&printNotice("regpattern=$regpattern");
&printNotice("regpattern=$regpattern");
}
}
if ((($position eq "START" || $position eq "EITHER") && $name =~ /^($regpattern).+/i) ||
if ((($position eq "START" || $position eq "EITHER") && $name =~ /^($regpattern).+/i) ||
(($position eq "END" || $position eq "EITHER") && $name =~ /.+($regpattern)$/i)) {
(($position eq "END" || $position eq "EITHER") && $name =~ /.+($regpattern)$/i)) {
if ($g_debug > 2) {
if ($g_debug > 2) {
&printNotice("pattern \"$regpattern\" matches \"$name\"! 1=\"$1\" 2=\"$2\"");
&printNotice("pattern \"$regpattern\" matches \"$name\"! 1=\"$1\" 2=\"$2\"");
}
}
$clanTag = $1;
$clanTag = $1;
$clanName = $2;
$clanName = $2;
last;
last;
}
}
}
}
unless ($clanTag) {
unless ($clanTag) {
return 0;
return 0;
}
}


my $query = "
my $query = "
SELECT
SELECT
clanId
clanId
FROM
FROM
hlstats_Clans
hlstats_Clans
WHERE
WHERE
tag='" . &quoteSQL($clanTag) . "' AND
tag='" . &quoteSQL($clanTag) . "' AND
game='$g_servers{$s_addr}->{game}'
game='$g_servers{$s_addr}->{game}'
";
";
$result = &doQuery($query);
$result = &doQuery($query);


if ($result->rows) {
if ($result->rows) {
($clanId) = $result->fetchrow_array;
($clanId) = $result->fetchrow_array;
$result->finish;
$result->finish;
return $clanId;
return $clanId;
} else {
} else {
# The clan doesn't exist yet, so we create it.
# The clan doesn't exist yet, so we create it.
$query = "
$query = "
REPLACE INTO
REPLACE INTO
hlstats_Clans
hlstats_Clans
(
(
tag,
tag,
name,
name,
game
game
)
)
VALUES
VALUES
(
(
'" . &quoteSQL($clanTag) . "',
'" . &quoteSQL($clanTag) . "',
'" . &quoteSQL($clanName) . "',
'" . &quoteSQL($clanName) . "',
'".&quoteSQL($g_servers{$s_addr}->{game})."'
'".&quoteSQL($g_servers{$s_addr}->{game})."'
)
)
";
";
&execNonQuery($query);
&execNonQuery($query);
$clanId = $db_conn->{'mysql_insertid'};
$clanId = $db_conn->{'mysql_insertid'};


&printNotice("Created clan \"$clanName\" <C:$clanId> with tag "
&printNotice("Created clan \"$clanName\" <C:$clanId> with tag "
. "\"$clanTag\" for player \"$name\"");
. "\"$clanTag\" for player \"$name\"");
return $clanId;
return $clanId;
}
}
}
}


#
#
# object getServer (string address, int port)
# object getServer (string address, int port)
#
#
# Looks up a server's ID number in the Servers table, by searching for a
# Looks up a server's ID number in the Servers table, by searching for a
# matching IP address and port. NOTE you must specify IP addresses in the
# matching IP address and port. NOTE you must specify IP addresses in the
# Servers table, NOT hostnames.
# Servers table, NOT hostnames.
#
#
# Returns a new "Server object".
# Returns a new "Server object".
#
#


sub getServer
sub getServer
{
{
my ($address, $port) = @_;
my ($address, $port) = @_;


my $query = "
my $query = "
SELECT
SELECT
a.serverId,
a.serverId,
a.game,
a.game,
a.name,
a.name,
a.rcon_password,
a.rcon_password,
a.publicaddress,
a.publicaddress,
IFNULL(b.`value`,3) AS game_engine,
IFNULL(b.`value`,3) AS game_engine,
IFNULL(c.`realgame`, 'hl2mp') AS realgame,
IFNULL(c.`realgame`, 'hl2mp') AS realgame,
IFNULL(a.max_players, 0) AS maxplayers
IFNULL(a.max_players, 0) AS maxplayers
FROM
FROM
hlstats_Servers a LEFT JOIN hlstats_Servers_Config b on a.serverId = b.serverId AND b.`parameter` = 'GameEngine' LEFT JOIN `hlstats_Games` c ON a.game = c.code
hlstats_Servers a LEFT JOIN hlstats_Servers_Config b on a.serverId = b.serverId AND b.`parameter` = 'GameEngine' LEFT JOIN `hlstats_Games` c ON a.game = c.code
WHERE
WHERE
address=? AND
address=? AND
port=? LIMIT 1
port=? LIMIT 1
";
";
my @vals = (
my @vals = (
$address,
$address,
$port
$port
);
);
my $result = &execCached("get_server_information", $query, @vals);
my $result = &execCached("get_server_information", $query, @vals);


if ($result->rows) {
if ($result->rows) {
my ($serverId, $game, $name, $rcon_pass, $publicaddress, $gameengine, $realgame, $maxplayers) = $result->fetchrow_array;
my ($serverId, $game, $name, $rcon_pass, $publicaddress, $gameengine, $realgame, $maxplayers) = $result->fetchrow_array;
$result->finish;
$result->finish;
if (!defined($g_games{$game})) {
if (!defined($g_games{$game})) {
$g_games{$game} = new HLstats_Game($game);
$g_games{$game} = new HLstats_Game($game);
}
}
# l4d code should be reused for l4d2
# l4d code should be reused for l4d2
# trying first using l4d as "realgame" code for l4d2 in db. if default server config settings won't work, will leave as own "realgame" code in db but uncomment line.
# trying first using l4d as "realgame" code for l4d2 in db. if default server config settings won't work, will leave as own "realgame" code in db but uncomment line.
#$realgame = "l4d" if $realgame eq "l4d2";
#$realgame = "l4d" if $realgame eq "l4d2";
return new HLstats_Server($serverId, $address, $port, $name, $rcon_pass, $game, $publicaddress, $gameengine, $realgame, $maxplayers);
return new HLstats_Server($serverId, $address, $port, $name, $rcon_pass, $game, $publicaddress, $gameengine, $realgame, $maxplayers);
} else {
} else {
$result->finish;
$result->finish;
return 0;
return 0;
}
}
}
}


#
#
#
#
#
#
#
#
#
#


sub queryServer
sub queryServer
{
{
my ($iaddr, $iport, @query) = @_;
my ($iaddr, $iport, @query) = @_;
my $game = "";
my $game = "";
my $timeout=2;
my $timeout=2;
my $message = IO::Socket::INET->new(Proto=>"udp",Timeout=>$timeout,PeerPort=>$iport,PeerAddr=>$iaddr) or die "Can't make UDP socket: $@";
my $message = IO::Socket::INET->new(Proto=>"udp",Timeout=>$timeout,PeerPort=>$iport,PeerAddr=>$iaddr) or die "Can't make UDP socket: $@";
$message->send("\xFF\xFF\xFF\xFFTSource Engine Query\x00");
$message->send("\xFF\xFF\xFF\xFFTSource Engine Query\x00");
my ($datagram,$flags);
my ($datagram,$flags);
my $end = time + $timeout;
my $end = time + $timeout;
my $rin = '';
my $rin = '';
vec($rin, fileno($message), 1) = 1;
vec($rin, fileno($message), 1) = 1;


my %hash = ();
my %hash = ();


while (1) {
while (1) {
my $timeleft = $end - time;
my $timeleft = $end - time;
last if ($timeleft <= 0);
last if ($timeleft <= 0);
my ($nfound, $t) = select(my $rout = $rin, undef, undef, $timeleft);
my ($nfound, $t) = select(my $rout = $rin, undef, undef, $timeleft);
last if ($nfound == 0); # either timeout or end of file
last if ($nfound == 0); # either timeout or end of file
$message->recv($datagram,1024,$flags);
$message->recv($datagram,1024,$flags);
@hash{qw/key type netver hostname mapname gamedir gamename id numplayers maxplayers numbots dedicated os passreq secure gamever edf port/} = unpack("LCCZ*Z*Z*Z*vCCCCCCCZ*Cv",$datagram);
@hash{qw/key type netver hostname mapname gamedir gamename id numplayers maxplayers numbots dedicated os passreq secure gamever edf port/} = unpack("LCCZ*Z*Z*Z*vCCCCCCCZ*Cv",$datagram);
}
}


return @hash{@query};
return @hash{@query};
}
}




sub getServerMod
sub getServerMod
{
{
my ($address, $port) = @_;
my ($address, $port) = @_;
my ($playgame);
my ($playgame);


&printEvent ("DETECT", "Querying $address".":$port for gametype");
&printEvent ("DETECT", "Querying $address".":$port for gametype");


my @query = (
my @query = (
'gamename',
'gamename',
'gamedir',
'gamedir',
'hostname',
'hostname',
'numplayers',
'numplayers',
'maxplayers',
'maxplayers',
'mapname'
'mapname'
);
);


my ($gamename, $gamedir, $hostname, $numplayers, $maxplayers, $mapname) = &queryServer($address, $port, @query);
my ($gamename, $gamedir, $hostname, $numplayers, $maxplayers, $mapname) = &queryServer($address, $port, @query);


if ($gamename =~ /^Counter-Strike$/i) {
if ($gamename =~ /^Counter-Strike$/i) {
$playgame = "cstrike";
$playgame = "cstrike";
} elsif ($gamename =~ /^Counter-Strike/i) {
} elsif ($gamename =~ /^Counter-Strike/i) {
$playgame = "css";
$playgame = "css";
} elsif ($gamename =~ /^Team Fortress C/i) {
} elsif ($gamename =~ /^Team Fortress C/i) {
$playgame = "tfc";
$playgame = "tfc";
} elsif ($gamename =~ /^Team Fortress/i) {
} elsif ($gamename =~ /^Team Fortress/i) {
$playgame = "tf";
$playgame = "tf";
} elsif ($gamename =~ /^Day of Defeat$/i) {
} elsif ($gamename =~ /^Day of Defeat$/i) {
$playgame = "dod";
$playgame = "dod";
} elsif ($gamename =~ /^Day of Defeat/i) {
} elsif ($gamename =~ /^Day of Defeat/i) {
$playgame = "dods";
$playgame = "dods";
} elsif ($gamename =~ /^Insurgency/i) {
} elsif ($gamename =~ /^Insurgency/i) {
$playgame = "insmod";
$playgame = "insmod";
} elsif ($gamename =~ /^Neotokyo/i) {
} elsif ($gamename =~ /^Neotokyo/i) {
$playgame = "nts";
$playgame = "nts";
} elsif ($gamename =~ /^Fortress Forever/i) {
} elsif ($gamename =~ /^Fortress Forever/i) {
$playgame = "ff";
$playgame = "ff";
} elsif ($gamename =~ /^Age of Chivalry/i) {
} elsif ($gamename =~ /^Age of Chivalry/i) {
$playgame = "aoc";
$playgame = "aoc";
} elsif ($gamename =~ /^Dystopia/i) {
} elsif ($gamename =~ /^Dystopia/i) {
$playgame = "dystopia";
$playgame = "dystopia";
} elsif ($gamename =~ /^Stargate/i) {
} elsif ($gamename =~ /^Stargate/i) {
$playgame = "sgtls";
$playgame = "sgtls";
} elsif ($gamename =~ /^Battle Grounds/i) {
} elsif ($gamename =~ /^Battle Grounds/i) {
$playgame = "bg2";
$playgame = "bg2";
} elsif ($gamename =~ /^Hidden/i) {
} elsif ($gamename =~ /^Hidden/i) {
$playgame = "hidden";
$playgame = "hidden";
} elsif ($gamename =~ /^L4D /i) {
} elsif ($gamename =~ /^L4D /i) {
$playgame = "l4d";
$playgame = "l4d";
} elsif ($gamename =~ /^Left 4 Dead 2/i) {
} elsif ($gamename =~ /^Left 4 Dead 2/i) {
$playgame = "l4d2";
$playgame = "l4d2";
} elsif ($gamename =~ /^ZPS /i) {
} elsif ($gamename =~ /^ZPS /i) {
$playgame = "zps";
$playgame = "zps";
} elsif ($gamename =~ /^NS /i) {
} elsif ($gamename =~ /^NS /i) {
$playgame = "ns";
$playgame = "ns";
} elsif ($gamename =~ /^pvkii/i) {
} elsif ($gamename =~ /^pvkii/i) {
$playgame = "pvkii";
$playgame = "pvkii";
} elsif ($gamename =~ /^CSPromod/i) {
} elsif ($gamename =~ /^CSPromod/i) {
$playgame = "csp";
$playgame = "csp";
} elsif ($gamename eq "Half-Life") {
} elsif ($gamename eq "Half-Life") {
$playgame = "valve";
$playgame = "valve";
} elsif ($gamename eq "Nuclear Dawn") {
} elsif ($gamename eq "Nuclear Dawn") {
$playgame = "nucleardawn";
$playgame = "nucleardawn";
# We didn't found our mod, trying secondary way. This is required for some games such as FOF and GES and is a fallback for others
# We didn't found our mod, trying secondary way. This is required for some games such as FOF and GES and is a fallback for others
} elsif ($gamedir =~ /^ges/i) {
} elsif ($gamedir =~ /^ges/i) {
$playgame = "ges";
$playgame = "ges";
} elsif ($gamedir =~ /^fistful_of_frags/i || $gamedir =~ /^fof/i) {
} elsif ($gamedir =~ /^fistful_of_frags/i || $gamedir =~ /^fof/i) {
$playgame = "fof";
$playgame = "fof";
} elsif ($gamedir =~ /^hl2mp/i) {
} elsif ($gamedir =~ /^hl2mp/i) {
$playgame = "hl2mp";
$playgame = "hl2mp";
} elsif ($gamedir =~ /^tfc/i) {
} elsif ($gamedir =~ /^tfc/i) {
$playgame = "tfc";
$playgame = "tfc";
} elsif ($gamedir =~ /^tf/i) {
} elsif ($gamedir =~ /^tf/i) {