1415 lines
36 KiB
Perl
1415 lines
36 KiB
Perl
|
package HLstats_Server;
|
||
|
# HLstatsX Community Edition - Real-time player and clan rankings and statistics
|
||
|
# Copyleft (L) 2008-20XX Nicholas Hastings (nshastings@gmail.com)
|
||
|
# http://www.hlxcommunity.com
|
||
|
#
|
||
|
# HLstatsX Community Edition is a continuation of
|
||
|
# ELstatsNEO - Real-time player and clan rankings and statistics
|
||
|
# Copyleft (L) 2008-20XX Malte Bayer (steam@neo-soft.org)
|
||
|
# http://ovrsized.neo-soft.org/
|
||
|
#
|
||
|
# 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
|
||
|
# http://www.hlstatsx.com/
|
||
|
# Copyright (C) 2005-2007 Tobias Oetzel (Tobi@hlstatsx.com)
|
||
|
#
|
||
|
# HLstatsX is an enhanced version of HLstats made by Simon Garner
|
||
|
# HLstats - Real-time player and clan rankings and statistics for Half-Life
|
||
|
# http://sourceforge.net/projects/hlstats/
|
||
|
# Copyright (C) 2001 Simon Garner
|
||
|
#
|
||
|
# This program is free software; you can redistribute it and/or
|
||
|
# modify it under the terms of the GNU General Public License
|
||
|
# as published by the Free Software Foundation; either version 2
|
||
|
# of the License, or (at your option) any later version.
|
||
|
#
|
||
|
# This program is distributed in the hope that it will be useful,
|
||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
# GNU General Public License for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU General Public License
|
||
|
# along with this program; if not, write to the Free Software
|
||
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||
|
#
|
||
|
# For support and installation notes visit http://www.hlxcommunity.com
|
||
|
|
||
|
use POSIX;
|
||
|
use IO::Socket;
|
||
|
use Socket;
|
||
|
use Encode;
|
||
|
|
||
|
do "$::opt_libdir/HLstats_GameConstants.plib";
|
||
|
|
||
|
sub new
|
||
|
{
|
||
|
my ($class_name, $serverId, $address, $port, $server_name, $rcon_pass, $game, $publicaddress, $gameengine, $realgame, $maxplayers) = @_;
|
||
|
|
||
|
my ($self) = {};
|
||
|
|
||
|
bless($self, $class_name);
|
||
|
|
||
|
$self->{id} = $serverId;
|
||
|
$self->{address} = $address;
|
||
|
$self->{port} = $port;
|
||
|
$self->{game} = $game;
|
||
|
$self->{rcon} = $rcon_pass;
|
||
|
$self->{srv_players} = ();
|
||
|
|
||
|
# Game Engine
|
||
|
# HL1 - 1
|
||
|
# HL2 (original) - 2
|
||
|
# HL2ep2 ("OrangeBox") - 3
|
||
|
$self->{game_engine} = $gameengine;
|
||
|
|
||
|
$self->{rcon_obj} = undef;
|
||
|
$self->{name} = $server_name;
|
||
|
$self->{auto_ban} = 0;
|
||
|
$self->{contact} = "";
|
||
|
$self->{hlstats_url} = "";
|
||
|
$self->{publicaddress} = $publicaddress;
|
||
|
$self->{play_game} = -1;
|
||
|
|
||
|
$self->{last_event} = 0;
|
||
|
$self->{last_check} = 0;
|
||
|
|
||
|
$self->{lines} = 0;
|
||
|
$self->{map} = "";
|
||
|
$self->{numplayers} = 0;
|
||
|
$self->{num_trackable_players} = 0;
|
||
|
$self->{minplayers} = 6;
|
||
|
$self->{maxplayers} = $maxplayers;
|
||
|
$self->{difficulty} = 0;
|
||
|
|
||
|
$self->{players} = 0;
|
||
|
$self->{rounds} = 0;
|
||
|
$self->{kills} = 0;
|
||
|
$self->{suicides} = 0;
|
||
|
$self->{headshots} = 0;
|
||
|
$self->{ct_shots} = 0;
|
||
|
$self->{ct_hits} = 0;
|
||
|
$self->{ts_shots} = 0;
|
||
|
$self->{ts_hits} = 0;
|
||
|
$self->{bombs_planted} = 0;
|
||
|
$self->{bombs_defused} = 0;
|
||
|
$self->{ct_wins} = 0;
|
||
|
$self->{ts_wins} = 0;
|
||
|
$self->{map_started} = time();
|
||
|
$self->{map_changes} = 0;
|
||
|
$self->{map_rounds} = 0;
|
||
|
$self->{map_ct_wins} = 0;
|
||
|
$self->{map_ts_wins} = 0;
|
||
|
$self->{map_ct_shots} = 0;
|
||
|
$self->{map_ct_hits} = 0;
|
||
|
$self->{map_ts_shots} = 0;
|
||
|
$self->{map_ts_hits} = 0;
|
||
|
|
||
|
# team balancer
|
||
|
$self->{ba_enabled} = 0;
|
||
|
$self->{ba_ct_wins} = 0;
|
||
|
$self->{ba_ts_win} = 0;
|
||
|
$self->{ba_ct_frags} = 0;
|
||
|
$self->{ba_ts_frags} = 0;
|
||
|
$self->{ba_winner} = ();
|
||
|
$self->{ba_map_rounds} = 0;
|
||
|
$self->{ba_last_swap} = 0;
|
||
|
$self->{ba_player_switch} = 0; # player switched on his own
|
||
|
|
||
|
# Messaging commands
|
||
|
$self->{show_stats} = 0;
|
||
|
$self->{broadcasting_events} = 0;
|
||
|
$self->{broadcasting_player_actions} = 0;
|
||
|
$self->{broadcasting_command} = "";
|
||
|
$self->{broadcasting_command_announce} = "say";
|
||
|
$self->{player_events} = 1;
|
||
|
$self->{player_command} = "say";
|
||
|
$self->{player_command_osd} = "";
|
||
|
$self->{player_command_hint} = "";
|
||
|
$self->{player_admin_command} = 0;
|
||
|
$self->{default_display_events} = 1;
|
||
|
$self->{browse_command} = "";
|
||
|
$self->{swap_command} = "";
|
||
|
$self->{exec_command} = "";
|
||
|
$self->{global_chat_command} = "say";
|
||
|
|
||
|
# Message format operators
|
||
|
$self->{format_color} = "";
|
||
|
$self->{format_action} = "";
|
||
|
$self->{format_actionend} = "";
|
||
|
|
||
|
$self->{total_kills} = 0;
|
||
|
$self->{total_headshots} = 0;
|
||
|
$self->{total_suicides} = 0;
|
||
|
$self->{total_rounds} = 0;
|
||
|
$self->{total_shots} = 0;
|
||
|
$self->{total_hits} = 0;
|
||
|
|
||
|
$self->{track_server_load} = 0;
|
||
|
$self->{track_server_timestamp} = 0;
|
||
|
|
||
|
$self->{ignore_nextban} = ();
|
||
|
$self->{use_browser} = 0;
|
||
|
$self->{round_status} = 0;
|
||
|
$self->{min_players_rank} = 1;
|
||
|
$self->{admins} = ();
|
||
|
$self->{ignore_bots} = 1;
|
||
|
$self->{tk_penalty} = 0;
|
||
|
$self->{suicide_penalty} = 0;
|
||
|
$self->{skill_mode} = 0;
|
||
|
$self->{game_type} = 0;
|
||
|
$self->{bonusroundignore} = 0;
|
||
|
$self->{bonusroundtime} = 0;
|
||
|
$self->{bonusroundtime_ts} = 0;
|
||
|
$self->{bonusroundtime_state} = 0;
|
||
|
$self->{lastdisabledbonus} = $::ev_unixtime;
|
||
|
$self->{mod} = "";
|
||
|
$self->{switch_admins} = 0;
|
||
|
$self->{public_commands} = 1;
|
||
|
$self->{connect_announce} = 1;
|
||
|
$self->{update_hostname} = 0;
|
||
|
|
||
|
$self->{lastblueflagdefend} = 0;
|
||
|
$self->{lastredflagdefend} = 0;
|
||
|
|
||
|
# location hax
|
||
|
$self->{nextkillx} = "";
|
||
|
$self->{nextkilly} = "";
|
||
|
$self->{nextkillz} = "";
|
||
|
$self->{nextkillvicx} = "";
|
||
|
$self->{nextkillvicy} = "";
|
||
|
$self->{nextkillvicz} = "";
|
||
|
|
||
|
$self->{nextkillheadshot} = 0;
|
||
|
|
||
|
$self->{next_timeout} = 0;
|
||
|
$self->{next_flush} = 0;
|
||
|
$self->{next_plyr_flush} = 0;
|
||
|
$self->{needsupdate} = 0;
|
||
|
|
||
|
$self->set_play_game($realgame);
|
||
|
|
||
|
if ($self->{rcon})
|
||
|
{
|
||
|
$self->init_rcon();
|
||
|
}
|
||
|
|
||
|
$self->updateDB();
|
||
|
$self->update_server_loc();
|
||
|
|
||
|
return $self;
|
||
|
}
|
||
|
|
||
|
sub set_play_game
|
||
|
{
|
||
|
my ($self, $realgame) = @_;
|
||
|
|
||
|
if (exists($gamecode_to_game{$realgame}))
|
||
|
{
|
||
|
$self->{play_game} = $gamecode_to_game{$realgame};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub is_admin
|
||
|
{
|
||
|
my($self, $steam_id) = @_;
|
||
|
for (@{$self->{admins}}) {
|
||
|
if ($_ eq $steam_id) {
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
sub get_game_mod_opts
|
||
|
{
|
||
|
# Runs immediately after server object is created and options are loaded.
|
||
|
my($self) = @_;
|
||
|
|
||
|
if ($self->{mod} ne "") {
|
||
|
my $mod = $self->{mod};
|
||
|
|
||
|
if ($mod eq "SOURCEMOD") {
|
||
|
$self->{browse_command} = "hlx_sm_browse";
|
||
|
$self->{swap_command} = "hlx_sm_swap";
|
||
|
$self->{global_chat_command} = "hlx_sm_psay";
|
||
|
$self->setHlxCvars();
|
||
|
} elsif ($mod eq "MANI") {
|
||
|
$self->{browse_command} = "ma_hlx_browse";
|
||
|
$self->{swap_command} = "ma_swapteam";
|
||
|
$self->{exec_command} = "ma_cexec";
|
||
|
$self->{global_chat_command} = "ma_psay";
|
||
|
} elsif ($mod eq "AMXX") {
|
||
|
$self->{browse_command} = "hlx_amx_browse";
|
||
|
$self->{swap_command} = "hlx_amx_swap";
|
||
|
$self->{global_chat_command} = "hlx_amx_psay";
|
||
|
$self->setHlxCvars();
|
||
|
} elsif ($mod eq "BEETLE") {
|
||
|
$self->{browse_command} = "hlx_browse";
|
||
|
$self->{swap_command} = "hlx_swap";
|
||
|
$self->{exec_command} = "hlx_exec";
|
||
|
$self->{global_chat_command} = "admin_psay";
|
||
|
} elsif ($mod eq "MINISTATS") {
|
||
|
$self->{browse_command} = "ms_browse";
|
||
|
$self->{swap_command} = "ms_swap";
|
||
|
$self->{global_chat_command} = "ms_psay";
|
||
|
}
|
||
|
|
||
|
# Turn on color and add game-specific color modifiers for when using hlx:ce sourcemod plugin
|
||
|
if (($self->{mod} eq "SOURCEMOD" &&
|
||
|
(
|
||
|
$self->{play_game} == CSS()
|
||
|
|| $self->{play_game} == TF()
|
||
|
|| $self->{play_game} == L4D()
|
||
|
|| $self->{play_game} == DODS()
|
||
|
|| $self->{play_game} == HL2MP()
|
||
|
|| $self->{play_game} == AOC()
|
||
|
|| $self->{play_game} == ZPS()
|
||
|
|| $self->{play_game} == FF()
|
||
|
|| $self->{play_game} == GES()
|
||
|
|| $self->{play_game} == FOF()
|
||
|
|| $self->{play_game} == PVKII()
|
||
|
|| $self->{play_game} == CSP()
|
||
|
|| $self->{play_game} == NUCLEARDAWN()
|
||
|
|| $self->{play_game} == DDD()
|
||
|
)
|
||
|
)
|
||
|
|| ($self->{mod} eq "AMXX"
|
||
|
&& $self->{play_game} == CSTRIKE())
|
||
|
) {
|
||
|
|
||
|
$self->{format_color} = " 1";
|
||
|
if ($self->{play_game} == ZPS() || $self->{play_game} == GES()) {
|
||
|
$self->{format_action} = "\x05";
|
||
|
} elsif ($self->{play_game} == FF()) {
|
||
|
$self->{format_action} = "^4";
|
||
|
} else {
|
||
|
$self->{format_action} = "\x04";
|
||
|
}
|
||
|
|
||
|
if ($self->{play_game} == FF()) {
|
||
|
$self->{format_actionend} = "^0";
|
||
|
} else {
|
||
|
$self->{format_actionend} = "\x01";
|
||
|
}
|
||
|
}
|
||
|
# Insurgency can only do one solid color afaik. The rest is handled in the plugin
|
||
|
if ($self->{mod} eq "SOURCEMOD" && $self->{play_game} == INSMOD()) {
|
||
|
$self->{format_color} = " 1";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub format_userid {
|
||
|
my($self, $userid) = @_;
|
||
|
if ($self->{mod} eq "AMXX") {
|
||
|
return "#".$userid;
|
||
|
}
|
||
|
return "\"".$userid."\"";
|
||
|
}
|
||
|
|
||
|
sub quoteparam {
|
||
|
my($self, $message) = @_;
|
||
|
$message =~ s/'/ ' /g;
|
||
|
$message =~ s/"/ '' /g;
|
||
|
|
||
|
if (($self->{game_engine} != 2 || $self->{mod} eq "SOURCEMOD") && $self->{mod} ne "MANI") {
|
||
|
return "\"".$message."\"";
|
||
|
}
|
||
|
return $message;
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Set property 'key' to 'value'
|
||
|
#
|
||
|
|
||
|
sub set
|
||
|
{
|
||
|
my ($self, $key, $value) = @_;
|
||
|
|
||
|
if (defined($self->{$key}))
|
||
|
{
|
||
|
if ($self->{$key} eq $value)
|
||
|
{
|
||
|
if ($::g_debug > 2)
|
||
|
{
|
||
|
&::printNotice("Hlstats_Server->set ignored: Value of \"$key\" is already \"$value\"");
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
$self->{$key} = $value;
|
||
|
|
||
|
if ($key eq "hlstats_url") {
|
||
|
# so ingame browsing will work correctly
|
||
|
$self->{ingame_url} = $value;
|
||
|
$self->{ingame_url} =~ s/\/hlstats.php//i;
|
||
|
$self->{ingame_url} =~ s/\/$//;
|
||
|
&::printEvent("SERVER", "Ingame-URL: ".$self->{ingame_url}, 1);
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
warn("HLstats_Server->set: \"$key\" is not a valid property name\n");
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#
|
||
|
# Increment (or decrement) the value of 'key' by 'amount' (or 1 by default)
|
||
|
#
|
||
|
|
||
|
sub increment
|
||
|
{
|
||
|
my ($self, $key, $amount) = @_;
|
||
|
if ($amount) {
|
||
|
$amount = int($amount);
|
||
|
} else {
|
||
|
$amount = 1
|
||
|
}
|
||
|
|
||
|
my $value = $self->{$key};
|
||
|
$self->set($key, $value + $amount);
|
||
|
}
|
||
|
|
||
|
|
||
|
sub init_rcon
|
||
|
{
|
||
|
my ($self) = @_;
|
||
|
my $server_ip = $self->{address};
|
||
|
my $server_port = $self->{port};
|
||
|
my $rcon_pass = $self->{rcon};
|
||
|
my $game = $self->{game};
|
||
|
|
||
|
if ($::g_rcon && $rcon_pass) {
|
||
|
if ($self->{game_engine} == 1) {
|
||
|
$self->{rcon_obj} = new BASTARDrcon($self);
|
||
|
} else {
|
||
|
$self->{rcon_obj} = new TRcon($self);
|
||
|
}
|
||
|
}
|
||
|
if ($self->{rcon_obj}) {
|
||
|
&::printEvent ("SERVER", "Connecting to rcon on $server_ip:$server_port ... ok");
|
||
|
#&::printEvent("SERVER", "Server running game: ".$self->{play_game}, 1);
|
||
|
&::printEvent("SERVER", "Server running map: ".$self->get_map(), 1);
|
||
|
if ($::g_mode eq "LAN") {
|
||
|
$self->get_lan_players();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub dorcon
|
||
|
{
|
||
|
my ($self, $command) = @_;
|
||
|
my $result;
|
||
|
my $rcon_obj = $self->{rcon_obj};
|
||
|
if (($rcon_obj) && ($::g_rcon == 1) && ($self->{rcon} ne "")) {
|
||
|
|
||
|
# replace ; to avoid executing multiple rcon commands.
|
||
|
$command =~ s/;//g;
|
||
|
|
||
|
&::printNotice("RCON", $command, 1);
|
||
|
$result = $rcon_obj->execute($command);
|
||
|
} else {
|
||
|
&::printNotice("Rcon error: No Object available");
|
||
|
}
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
sub dorcon_multi
|
||
|
{
|
||
|
my ($self, @commands) = @_;
|
||
|
my $result;
|
||
|
my $rcon_obj = $self->{rcon_obj};
|
||
|
if (($rcon_obj) && ($::g_rcon == 1) && ($self->{rcon} ne "")) {
|
||
|
if ($self->{game_engine} > 1)
|
||
|
{
|
||
|
my $fullcmd = "";
|
||
|
foreach (@commands)
|
||
|
{
|
||
|
# replace ; to avoid executing multiple rcon commands.
|
||
|
my $cmd = $_;
|
||
|
$cmd =~ s/;//g;
|
||
|
$fullcmd .="$cmd;";
|
||
|
}
|
||
|
&::printNotice("RCON", $fullcmd, 1);
|
||
|
$result = $rcon_obj->execute($fullcmd);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
foreach (@commands)
|
||
|
{
|
||
|
&::printNotice("RCON", $_, 1);
|
||
|
$result = $rcon_obj->execute($_);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
&::printNotice("Rcon error: No Object available");
|
||
|
}
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
sub rcon_getaddress
|
||
|
{
|
||
|
my ($self, $uniqueid) = @_;
|
||
|
my $result;
|
||
|
my $rcon_obj = $self->{rcon_obj};
|
||
|
if (($rcon_obj) && ($::g_rcon == 1) && ($self->{rcon} ne ""))
|
||
|
{
|
||
|
$result = $rcon_obj->getPlayer($uniqueid);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
&::printNotice("Rcon error: No Object available");
|
||
|
}
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
sub rcon_getStatus
|
||
|
{
|
||
|
my ($self) = @_;
|
||
|
my $rcon_obj = $self->{rcon_obj};
|
||
|
my $map_result = "";
|
||
|
my $max_player_result = -1;
|
||
|
my $servhostname = "";
|
||
|
my $difficulty = 0;
|
||
|
|
||
|
if (($rcon_obj) && ($::g_rcon == 1) && ($self->{rcon} ne "")) {
|
||
|
($servhostname, $map_result, $max_player_result, $difficulty) = $rcon_obj->getServerData();
|
||
|
($visible_maxplayers) = $rcon_obj->getVisiblePlayers();
|
||
|
if (($visible_maxplayers != -1) && ($visible_maxplayers < $max_player_result)) {
|
||
|
$max_player_result = $visible_maxplayers;
|
||
|
}
|
||
|
} else {
|
||
|
&::printNotice("Rcon error: No Object available");
|
||
|
}
|
||
|
return ($map_result, $max_player_result, $servhostname, $difficulty);
|
||
|
}
|
||
|
|
||
|
sub rcon_getplayers
|
||
|
{
|
||
|
my ($self) = @_;
|
||
|
my %result;
|
||
|
my $rcon_obj = $self->{rcon_obj};
|
||
|
if (($rcon_obj) && ($::g_rcon == 1) && ($self->{rcon} ne ""))
|
||
|
{
|
||
|
%result = $rcon_obj->getPlayers();
|
||
|
} else {
|
||
|
&::printNotice("Rcon error: No Object available");
|
||
|
}
|
||
|
return %result;
|
||
|
}
|
||
|
|
||
|
sub track_server_load
|
||
|
{
|
||
|
my ($self) = @_;
|
||
|
|
||
|
if (($::g_stdin == 0) && ($self->{track_server_load} > 0))
|
||
|
{
|
||
|
my $last_timestamp = $self->{track_server_timestamp};
|
||
|
my $new_timestamp = time();
|
||
|
if ($last_timestamp > 0)
|
||
|
{
|
||
|
if ($last_timestamp+299 < $new_timestamp)
|
||
|
{
|
||
|
# fetch fps and uptime via rcon
|
||
|
|
||
|
# Old style stats output:
|
||
|
#$string = " 0.00 0.00 0.00 54 1 249.81 0 dhjdsk";
|
||
|
|
||
|
# New style stats output:
|
||
|
#CPU In (KB/s) Out (KB/s) Uptime Map changes FPS Players Connects
|
||
|
#0.00 0.00 0.00 0 0 00.00 0 0
|
||
|
|
||
|
|
||
|
$string = $self->dorcon("stats");
|
||
|
|
||
|
# Remove first line of output
|
||
|
$string =~ /CPU.*\n(.*)\n*L{0,1}.*\Z/;
|
||
|
$string = $1;
|
||
|
|
||
|
# Grab FPS and Uptime from the output
|
||
|
$string =~ /([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s*([^ ]*)/;
|
||
|
$uptime = $4;
|
||
|
$fps = $6;
|
||
|
|
||
|
my $act_players = $self->{numplayers};
|
||
|
my $max_players = $self->{maxplayers};
|
||
|
if ($max_players > 0) {
|
||
|
if ($act_players > $max_players) {
|
||
|
$act_players = $max_players;
|
||
|
}
|
||
|
}
|
||
|
&::execCached("flush_server_load",
|
||
|
"INSERT IGNORE INTO hlstats_server_load
|
||
|
SET
|
||
|
server_id=?,
|
||
|
timestamp=?,
|
||
|
act_players=?,
|
||
|
min_players=?,
|
||
|
max_players=?,
|
||
|
map=?,
|
||
|
uptime=?,
|
||
|
fps=?",
|
||
|
$self->{id},
|
||
|
$new_timestamp,
|
||
|
$act_players,
|
||
|
$self->{minplayers},
|
||
|
$max_players,
|
||
|
$self->{map},
|
||
|
(($uptime)?$uptime:0),
|
||
|
(($fps)?$fps:0)
|
||
|
);
|
||
|
$self->set("track_server_timestamp", $new_timestamp);
|
||
|
&::printEvent("SERVER", "Insert new server load timestamp", 1);
|
||
|
}
|
||
|
} else {
|
||
|
$self->set("track_server_timestamp", $new_timestamp);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub dostats
|
||
|
{
|
||
|
my ($self) = @_;
|
||
|
my $rcon_obj = $self->{rcon_obj};
|
||
|
$rcmd = $self->{broadcasting_command_announce};
|
||
|
|
||
|
if (($::g_stdin == 0) && ($rcon_obj) && ($self->{rcon} ne ""))
|
||
|
{
|
||
|
if ($self->{broadcasting_events} == 1)
|
||
|
{
|
||
|
my $hpk = sprintf("%.0f", 0);
|
||
|
if ($self->{total_kills} > 0) {
|
||
|
$hpk = sprintf("%.2f", (100/$self->{total_kills})*$self->{total_headshots});
|
||
|
}
|
||
|
if ($rcmd ne "") {
|
||
|
$self->dorcon("$rcmd ".$self->quoteparam("HLstatsX:CE - Tracking ".&::number_format($self->{players})." players with ".&::number_format($self->{total_kills})." kills and ".&::number_format($self->{total_headshots})." headshots ($hpk%)"));
|
||
|
} else {
|
||
|
$self->messageAll("HLstatsX:CE - Tracking ".&::number_format($self->{players})." players with ".&::number_format($self->{total_kills})." kills and ".&::number_format($self->{total_headshots})." headshots ($hpk%)");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub get_map
|
||
|
{
|
||
|
my ($self, $fromupdate) = @_;
|
||
|
|
||
|
if ($::g_stdin == 0) {
|
||
|
if ((time() - $self->{last_check})>120) {
|
||
|
$self->{last_check} = time();
|
||
|
&::printNotice("get_rcon_status");
|
||
|
my $temp_map = "";
|
||
|
my $temp_maxplayers = -1;
|
||
|
my $servhostname = "";
|
||
|
my $difficulty = 0;
|
||
|
my $update = 0;
|
||
|
|
||
|
if ($self->{rcon_obj})
|
||
|
{
|
||
|
($temp_map, $temp_maxplayers, $servhostname, $difficulty) = $self->rcon_getStatus();
|
||
|
|
||
|
if ($temp_map eq "") {
|
||
|
goto STATUSFAIL;
|
||
|
}
|
||
|
|
||
|
if ($self->{map} ne $temp_map) {
|
||
|
$self->{map} = $temp_map;
|
||
|
$update++;
|
||
|
}
|
||
|
|
||
|
if (($temp_maxplayers != -1) && ($temp_maxplayers > 0) && ($temp_maxplayers ne "")) {
|
||
|
if ($self->{maxplayers} != $temp_maxplayers) {
|
||
|
$self->{maxplayers} = $temp_maxplayers;
|
||
|
$update++;
|
||
|
}
|
||
|
}
|
||
|
if (($difficulty > 0) && ($self->{play_game} == L4D())) {
|
||
|
$self->{difficulty} = $difficulty;
|
||
|
}
|
||
|
if (($self->{update_hostname} > 0) && ($self->{name} ne $servhostname) && ($servhostname ne "")) {
|
||
|
$self->{name} = $servhostname;
|
||
|
$update++;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{ # no rcon or status command failed
|
||
|
STATUSFAIL:
|
||
|
my ($querymap, $queryhost, $querymax) = &::queryServer($self->{address}, $self->{port}, 'mapname', 'hostname', 'maxplayers');
|
||
|
if ($querymap ne "") {
|
||
|
$self->{map} = $querymap;
|
||
|
$update++;
|
||
|
|
||
|
#if map is blank, query likely failed as a whole
|
||
|
|
||
|
if (($querymax != -1) && ($querymax > 0)) {
|
||
|
if ($self->{maxplayers} != $querymax) {
|
||
|
$self->{maxplayers} = $querymax;
|
||
|
$update++;
|
||
|
}
|
||
|
}
|
||
|
if ($self->{update_hostname} > 0 && $queryhost ne "" && $self->{name} ne $queryhost) {
|
||
|
$self->{name} = $queryhost;
|
||
|
$update++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($update > 0 && $fromupdate == 0) {
|
||
|
$self->updateDB();
|
||
|
}
|
||
|
|
||
|
&::printNotice("get_rcon_status successfully");
|
||
|
}
|
||
|
}
|
||
|
return $self->{map};
|
||
|
}
|
||
|
|
||
|
|
||
|
sub update_players_pings
|
||
|
{
|
||
|
my ($self) = @_;
|
||
|
|
||
|
if ($self->{num_trackable_players} < $self->{minplayers})
|
||
|
{
|
||
|
&::printNotice("(IGNORED) NOTMINPLAYERS: Update_player_pings");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
&::printNotice("update_player_pings");
|
||
|
&::printEvent("RCON", "Update Player pings", 1);
|
||
|
my %players = $self->rcon_getplayers();
|
||
|
while ( my($pl, $player) = each(%{$self->{srv_players}}) )
|
||
|
{
|
||
|
my $uniqueid = $player->{uniqueid};
|
||
|
if (defined($players{$uniqueid}))
|
||
|
{
|
||
|
if ($player->{is_bot} == 0 && $player->{userid} > 0) {
|
||
|
my $ping = $players{$uniqueid}->{"Ping"};
|
||
|
$player->set("ping", $ping);
|
||
|
if ($ping > 0) {
|
||
|
&::recordEvent(
|
||
|
"Latency", 0,
|
||
|
$player->{playerid},
|
||
|
$ping
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
&::printNotice("update_player_pings successfully");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub get_lan_players
|
||
|
{
|
||
|
my ($self) = @_;
|
||
|
|
||
|
if ($::g_mode eq "LAN") {
|
||
|
&::printNotice("get_lan_players");
|
||
|
&::printEvent("RCON", "Get LAN players", 1);
|
||
|
my %players = $self->rcon_getplayers();
|
||
|
while ( my($p_uid, $p_obj) = each(%players) )
|
||
|
{
|
||
|
my $srv_addr = $self->{address}.":".$self->{port};
|
||
|
my $userid = $p_obj->{"UserID"};
|
||
|
my $name = $p_obj->{"Name"};
|
||
|
my $address = $p_obj->{"Address"};
|
||
|
::g_lan_noplayerinfo->{"$srv_addr/$userid/$name"} = {
|
||
|
ipaddress => $address,
|
||
|
userid => $userid,
|
||
|
name => $name,
|
||
|
server => $srv_addr
|
||
|
};
|
||
|
}
|
||
|
&::printNotice("get_lan_players successfully");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub clear_winner
|
||
|
{
|
||
|
my ($self) = @_;
|
||
|
&::printNotice("clear_winner");
|
||
|
@{$self->{winner}} = ();
|
||
|
}
|
||
|
|
||
|
sub add_round_winner
|
||
|
{
|
||
|
my ($self, $team) = @_;
|
||
|
|
||
|
&::printNotice("add_round_winner");
|
||
|
$self->{winner}[($self->{map_rounds} % 7)] = $team;
|
||
|
$self->increment("ba_map_rounds");
|
||
|
$self->increment("map_rounds");
|
||
|
$self->increment("rounds");
|
||
|
$self->increment("total_rounds");
|
||
|
|
||
|
$self->{ba_ct_wins} = 0;
|
||
|
$self->{ba_ts_wins} = 0;
|
||
|
|
||
|
for (@{$self->{winner}})
|
||
|
{
|
||
|
if ($_ eq "ct") {
|
||
|
$self->increment("ba_ct_wins");
|
||
|
} elsif ($_ eq "ts") {
|
||
|
$self->increment("ba_ts_wins");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub switch_player
|
||
|
{
|
||
|
my ($self, $playerid, $name) = @_;
|
||
|
my $rcmd = $self->{player_command_hint};
|
||
|
|
||
|
$self->dorcon($self->{swap_command}." ".$self->format_userid($playerid));
|
||
|
if ($self->{player_command_hint} eq "") {
|
||
|
$rcmd = $self->{player_command};
|
||
|
}
|
||
|
$self->dorcon(sprintf("%s %s %s", $rcmd, $self->format_userid($playerid), $self->quoteparam("HLstatsX:CE - You were switched to balance teams")));
|
||
|
if ($self->{player_admin_command} ne "") {
|
||
|
$self->dorcon(sprintf("%s %s",$self->{player_admin_command}, $self->quoteparam("HLstatsX:CE - $name was switched to balance teams")));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
sub analyze_teams
|
||
|
{
|
||
|
my ($self) = @_;
|
||
|
|
||
|
if (($::g_stdin == 0) && ($self->{num_trackable_players} < $self->{minplayers}))
|
||
|
{
|
||
|
&::printNotice("(IGNORED) NOTMINPLAYERS: analyze_teams");
|
||
|
}
|
||
|
elsif (($::g_stdin == 0) && ($self->{ba_enabled} > 0))
|
||
|
{
|
||
|
&::printNotice("analyze_teams");
|
||
|
my $ts_skill = 0;
|
||
|
my $ts_avg_skill = 0;
|
||
|
my $ts_count = 0;
|
||
|
my $ts_wins = $self->{ba_ts_wins};
|
||
|
my $ts_kills = 0;
|
||
|
my $ts_deaths = 0;
|
||
|
my $ts_diff = 0;
|
||
|
my @ts_players = ();
|
||
|
|
||
|
my $ct_skill = 0;
|
||
|
my $ct_avg_skill = 0;
|
||
|
my $ct_count = 0;
|
||
|
my $ct_wins = $self->{ba_ct_wins};
|
||
|
my $ct_kills = 0;
|
||
|
my $ct_deaths = 0;
|
||
|
my $ct_diff = 0;
|
||
|
my @ct_players = ();
|
||
|
|
||
|
my $server_id = $self->{id};
|
||
|
while ( my($pl, $player) = each(%{$self->{srv_players}}) )
|
||
|
{
|
||
|
my @Player = ( $player->{name}, #0
|
||
|
$player->{uniqueid}, #1
|
||
|
$player->{skill}, #2
|
||
|
$player->{team}, #3
|
||
|
$player->{map_kills}, #4
|
||
|
$player->{map_deaths}, #5
|
||
|
($player->{map_kills}-$player->{map_deaths}), #6
|
||
|
$player->{is_dead}, #7
|
||
|
$player->{userid}, #8
|
||
|
);
|
||
|
|
||
|
if ($Player[3] eq "TERRORIST")
|
||
|
{
|
||
|
push(@{$ts_players[$ts_count]}, @Player);
|
||
|
$ts_skill += $Player[2];
|
||
|
$ts_count += 1;
|
||
|
$ts_kills += $Player[4];
|
||
|
$ts_deaths += $Player[5];
|
||
|
}
|
||
|
elsif ($Player[3] eq "CT")
|
||
|
{
|
||
|
push(@{$ct_players[$ct_count]}, @Player);
|
||
|
$ct_skill += $Player[2];
|
||
|
$ct_count += 1;
|
||
|
$ct_kills += $Player[4];
|
||
|
$ct_deaths += $Player[5];
|
||
|
}
|
||
|
}
|
||
|
@ct_players = sort { $b->[6] <=> $a->[6]} @ct_players;
|
||
|
@ts_players = sort { $b->[6] <=> $a->[6]} @ts_players;
|
||
|
|
||
|
&::printEvent("TEAM", "Checking Teams", 1);
|
||
|
$admin_msg = "AUTO-TEAM BALANCER: CT ($ct_count) $ct_kills:$ct_deaths [$ct_wins - $ts_wins] $ts_kills:$ts_deaths ($ts_count) TS";
|
||
|
if ($self->{player_events} == 1)
|
||
|
{
|
||
|
if ($self->{player_admin_command} ne "") {
|
||
|
$cmd_str = $self->{player_admin_command}." $admin_msg";
|
||
|
$self->dorcon($cmd_str);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$self->messageAll("HLstatsX:CE - ATB - Checking Teams", 0, 1);
|
||
|
|
||
|
if ($self->{ba_map_rounds} >= 2) # need all players for numerical balacing, at least 2 for getting all players
|
||
|
{
|
||
|
my $action_done = 0;
|
||
|
if ($self->{ba_last_swap} > 0)
|
||
|
{
|
||
|
$self->{ba_last_swap}--;
|
||
|
}
|
||
|
|
||
|
if ($ct_count + 1 < $ts_count) # ct need players
|
||
|
{
|
||
|
$needed_players = floor( ($ts_count - $ct_count) / 2);
|
||
|
if ($ct_wins < 2)
|
||
|
{
|
||
|
@ts_players = sort { $b->[7] <=> $a->[7]} @ts_players;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
@ts_players = sort { $a->[7] <=> $b->[7]} @ts_players;
|
||
|
}
|
||
|
foreach my $entry (@ts_players)
|
||
|
{
|
||
|
if ($needed_players > 0) # how many we need to make teams even (only numerical)
|
||
|
{
|
||
|
if (@{$entry}[7] == 1) # only dead players!!
|
||
|
{
|
||
|
if (($self->{switch_admins} == 1) || (($self->{switch_admins} == 0) && ($self->is_admin(@{$entry}[1]) == 0))) {
|
||
|
$self->switch_player(@{$entry}[8], @{$entry}[0]);
|
||
|
$action_done++;
|
||
|
$needed_players--;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
elsif ($ts_count + 1 < $ct_count) # ts need players
|
||
|
{
|
||
|
$needed_players = floor( ($ct_count - $ts_count) / 2);
|
||
|
if ($ts_wins < 2)
|
||
|
{
|
||
|
@ct_players = sort { $b->[6] <=> $a->[6]} @ct_players; # best player
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
@ct_players = sort { $a->[6] <=> $b->[6]} @ct_players; # worst player
|
||
|
}
|
||
|
foreach my $entry (@ct_players)
|
||
|
{
|
||
|
if ($needed_players > 0) # how many we need to make teams even (only numerical)
|
||
|
{
|
||
|
if (@{$entry}[7] == 1) # only dead players!!
|
||
|
{
|
||
|
if (($self->{switch_admins} == 1) || (($self->{switch_admins} == 0) && ($self->is_admin(@{$entry}[1]) == 0))) {
|
||
|
$self->switch_player(@{$entry}[8], @{$entry}[0]);
|
||
|
$action_done++;
|
||
|
$needed_players--;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (($action_done == 0) && ($self->{ba_last_swap} == 0) && ($self->{ba_map_rounds} >= 7) && ($self->{ba_player_switch} == 0)) # frags balancing (last swap 3 rounds before)
|
||
|
{
|
||
|
if ($ct_wins < 2)
|
||
|
{
|
||
|
if ($ct_count < $ts_count) # one player less we dont need swap just bring one over
|
||
|
{
|
||
|
my $ts_found = 0;
|
||
|
@ts_players = sort { $b->[6] <=> $a->[6]} @ts_players; # best player
|
||
|
foreach my $entry (@ts_players)
|
||
|
{
|
||
|
if ($ts_found == 0)
|
||
|
{
|
||
|
if (@{$entry}[7] == 1) # only dead players!!
|
||
|
{
|
||
|
if (($self->{switch_admins} == 1) || (($self->{switch_admins} == 0) && ($self->is_admin(@{$entry}[1]) == 0))) {
|
||
|
$self->{ba_last_swap} = 3;
|
||
|
$self->switch_player(@{$entry}[9], @{$entry}[0]);
|
||
|
$ts_found++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else # need to swap to players
|
||
|
{
|
||
|
my $ts_playerid = 0;
|
||
|
my $ts_name = "";
|
||
|
my $ts_kills = 0;
|
||
|
my $ts_deaths = 0;
|
||
|
my $ct_playerid = 0;
|
||
|
my $ct_name = "";
|
||
|
my $ct_kills = 0;
|
||
|
my $ct_deaths = 0;
|
||
|
my $ts_found = 0;
|
||
|
@ts_players = sort { $b->[6] <=> $a->[6]} @ts_players; # best player
|
||
|
foreach my $entry (@ts_players)
|
||
|
{
|
||
|
if ($ts_found == 0)
|
||
|
{
|
||
|
if (@{$entry}[7] == 1) # only dead players!!
|
||
|
{
|
||
|
if (($self->{switch_admins} == 1) || (($self->{switch_admins} == 0) && ($self->is_admin(@{$entry}[1]) == 0))) {
|
||
|
$ts_playerid = @{$entry}[8];
|
||
|
$ts_name = @{$entry}[0];
|
||
|
$ts_found++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
my $ct_found = 0;
|
||
|
@ct_players = sort { $a->[6] <=> $b->[6]} @ct_players; # worst player
|
||
|
foreach my $entry (@ct_players)
|
||
|
{
|
||
|
if ($ct_found == 0)
|
||
|
{
|
||
|
if (@{$entry}[7] == 1) # only dead players!!
|
||
|
{
|
||
|
if (($self->{switch_admins} == 1) || (($self->{switch_admins} == 0) && ($self->is_admin(@{$entry}[1]) == 0))) {
|
||
|
$ct_playerid = @{$entry}[8];
|
||
|
$ct_name = @{$entry}[0];
|
||
|
$ct_found++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (($ts_found>0) && ($ct_found>0))
|
||
|
{
|
||
|
$self->{ba_last_swap} = 3;
|
||
|
$self->switch_player($ts_playerid, $ts_name);
|
||
|
$self->switch_player($ct_playerid, $ct_name);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
elsif ($ts_wins < 2)
|
||
|
{
|
||
|
if ($ts_count < $ct_count) # one player less we dont need swap just bring one over
|
||
|
{
|
||
|
my $ct_found = 0;
|
||
|
@ct_players = sort { $b->[6] <=> $a->[6]} @ct_players; # best player
|
||
|
foreach my $entry (@ct_players)
|
||
|
{
|
||
|
if ($ct_found == 0)
|
||
|
{
|
||
|
if (@{$entry}[7] == 1) # only dead players!!
|
||
|
{
|
||
|
if (($self->{switch_admins} == 1) || (($self->{switch_admins} == 0) && ($self->is_admin(@{$entry}[1]) == 0))) {
|
||
|
$self->{ba_last_swap} = 3;
|
||
|
$self->switch_player(@{$entry}[8], @{$entry}[0]);
|
||
|
$ct_found++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else # need to swap to players
|
||
|
{
|
||
|
my $ts_playerid = 0;
|
||
|
my $ts_name = "";
|
||
|
my $ct_playerid = 0;
|
||
|
my $ct_name = "";
|
||
|
my $ct_found = 0;
|
||
|
@ct_players = sort { $b->[6] <=> $a->[6]} @ct_players; # best player
|
||
|
foreach my $entry (@ct_players)
|
||
|
{
|
||
|
if ($ct_found == 0)
|
||
|
{
|
||
|
if (@{$entry}[7] == 1) # only dead players!!
|
||
|
{
|
||
|
if (($self->{switch_admins} == 1) || (($self->{switch_admins} == 0) && ($self->is_admin(@{$entry}[1]) == 0))) {
|
||
|
$ct_playerid = @{$entry}[8];
|
||
|
$ct_name = @{$entry}[0];
|
||
|
$ct_found++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
my $ts_found = 0;
|
||
|
@ts_players = sort { $a->[6] <=> $b->[6]} @ts_players; # worst player
|
||
|
foreach my $entry (@ts_players)
|
||
|
{
|
||
|
if ($ts_found == 0)
|
||
|
{
|
||
|
if (@{$entry}[7] == 1) # only dead players!!
|
||
|
{
|
||
|
if (($self->{switch_admins} == 1) || (($self->{switch_admins} == 0) && ($self->is_admin(@{$entry}[1]) == 0))) {
|
||
|
$ts_playerid = @{$entry}[8];
|
||
|
$ts_name = @{$entry}[0];
|
||
|
$ts_found++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (($ts_found > 0) && ($ct_found > 0))
|
||
|
{
|
||
|
$self->{ba_last_swap} = 3;
|
||
|
$self->switch_player($ts_playerid, $ts_name);
|
||
|
$self->switch_player($ct_playerid, $ct_name);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} # end if rounds > 1
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Marks server as needing flush
|
||
|
#
|
||
|
|
||
|
sub updateDB
|
||
|
{
|
||
|
my ($self) = @_;
|
||
|
$self->{needsupdate} = 1;
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Flushes server information in database
|
||
|
#
|
||
|
|
||
|
sub flushDB
|
||
|
{
|
||
|
my ($self) = @_;
|
||
|
$self->get_map(1);
|
||
|
|
||
|
my $serverid = $self->{id};
|
||
|
|
||
|
if ($self->{total_kills} == 0)
|
||
|
{
|
||
|
my $result = &::execCached(
|
||
|
"get_server_player_info",
|
||
|
"SELECT
|
||
|
kills,
|
||
|
headshots,
|
||
|
suicides,
|
||
|
rounds,
|
||
|
ct_shots+ts_shots as shots,
|
||
|
ct_hits+ts_hits as hits
|
||
|
FROM
|
||
|
hlstats_Servers
|
||
|
WHERE
|
||
|
serverId=?",
|
||
|
$self->{id}
|
||
|
);
|
||
|
($self->{total_kills}, $self->{total_headshots}, $self->{total_suicides},$self->{total_rounds},$self->{total_shots},$self->{total_hits}) = $result->fetchrow_array();
|
||
|
$result->finish;
|
||
|
}
|
||
|
|
||
|
my $result = &::execCached(
|
||
|
"get_player_count",
|
||
|
"SELECT count(*) as players FROM hlstats_Players WHERE game=? and hideranking<>2",
|
||
|
&::quoteSQL($self->{game}));
|
||
|
$self->{players} = $result->fetchrow_array();
|
||
|
$result->finish;
|
||
|
|
||
|
|
||
|
# Update player details
|
||
|
my $query = "
|
||
|
UPDATE
|
||
|
hlstats_Servers
|
||
|
SET
|
||
|
name=?,
|
||
|
rounds=rounds + ?,
|
||
|
kills=kills + ?,
|
||
|
suicides=suicides + ?,
|
||
|
headshots=headshots + ?,
|
||
|
bombs_planted=bombs_planted + ?,
|
||
|
bombs_defused=bombs_defused + ?,
|
||
|
players=?,
|
||
|
ct_wins=ct_wins + ?,
|
||
|
ts_wins=ts_wins + ?,
|
||
|
act_players=?,
|
||
|
max_players=?,
|
||
|
act_map=?,
|
||
|
map_rounds=?,
|
||
|
map_ct_wins=?,
|
||
|
map_ts_wins=?,
|
||
|
map_started=?,
|
||
|
map_changes=map_changes + ?,
|
||
|
ct_shots=ct_shots + ?,
|
||
|
ct_hits=ct_hits + ?,
|
||
|
ts_shots=ts_shots + ?,
|
||
|
ts_hits=ts_hits + ?,
|
||
|
map_ct_shots=?,
|
||
|
map_ct_hits=?,
|
||
|
map_ts_shots=?,
|
||
|
map_ts_hits=?,
|
||
|
last_event=?
|
||
|
WHERE
|
||
|
serverId=?
|
||
|
";
|
||
|
my @vals = (
|
||
|
&::quoteSQL($self->{name}),
|
||
|
$self->{rounds},
|
||
|
$self->{kills},
|
||
|
$self->{suicides},
|
||
|
$self->{headshots},
|
||
|
$self->{bombs_planted},
|
||
|
$self->{bombs_defused},
|
||
|
$self->{players},
|
||
|
$self->{ct_wins},
|
||
|
$self->{ts_wins},
|
||
|
$self->{numplayers},
|
||
|
$self->{maxplayers},
|
||
|
&::quoteSQL($self->{map}),
|
||
|
$self->{map_rounds},
|
||
|
$self->{map_ct_wins},
|
||
|
$self->{map_ts_wins},
|
||
|
$self->{map_started},
|
||
|
$self->{map_changes},
|
||
|
$self->{ct_shots},
|
||
|
$self->{ct_hits},
|
||
|
$self->{ts_shots},
|
||
|
$self->{ts_hits},
|
||
|
$self->{map_ct_shots},
|
||
|
$self->{map_ct_hits},
|
||
|
$self->{map_ts_shots},
|
||
|
$self->{map_ts_hits},
|
||
|
$::ev_unixtime,
|
||
|
$serverid
|
||
|
);
|
||
|
&::execCached("update_server_stats", $query, @vals);
|
||
|
|
||
|
$self->set("rounds", 0);
|
||
|
$self->set("kills", 0);
|
||
|
$self->set("suicides", 0);
|
||
|
$self->set("headshots", 0);
|
||
|
$self->set("bombs_planted", 0);
|
||
|
$self->set("bombs_defused", 0);
|
||
|
$self->set("ct_wins", 0);
|
||
|
$self->set("ts_wins", 0);
|
||
|
$self->set("ct_shots", 0);
|
||
|
$self->set("ct_hits", 0);
|
||
|
$self->set("ts_shots", 0);
|
||
|
$self->set("ts_hits", 0);
|
||
|
$self->set("map_changes", 0);
|
||
|
$self->{needsupdate} = 0;
|
||
|
}
|
||
|
|
||
|
sub flush_player_count
|
||
|
{
|
||
|
my ($self) = @_;
|
||
|
|
||
|
&::execCached("flush_plyr_cnt",
|
||
|
"UPDATE hlstats_Servers SET act_players=? WHERE serverId=?",
|
||
|
$self->{numplayers},
|
||
|
$self->{id}
|
||
|
);
|
||
|
}
|
||
|
|
||
|
sub update_server_loc
|
||
|
{
|
||
|
my ($self) = @_;
|
||
|
my $serverid = $self->{id};
|
||
|
my $server_ip = $self->{address};
|
||
|
my $publicaddress = $self->{publicaddress};
|
||
|
|
||
|
if ($publicaddress =~ /^(\d+\.\d+\.\d+\.\d+)/) {
|
||
|
$server_ip = $publicaddress;
|
||
|
} elsif ($publicaddress =~ /^([0-9a-zA-Z\-\.]+)\:*.*/) {
|
||
|
my $hostip = inet_aton($1);
|
||
|
if ($hostip) {
|
||
|
$server_ip = inet_ntoa($hostip);
|
||
|
}
|
||
|
}
|
||
|
my $found = 0;
|
||
|
my $servcity = "";
|
||
|
my $servcountry = "";
|
||
|
my $servlat=undef;
|
||
|
my $servlng=undef;
|
||
|
if ($::g_geoip_binary > 0) {
|
||
|
if(!defined($::g_gi)) {
|
||
|
return;
|
||
|
}
|
||
|
my ($country_code, $country_code3, $country_name, $region, $city, $postal_code, $latitude, $longitude,
|
||
|
$metro_code, $area_code) = $::g_gi->get_city_record($server_ip);
|
||
|
if ($longitude) {
|
||
|
$found++;
|
||
|
$servcity = ((defined($city))?encode("utf8",$city):"");
|
||
|
$servcountry = ((defined($country_name))?encode("utf8",$country_name):"");
|
||
|
$servlat = $latitude;
|
||
|
$servlng = $longitude;
|
||
|
}
|
||
|
} else {
|
||
|
my @ipp = split (/\./,$server_ip);
|
||
|
my $ip_number = $ipp[0]*16777216+$ipp[1]*65536+$ipp[2]*256+$ipp[3];
|
||
|
my $query = "
|
||
|
SELECT locId FROM geoLiteCity_Blocks WHERE startIpNum<= $ip_number AND endIpNum>= $ip_number LIMIT 1";
|
||
|
my $result = &::doQuery($query);
|
||
|
if ($result->rows > 0) {
|
||
|
my $locid = $result->fetchrow_array;
|
||
|
$result->finish;
|
||
|
my $query = "
|
||
|
SELECT
|
||
|
city,
|
||
|
name AS country,
|
||
|
latitude AS lat,
|
||
|
longitude AS lng
|
||
|
FROM
|
||
|
geoLiteCity_Location a
|
||
|
INNER JOIN
|
||
|
hlstats_Countries b ON a.country=b.flag
|
||
|
WHERE
|
||
|
locId= $locid
|
||
|
LIMIT 1";
|
||
|
my $result = &::doQuery($query);
|
||
|
if ($result->rows > 0) {
|
||
|
$found++;
|
||
|
($servcity,$servcountry,$servlat,$servlng) = $result->fetchrow_array;
|
||
|
$result->finish;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if ($found > 0) {
|
||
|
my $query = "
|
||
|
UPDATE
|
||
|
`hlstats_Servers`
|
||
|
SET
|
||
|
city = '".(defined($servcity)?&::quoteSQL($servcity):"")."',
|
||
|
country='".(defined($servcountry)?&::quoteSQL($servcountry):"")."',
|
||
|
lat=".((defined($servlat))?$servlat:"NULL").",
|
||
|
lng=".((defined($servlng))?$servlng:"NULL")."
|
||
|
WHERE
|
||
|
serverId =$serverid
|
||
|
";
|
||
|
&::execNonQuery($query);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub messageAll
|
||
|
{
|
||
|
my($self, $msg, $noshow, $force) = @_;
|
||
|
|
||
|
if ($self->{broadcasting_events} == 1 || $force == 1)
|
||
|
{
|
||
|
if ($self->{mod} eq "SOURCEMOD" || $self->{mod} eq "AMXX")
|
||
|
{
|
||
|
my @userlist;
|
||
|
|
||
|
foreach $player (values(%{$self->{srv_players}}))
|
||
|
{
|
||
|
if (($player->{is_bot} == 0) && ($player->{userid} > 0) && ($player->{playerid} != $noshow) && ($player->{display_events} == 1 || $force == 1))
|
||
|
{
|
||
|
push(@userlist, $player->{userid});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($self->{play_game} != FF())
|
||
|
{
|
||
|
$msg = $self->{format_action}.$msg;
|
||
|
}
|
||
|
$self->messageMany($msg, 1, @userlist);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$self->dorcon("say ".$msg);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub messageMany
|
||
|
{
|
||
|
my($self, $msg, $toall, @userlist) = @_;
|
||
|
if (scalar(@userlist) > 0)
|
||
|
{
|
||
|
if ($self->{mod} eq "SOURCEMOD")
|
||
|
{
|
||
|
my $usersendlist = "";
|
||
|
foreach (@userlist)
|
||
|
{
|
||
|
$usersendlist .= $_.",";
|
||
|
}
|
||
|
$usersendlist =~ s/,$//;
|
||
|
my $color = $self->{format_color};
|
||
|
if ($toall > 0 && $color eq " 1")
|
||
|
{
|
||
|
$color = " 2";
|
||
|
}
|
||
|
$self->dorcon($self->{player_command}." \"$usersendlist\"$color ".$self->quoteparam($msg));
|
||
|
}
|
||
|
elsif ($self->{mod} eq "AMXX")
|
||
|
{
|
||
|
while (@userlist)
|
||
|
{
|
||
|
my $usersendlist = "";
|
||
|
for ($i = 0; $i < 8; $i++)
|
||
|
{
|
||
|
$usersendlist .= shift(@userlist);
|
||
|
if ($i < 7)
|
||
|
{
|
||
|
$usersendlist .= ",";
|
||
|
}
|
||
|
}
|
||
|
$self->dorcon("hlx_amx_bulkpsay \"$usersendlist\"".$self->{format_color}." ".$self->quoteparam($msg));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$rcmd = $self->{broadcasting_command};
|
||
|
foreach (@userlist)
|
||
|
{
|
||
|
$self->dorcon(sprintf("%s %s%s %s",$rcmd, $self->format_userid($_), $self->{format_color}, $self->quoteparam($msg)));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
sub setHlxCvars
|
||
|
{
|
||
|
my ($self) = @_;
|
||
|
|
||
|
if ($self->{hlstats_url} ne "")
|
||
|
{
|
||
|
$self->dorcon("hlxce_webpage \"".$self->{hlstats_url}."\"");
|
||
|
}
|
||
|
$self->dorcon("hlxce_version \"".$::g_version."\"");
|
||
|
|
||
|
if ($self->{play_game} eq "MANI" && $self->dorcon("mani_hlx_prefix" =~ /gameme/i))
|
||
|
{
|
||
|
$self->dorcon("mani_hlx_prefix \"HLstatsX\"");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sub updatePlayerCount
|
||
|
{
|
||
|
my ($self) = @_;
|
||
|
|
||
|
if ($::g_debug > 1) {
|
||
|
&::printEvent("SERVER","Updating Player Count");
|
||
|
}
|
||
|
|
||
|
my $trackable = 0;
|
||
|
|
||
|
if ($self->{play_game} == L4D()) {
|
||
|
my $num = 0;
|
||
|
while (my($pl, $player) = each(%{$self->{srv_players}})) {
|
||
|
if ($player->{trackable} == 1) {
|
||
|
$trackable++;
|
||
|
}
|
||
|
if ($player->{userid} > 0) {
|
||
|
$num++;
|
||
|
}
|
||
|
}
|
||
|
$self->{numplayers} = $num;
|
||
|
$self->{num_trackable_players} = $trackable;
|
||
|
} else {
|
||
|
$self->{numplayers} = scalar keys %{$self->{srv_players}};
|
||
|
while (my($pl, $player) = each(%{$self->{srv_players}})) {
|
||
|
if ($player->{trackable} == 1) {
|
||
|
$trackable++;
|
||
|
}
|
||
|
}
|
||
|
$self->{num_trackable_players} = $trackable;
|
||
|
}
|
||
|
|
||
|
$self->flush_player_count();
|
||
|
}
|
||
|
|
||
|
1;
|