Advertisement
Guest User

Untitled

a guest
Dec 5th, 2016
304
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 105.81 KB | None | 0 0
  1. #!/usr/bin/perl
  2. # HLstatsX Community Edition - Real-time player and clan rankings and statistics
  3. # Copyleft (L) 2008-20XX Nicholas Hastings (nshastings@gmail.com)
  4. # http://www.hlxcommunity.com
  5. #
  6. # HLstatsX Community Edition is a continuation of
  7. # ELstatsNEO - Real-time player and clan rankings and statistics
  8. # Copyleft (L) 2008-20XX Malte Bayer (steam@neo-soft.org)
  9. # http://ovrsized.neo-soft.org/
  10. #
  11. # ELstatsNEO is an very improved & enhanced - so called Ultra-Humongus Edition of HLstatsX
  12. # HLstatsX - Real-time player and clan rankings and statistics for Half-Life 2
  13. # http://www.hlstatsx.com/
  14. # Copyright (C) 2005-2007 Tobias Oetzel (Tobi@hlstatsx.com)
  15. #
  16. # HLstatsX is an enhanced version of HLstats made by Simon Garner
  17. # HLstats - Real-time player and clan rankings and statistics for Half-Life
  18. # http://sourceforge.net/projects/hlstats/
  19. # Copyright (C) 2001 Simon Garner
  20. #
  21. # This program is free software; you can redistribute it and/or
  22. # modify it under the terms of the GNU General Public License
  23. # as published by the Free Software Foundation; either version 2
  24. # of the License, or (at your option) any later version.
  25. #
  26. # This program is distributed in the hope that it will be useful,
  27. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  28. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  29. # GNU General Public License for more details.
  30. #
  31. # You should have received a copy of the GNU General Public License
  32. # along with this program; if not, write to the Free Software
  33. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  34. #
  35. # For support and installation notes visit http://www.hlxcommunity.com
  36.  
  37. use strict;
  38. no strict 'vars';
  39.  
  40. $SIG{HUP} = 'HUP_handler';
  41. $SIG{INT} = 'INT_handler'; # unix
  42. $SIG{INT2} = 'INT_handler'; # windows
  43.  
  44. ##
  45. ## Settings
  46. ##
  47.  
  48. # $opt_configfile - Absolute path and filename of configuration file.
  49. $opt_configfile = "./hlstats.conf";
  50.  
  51. # $opt_libdir - Directory to look in for local required files
  52. # (our *.plib, *.pm files).
  53. $opt_libdir = "./";
  54.  
  55.  
  56. ##
  57. ##
  58. ################################################################################
  59. ## No need to edit below this line
  60. ##
  61.  
  62. use Getopt::Long;
  63. use Time::Local;
  64. use IO::Socket;
  65. use IO::Select;
  66. use DBI;
  67. use Digest::MD5;
  68. use Encode;
  69. use bytes;
  70.  
  71. require "$opt_libdir/ConfigReaderSimple.pm";
  72. require "$opt_libdir/TRcon.pm";
  73. require "$opt_libdir/BASTARDrcon.pm";
  74. require "$opt_libdir/HLstats_Server.pm";
  75. require "$opt_libdir/HLstats_Player.pm";
  76. require "$opt_libdir/HLstats_Game.pm";
  77. do "$opt_libdir/HLstats_GameConstants.plib";
  78. do "$opt_libdir/HLstats.plib";
  79. do "$opt_libdir/HLstats_EventHandlers.plib";
  80.  
  81. $|=1;
  82. Getopt::Long::Configure ("bundling");
  83.  
  84. $last_trend_timestamp = 0;
  85.  
  86. binmode STDIN, ":utf8";
  87. binmode STDOUT, ":utf8";
  88.  
  89. ##
  90. ## Functions
  91. ##
  92.  
  93. sub lookupPlayer
  94. {
  95. my ($saddr, $id, $uniqueid) = @_;
  96. if (defined($g_servers{$saddr}->{"srv_players"}->{"$id/$uniqueid"}))
  97. {
  98. return $g_servers{$saddr}->{"srv_players"}->{"$id/$uniqueid"};
  99. }
  100. return undef;
  101. }
  102.  
  103. sub removePlayer
  104. {
  105. my ($saddr, $id, $uniqueid, $dontUpdateCount) = @_;
  106. my $deleteplayer = 0;
  107. if(defined($g_servers{$saddr}->{"srv_players"}->{"$id/$uniqueid"}))
  108. {
  109. $deleteplayer = 1;
  110. }
  111. else
  112. {
  113. &::printEvent("400", "Bad attempted delete ($saddr) ($id/$uniqueid)");
  114. }
  115.  
  116. if ($deleteplayer == 1) {
  117. $g_servers{$saddr}->{"srv_players"}->{"$id/$uniqueid"}->playerCleanup();
  118. delete($g_servers{$saddr}->{"srv_players"}->{"$id/$uniqueid"});
  119. if (!$dontUpdateCount) # double negative, i know...
  120. {
  121. $g_servers{$saddr}->updatePlayerCount();
  122. }
  123. }
  124. }
  125.  
  126. sub checkBonusRound
  127. {
  128. if ($g_servers{$s_addr}->{bonusroundtime} > 0 && ($::ev_remotetime > ($g_servers{$s_addr}->{bonusroundtime_ts} + $g_servers{$s_addr}->{bonusroundtime}))) {
  129. if ($g_servers{$s_addr}->{bonusroundtime_state} == 1) {
  130. &printEvent("SERVER", "Bonus Round Expired",1);
  131. }
  132. $g_servers{$s_addr}->set("bonusroundtime_state",0);
  133. }
  134.  
  135. if($g_servers{$s_addr}->{bonusroundignore} == 1 && $g_servers{$s_addr}->{bonusroundtime_state} == 1) {
  136. return 1;
  137. }
  138. return 0;
  139. }
  140.  
  141. sub is_number ($) { ( $_[0] ^ $_[0] ) eq '0' }
  142.  
  143.  
  144. #
  145. # void printNotice (string notice)
  146. #
  147. # Prins a debugging notice to stdout.
  148. #
  149.  
  150. sub printNotice
  151. {
  152. my ($notice) = @_;
  153.  
  154. if ($g_debug > 1) {
  155. print ">> $notice\n";
  156. }
  157. }
  158.  
  159. sub track_hlstats_trend
  160. {
  161. if ($last_trend_timestamp > 0) {
  162. if ($last_trend_timestamp+299 < $ev_daemontime) {
  163. my $query = "
  164. SELECT
  165. COUNT(*),
  166. a.game
  167. FROM
  168. hlstats_Players a
  169. INNER JOIN
  170. (
  171. SELECT
  172. game
  173. FROM
  174. hlstats_Servers
  175. GROUP BY
  176. game
  177. ) AS b
  178. ON
  179. a.game = b.game
  180. GROUP BY
  181. a.game
  182. ";
  183. my $result = &execCached("get_total_player_counts", $query);
  184. my $insvalues = "";
  185. while ( my($total_players, $game) = $result->fetchrow_array) {
  186. my $query = "
  187. SELECT
  188. SUM(kills),
  189. SUM(headshots),
  190. COUNT(serverId),
  191. SUM(act_players),
  192. SUM(max_players)
  193. FROM
  194. hlstats_Servers
  195. WHERE
  196. game=?
  197. ";
  198. my $data = &execCached("get_game_stat_counts", $query, &quoteSQL($game));
  199. my ($total_kills, $total_headshots, $total_servers, $act_slots, $max_slots) = $data->fetchrow_array;
  200. if ($max_slots > 0) {
  201. if ($act_slots > $max_slots) {
  202. $act_slots = $max_slots;
  203. }
  204. }
  205. if ($insvalues ne "") {
  206. $insvalues .= ",";
  207. }
  208. $insvalues .= "
  209. (
  210. $ev_daemontime,
  211. '".&quoteSQL($game)."',
  212. $total_players,
  213. $total_kills,
  214. $total_headshots,
  215. $total_servers,
  216. $act_slots,
  217. $max_slots
  218. )
  219. ";
  220. }
  221. if ($insvalues ne "") {
  222. &execNonQuery("
  223. INSERT INTO
  224. hlstats_Trend
  225. (
  226. timestamp,
  227. game,
  228. players,
  229. kills,
  230. headshots,
  231. servers,
  232. act_slots,
  233. max_slots
  234. )
  235. VALUES $insvalues
  236. ");
  237. }
  238. $last_trend_timestamp = $ev_daemontime;
  239. &::printEvent("HLSTATSX", "Insert new server trend timestamp", 1);
  240. }
  241. } else {
  242. $last_trend_timestamp = $ev_daemontime;
  243. }
  244. }
  245.  
  246. sub send_global_chat
  247. {
  248. my ($message) = @_;
  249. while( my($server) = each(%g_servers))
  250. {
  251. if ($server ne $s_addr && $g_servers{$server}->{"srv_players"})
  252. {
  253. my @userlist;
  254. my %players_temp=%{$g_servers{$server}->{"srv_players"}};
  255. my $pcount = scalar keys %players_temp;
  256.  
  257. if ($pcount > 0) {
  258. while ( my($pl, $b_player) = each(%players_temp) ) {
  259. my $b_userid = $b_player->{userid};
  260. if ($g_global_chat == 2) {
  261. my $b_steamid = $b_player->{uniqueid};
  262. if ($g_servers{$server}->is_admin($b_steamid) == 1) {
  263. if (($b_player->{display_events} == 1) && ($b_player->{display_chat} == 1)) {
  264. push(@userlist, $b_player->{userid});
  265. }
  266. }
  267. } else {
  268. if (($b_player->{display_events} == 1) && ($b_player->{display_chat} == 1)) {
  269. push(@userlist, $b_player->{userid});
  270. }
  271. }
  272. }
  273. $g_servers{$server}->messageMany($message, 0, @userlist);
  274. }
  275. }
  276. }
  277. }
  278.  
  279. #
  280. # void buildEventInsertData ()
  281. #
  282. # Ran at startup to init event table queues, build initial queries, and set allowed-null columns
  283. #
  284.  
  285. my %g_eventtable_data = ();
  286.  
  287. sub buildEventInsertData
  288. {
  289. my $insertType = "";
  290. $insertType = " DELAYED" if ($db_lowpriority);
  291. while ( my ($table, $colsref) = each(%g_eventTables) )
  292. {
  293. $g_eventtable_data{$table}{queue} = [];
  294. $g_eventtable_data{$table}{nullallowed} = 0;
  295. $g_eventtable_data{$table}{lastflush} = $ev_daemontime;
  296. $g_eventtable_data{$table}{query} = "
  297. INSERT$insertType INTO
  298. hlstats_Events_$table
  299. (
  300. eventTime,
  301. serverId,
  302. map"
  303. ;
  304. my $j = 0;
  305. foreach $i (@{$colsref})
  306. {
  307. $g_eventtable_data{$table}{query} .= ",\n$i";
  308. if (substr($i, 0, 4) eq 'pos_') {
  309. $g_eventtable_data{$table}{nullallowed} |= (1 << $j);
  310. }
  311. $j++;
  312. }
  313. $g_eventtable_data{$table}{query} .= ") VALUES\n";
  314. }
  315. }
  316.  
  317. #
  318. # void recordEvent (string table, array cols, bool getid, [mixed eventData ...])
  319. #
  320. # Queues an event for addition to an Events table, flushing when hitting table queue limit.
  321. #
  322.  
  323. sub recordEvent
  324. {
  325. my $table = shift;
  326. my $unused = shift;
  327. my @coldata = @_;
  328.  
  329. my $value = "(FROM_UNIXTIME($::ev_unixtime),".$g_servers{$s_addr}->{'id'}.",'".quoteSQL($g_servers{$s_addr}->get_map())."'";
  330. $j = 0;
  331. for $i (@coldata) {
  332. if ($g_eventtable_data{$table}{nullallowed} & (1 << $j) && (!defined($i) || $i eq "")) {
  333. $value .= ",NULL";
  334. } elsif (!defined($i)) {
  335. $value .= ",''";
  336. } else {
  337. $value .= ",'".quoteSQL($i)."'";
  338. }
  339. $j++;
  340. }
  341. $value .= ")";
  342.  
  343. push(@{$g_eventtable_data{$table}{queue}}, $value);
  344.  
  345. if (scalar(@{$g_eventtable_data{$table}{queue}}) > $g_event_queue_size)
  346. {
  347. flushEventTable($table);
  348. }
  349. }
  350.  
  351. sub flushEventTable
  352. {
  353. my ($table) = @_;
  354.  
  355. if (scalar(@{$g_eventtable_data{$table}{queue}}) == 0)
  356. {
  357. return;
  358. }
  359.  
  360. my $query = $g_eventtable_data{$table}{query};
  361. foreach (@{$g_eventtable_data{$table}{queue}})
  362. {
  363. $query .= $_.",";
  364. }
  365. $query =~ s/,$//;
  366. execNonQuery($query);
  367. $g_eventtable_data{$table}{lastflush} = $ev_daemontime;
  368. $g_eventtable_data{$table}{queue} = [];
  369. }
  370.  
  371.  
  372. #
  373. # array calcSkill (int skill_mode, int killerSkill, int killerKills, int victimSkill, int victimKills, string weapon)
  374. #
  375. # Returns an array, where the first index contains the killer's new skill, and
  376. # the second index contains the victim's new skill.
  377. #
  378.  
  379. sub calcSkill
  380. {
  381. my ($skill_mode, $killerSkill, $killerKills, $victimSkill, $victimKills, $weapon, $killerTeam) = @_;
  382. my @newSkill;
  383.  
  384. # ignored bots never do a "comeback"
  385. return ($g_skill_minchange, $victimSkill) if ($killerSkill < 1);
  386. return ($killerSkill + $g_skill_minchange, $victimSkill) if ($victimSkill < 1);
  387.  
  388. if ($g_debug > 2) {
  389. &printNotice("Begin calcSkill: killerSkill=$killerSkill");
  390. &printNotice("Begin calcSkill: victimSkill=$victimSkill");
  391. }
  392.  
  393. my $modifier = 1.00;
  394. # Look up the weapon's skill modifier
  395. if (defined($g_games{$g_servers{$s_addr}->{game}}{weapons}{$weapon})) {
  396. $modifier = $g_games{$g_servers{$s_addr}->{game}}{weapons}{$weapon}{modifier};
  397. }
  398.  
  399. # Calculate the new skills
  400.  
  401. my $killerSkillChange = 0;
  402. if ($g_skill_ratio_cap > 0) {
  403. # SkillRatioCap, from *XYZ*SaYnt
  404. #
  405. # dgh...we want to cap the ratio between the victimkill and killerskill. For example, if the number 1 player
  406. # kills a newbie, he gets 1000/5000 * 5 * 1 = 1 points. If gets killed by the newbie, he gets 5000/1000 * 5 *1
  407. # = -25 points. Not exactly fair. To fix this, I'm going to cap the ratio to 1/2 and 2/1.
  408. # these numbers are designed such that an excellent player will have to get about a 2:1 ratio against noobs to
  409. # hold steady in points.
  410. my $lowratio = 0.7;
  411. my $highratio = 1.0 / $lowratio;
  412. my $ratio = ($victimSkill / $killerSkill);
  413. if ($ratio < $lowratio) { $ratio = $lowratio; }
  414. if ($ratio > $highratio) { $ratio = $highratio; }
  415. $killerSkillChange = $ratio * 5 * $modifier;
  416. } else {
  417. $killerSkillChange = ($victimSkill / $killerSkill) * 5 * $modifier;
  418. }
  419.  
  420. if ($killerSkillChange > $g_skill_maxchange) {
  421. &printNotice("Capping killer skill change of $killerSkillChange to $g_skill_maxchange") if ($g_debug > 2);
  422. $killerSkillChange = $g_skill_maxchange;
  423. }
  424.  
  425. my $victimSkillChange = $killerSkillChange;
  426.  
  427. if ($skill_mode == 1)
  428. {
  429. $victimSkillChange = $killerSkillChange * 0.75;
  430. }
  431. elsif ($skill_mode == 2)
  432. {
  433. $victimSkillChange = $killerSkillChange * 0.5;
  434. }
  435. elsif ($skill_mode == 3)
  436. {
  437. $victimSkillChange = $killerSkillChange * 0.25;
  438. }
  439. elsif ($skill_mode == 4)
  440. {
  441. $victimSkillChange = 0;
  442. }
  443. elsif ($skill_mode == 5)
  444. {
  445. #Zombie Panic: Source only
  446. #Method suggested by heimer. Survivor's lose half of killer's gain when dying, but Zombie's only lose a quarter.
  447. if ($killerTeam eq "Undead")
  448. {
  449. $victimSkillChange = $killerSkillChange * 0.5;
  450. }
  451. elsif ($killerTeam eq "Survivor")
  452. {
  453. $victimSkillChange = $killerSkillChange * 0.25;
  454. }
  455. }
  456.  
  457. if ($victimSkillChange > $g_skill_maxchange) {
  458. &printNotice("Capping victim skill change of $victimSkillChange to $g_skill_maxchange") if ($g_debug > 2);
  459. $victimSkillChange = $g_skill_maxchange;
  460. }
  461.  
  462. if ($g_skill_maxchange >= $g_skill_minchange) {
  463. if ($killerSkillChange < $g_skill_minchange) {
  464. &printNotice("Capping killer skill change of $killerSkillChange to $g_skill_minchange") if ($g_debug > 2);
  465. $killerSkillChange = $g_skill_minchange;
  466. }
  467.  
  468. if (($victimSkillChange < $g_skill_minchange) && ($skill_mode != 4)) {
  469. &printNotice("Capping victim skill change of $victimSkillChange to $g_skill_minchange") if ($g_debug > 2);
  470. $victimSkillChange = $g_skill_minchange;
  471. }
  472. }
  473. if (($killerKills < $g_player_minkills ) || ($victimKills < $g_player_minkills )) {
  474. $killerSkillChange = $g_skill_minchange;
  475. if ($skill_mode != 4) {
  476. $victimSkillChange = $g_skill_minchange;
  477. } else {
  478. $victimSkillChange = 0;
  479. }
  480. }
  481.  
  482. $killerSkill += $killerSkillChange;
  483. $victimSkill -= $victimSkillChange;
  484.  
  485. # we want int not float
  486. $killerSkill = sprintf("%d", $killerSkill + 0.5);
  487. $victimSkill = sprintf("%d", $victimSkill + 0.5);
  488.  
  489. if ($g_debug > 2) {
  490. &printNotice("End calcSkill: killerSkill=$killerSkill");
  491. &printNotice("End calcSkill: victimSkill=$victimSkill");
  492. }
  493.  
  494. return ($killerSkill, $victimSkill);
  495. }
  496.  
  497. sub calcL4DSkill
  498. {
  499. my ($killerSkill, $weapon, $difficulty) = @_;
  500.  
  501. # ignored bots never do a "comeback"
  502. #return ($killerSkill, $victimSkill) if ($killerSkill < 1);
  503. #return ($killerSkill, $victimSkill) if ($victimSkill < 1);
  504.  
  505. if ($g_debug > 2) {
  506. &printNotice("Begin calcSkill: killerSkill=$killerSkill");
  507. &printNotice("Begin calcSkill: victimSkill=$victimSkill");
  508. }
  509.  
  510. my $modifier = 1.00;
  511. # Look up the weapon's skill modifier
  512. if (defined($g_games{$g_servers{$s_addr}->{game}}{weapons}{$weapon})) {
  513. $modifier = $g_games{$g_servers{$s_addr}->{game}}{weapons}{$weapon}{modifier};
  514. }
  515.  
  516. # Calculate the new skills
  517.  
  518. $diffweight=0.5;
  519. if ($difficulty > 0) {
  520. $diffweight = $difficulty / 2;
  521. }
  522.  
  523. my $killerSkillChange = $pointvalue * $diffweight;
  524.  
  525. if ($killerSkillChange > $g_skill_maxchange) {
  526. &printNotice("Capping killer skill change of $killerSkillChange to $g_skill_maxchange") if ($g_debug > 2);
  527. $killerSkillChange = $g_skill_maxchange;
  528. }
  529.  
  530. if ($g_skill_maxchange >= $g_skill_minchange) {
  531. if ($killerSkillChange < $g_skill_minchange) {
  532. &printNotice("Capping killer skill change of $killerSkillChange to $g_skill_minchange") if ($g_debug > 2);
  533. $killerSkillChange = $g_skill_minchange;
  534. }
  535. }
  536.  
  537. $killerSkill += $killerSkillChange;
  538. # we want int not float
  539. $killerSkill = sprintf("%d", $killerSkill + 0.5);
  540.  
  541. if ($g_debug > 2) {
  542. &printNotice("End calcSkill: killerSkill=$killerSkill");
  543. }
  544.  
  545. return $killerSkill;
  546. }
  547.  
  548.  
  549. # Gives members of 'team' an extra 'reward' skill points. Members of the team
  550. # who have been inactive (no events) for more than 2 minutes are not rewarded.
  551. #
  552.  
  553. sub rewardTeam
  554. {
  555. my ($team, $reward, $actionid, $actionname, $actioncode) = @_;
  556. $rcmd = $g_servers{$s_addr}->{broadcasting_command};
  557.  
  558. my $player;
  559.  
  560. &printNotice("Rewarding team \"$team\" with \"$reward\" skill for action \"$actionid\" ...");
  561. my @userlist;
  562. foreach $player (values(%g_players)) {
  563. my $player_team = $player->{team};
  564. my $player_timestamp = $player->{timestamp};
  565. if (($g_servers{$s_addr}->{ignore_bots} == 1) && (($player->{is_bot} == 1) || ($player->{userid} <= 0))) {
  566. $desc = "(IGNORED) BOT: ";
  567. } else {
  568. if ($player_team eq $team) {
  569. if ($g_debug > 2) {
  570. &printNotice("Rewarding " . $player->getInfoString() . " with \"$reward\" skill for action \"$actionid\"");
  571. }
  572.  
  573. &recordEvent(
  574. "TeamBonuses", 0,
  575. $player->{playerid},
  576. $actionid,
  577. $reward
  578. );
  579. $player->increment("skill", $reward, 1);
  580. $player->increment("session_skill", $reward, 1);
  581. $player->updateDB();
  582. }
  583. if ($player->{is_bot} == 0 && $player->{userid} > 0 && $player->{display_events} == 1) {
  584. push(@userlist, $player->{userid});
  585. }
  586. }
  587. }
  588. if (($g_servers{$s_addr}->{broadcasting_events} == 1) && ($g_servers{$s_addr}->{broadcasting_player_actions} == 1)) {
  589. my $coloraction = $g_servers{$s_addr}->{format_action};
  590. my $verb = "got";
  591. if ($reward < 0) {
  592. $verb = "lost";
  593. }
  594. my $msg = sprintf("%s %s %s points for %s%s", $team, $verb, abs($reward), $coloraction, $actionname);
  595. $g_servers{$s_addr}->messageMany($msg, 0, @userlist);
  596. }
  597. }
  598.  
  599.  
  600. #
  601. # int getPlayerId (uniqueId)
  602. #
  603. # Looks up a player's ID number, from their unique (WON) ID. Returns their PID.
  604. #
  605.  
  606. sub getPlayerId
  607. {
  608. my ($uniqueId) = @_;
  609.  
  610. my $query = "
  611. SELECT
  612. playerId
  613. FROM
  614. hlstats_PlayerUniqueIds
  615. WHERE
  616. uniqueId='" . &::quoteSQL($uniqueId) . "' AND
  617. game='" . $g_servers{$s_addr}->{game} . "'
  618. ";
  619. my $result = &doQuery($query);
  620.  
  621. if ($result->rows > 0) {
  622. my ($playerId) = $result->fetchrow_array;
  623. $result->finish;
  624. return $playerId;
  625. } else {
  626. $result->finish;
  627. return 0;
  628. }
  629. }
  630.  
  631.  
  632. #
  633. # int updatePlayerProfile (object player, string field, string value)
  634. #
  635. # Updates a player's profile information in the database.
  636. #
  637.  
  638. sub updatePlayerProfile
  639. {
  640. my ($player, $field, $value) = @_;
  641. $rcmd = $g_servers{$s_addr}->{player_command};
  642.  
  643. unless ($player) {
  644. &printNotice("updatePlayerInfo: Bad player");
  645. return 0;
  646. }
  647.  
  648. $value = &quoteSQL($value);
  649. if ($value eq "none" || $value eq " ") {
  650. $value = "";
  651. }
  652.  
  653. my $playerName = &abbreviate($player->{name});
  654. my $playerId = $player->{playerid};
  655.  
  656. &execNonQuery("
  657. UPDATE
  658. hlstats_Players
  659. SET
  660. $field='$value'
  661. WHERE
  662. playerId=$playerId
  663. ");
  664.  
  665. if ($g_servers{$s_addr}->{player_events} == 1) {
  666. my $p_userid = $g_servers{$s_addr}->format_userid($player->{userid});
  667. my $p_is_bot = $player->{is_bot};
  668. $cmd_str = $rcmd." $p_userid ".$g_servers{$s_addr}->quoteparam("SET command successful for '$playerName'.");
  669. $g_servers{$s_addr}->dorcon($cmd_str);
  670. }
  671. return 1;
  672. }
  673.  
  674.  
  675. #
  676. # mixed getClanId (string name)
  677. #
  678. # Looks up a player's clan ID from their name. Compares the player's name to tag
  679. # patterns in hlstats_ClanTags. Patterns look like: [AXXXXX] (matches 1 to 6
  680. # letters inside square braces, e.g. [ZOOM]Player) or =\*AAXX\*= (matches
  681. # 2 to 4 letters between an equals sign and an asterisk, e.g. =*RAGE*=Player).
  682. #
  683. # Special characters in the pattern:
  684. # A matches one character (i.e. a character is required)
  685. # X matches zero or one characters (i.e. a character is optional)
  686. # a matches literal A or a
  687. # x matches literal X or x
  688. #
  689. # If no clan exists for the tag, it will be created. Returns the clan's ID, or
  690. # 0 if the player is not in a clan.
  691. #
  692.  
  693. sub getClanId
  694. {
  695. my ($name) = @_;
  696. my $clanTag = "";
  697. my $clanName = "";
  698. my $clanId = 0;
  699. my $result = &doQuery("
  700. SELECT
  701. pattern,
  702. position,
  703. LENGTH(pattern) AS pattern_length
  704. FROM
  705. hlstats_ClanTags
  706. ORDER BY
  707. pattern_length DESC,
  708. id
  709. ");
  710.  
  711. while ( my($pattern, $position) = $result->fetchrow_array) {
  712. my $regpattern = quotemeta($pattern);
  713. $regpattern =~ s/([A-Za-z0-9]+[A-Za-z0-9_-]*)/\($1\)/; # to find clan name from tag
  714. $regpattern =~ s/A/./g;
  715. $regpattern =~ s/X/.?/g;
  716.  
  717. if ($g_debug > 2) {
  718. &printNotice("regpattern=$regpattern");
  719. }
  720.  
  721. if ((($position eq "START" || $position eq "EITHER") && $name =~ /^($regpattern).+/i) ||
  722. (($position eq "END" || $position eq "EITHER") && $name =~ /.+($regpattern)$/i)) {
  723.  
  724. if ($g_debug > 2) {
  725. &printNotice("pattern \"$regpattern\" matches \"$name\"! 1=\"$1\" 2=\"$2\"");
  726. }
  727.  
  728. $clanTag = $1;
  729. $clanName = $2;
  730. last;
  731. }
  732. }
  733.  
  734. unless ($clanTag) {
  735. return 0;
  736. }
  737.  
  738. my $query = "
  739. SELECT
  740. clanId
  741. FROM
  742. hlstats_Clans
  743. WHERE
  744. tag='" . &quoteSQL($clanTag) . "' AND
  745. game='$g_servers{$s_addr}->{game}'
  746. ";
  747. $result = &doQuery($query);
  748.  
  749. if ($result->rows) {
  750. ($clanId) = $result->fetchrow_array;
  751. $result->finish;
  752. return $clanId;
  753. } else {
  754. # The clan doesn't exist yet, so we create it.
  755. $query = "
  756. REPLACE INTO
  757. hlstats_Clans
  758. (
  759. tag,
  760. name,
  761. game
  762. )
  763. VALUES
  764. (
  765. '" . &quoteSQL($clanTag) . "',
  766. '" . &quoteSQL($clanName) . "',
  767. '".&quoteSQL($g_servers{$s_addr}->{game})."'
  768. )
  769. ";
  770. &execNonQuery($query);
  771.  
  772. $clanId = $db_conn->{'mysql_insertid'};
  773.  
  774. &printNotice("Created clan \"$clanName\" <C:$clanId> with tag "
  775. . "\"$clanTag\" for player \"$name\"");
  776. return $clanId;
  777. }
  778. }
  779.  
  780.  
  781. #
  782. # object getServer (string address, int port)
  783. #
  784. # Looks up a server's ID number in the Servers table, by searching for a
  785. # matching IP address and port. NOTE you must specify IP addresses in the
  786. # Servers table, NOT hostnames.
  787. #
  788. # Returns a new "Server object".
  789. #
  790.  
  791. sub getServer
  792. {
  793. my ($address, $port) = @_;
  794.  
  795. my $query = "
  796. SELECT
  797. a.serverId,
  798. a.game,
  799. a.name,
  800. a.rcon_password,
  801. a.publicaddress,
  802. IFNULL(b.`value`,3) AS game_engine,
  803. IFNULL(c.`realgame`, 'hl2mp') AS realgame,
  804. IFNULL(a.max_players, 0) AS maxplayers
  805.  
  806. FROM
  807. 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
  808. WHERE
  809. address=? AND
  810. port=? LIMIT 1
  811. ";
  812. my @vals = (
  813. $address,
  814. $port
  815. );
  816. my $result = &execCached("get_server_information", $query, @vals);
  817.  
  818. if ($result->rows) {
  819. my ($serverId, $game, $name, $rcon_pass, $publicaddress, $gameengine, $realgame, $maxplayers) = $result->fetchrow_array;
  820. $result->finish;
  821. if (!defined($g_games{$game})) {
  822. $g_games{$game} = new HLstats_Game($game);
  823. }
  824. # l4d code should be reused for l4d2
  825. # 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.
  826. #$realgame = "l4d" if $realgame eq "l4d2";
  827.  
  828. return new HLstats_Server($serverId, $address, $port, $name, $rcon_pass, $game, $publicaddress, $gameengine, $realgame, $maxplayers);
  829. } else {
  830. $result->finish;
  831. return 0;
  832. }
  833. }
  834.  
  835. #
  836. #
  837. #
  838. #
  839. #
  840.  
  841. sub queryServer
  842. {
  843. my ($iaddr, $iport, @query) = @_;
  844. my $game = "";
  845. my $timeout=2;
  846. my $message = IO::Socket::INET->new(Proto=>"udp",Timeout=>$timeout,PeerPort=>$iport,PeerAddr=>$iaddr) or die "Can't make UDP socket: $@";
  847. $message->send("\xFF\xFF\xFF\xFFTSource Engine Query\x00");
  848. my ($datagram,$flags);
  849. my $end = time + $timeout;
  850. my $rin = '';
  851. vec($rin, fileno($message), 1) = 1;
  852.  
  853. my %hash = ();
  854.  
  855. while (1) {
  856. my $timeleft = $end - time;
  857. last if ($timeleft <= 0);
  858. my ($nfound, $t) = select(my $rout = $rin, undef, undef, $timeleft);
  859. last if ($nfound == 0); # either timeout or end of file
  860. $message->recv($datagram,1024,$flags);
  861. @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);
  862. }
  863.  
  864. return @hash{@query};
  865. }
  866.  
  867.  
  868. sub getServerMod
  869. {
  870. my ($address, $port) = @_;
  871. my ($playgame);
  872.  
  873. &printEvent ("DETECT", "Querying $address".":$port for gametype");
  874.  
  875. my @query = (
  876. 'gamename',
  877. 'gamedir',
  878. 'hostname',
  879. 'numplayers',
  880. 'maxplayers',
  881. 'mapname'
  882. );
  883.  
  884. my ($gamename, $gamedir, $hostname, $numplayers, $maxplayers, $mapname) = &queryServer($address, $port, @query);
  885.  
  886. if ($gamename =~ /^Counter-Strike$/i) {
  887. $playgame = "cstrike";
  888. } elsif ($gamename =~ /^Counter-Strike/i) {
  889. $playgame = "css";
  890. } elsif ($gamename =~ /^Team Fortress C/i) {
  891. $playgame = "tfc";
  892. } elsif ($gamename =~ /^Team Fortress/i) {
  893. $playgame = "tf";
  894. } elsif ($gamename =~ /^Day of Defeat$/i) {
  895. $playgame = "dod";
  896. } elsif ($gamename =~ /^Day of Defeat/i) {
  897. $playgame = "dods";
  898. } elsif ($gamename =~ /^Insurgency/i) {
  899. $playgame = "insmod";
  900. } elsif ($gamename =~ /^Neotokyo/i) {
  901. $playgame = "nts";
  902. } elsif ($gamename =~ /^Fortress Forever/i) {
  903. $playgame = "ff";
  904. } elsif ($gamename =~ /^Age of Chivalry/i) {
  905. $playgame = "aoc";
  906. } elsif ($gamename =~ /^Dystopia/i) {
  907. $playgame = "dystopia";
  908. } elsif ($gamename =~ /^Stargate/i) {
  909. $playgame = "sgtls";
  910. } elsif ($gamename =~ /^Battle Grounds/i) {
  911. $playgame = "bg2";
  912. } elsif ($gamename =~ /^Hidden/i) {
  913. $playgame = "hidden";
  914. } elsif ($gamename =~ /^L4D /i) {
  915. $playgame = "l4d";
  916. } elsif ($gamename =~ /^Left 4 Dead 2/i) {
  917. $playgame = "l4d2";
  918. } elsif ($gamename =~ /^ZPS /i) {
  919. $playgame = "zps";
  920. } elsif ($gamename =~ /^NS /i) {
  921. $playgame = "ns";
  922. } elsif ($gamename =~ /^pvkii/i) {
  923. $playgame = "pvkii";
  924. } elsif ($gamename =~ /^CSPromod/i) {
  925. $playgame = "csp";
  926. } elsif ($gamename eq "Half-Life") {
  927. $playgame = "valve";
  928. } elsif ($gamename eq "Nuclear Dawn") {
  929. $playgame = "nucleardawn";
  930.  
  931. # 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
  932. } elsif ($gamedir =~ /^ges/i) {
  933. $playgame = "ges";
  934. } elsif ($gamedir =~ /^fistful_of_frags/i || $gamedir =~ /^fof/i) {
  935. $playgame = "fof";
  936. } elsif ($gamedir =~ /^hl2mp/i) {
  937. $playgame = "hl2mp";
  938. } elsif ($gamedir =~ /^tfc/i) {
  939. $playgame = "tfc";
  940. } elsif ($gamedir =~ /^tf/i) {
  941. $playgame = "tf";
  942. } elsif ($gamedir =~ /^ins/i) {
  943. $playgame = "insmod";
  944. } elsif ($gamedir =~ /^neotokyo/i) {
  945. $playgame = "nts";
  946. } elsif ($gamedir =~ /^fortressforever/i) {
  947. $playgame = "ff";
  948. } elsif ($gamedir =~ /^ageofchivalry/i) {
  949. $playgame = "aoc";
  950. } elsif ($gamedir =~ /^dystopia/i) {
  951. $playgame = "dystopia";
  952. } elsif ($gamedir =~ /^sgtls/i) {
  953. $playgame = "sgtls";
  954. } elsif ($gamedir =~ /^hidden/i) {
  955. $playgame = "hidden";
  956. } elsif ($gamedir =~ /^left4dead/i) {
  957. $playgame = "l4d";
  958. } elsif ($gamedir =~ /^left4dead2/i) {
  959. $playgame = "l4d2";
  960. } elsif ($gamedir =~ /^zps/i) {
  961. $playgame = "zps";
  962. } elsif ($gamedir =~ /^ns/i) {
  963. $playgame = "ns";
  964. } elsif ($gamedir =~ /^bg/i) {
  965. $playgame = "bg2";
  966. } elsif ($gamedir =~ /^pvkii/i) {
  967. $playgame = "pvkii";
  968. } elsif ($gamedir =~ /^cspromod/i) {
  969. $playgame = "csp";
  970. } elsif ($gamedir =~ /^valve$/i) {
  971. $playgame = "valve";
  972. } elsif ($gamedir =~ /^nucleardawn$/i) {
  973. $playgame = "nucleardawn";
  974. } elsif ($gamedir =~ /^dinodday$/i) {
  975. $playgame = "dinodday";
  976. } else {
  977. # We didn't found our mod, giving up.
  978. &printEvent("DETECT", "Failed to get Server Mod");
  979. return 0;
  980. }
  981. &printEvent("DETECT", "Saving server " . $address . ":" . $port . " with gametype " . $playgame);
  982. &addServerToDB($address, $port, $hostname, $playgame, $numplayers, $maxplayers, $mapname);
  983. return $playgame;
  984. }
  985.  
  986. sub addServerToDB
  987. {
  988. my ($address, $port, $name, $game, $act_players, $max_players, $act_map) = @_;
  989. my $sql = "INSERT INTO hlstats_Servers (address, port, name, game, act_players, max_players, act_map) VALUES ('$address', $port, '".&quoteSQL($name)."', '".&quoteSQL($game)."', $act_players, $max_players, '".&quoteSQL($act_map)."')";
  990. &execNonQuery($sql);
  991.  
  992. my $last_id = $db_conn->{'mysql_insertid'};
  993. &execNonQuery("DELETE FROM `hlstats_Servers_Config` WHERE serverId = $last_id");
  994. &execNonQuery("INSERT INTO `hlstats_Servers_Config` (`serverId`, `parameter`, `value`)
  995. SELECT $last_id, `parameter`, `value`
  996. FROM `hlstats_Mods_Defaults` WHERE `code` = '';");
  997. &execNonQuery("INSERT INTO `hlstats_Servers_Config` (`serverId`, `parameter`, `value`) VALUES
  998. ($last_id, 'Mod', '');");
  999. &execNonQuery("INSERT INTO `hlstats_Servers_Config` (`serverId`, `parameter`, `value`)
  1000. SELECT $last_id, `parameter`, `value`
  1001. FROM `hlstats_Games_Defaults` WHERE `code` = '".&quoteSQL($game)."'
  1002. ON DUPLICATE KEY UPDATE `value` = VALUES(`value`);");
  1003. &readDatabaseConfig();
  1004.  
  1005. return 1;
  1006. }
  1007.  
  1008. #
  1009. # boolean sameTeam (string team1, string team2)
  1010. #
  1011. # This should be expanded later to allow for team alliances (e.g. TFC-hunted).
  1012. #
  1013.  
  1014. sub sameTeam
  1015. {
  1016. my ($team1, $team2) = @_;
  1017.  
  1018. if (($team1 eq $team2) && (($team1 ne "Unassigned") || ($team2 ne "Unassigned"))) {
  1019. return 1;
  1020. } else {
  1021. return 0;
  1022. }
  1023. }
  1024.  
  1025.  
  1026. #
  1027. # string getPlayerInfoString (object player, string ident)
  1028. #
  1029.  
  1030. sub getPlayerInfoString
  1031. {
  1032. my ($player) = shift;
  1033. my @ident = @_;
  1034.  
  1035. if ($player) {
  1036. return $player->getInfoString();
  1037. } else {
  1038. return "(" . join(",", @ident) . ")";
  1039. }
  1040. }
  1041.  
  1042.  
  1043.  
  1044. #
  1045. # array getPlayerInfo (string player, string $ipAddr)
  1046. #
  1047. # Get a player's name, uid, wonid and team from "Name<uid><wonid><team>".
  1048. #
  1049.  
  1050. sub getPlayerInfo
  1051. {
  1052. my ($player, $create_player, $ipAddr) = @_;
  1053.  
  1054. if ($player =~ /^(.*?)<(\d+)><([^<>]*)><([^<>]*)>(?:<([^<>]*)>)?.*$/) {
  1055. my $name = $1;
  1056. my $userid = $2;
  1057. my $uniqueid = $3;
  1058. my $team = $4;
  1059. my $role = $5;
  1060. my $bot = 0;
  1061. my $haveplayer = 0;
  1062.  
  1063. $plainuniqueid = $uniqueid;
  1064. $uniqueid =~ s/^STEAM_[0-9]+?\://;
  1065.  
  1066. if (($uniqueid eq "Console") && ($team eq "Console")) {
  1067. return 0;
  1068. }
  1069. if ($g_servers{$s_addr}->{play_game} == L4D()) {
  1070. #for l4d, create meta player object for each role
  1071. if ($uniqueid eq "") {
  1072. #infected & witch have blank steamid
  1073. if ($name eq "infected") {
  1074. $uniqueid = "BOT-Horde";
  1075. $team = "Infected";
  1076. $userid = -9;
  1077. } elsif ($name eq "witch") {
  1078. $uniqueid = "BOT-Witch";
  1079. $team = "Infected";
  1080. $userid = -10;
  1081. } else {
  1082. return 0;
  1083. }
  1084. } elsif ($uniqueid eq "BOT") {
  1085. #all other bots have BOT for steamid
  1086. if ($team eq "Survivor") {
  1087. if ($name eq "Nick") {
  1088. $userid = -11;
  1089. } elsif ($name eq "Ellis") {
  1090. $userid = -13;
  1091. } elsif ($name eq "Rochelle") {
  1092. $userid = -14;
  1093. } elsif ($name eq "Coach") {
  1094. $userid = -12;
  1095. } elsif ($name eq "Louis") {
  1096. $userid = -4;
  1097. } elsif ($name eq "Zoey") {
  1098. $userid = -1;
  1099. } elsif ($name eq "Francis") {
  1100. $userid = -2;
  1101. } elsif ($name eq "Bill") {
  1102. $userid = -3;
  1103. } else {
  1104. &printEvent("ERROR", "No survivor match for $name",0,1);
  1105. $userid = -4;
  1106. }
  1107. } else {
  1108. if ($name eq "Smoker") {
  1109. $userid = -5;
  1110. } elsif ($name eq "Boomer") {
  1111. $userid = -6;
  1112. } elsif ($name eq "Hunter") {
  1113. $userid = -7;
  1114. } elsif ($name eq "Spitter") {
  1115. $userid = -15;
  1116. } elsif ($name eq "Jockey") {
  1117. $userid = -16;
  1118. } elsif ($name eq "Charger") {
  1119. $userid = -17;
  1120. } elsif ($name eq "Tank") {
  1121. $userid = -8;
  1122. } else {
  1123. &printEvent("DEBUG", "No infected match for $name",0,1);
  1124. $userid = -8;
  1125. }
  1126. }
  1127. $uniqueid = "BOT-".$name;
  1128. $name = "BOT-".$name;
  1129. }
  1130. }
  1131.  
  1132. if ($ipAddr eq "none") {
  1133. $ipAddr = "";
  1134. }
  1135.  
  1136. $bot = botidcheck($uniqueid);
  1137.  
  1138. if ($g_mode eq "NameTrack") {
  1139. $uniqueid = $name;
  1140. } else {
  1141. if ($g_mode eq "LAN" && !$bot && $userid > 0) {
  1142. if ($ipAddr ne "") {
  1143. $g_lan_noplayerinfo->{"$s_addr/$userid/$name"} = {
  1144. ipaddress => $ipAddr,
  1145. userid => $userid,
  1146. name => $name,
  1147. server => $s_addr
  1148. };
  1149. $uniqueid = $ipAddr;
  1150. } else {
  1151. while ( my($index, $player) = each(%g_players) ) {
  1152. if (($player->{userid} eq $userid) &&
  1153. ($player->{name} eq $name)) {
  1154.  
  1155. $uniqueid = $player->{uniqueid};
  1156. $haveplayer = 1;
  1157. last;
  1158. }
  1159. }
  1160. if (!$haveplayer) {
  1161. while ( my($index, $player) = each(%g_lan_noplayerinfo) ) {
  1162. if (($player->{server} eq $s_addr) &&
  1163. ($player->{userid} eq $userid) &&
  1164. ($player->{name} eq $name)) {
  1165.  
  1166. $uniqueid = $player->{ipaddress};
  1167. $haveplayer = 1;
  1168. }
  1169. }
  1170. }
  1171. if (!$haveplayer) {
  1172. $uniqueid = "UNKNOWN";
  1173. }
  1174. }
  1175. } else {
  1176. # Normal (steamid) mode player and bot, as well as lan mode bots
  1177. if ($bot) {
  1178. $md5 = Digest::MD5->new;
  1179. $md5->add($name);
  1180. $md5->add($s_addr);
  1181. $uniqueid = "BOT:" . $md5->hexdigest;
  1182. $unique_id = $uniqueid if ($g_mode eq "LAN");
  1183. }
  1184.  
  1185. if ($uniqueid eq "UNKNOWN"
  1186. || $uniqueid eq "STEAM_ID_PENDING" || $uniqueid eq "STEAM_ID_LAN"
  1187. || $uniqueid eq "VALVE_ID_PENDING" || $uniqueid eq "VALVE_ID_LAN"
  1188. ) {
  1189. return {
  1190. name => $name,
  1191. userid => $userid,
  1192. uniqueid => $uniqueid,
  1193. team => $team
  1194. };
  1195. }
  1196. }
  1197. }
  1198.  
  1199. if (!$haveplayer)
  1200. {
  1201. while ( my ($index, $player) = each(%g_players) ) {
  1202. # Cannot exit loop early as more than one player can exist with same uniqueid
  1203. # (bug? or just bad logging)
  1204. # Either way, we disconnect any that don't match the current line
  1205. if ($player->{uniqueid} eq $uniqueid) {
  1206. $haveplayer = 1;
  1207. # Catch players reconnecting without first disconnecting
  1208. if ($player->{userid} != $userid) {
  1209.  
  1210. &doEvent_Disconnect(
  1211. $player->{"userid"},
  1212. $uniqueid,
  1213. ""
  1214. );
  1215. $haveplayer = 0;
  1216. }
  1217. }
  1218. }
  1219. }
  1220.  
  1221. if ($haveplayer) {
  1222. my $player = lookupPlayer($s_addr, $userid, $uniqueid);
  1223. if ($player) {
  1224. # The only time team should go /back/ to unassigned ("") is on mapchange
  1225. # (which is already handled in the ChangeMap handler)
  1226. # So ignore when team is blank (<>) from lazy log lines
  1227. if ($team ne "" && $player->{team} ne $team) {
  1228. &doEvent_TeamSelection(
  1229. $userid,
  1230. $uniqueid,
  1231. $team
  1232. );
  1233. }
  1234. if ($role ne "" && $role ne $player->{role}) {
  1235. &doEvent_RoleSelection(
  1236. $player->{"userid"},
  1237. $player->{"uniqueid"},
  1238. $role
  1239. );
  1240. }
  1241.  
  1242. $player->updateTimestamp();
  1243. }
  1244. } else {
  1245. if ($userid != 0) {
  1246. if ($create_player > 0) {
  1247. my $preIpAddr = "";
  1248. if ($g_preconnect->{"$s_addr/$userid/$name"}) {
  1249. $preIpAddr = $g_preconnect->{"$s_addr/$userid/$name"}->{"ipaddress"};
  1250. }
  1251. # Add the player to our hash of player objects
  1252. $g_servers{$s_addr}->{"srv_players"}->{"$userid/$uniqueid"} = new HLstats_Player(
  1253. server => $s_addr,
  1254. server_id => $g_servers{$s_addr}->{id},
  1255. userid => $userid,
  1256. uniqueid => $uniqueid,
  1257. plain_uniqueid => $plainuniqueid,
  1258. game => $g_servers{$s_addr}->{game},
  1259. name => $name,
  1260. team => $team,
  1261. role => $role,
  1262. is_bot => $bot,
  1263. display_events => $g_servers{$s_addr}->{default_display_events},
  1264. address => (($preIpAddr ne "") ? $preIpAddr : $ipAddr)
  1265. );
  1266.  
  1267. if ($preIpAddr ne "") {
  1268. &printEvent("SERVER", "LATE CONNECT [$name/$userid] - steam userid validated");
  1269. &doEvent_Connect($userid, $uniqueid, $preIpAddr);
  1270. delete($g_preconnect->{"$s_addr/$userid/$name"});
  1271. }
  1272. # Increment number of players on server
  1273. $g_servers{$s_addr}->updatePlayerCount();
  1274. }
  1275. } elsif (($g_mode eq "LAN") && (defined($g_lan_noplayerinfo{"$s_addr/$userid/$name"}))) {
  1276. if ((!$haveplayer) && ($uniqueid ne "UNKNOWN") && ($create_player > 0)) {
  1277. $g_servers{$s_addr}->{srv_players}->{"$userid/$uniqueid"} = new HLstats_Player(
  1278. server => $s_addr,
  1279. server_id => $g_servers{$s_addr}->{id},
  1280. userid => $userid,
  1281. uniqueid => $uniqueid,
  1282. plain_uniqueid => $plainuniqueid,
  1283. game => $g_servers{$s_addr}->{game},
  1284. name => $name,
  1285. team => $team,
  1286. role => $role,
  1287. is_bot => $bot
  1288. );
  1289. delete($g_lan_noplayerinfo{"$s_addr/$userid/$name"});
  1290. # Increment number of players on server
  1291.  
  1292. $g_servers{$s_addr}->updatePlayerCount();
  1293. }
  1294. } else {
  1295. &printNotice("No player object available for player \"$name\" <U:$userid>");
  1296. }
  1297. }
  1298.  
  1299. return {
  1300. name => $name,
  1301. userid => $userid,
  1302. uniqueid => $uniqueid,
  1303. team => $team,
  1304. is_bot => $bot
  1305. };
  1306. } elsif ($player =~ /^(.+)<([^<>]+)>$/) {
  1307. my $name = $1;
  1308. my $uniqueid = $2;
  1309. my $bot = 0;
  1310.  
  1311. if (&botidcheck($uniqueid)) {
  1312. $md5 = Digest::MD5->new;
  1313. $md5->add($ev_daemontime);
  1314. $md5->add($s_addr);
  1315. $uniqueid = "BOT:" . $md5->hexdigest;
  1316. $bot = 1;
  1317. }
  1318. return {
  1319. name => $name,
  1320. uniqueid => $uniqueid,
  1321. is_bot => $bot
  1322. };
  1323. } elsif ($player =~ /^<><([^<>]+)><>$/) {
  1324. my $uniqueid = $1;
  1325. my $bot = 0;
  1326. if (&botidcheck($uniqueid)) {
  1327. $md5 = Digest::MD5->new;
  1328. $md5->add($ev_daemontime);
  1329. $md5->add($s_addr);
  1330. $uniqueid = "BOT:" . $md5->hexdigest;
  1331. $bot = 1;
  1332. }
  1333. return {
  1334. uniqueid => $uniqueid,
  1335. is_bot => $bot
  1336. };
  1337. } else {
  1338. return 0;
  1339. }
  1340. }
  1341.  
  1342.  
  1343. #
  1344. # hash getProperties (string propstring)
  1345. #
  1346. # Parse (key "value") properties into a hash.
  1347. #
  1348.  
  1349. sub getProperties
  1350. {
  1351. my ($propstring) = @_;
  1352. my %properties;
  1353. my $dods_flag = 0;
  1354.  
  1355. while ($propstring =~ s/^\s*\((\S+)(?:(?: "(.+?)")|(?: ([^\)]+)))?\)//) {
  1356. my $key = $1;
  1357. if (defined($2)) {
  1358. if ($key eq "player") {
  1359. if ($dods_flag == 1) {
  1360. $key = "player_a";
  1361. $dods_flag++;
  1362. } elsif ($dods_flag == 2) {
  1363. $key = "player_b";
  1364. }
  1365. }
  1366. $properties{$key} = $2;
  1367. } elsif (defined($3)) {
  1368. $properties{$key} = $3;
  1369. } else {
  1370. $properties{$key} = 1; # boolean property
  1371. }
  1372. if ($key eq "flagindex") {
  1373. $dods_flag++;
  1374. }
  1375. }
  1376.  
  1377. return %properties;
  1378. }
  1379.  
  1380.  
  1381. #
  1382. # boolean like (string subject, string compare)
  1383. #
  1384. # Returns true if 'subject' equals 'compare' with optional whitespace.
  1385. #
  1386.  
  1387. sub like
  1388. {
  1389. my ($subject, $compare) = @_;
  1390.  
  1391. if ($subject =~ /^\s*\Q$compare\E\s*$/) {
  1392. return 1;
  1393. } else {
  1394. return 0;
  1395. }
  1396. }
  1397.  
  1398.  
  1399. #
  1400. # boolean botidcheck (string uniqueid)
  1401. #
  1402. # Returns true if 'uniqueid' is that of a bot.
  1403. #
  1404.  
  1405. sub botidcheck
  1406. {
  1407. # needs cleaned up
  1408. # added /^00000000\:\d+\:0$/ check for "whichbot"
  1409. my ($uniqueid) = @_;
  1410. if ($uniqueid eq "BOT" || $uniqueid eq "0" || $uniqueid =~ /^00000000\:\d+\:0$/) {
  1411. return 1
  1412. }
  1413. return 0;
  1414. }
  1415.  
  1416. sub isTrackableTeam
  1417. {
  1418. my ($team) = @_;
  1419. #if ($team =~ /spectator/i || $team =~ /unassigned/i || $team eq "") {
  1420. if ($team =~ /spectator/i || $team eq "") {
  1421. return 0;
  1422. }
  1423. return 1;
  1424. }
  1425.  
  1426. sub reloadConfiguration
  1427. {
  1428. &flushAll;
  1429. &readDatabaseConfig;
  1430. }
  1431.  
  1432.  
  1433. sub flushAll
  1434. {
  1435. # we only need to flush events if we're about to shut down. they are unaffected by server/player deletion
  1436. my ($flushevents) = @_;
  1437. if ($flushevents)
  1438. {
  1439. while ( my ($table, $colsref) = each(%g_eventTables) )
  1440. {
  1441. flushEventTable($table);
  1442. }
  1443. }
  1444.  
  1445. while( my($se, $server) = each(%g_servers))
  1446. {
  1447. while ( my($pl, $player) = each(%{$server->{"srv_players"}}) )
  1448. {
  1449. if ($player)
  1450. {
  1451. $player->playerCleanup();
  1452. }
  1453. }
  1454. $server->flushDB();
  1455. }
  1456. }
  1457.  
  1458.  
  1459. ##
  1460. ## MAIN
  1461. ##
  1462.  
  1463. # Options
  1464.  
  1465. $opt_help = 0;
  1466. $opt_version = 0;
  1467.  
  1468. $db_host = "localhost";
  1469. $db_user = "";
  1470. $db_pass = "";
  1471. $db_name = "hlstats";
  1472. $db_lowpriority = 1;
  1473.  
  1474. $s_ip = "";
  1475. $s_port = "27500";
  1476.  
  1477. $g_mailto = "";
  1478. $g_mailpath = "/bin/mail";
  1479. $g_mode = "Normal";
  1480. $g_deletedays = 5;
  1481. $g_requiremap = 0;
  1482. $g_debug = 1;
  1483. $g_nodebug = 0;
  1484. $g_rcon = 1;
  1485. $g_rcon_ignoreself = 0;
  1486. $g_rcon_record = 1;
  1487. $g_stdin = 0;
  1488. $g_server_ip = "";
  1489. $g_server_port = 27015;
  1490. $g_timestamp = 0;
  1491. $g_cpanelhack = 0;
  1492. $g_event_queue_size = 10;
  1493. $g_dns_resolveip = 1;
  1494. $g_dns_timeout = 5;
  1495. $g_skill_maxchange = 100;
  1496. $g_skill_minchange = 2;
  1497. $g_skill_ratio_cap = 0;
  1498. $g_geoip_binary = 0;
  1499. $g_player_minkills = 50;
  1500. $g_onlyconfig_servers = 1;
  1501. $g_track_stats_trend = 0;
  1502. %g_lan_noplayerinfo = ();
  1503. %g_preconnect = ();
  1504. $g_global_banning = 0;
  1505. $g_log_chat = 0;
  1506. $g_log_chat_admins = 0;
  1507. $g_global_chat = 0;
  1508. $g_ranktype = "skill";
  1509. $g_gi = undef;
  1510.  
  1511. my %dysweaponcodes = (
  1512. "1" => "Light Katana",
  1513. "2" => "Medium Katana",
  1514. "3" => "Fatman Fist",
  1515. "4" => "Machine Pistol",
  1516. "5" => "Shotgun",
  1517. "6" => "Laser Rifle",
  1518. "7" => "BoltGun",
  1519. "8" => "SmartLock Pistols",
  1520. "9" => "Assault Rifle",
  1521. "10" => "Grenade Launcher",
  1522. "11" => "MK-808 Rifle",
  1523. "12" => "Tesla Rifle",
  1524. "13" => "Rocket Launcher",
  1525. "14" => "Minigun",
  1526. "15" => "Ion Cannon",
  1527. "16" => "Basilisk",
  1528. "17" => "Frag Grenade",
  1529. "18" => "EMP Grenade",
  1530. "19" => "Spider Grenade",
  1531. "22" => "Cortex Bomb"
  1532. );
  1533.  
  1534. # Usage message
  1535.  
  1536. $usage = <<EOT
  1537. Usage: hlstats.pl [OPTION]...
  1538. Collect statistics from one or more Half-Life2 servers for insertion into
  1539. a MySQL database.
  1540.  
  1541. -h, --help display this help and exit
  1542. -v, --version output version information and exit
  1543. -d, --debug enable debugging output (-dd for more)
  1544. -n, --nodebug disables above; reduces debug level
  1545. -m, --mode=MODE player tracking mode (Normal, LAN or NameTrack) [$g_mode]
  1546. --db-host=HOST database ip or ip:port [$db_host]
  1547. --db-name=DATABASE database name [$db_name]
  1548. --db-password=PASSWORD database password (WARNING: specifying the
  1549. password on the command line is insecure.
  1550. Use the configuration file instead.)
  1551. --db-username=USERNAME database username
  1552. --dns-resolveip resolve player IP addresses to hostnames
  1553. (requires working DNS)
  1554. -c,--configfile Specific configfile to use, settings in this file can now
  1555. be overidden with commandline settings.
  1556. --nodns-resolveip disables above
  1557. --dns-timeout=SEC timeout DNS queries after SEC seconds [$g_dns_timeout]
  1558. -i, --ip=IP set IP address to listen on for UDP log data
  1559. -p, --port=PORT set port to listen on for UDP log data [$s_port]
  1560. -r, --rcon enables rcon command exec support (the default)
  1561. --norcon disables rcon command exec support
  1562. -s, --stdin read log data from standard input, instead of
  1563. from UDP socket. Must specify --server-ip
  1564. and --server-port to indicate the generator
  1565. of the inputted log data (implies --norcon)
  1566. --nostdin disables above
  1567. --server-ip specify data source IP address for --stdin
  1568. --server-port specify data source port for --stdin [$g_server_port]
  1569. -t, --timestamp tells HLstatsX:CE to use the timestamp in the log
  1570. data, instead of the current time on the
  1571. database server, when recording events
  1572. --notimestamp disables above
  1573. --event-queue-size=SIZE manually set event queue size to control flushing
  1574. (recommend 100+ for STDIN)
  1575.  
  1576. Long options can be abbreviated, where such abbreviation is not ambiguous.
  1577. Default values for options are indicated in square brackets [...].
  1578.  
  1579. Most options can be specified in the configuration file:
  1580. $opt_configfile
  1581. Note: Options set on the command line take precedence over options set in the
  1582. configuration file. The configuration file name is set at the top of hlstats.pl.
  1583.  
  1584. HLstatsX: Community Edition http://www.hlxcommunity.com
  1585. EOT
  1586. ;
  1587.  
  1588. %g_config_servers = ();
  1589.  
  1590.  
  1591. sub readDatabaseConfig()
  1592. {
  1593. &printEvent("CONFIG", "Reading database config...", 1);
  1594. %g_config_servers = ();
  1595. %g_servers = ();
  1596. %g_games = ();
  1597.  
  1598. # elstatsneo: read the servers portion from the mysql database
  1599. my $srv_id = &doQuery("SELECT serverId,CONCAT(address,':',port) AS addr FROM hlstats_Servers");
  1600. while ( my($serverId,$addr) = $srv_id->fetchrow_array) {
  1601. $g_config_servers{$addr} = ();
  1602. my $serverConfig = &doQuery("SELECT parameter,value FROM hlstats_Servers_Config WHERE serverId=$serverId");
  1603. while ( my($p,$v) = $serverConfig->fetchrow_array) {
  1604. $g_config_servers{$addr}{$p} = $v;
  1605. }
  1606. }
  1607. $srv_id->finish;
  1608. # hlxce: read the global settings from the database!
  1609. my $gsettings = &doQuery("SELECT keyname,value FROM hlstats_Options WHERE opttype <= 1");
  1610. while ( my($p,$v) = $gsettings->fetchrow_array) {
  1611. if ($g_debug > 1) {
  1612. print "Config parameter '$p' = '$v'\n";
  1613. }
  1614. $tmp = "\$".$directives_mysql{$p}." = '$v'";
  1615. #print " -> setting ".$tmp."\n";
  1616. eval $tmp;
  1617. }
  1618. $gsettings->finish;
  1619. # setting defaults
  1620.  
  1621. &printEvent("DAEMON", "Proxy_Key DISABLED", 1) if ($proxy_key eq "");
  1622. while (my($addr, $server) = each(%g_config_servers)) {
  1623.  
  1624. if (!defined($g_config_servers{$addr}{"MinPlayers"})) {
  1625. $g_config_servers{$addr}{"MinPlayers"} = 6;
  1626. }
  1627. if (!defined($g_config_servers{$addr}{"DisplayResultsInBrowser"})) {
  1628. $g_config_servers{$addr}{"DisplayResultsInBrowser"} = 0;
  1629. }
  1630. if (!defined($g_config_servers{$addr}{"BroadCastEvents"})) {
  1631. $g_config_servers{$addr}{"BroadCastEvents"} = 0;
  1632. }
  1633. if (!defined($g_config_servers{$addr}{"BroadCastPlayerActions"})) {
  1634. $g_config_servers{$addr}{"BroadCastPlayerActions"} = 0;
  1635. }
  1636. if (!defined($g_config_servers{$addr}{"BroadCastEventsCommand"})) {
  1637. $g_config_servers{$addr}{"BroadCastEventsCommand"} = "say";
  1638. }
  1639. if (!defined($g_config_servers{$addr}{"BroadCastEventsCommandAnnounce"})) {
  1640. $g_config_servers{$addr}{"BroadCastEventsCommandAnnounce"} = "say";
  1641. }
  1642. if (!defined($g_config_servers{$addr}{"PlayerEvents"})) {
  1643. $g_config_servers{$addr}{"PlayerEvents"} = 1;
  1644. }
  1645. if (!defined($g_config_servers{$addr}{"PlayerEventsCommand"})) {
  1646. $g_config_servers{$addr}{"PlayerEventsCommand"} = "say";
  1647. }
  1648. if (!defined($g_config_servers{$addr}{"PlayerEventsCommandOSD"})) {
  1649. $g_config_servers{$addr}{"PlayerEventsCommandOSD"} = "";
  1650. }
  1651. if (!defined($g_config_servers{$addr}{"PlayerEventsCommandHint"})) {
  1652. $g_config_servers{$addr}{"PlayerEventsCommandHint"} = "";
  1653. }
  1654. if (!defined($g_config_servers{$addr}{"PlayerEventsAdminCommand"})) {
  1655. $g_config_servers{$addr}{"PlayerEventsAdminCommand"} = "";
  1656. }
  1657. if (!defined($g_config_servers{$addr}{"ShowStats"})) {
  1658. $g_config_servers{$addr}{"ShowStats"} = 1;
  1659. }
  1660. if (!defined($g_config_servers{$addr}{"AutoTeamBalance"})) {
  1661. $g_config_servers{$addr}{"AutoTeamBalance"} = 0;
  1662. }
  1663. if (!defined($g_config_servers{$addr}{"AutoBanRetry"})) {
  1664. $g_config_servers{$addr}{"AutoBanRetry"} = 0;
  1665. }
  1666. if (!defined($g_config_servers{$addr}{"TrackServerLoad"})) {
  1667. $g_config_servers{$addr}{"TrackServerLoad"} = 0;
  1668. }
  1669. if (!defined($g_config_servers{$addr}{"MinimumPlayersRank"})) {
  1670. $g_config_servers{$addr}{"MinimumPlayersRank"} = 0;
  1671. }
  1672. if (!defined($g_config_servers{$addr}{"Admins"})) {
  1673. $g_config_servers{$addr}{"Admins"} = "";
  1674. }
  1675. if (!defined($g_config_servers{$addr}{"SwitchAdmins"})) {
  1676. $g_config_servers{$addr}{"SwitchAdmins"} = 0;
  1677. }
  1678. if (!defined($g_config_servers{$addr}{"IgnoreBots"})) {
  1679. $g_config_servers{$addr}{"IgnoreBots"} = 1;
  1680. }
  1681. if (!defined($g_config_servers{$addr}{"SkillMode"})) {
  1682. $g_config_servers{$addr}{"SkillMode"} = 0;
  1683. }
  1684. if (!defined($g_config_servers{$addr}{"GameType"})) {
  1685. $g_config_servers{$addr}{"GameType"} = 0;
  1686. }
  1687. if (!defined($g_config_servers{$addr}{"BonusRoundTime"})) {
  1688. $g_config_servers{$addr}{"BonusRoundTime"} = 0;
  1689. }
  1690. if (!defined($g_config_servers{$addr}{"BonusRoundIgnore"})) {
  1691. $g_config_servers{$addr}{"BonusRoundIgnore"} = 0;
  1692. }
  1693. if (!defined($g_config_servers{$addr}{"Mod"})) {
  1694. $g_config_servers{$addr}{"Mod"} = "";
  1695. }
  1696. if (!defined($g_config_servers{$addr}{"EnablePublicCommands"})) {
  1697. $g_config_servers{$addr}{"EnablePublicCommands"} = 1;
  1698. }
  1699. if (!defined($g_config_servers{$addr}{"ConnectAnnounce"})) {
  1700. $g_config_servers{$addr}{"ConnectAnnounce"} = 1;
  1701. }
  1702. if (!defined($g_config_servers{$addr}{"UpdateHostname"})) {
  1703. $g_config_servers{$addr}{"UpdateHostname"} = 0;
  1704. }
  1705. if (!defined($g_config_servers{$addr}{"DefaultDisplayEvents"})) {
  1706. $g_config_servers{$addr}{"DefaultDisplayEvents"} = 1;
  1707. }
  1708. }
  1709.  
  1710. &printEvent("CONFIG", "I have found the following server configs in database:", 1);
  1711. while (my($addr, $server) = each(%g_config_servers)) {
  1712. &printEvent("S_CONFIG", $addr, 1);
  1713. }
  1714.  
  1715. my $geotell = ((!defined($g_gi)) ? -1 : tell $g_gi{fh});
  1716.  
  1717. if ($g_geoip_binary > 0 && $geotell == -1) {
  1718. my $geoipfile = "$opt_libdir/GeoLiteCity/GeoLiteCity.dat";
  1719. if (-r $geoipfile) {
  1720. eval "use Geo::IP::PurePerl"; my $hasGeoIP = $@ ? 0 : 1;
  1721. if ($hasGeoIP) {
  1722. $g_gi = Geo::IP::PurePerl->open($geoipfile, "GEOIP_STANDARD");
  1723. } else {
  1724. &printEvent("ERROR", "GeoIP method set to binary file lookup but Geo::IP::PurePerl module NOT FOUND", 1);
  1725. $g_gi = undef;
  1726. }
  1727. } else {
  1728. &printEvent("ERROR", "GeoIP method set to binary file lookup but $geoipfile NOT FOUND", 1);
  1729. $g_gi = undef;
  1730. }
  1731. } elsif ($g_geoip_binary == 0 && $geotell > -1) {
  1732. close($g_gi{fh});
  1733. $g_gi = undef;
  1734. }
  1735. }
  1736.  
  1737. # Read Config File
  1738.  
  1739. if ($opt_configfile && -r $opt_configfile) {
  1740. $conf = ConfigReaderSimple->new($opt_configfile);
  1741. $conf->parse();
  1742. %directives = (
  1743. "DBHost", "db_host",
  1744. "DBUsername", "db_user",
  1745. "DBPassword", "db_pass",
  1746. "DBName", "db_name",
  1747. "DBLowPriority", "db_lowpriority",
  1748. "BindIP", "s_ip",
  1749. "Port", "s_port",
  1750. "DebugLevel", "g_debug",
  1751. "CpanelHack", "g_cpanelhack",
  1752. "EventQueueSize", "g_event_queue_size"
  1753. );
  1754.  
  1755. %directives_mysql = (
  1756. "version", "g_version",
  1757. "MailTo", "g_mailto",
  1758. "MailPath", "g_mailpath",
  1759. "Mode", "g_mode",
  1760. "DeleteDays", "g_deletedays",
  1761. "UseTimestamp", "g_timestamp",
  1762. "DNSResolveIP", "g_dns_resolveip",
  1763. "DNSTimeout", "g_dns_timeout",
  1764. "RconIgnoreSelf", "g_rcon_ignoreself",
  1765. "Rcon", "g_rcon",
  1766. "RconRecord", "g_rcon_record",
  1767. "MinPlayers", "g_minplayers",
  1768. "SkillMaxChange", "g_skill_maxchange",
  1769. "SkillMinChange", "g_skill_minchange",
  1770. "PlayerMinKills", "g_player_minkills",
  1771. "AllowOnlyConfigServers", "g_onlyconfig_servers",
  1772. "TrackStatsTrend", "g_track_stats_trend",
  1773. "GlobalBanning", "g_global_banning",
  1774. "LogChat", "g_log_chat",
  1775. "LogChatAdmins", "g_log_chat_admins",
  1776. "GlobalChat", "g_global_chat",
  1777. "SkillRatioCap", "g_skill_ratio_cap",
  1778. "rankingtype", "g_ranktype",
  1779. "UseGeoIPBinary", "g_geoip_binary",
  1780. "Proxy_Key", "proxy_key"
  1781. );
  1782.  
  1783. # "Servers", "g_config_servers"
  1784. &doConf($conf, %directives);
  1785.  
  1786. } else {
  1787. print "-- Warning: unable to open configuration file '$opt_configfile'\n";
  1788. }
  1789.  
  1790. # Read Command Line Arguments
  1791.  
  1792. %copts = ();
  1793.  
  1794. GetOptions(
  1795. "help|h" => \$copts{opt_help},
  1796. "version|v" => \$copts{opt_version},
  1797. "debug|d+" => \$copts{g_debug},
  1798. "nodebug|n+" => \$copts{g_nodebug},
  1799. "mode|m=s" => \$copts{g_mode},
  1800. "configfile|c=s" => \$copts{configfile},
  1801. "db-host=s" => \$copts{db_host},
  1802. "db-name=s" => \$copts{db_name},
  1803. "db-password=s" => \$copts{db_pass},
  1804. "db-username=s" => \$copts{db_user},
  1805. "dns-resolveip!" => \$copts{g_dns_resolveip},
  1806. "dns-timeout=i" => \$copts{g_dns_timeout},
  1807. "ip|i=s" => \$copts{s_ip},
  1808. "port|p=i" => \$copts{s_port},
  1809. "rcon!" => \$copts{g_rcon},
  1810. "r" => \$copts{g_rcon},
  1811. "stdin!" => \$copts{g_stdin},
  1812. "s" => \$copts{g_stdin},
  1813. "server-ip=s" => \$copts{g_server_ip},
  1814. "server-port=i" => \$copts{g_server_port},
  1815. "timestamp!" => \$copts{g_timestamp},
  1816. "t" => \$copts{g_timestamp},
  1817. "event-queue-size" => \$copts{g_event_queue_size}
  1818. ) or die($usage);
  1819.  
  1820.  
  1821. if ($configfile && -r $configfile) {
  1822. $conf = '';
  1823. $conf = ConfigReaderSimple->new($configfile);
  1824. $conf->parse();
  1825. &doConf($conf, %directives);
  1826. }
  1827.  
  1828. # these are set above, we then reload them to override values in the actual config
  1829. setOptionsConf(%copts);
  1830.  
  1831. if ($g_cpanelhack) {
  1832. my $home_dir = $ENV{ HOME };
  1833. my $base_module_dir = (-d "$home_dir/perl" ? "$home_dir/perl" : ( getpwuid($>) )[7] . '/perl/');
  1834. unshift @INC, map { $base_module_dir . $_ } @INC;
  1835. }
  1836.  
  1837. eval {
  1838. require Geo::IP::PurePerl;
  1839. };
  1840. import Geo::IP::PurePerl;
  1841.  
  1842. if ($opt_help) {
  1843. print $usage;
  1844. exit(0);
  1845. }
  1846.  
  1847. if ($opt_version) {
  1848. &doConnect;
  1849. my $result = &doQuery("
  1850. SELECT
  1851. value
  1852. FROM
  1853. hlstats_Options
  1854. WHERE
  1855. keyname='version'
  1856. ");
  1857.  
  1858. if ($result->rows > 0) {
  1859. $g_version = $result->fetchrow_array;
  1860. }
  1861. $result->finish;
  1862. print "\nhlstats.pl (HLstatsX Community Edition) Version $g_version\n"
  1863. . "Real-time player and clan rankings and statistics for Half-Life 2\n"
  1864. . "Modified (C) 2008-20XX Nicholas Hastings (nshastings@gmail.com)\n"
  1865. . "Copyleft (L) 2007-2008 Malte Bayer\n"
  1866. . "Modified (C) 2005-2007 Tobias Oetzel (Tobi@hlstatsx.com)\n"
  1867. . "Original (C) 2001 by Simon Garner \n\n";
  1868.  
  1869. print "Using ConfigReaderSimple module version $ConfigReaderSimple::VERSION\n";
  1870. if ($g_rcon) {
  1871. print "Using rcon module\n";
  1872. }
  1873.  
  1874. print "\nThis is free software; see the source for copying conditions. There is NO\n"
  1875. . "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n";
  1876. exit(0);
  1877. }
  1878.  
  1879. # Connect to the database
  1880.  
  1881. &doConnect;
  1882. &readDatabaseConfig;
  1883. &buildEventInsertData;
  1884.  
  1885. if ($g_mode ne "Normal" && $g_mode ne "LAN" && $g_mode ne "NameTrack") {
  1886. $g_mode = "Normal";
  1887. }
  1888.  
  1889. $g_debug -= $g_nodebug;
  1890. $g_debug = 0 if ($g_debug < 0);
  1891.  
  1892.  
  1893. # Init Timestamp
  1894. my ($sec,$min,$hour,$mday,$mon,$year) = localtime(time());
  1895. $ev_timestamp = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec);
  1896. $ev_unixtime = time();
  1897. $ev_daemontime = $ev_unixtime;
  1898.  
  1899. # Startup
  1900.  
  1901. &printEvent("HLSTATSX", "HLstatsX:CE $g_version starting...", 1);
  1902.  
  1903. # Create the UDP & TCP socket
  1904.  
  1905. if ($g_stdin) {
  1906. $g_rcon = 0;
  1907. &printEvent("UDP", "UDP listen socket disabled, reading log data from STDIN.", 1);
  1908. if (!$g_server_ip || !$g_server_port) {
  1909. &printEvent("UDP", "ERROR: You must specify source of STDIN data using --server-ip and --server-port", 1);
  1910. &printEvent("UDP", "Example: ./hlstats.pl --stdin --server-ip 12.34.56.78 --server-port 27015", 1);
  1911. exit(255);
  1912. } else {
  1913. &printEvent("UDP", "All data from STDIN will be allocated to server '$g_server_ip:$g_server_port'.", 1);
  1914. $s_peerhost = $g_server_ip;
  1915. $s_peerport = $g_server_port;
  1916. $s_addr = "$s_peerhost:$s_peerport";
  1917. }
  1918. } else {
  1919. if ($s_ip) { $ip = $s_ip . ":"; } else { $ip = "port "; }
  1920. $s_socket = IO::Socket::INET->new(
  1921. Proto=>"udp",
  1922. LocalAddr=>"$s_ip",
  1923. LocalPort=>"$s_port"
  1924. ) or die ("\nCan't setup UDP socket on $ip$s_port: $!\n");
  1925.  
  1926. &printEvent("UDP", "Opening UDP listen socket on $ip$s_port ... ok", 1);
  1927. }
  1928.  
  1929. if ($g_track_stats_trend > 0) {
  1930. &printEvent("HLSTATSX", "Tracking Trend of the stats are enabled", 1);
  1931. }
  1932.  
  1933. if ($g_global_banning > 0) {
  1934. &printEvent("HLSTATSX", "Global Banning on all servers is enabled", 1);
  1935. }
  1936.  
  1937. &printEvent("HLSTATSX", "Maximum Skill Change on all servers are ".$g_skill_maxchange." points", 1);
  1938. &printEvent("HLSTATSX", "Minimum Skill Change on all servers are ".$g_skill_minchange." points", 1);
  1939. &printEvent("HLSTATSX", "Minimum Players Kills on all servers are ".$g_player_minkills." kills", 1);
  1940.  
  1941. if ($g_log_chat > 0) {
  1942. &printEvent("HLSTATSX", "Players chat logging is enabled", 1);
  1943. if ($g_log_chat_admins > 0) {
  1944. &printEvent("HLSTATSX", "Admins chat logging is enabled", 1);
  1945. }
  1946. }
  1947.  
  1948. if ($g_global_chat == 1) {
  1949. &printEvent("HLSTATSX", "Broadcasting public chat to all players is enabled", 1);
  1950. } elsif ($g_gloabl_chat == 2) {
  1951. &printEvent("HLSTATSX", "Broadcasting public chat to admins is enabled", 1);
  1952. } else {
  1953. &printEvent("HLSTATSX", "Broadcasting public chat is disabled", 1);
  1954. }
  1955.  
  1956. &printEvent("HLSTATSX", "Event queue size is set to ".$g_event_queue_size, 1);
  1957.  
  1958.  
  1959. %g_servers = ();
  1960.  
  1961. &printEvent("HLSTATSX", "HLstatsX:CE is now running ($g_mode mode, debug level $g_debug)", 1);
  1962.  
  1963. $start_time = time();
  1964. if ($g_stdin) {
  1965. $g_timestamp = 1;
  1966. $start_parse_time = time();
  1967. $import_logs_count = 0;
  1968. &printEvent("IMPORT", "Start importing logs. Every dot signs 500 parsed lines", 1, 1);
  1969. }
  1970.  
  1971. # Main data loop
  1972. $c = 0;
  1973.  
  1974. sub getLine
  1975. {
  1976. if ($g_stdin) {
  1977. return <STDIN>;
  1978. } else {
  1979. return 1;
  1980. }
  1981. }
  1982.  
  1983.  
  1984. &execNonQuery("TRUNCATE TABLE hlstats_Livestats");
  1985. $timeout = 0;
  1986. $s_output = "";
  1987. my ($proxy_s_peerhost, $proxy_s_peerport);
  1988. while ($loop = &getLine()) {
  1989.  
  1990. my ($sec,$min,$hour,$mday,$mon,$year) = localtime(time());
  1991. $ev_timestamp = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec);
  1992. $ev_unixtime = time();
  1993. $ev_daemontime = $ev_unixtime; #time()
  1994.  
  1995. if ($g_stdin) {
  1996. $s_output = $loop;
  1997. if (($import_logs_count > 0) && ($import_logs_count % 500 == 0)) {
  1998. $parse_time = $ev_unixtime - $start_parse_time;
  1999. if ($parse_time == 0) {
  2000. $parse_time++;
  2001. }
  2002. print ". [".($parse_time)." sec (".sprintf("%.3f", (500 / $parse_time)).")]\n";
  2003. $start_parse_time = $ev_unixtime;
  2004. }
  2005. } else {
  2006. if(IO::Select->new($s_socket)->can_read(2)) { # 2 second timeout
  2007. $s_socket->recv($s_output, 1024);
  2008. $s_output = decode( 'utf8', $s_output );
  2009. $timeout = 0;
  2010. } else {
  2011. $timeout++;
  2012. if ($timeout % 60 == 0) {
  2013. &printEvent("HLSTATSX", "No data since 120 seconds");
  2014. }
  2015. }
  2016.  
  2017. if (($s_output =~ /^.*PROXY Key=(.+) (.*)PROXY.+/) && $proxy_key ne "") {
  2018. $rproxy_key = $1;
  2019. $s_addr = $2;
  2020.  
  2021. if ($s_addr ne "") {
  2022. ($s_peerhost, $s_peerport) = split(/:/, $s_addr);
  2023. }
  2024.  
  2025. $proxy_s_peerhost = $s_socket->peerhost;
  2026. $proxy_s_peerport = $s_socket->peerport;
  2027. &printEvent("PROXY", "Detected proxy call from $proxy_s_peerhost:$proxy_s_peerport") if ($d_debug > 2);
  2028.  
  2029.  
  2030. if ($proxy_key eq $rproxy_key) {
  2031. $s_output =~ s/PROXY.*PROXY //;
  2032. if ($s_output =~ /^C;HEARTBEAT;/) {
  2033. &printEvent("PROXY, Heartbeat request from $proxy_s_peerhost:$proxy_s_peerport");
  2034. } elsif ($s_output =~ /^C;RELOAD;/) {
  2035. &printEvent("PROXY, Reload request from $proxy_s_peerhost:$proxy_s_peerport");
  2036. } elsif ($s_output =~ /^C;KILL;/) {
  2037. &printEvent("PROXY, Kill request from $proxy_s_peerhost:$proxy_s_peerport");
  2038. } else {
  2039. &printEvent("PROXY", $s_output);
  2040. }
  2041. } else {
  2042. &printEvent("PROXY", "proxy_key mismatch, dropping package");
  2043. &printEvent("PROXY", $s_output) if ($g_debug > 2);
  2044. $s_output = "";
  2045. next;
  2046. }
  2047. } else {
  2048. # Reset the proxy stuff and use it as "normal"
  2049. $rproxy_key = "";
  2050. $proxy_s_peerhost = "";
  2051. $proxy_s_peerport = "";
  2052.  
  2053. $s_peerhost = $s_socket->peerhost;
  2054. $s_peerport = $s_socket->peerport;
  2055.  
  2056. $s_addr = "$s_peerhost:$s_peerport";
  2057. }
  2058. }
  2059.  
  2060. if ($timeout == 0) {
  2061. my ($address, $port);
  2062. my @data = split ";", $s_output;
  2063. $cmd = $data[0];
  2064. if ($cmd eq "C" && ($s_peerhost eq "127.0.0.1" || (($proxy_key eq $rproxy_key) && $proxy_key ne ""))) {
  2065. &printEvent("CONTROL", "Command received: ".$data[1], 1);
  2066. if ($proxy_s_peerhost ne "" && $proxy_s_peerport ne "") {
  2067. $address = $proxy_s_peerhost;
  2068. $port = $proxy_s_peerport;
  2069. } else {
  2070. $address = $s_peerhost;
  2071. $port = $s_peerport;
  2072. }
  2073.  
  2074. $s_addr = "$address:$port";
  2075.  
  2076. my $dest = sockaddr_in($port, inet_aton($address));
  2077. if ($data[1] eq "HEARTBEAT") {
  2078. my $msg = "Heartbeat OK";
  2079. $bytes = send($::s_socket, $msg, 0, $dest);
  2080. &printEvent("CONTROL", "Send heartbeat status to frontend at '$address:$port'", 1);
  2081. } else {
  2082. my $msg = "OK, EXECUTING COMMAND: ".$data[1];
  2083. $bytes = send($::s_socket, $msg, 0, $dest);
  2084. &printEvent("CONTROL", "Sent $bytes bytes to frontend at '$address:$port'", 1);
  2085. }
  2086.  
  2087. if ($data[1] eq "RELOAD") {
  2088. &printEvent("CONTROL", "Re-Reading Configuration by request from Frontend...", 1);
  2089. &reloadConfiguration;
  2090. }
  2091.  
  2092. if ($data[1] eq "KILL") {
  2093. &printEvent("CONTROL", "SHUTTING DOWN SCRIPT", 1);
  2094. &flushAll;
  2095. die "Exit script by request";
  2096. }
  2097.  
  2098. next;
  2099. }
  2100. $s_output =~ s/[\r\n\0]//g; # remove naughty characters
  2101. $s_output =~ s/\[No.C-D\]//g; # remove [No C-D] tag
  2102. $s_output =~ s/\[OLD.C-D\]//g; # remove [OLD C-D] tag
  2103. $s_output =~ s/\[NOCL\]//g; # remove [NOCL] tag
  2104.  
  2105. # Get the server info, if we know the server, otherwise ignore the data
  2106. if (!defined($g_servers{$s_addr})) {
  2107. if (($g_onlyconfig_servers == 1) && (!defined($g_config_servers{$s_addr}))) {
  2108. # HELLRAISER disabled this for testing
  2109. &printEvent(997, "NOT ALLOWED SERVER: " . $s_output);
  2110. next;
  2111. } elsif (!defined($g_config_servers{$s_addr})) { # create std cfg.
  2112. my %std_cfg;
  2113. $std_cfg{"MinPlayers"} = 6;
  2114. $std_cfg{"HLStatsURL"} = "";
  2115. $std_cfg{"DisplayResultsInBrowser"} = 0;
  2116. $std_cfg{"BroadCastEvents"} = 0;
  2117. $std_cfg{"BroadCastPlayerActions"} = 0;
  2118. $std_cfg{"BroadCastEventsCommand"} = "say";
  2119. $std_cfg{"BroadCastEventsCommandAnnounce"} = "say",
  2120. $std_cfg{"PlayerEvents"} = 1;
  2121. $std_cfg{"PlayerEventsCommand"} = "say";
  2122. $std_cfg{"PlayerEventsCommandOSD"} = "",
  2123. $std_cfg{"PlayerEventsCommandHint"} = "",
  2124. $std_cfg{"PlayerEventsAdminCommand"} = "";
  2125. $std_cfg{"ShowStats"} = 1;
  2126. $std_cfg{"TKPenalty"} = 50;
  2127. $std_cfg{"SuicidePenalty"} = 5;
  2128. $std_cfg{"AutoTeamBalance"} = 0;
  2129. $std_cfg{"AutobanRetry"} = 0;
  2130. $std_cfg{"TrackServerLoad"} = 0;
  2131. $std_cfg{"MinimumPlayersRank"} = 0;
  2132. $std_cfg{"EnablePublicCommands"} = 1;
  2133. $std_cfg{"Admins"} = "";
  2134. $std_cfg{"SwitchAdmins"} = 0;
  2135. $std_cfg{"IgnoreBots"} = 1;
  2136. $std_cfg{"SkillMode"} = 0;
  2137. $std_cfg{"GameType"} = 0;
  2138. $std_cfg{"Mod"} = "";
  2139. $std_cfg{"BonusRoundIgnore"} = 0;
  2140. $std_cfg{"BonusRoundTime"} = 20;
  2141. $std_cfg{"UpdateHostname"} = 0;
  2142. $std_cfg{"ConnectAnnounce"} = 1;
  2143. $std_cfg{"DefaultDisplayEvents"} = 1;
  2144. %{$g_config_servers{$s_addr}} = %std_cfg;
  2145. &printEvent("CFG", "Created default config for unknown server [$s_addr]");
  2146. &printEvent("DETECT", "New server with game: " . &getServerMod($s_peerhost, $s_peerport));
  2147. }
  2148.  
  2149. if ($g_config_servers{$s_addr}) {
  2150. my $tempsrv = &getServer($s_peerhost, $s_peerport);
  2151. next if ($tempsrv == 0);
  2152. $g_servers{$s_addr} = $tempsrv;
  2153. my %s_cfg = %{$g_config_servers{$s_addr}};
  2154. $g_servers{$s_addr}->set("minplayers", $s_cfg{"MinPlayers"});
  2155. $g_servers{$s_addr}->set("hlstats_url", $s_cfg{"HLStatsURL"});
  2156. if ($s_cfg{"DisplayResultsInBrowser"} > 0) {
  2157. $g_servers{$s_addr}->set("use_browser", 1);
  2158. &printEvent("SERVER", "Query results will displayed in valve browser", 1);
  2159. } else {
  2160. $g_servers{$s_addr}->set("use_browser", 0);
  2161. &printEvent("SERVER", "Query results will not displayed in valve browser", 1);
  2162. }
  2163. if ($s_cfg{"ShowStats"} == 1) {
  2164. $g_servers{$s_addr}->set("show_stats", 1);
  2165. &printEvent("SERVER", "Showing stats is enabled", 1);
  2166. } else {
  2167. $g_servers{$s_addr}->set("show_stats", 0);
  2168. &printEvent("SERVER", "Showing stats is disabled", 1);
  2169. }
  2170. if ($s_cfg{"BroadCastEvents"} == 1) {
  2171. $g_servers{$s_addr}->set("broadcasting_events", 1);
  2172. $g_servers{$s_addr}->set("broadcasting_player_actions", $s_cfg{"BroadCastPlayerActions"});
  2173. $g_servers{$s_addr}->set("broadcasting_command", $s_cfg{"BroadCastEventsCommand"});
  2174. if ($s_cfg{"BroadCastEventsCommandAnnounce"} eq "ma_hlx_csay") {
  2175. $s_cfg{"BroadCastEventsCommandAnnounce"} = $s_cfg{"BroadCastEventsCommandAnnounce"}." #all";
  2176. }
  2177. $g_servers{$s_addr}->set("broadcasting_command_announce", $s_cfg{"BroadCastEventsCommandAnnounce"});
  2178.  
  2179. &printEvent("SERVER", "Broadcasting Live-Events with \"".$s_cfg{"BroadCastEventsCommand"}."\" is enabled", 1);
  2180. if ($s_cfg{"BroadCastEventsCommandAnnounce"} ne "") {
  2181. &printEvent("SERVER", "Broadcasting Announcements with \"".$s_cfg{"BroadCastEventsCommandAnnounce"}."\" is enabled", 1);
  2182. }
  2183. } else {
  2184. $g_servers{$s_addr}->set("broadcasting_events", 0);
  2185. &printEvent("SERVER", "Broadcasting Live-Events is disabled", 1);
  2186. }
  2187. if ($s_cfg{"PlayerEvents"} == 1) {
  2188. $g_servers{$s_addr}->set("player_events", 1);
  2189. $g_servers{$s_addr}->set("player_command", $s_cfg{"PlayerEventsCommand"});
  2190. $g_servers{$s_addr}->set("player_command_osd", $s_cfg{"PlayerEventsCommandOSD"});
  2191. $g_servers{$s_addr}->set("player_command_hint", $s_cfg{"PlayerEventsCommandHint"});
  2192. $g_servers{$s_addr}->set("player_admin_command", $s_cfg{"PlayerEventsAdminCommand"});
  2193. &printEvent("SERVER", "Player Event-Handler with \"".$s_cfg{"PlayerEventsCommand"}."\" is enabled", 1);
  2194. if ($s_cfg{"PlayerEventsCommandOSD"} ne "") {
  2195. &printEvent("SERVER", "Displaying amx style menu with \"".$s_cfg{"PlayerEventsCommandOSD"}."\" is enabled", 1);
  2196. }
  2197. } else {
  2198. $g_servers{$s_addr}->set("player_events", 0);
  2199. &printEvent("SERVER", "Player Event-Handler is disabled", 1);
  2200. }
  2201. if ($s_cfg{"DefaultDisplayEvents"} > 0) {
  2202. $g_servers{$s_addr}->set("default_display_events", "1");
  2203. &printEvent("SERVER", "New Players defaulting to show event messages", 1);
  2204. } else {
  2205. $g_servers{$s_addr}->set("default_display_events", "0");
  2206. &printEvent("SERVER", "New Players defaulting to NOT show event messages", 1);
  2207. }
  2208. if ($s_cfg{"TrackServerLoad"} > 0) {
  2209. $g_servers{$s_addr}->set("track_server_load", "1");
  2210. &printEvent("SERVER", "Tracking server load is enabled", 1);
  2211. } else {
  2212. $g_servers{$s_addr}->set("track_server_load", "0");
  2213. &printEvent("SERVER", "Tracking server load is disabled", 1);
  2214. }
  2215.  
  2216. if ($s_cfg{"TKPenalty"} > 0) {
  2217. $g_servers{$s_addr}->set("tk_penalty", $s_cfg{"TKPenalty"});
  2218. &printEvent("SERVER", "Penalty team kills with ".$s_cfg{"TKPenalty"}." points", 1);
  2219. }
  2220. if ($s_cfg{"SuicidePenalty"} > 0) {
  2221. $g_servers{$s_addr}->set("suicide_penalty", $s_cfg{"SuicidePenalty"});
  2222. &printEvent("SERVER", "Penalty suicides with ".$s_cfg{"SuicidePenalty"}." points", 1);
  2223. }
  2224. if ($s_cfg{"BonusRoundTime"} > 0) {
  2225. $g_servers{$s_addr}->set("bonusroundtime", $s_cfg{"BonusRoundTime"});
  2226. &printEvent("SERVER", "Bonus Round time set to: ".$s_cfg{"BonusRoundTime"}, 1);
  2227. }
  2228. if ($s_cfg{"BonusRoundIgnore"} > 0) {
  2229. $g_servers{$s_addr}->set("bonusroundignore", $s_cfg{"BonusRoundIgnore"});
  2230. &printEvent("SERVER", "Bonus Round is being ignored. Length: (".$s_cfg{"BonusRoundTime"}.")", 1);
  2231. }
  2232. if ($s_cfg{"AutoTeamBalance"} > 0) {
  2233. $g_servers{$s_addr}->set("ba_enabled", "1");
  2234. &printEvent("TEAMS", "Auto-Team-Balancing is enabled", 1);
  2235. } else {
  2236. $g_servers{$s_addr}->set("ba_enabled", "0");
  2237. &printEvent("TEAMS", "Auto-Team-Balancing is disabled", 1);
  2238. }
  2239. if ($s_cfg{"AutoBanRetry"} > 0) {
  2240. $g_servers{$s_addr}->set("auto_ban", "1");
  2241. &printEvent("TEAMS", "Auto-Retry-Banning is enabled", 1);
  2242. } else {
  2243. $g_servers{$s_addr}->set("auto_ban", "0");
  2244. &printEvent("TEAMS", "Auto-Retry-Banning is disabled", 1);
  2245. }
  2246.  
  2247. if ($s_cfg{"MinimumPlayersRank"} > 0) {
  2248. $g_servers{$s_addr}->set("min_players_rank", $s_cfg{"MinimumPlayersRank"});
  2249. &printEvent("SERVER", "Requires minimum players rank is enabled [MinPos:".$s_cfg{"MinimumPlayersRank"}."]", 1);
  2250. } else {
  2251. $g_servers{$s_addr}->set("min_players_rank", "0");
  2252. &printEvent("SERVER", "Requires minimum players rank is disabled", 1);
  2253. }
  2254.  
  2255. if ($s_cfg{"EnablePublicCommands"} > 0) {
  2256. $g_servers{$s_addr}->set("public_commands", $s_cfg{"EnablePublicCommands"});
  2257. &printEvent("SERVER", "Public chat commands are enabled", 1);
  2258. } else {
  2259. $g_servers{$s_addr}->set("public_commands", "0");
  2260. &printEvent("SERVER", "Public chat commands are disabled", 1);
  2261. }
  2262.  
  2263. if ($s_cfg{"Admins"} ne "") {
  2264. @{$g_servers{$s_addr}->{admins}} = split(/,/, $s_cfg{"Admins"});
  2265. foreach(@{$g_servers{$s_addr}->{admins}})
  2266. {
  2267. $_ =~ s/^STEAM_[0-9]+?\://i;
  2268. }
  2269. &printEvent("SERVER", "Admins: ".$s_cfg{"Admins"}, 1);
  2270. }
  2271.  
  2272. if ($s_cfg{"SwitchAdmins"} > 0) {
  2273. $g_servers{$s_addr}->set("switch_admins", "1");
  2274. &printEvent("TEAMS", "Switching Admins on Auto-Team-Balance is enabled", 1);
  2275. } else {
  2276. $g_servers{$s_addr}->set("switch_admins", "0");
  2277. &printEvent("TEAMS", "Switching Admins on Auto-Team-Balance is disabled", 1);
  2278. }
  2279.  
  2280. if ($s_cfg{"IgnoreBots"} > 0) {
  2281. $g_servers{$s_addr}->set("ignore_bots", "1");
  2282. &printEvent("SERVER", "Ignoring bots is enabled", 1);
  2283. } else {
  2284. $g_servers{$s_addr}->set("ignore_bots", "0");
  2285. &printEvent("SERVER", "Ignoring bots is disabled", 1);
  2286. }
  2287. $g_servers{$s_addr}->set("skill_mode", $s_cfg{"SkillMode"});
  2288. &printEvent("SERVER", "Using skill mode ".$s_cfg{"SkillMode"}, 1);
  2289.  
  2290. if ($s_cfg{"GameType"} == 1) {
  2291. $g_servers{$s_addr}->set("game_type", $s_cfg{"GameType"});
  2292. &printEvent("SERVER", "Game type: Counter-Strike: Source - Deathmatch", 1);
  2293. } else {
  2294. $g_servers{$s_addr}->set("game_type", "0");
  2295. &printEvent("SERVER", "Game type: Normal", 1);
  2296. }
  2297.  
  2298. $g_servers{$s_addr}->set("mod", $s_cfg{"Mod"});
  2299.  
  2300. if ($s_cfg{"Mod"} ne "") {
  2301. &printEvent("SERVER", "Using plugin ".$s_cfg{"Mod"}." for internal functions!", 1);
  2302. }
  2303. if ($s_cfg{"ConnectAnnounce"} == 1) {
  2304. $g_servers{$s_addr}->set("connect_announce", $s_cfg{"ConnectAnnounce"});
  2305. &printEvent("SERVER", "Connect Announce is enabled", 1);
  2306. } else {
  2307. $g_servers{$s_addr}->set("connect_announce", "0");
  2308. &printEvent("SERVER", "Connect Announce is disabled", 1);
  2309. }
  2310. if ($s_cfg{"UpdateHostname"} == 1) {
  2311. $g_servers{$s_addr}->set("update_hostname", $s_cfg{"UpdateHostname"});
  2312. &printEvent("SERVER", "Auto-updating hostname is enabled", 1);
  2313. } else {
  2314. $g_servers{$s_addr}->set("update_hostname", "0");
  2315. &printEvent("SERVER", "Auto-updating hostname is disabled", 1);
  2316. }
  2317. $g_servers{$s_addr}->get_game_mod_opts();
  2318. }
  2319. }
  2320.  
  2321. if (!$g_servers{$s_addr}->{"srv_players"})
  2322. {
  2323. $g_servers{$s_addr}->{"srv_players"} = ();
  2324. %g_players=();
  2325. }
  2326. else
  2327. {
  2328. %g_players=%{$g_servers{$s_addr}->{"srv_players"}};
  2329. }
  2330.  
  2331. # Get the datestamp (or complain)
  2332. #if ($s_output =~ s/^.*L (\d\d)\/(\d\d)\/(\d{4}) - (\d\d):(\d\d):(\d\d):\s*//)
  2333.  
  2334. #$is_streamed = 0;
  2335. #$test_for_date = 0;
  2336. #$is_streamed = ($s_output !~ m/^L\s*/);
  2337.  
  2338. #if ( !$is_streamed ) {
  2339. # $test_for_date = ($s_output =~ s/^L (\d\d)\/(\d\d)\/(\d{4}) - (\d\d):(\d\d):(\d\d):\s*//);
  2340. #} else {
  2341. # $test_for_date = ($s_output =~ s/^\S*L (\d\d)\/(\d\d)\/(\d{4}) - (\d\d):(\d\d):(\d\d):\s*//);
  2342. #}
  2343.  
  2344. #if ($test_for_date)
  2345.  
  2346. # EXPLOIT FIX
  2347.  
  2348. if ($s_output =~ s/^(?:.*?)?L (\d\d)\/(\d\d)\/(\d{4}) - (\d\d):(\d\d):(\d\d):\s*//) {
  2349. $ev_month = $1;
  2350. $ev_day = $2;
  2351. $ev_year = $3;
  2352. $ev_hour = $4;
  2353. $ev_min = $5;
  2354. $ev_sec = $6;
  2355. $ev_time = "$ev_hour:$ev_min:$ev_sec";
  2356. $ev_remotetime = timelocal($ev_sec,$ev_min,$ev_hour,$ev_day,$ev_month-1,$ev_year);
  2357.  
  2358. if ($g_timestamp) {
  2359. $ev_timestamp = "$ev_year-$ev_month-$ev_day $ev_time";
  2360. $ev_unixtime = $ev_remotetime;
  2361. if ($g_stdin)
  2362. {
  2363. $ev_daemontime = $ev_unixtime;
  2364. }
  2365. }
  2366. } else {
  2367. &printEvent(998, "MALFORMED DATA: " . $s_output);
  2368. next;
  2369. }
  2370.  
  2371. if ($g_debug >= 4) {
  2372. print $s_addr.": \"".$s_output."\"\n";
  2373. }
  2374.  
  2375. if (($g_stdin == 0) && ($g_servers{$s_addr}->{last_event} > 0) && ( ($ev_unixtime - $g_servers{$s_addr}->{last_event}) > 299) ) {
  2376. $g_servers{$s_addr}->set("map", "");
  2377. $g_servers{$s_addr}->get_map();
  2378. }
  2379.  
  2380. $g_servers{$s_addr}->set("last_event", $ev_unixtime);
  2381.  
  2382. # Now we parse the events.
  2383.  
  2384. my $ev_type = 0;
  2385. my $ev_status = "";
  2386. my $ev_team = "";
  2387. my $ev_player = 0;
  2388. my $ev_verb = "";
  2389. my $ev_obj_a = "";
  2390. my $ev_obj_b = "";
  2391. my $ev_obj_c = "";
  2392. my $ev_properties = "";
  2393. my %ev_properties = ();
  2394. my %ev_player = ();
  2395.  
  2396. # pvkii parrot log lines also fit the death line parsing
  2397. if ($g_servers{$s_addr}->{play_game} == PVKII()
  2398. && $s_output =~ /^
  2399. "(.+?(?:<[^>]*>){3})" # player string
  2400. \s[a-z]{6}\s # 'killed'
  2401. "npc_parrot<.+?>" # parrot string
  2402. \s[a-z]{5}\s[a-z]{2}\s # 'owned by'
  2403. "(.+?(?:<[^>]*>){3})" # owner string
  2404. \s[a-z]{4}\s # 'with'
  2405. "([^"]*)" #weapon
  2406. (.*) #properties
  2407. /x)
  2408. {
  2409. $ev_player = $1; # player
  2410. $ev_obj_b = $2; # victim
  2411. $ev_obj_c = $3; # weapon
  2412. $ev_properties = $4;
  2413. %ev_properties_hash = &getProperties($ev_properties);
  2414.  
  2415. my $playerinfo = &getPlayerInfo($ev_player, 1);
  2416. my $victiminfo = &getPlayerInfo($ev_obj_b, 1);
  2417. $ev_type = 10;
  2418.  
  2419. if ($playerinfo) {
  2420. if ($victiminfo) {
  2421. $ev_status = &doEvent_PlayerPlayerAction(
  2422. $playerinfo->{"userid"},
  2423. $playerinfo->{"uniqueid"},
  2424. $victiminfo->{"userid"},
  2425. $victiminfo->{"uniqueid"},
  2426. "killed_parrot",
  2427. undef,
  2428. undef,
  2429. undef,
  2430. undef,
  2431. undef,
  2432. undef,
  2433. &getProperties($ev_properties)
  2434. );
  2435. }
  2436.  
  2437. $ev_type = 11;
  2438.  
  2439. $ev_status = &doEvent_PlayerAction(
  2440. $playerinfo->{"userid"},
  2441. $playerinfo->{"uniqueid"},
  2442. "killed_parrot",
  2443. undef,
  2444. undef,
  2445. undef,
  2446. &getProperties($ev_properties)
  2447. );
  2448. }
  2449. } elsif ($s_output =~ /^
  2450. (?:\(DEATH\))? # l4d prefix, such as (DEATH) or (INCAP)
  2451. "(.+?(?:<.+?>)*?
  2452. (?:<setpos_exact\s(-?\d+?\.\d\d)\s(-?\d+?\.\d\d)\s(-?\d+?\.\d\d);[^"]*)?
  2453. )" # player string with or without l4d-style location coords
  2454. (?:\s\[(-?\d+)\s(-?\d+)\s(-?\d+)\])?
  2455. \skilled\s # verb (ex. attacked, killed, triggered)
  2456. "(.+?(?:<.+?>)*?
  2457. (?:<setpos_exact\s(-?\d+?\.\d\d)\s(-?\d+?\.\d\d)\s(-?\d+?\.\d\d);[^"]*)?
  2458. )" # player string as above or action name
  2459. (?:\s\[(-?\d+)\s(-?\d+)\s(-?\d+)\])?
  2460. \swith\s # (ex. with, against)
  2461. "([^"]*)"
  2462. (.*) #properties
  2463. /x)
  2464. {
  2465.  
  2466. # Prototype: "player" verb "obj_a" ?... "obj_b"[properties]
  2467. # Matches:
  2468. # 8. Kills
  2469.  
  2470. $ev_player = $1;
  2471. $ev_Xcoord = $2; # attacker/player coords (L4D)
  2472. $ev_Ycoord = $3;
  2473. $ev_Zcoord = $4;
  2474. if( !defined($ev_Xcoord) ) {
  2475. # if we didn't get L4D style, overwrite with CSGO style (which we may still not have)
  2476. $ev_Xcoord = $5;
  2477. $ev_Ycoord = $6;
  2478. $ev_Zcoord = $7;
  2479. }
  2480. $ev_obj_a = $8; # victim
  2481. $ev_XcoordKV = $9; # kill victim coords (L4D)
  2482. $ev_YcoordKV = $10;
  2483. $ev_ZcoordKV = $11;
  2484. if( !defined($ev_XcoordKV) ) {
  2485. $ev_XcoordKV = $12; # kill victim coords (CSGO)
  2486. $ev_YcoordKV = $13;
  2487. $ev_ZcoordKV = $14;
  2488. }
  2489. $ev_obj_b = $15; # weapon
  2490. $ev_properties = $16;
  2491. %ev_properties_hash = &getProperties($ev_properties);
  2492.  
  2493. my $killerinfo = &getPlayerInfo($ev_player, 1);
  2494. my $victiminfo = &getPlayerInfo($ev_obj_a, 1);
  2495. $ev_type = 8;
  2496.  
  2497. $headshot = 0;
  2498. if ($ev_properties =~ m/headshot/) {
  2499. $headshot = 1;
  2500. }
  2501.  
  2502. if ($killerinfo && $victiminfo) {
  2503. my $killerId = $killerinfo->{"userid"};
  2504. my $killerUniqueId = $killerinfo->{"uniqueid"};
  2505. my $killer = lookupPlayer($s_addr, $killerId, $killerUniqueId);
  2506.  
  2507. # octo
  2508. if($killer->{role} eq "scout") {
  2509. $ev_status = &doEvent_PlayerAction(
  2510. $killerinfo->{"userid"},
  2511. $killerinfo->{"uniqueid"},
  2512. "kill_as_scout",
  2513. "kill_as_scout"
  2514. );
  2515. }
  2516. if($killer->{role} eq "spy") {
  2517. $ev_status = &doEvent_PlayerAction(
  2518. $killerinfo->{"userid"},
  2519. $killerinfo->{"uniqueid"},
  2520. "kill_as_spy",
  2521. "kill_as_spy"
  2522. );
  2523. }
  2524.  
  2525. my $victimId = $victiminfo->{"userid"};
  2526. my $victimUniqueId = $victiminfo->{"uniqueid"};
  2527. my $victim = lookupPlayer($s_addr, $victimId, $victimUniqueId);
  2528.  
  2529. $ev_status = &doEvent_Frag(
  2530. $killerinfo->{"userid"},
  2531. $killerinfo->{"uniqueid"},
  2532. $victiminfo->{"userid"},
  2533. $victiminfo->{"uniqueid"},
  2534. $ev_obj_b,
  2535. $headshot,
  2536. $ev_Xcoord,
  2537. $ev_Ycoord,
  2538. $ev_Zcoord,
  2539. $ev_XcoordKV,
  2540. $ev_YcoordKV,
  2541. $ev_ZcoordKV,
  2542. %ev_properties_hash
  2543. );
  2544. }
  2545. } elsif ($g_servers{$s_addr}->{play_game} == L4D() && $s_output =~ /^
  2546. \(INCAP\) # l4d prefix, such as (DEATH) or (INCAP)
  2547. "(.+?(?:<.+?>)*?
  2548. <setpos_exact\s(-?\d+?\.\d\d)\s(-?\d+?\.\d\d)\s(-?\d+?\.\d\d);[^"]*
  2549. )" # player string with or without l4d-style location coords
  2550. \swas\sincapped\sby\s # verb (ex. attacked, killed, triggered)
  2551. "(.+?(?:<.+?>)*?
  2552. <setpos_exact\s(-?\d+?\.\d\d)\s(-?\d+?\.\d\d)\s(-?\d+?\.\d\d);[^"]*
  2553. )" # player string as above or action name
  2554. \swith\s # (ex. with, against)
  2555. "([^"]*)" # weapon name
  2556. (.*) #properties
  2557. /x)
  2558. {
  2559. # 800. L4D Incapacitation
  2560.  
  2561. $ev_player = $1;
  2562. $ev_l4dXcoord = $2; # attacker/player coords (L4D)
  2563. $ev_l4dYcoord = $3;
  2564. $ev_l4dZcoord = $4;
  2565. $ev_obj_a = $5; # victim
  2566. $ev_l4dXcoordKV = $6; # kill victim coords (L4D)
  2567. $ev_l4dYcoordKV = $7;
  2568. $ev_l4dZcoordKV = $8;
  2569. $ev_obj_b = $9; # weapon
  2570. $ev_properties = $10;
  2571. %ev_properties_hash = &getProperties($ev_properties);
  2572.  
  2573. # reverse killer/victim (x was incapped by y = y killed x)
  2574. my $killerinfo = &getPlayerInfo($ev_obj_a, 1);
  2575. my $victiminfo = &getPlayerInfo($ev_player, 1);
  2576.  
  2577. if ($victiminfo->{team} eq "Infected") {
  2578. $victiminfo = undef;
  2579. }
  2580. $ev_type = 800;
  2581.  
  2582. $headshot = 0;
  2583. if ($ev_properties =~ m/headshot/) {
  2584. $headshot = 1;
  2585. }
  2586. if ($killerinfo && $victiminfo) {
  2587. my $killerId = $killerinfo->{"userid"};
  2588. my $killerUniqueId = $killerinfo->{"uniqueid"};
  2589. my $killer = lookupPlayer($s_addr, $killerId, $killerUniqueId);
  2590.  
  2591. my $victimId = $victiminfo->{"userid"};
  2592. my $victimUniqueId = $victiminfo->{"uniqueid"};
  2593. my $victim = lookupPlayer($s_addr, $victimId, $victimUniqueId);
  2594.  
  2595. $ev_status = &doEvent_Frag(
  2596. $killerinfo->{"userid"},
  2597. $killerinfo->{"uniqueid"},
  2598. $victiminfo->{"userid"},
  2599. $victiminfo->{"uniqueid"},
  2600. $ev_obj_b,
  2601. $headshot,
  2602. $ev_l4dXcoord,
  2603. $ev_l4dYcoord,
  2604. $ev_l4dZcoord,
  2605. $ev_l4dXcoordKV,
  2606. $ev_l4dYcoordKV,
  2607. $ev_l4dZcoordKV,
  2608. &getProperties($ev_properties)
  2609. );
  2610. }
  2611. } elsif ($g_servers{$s_addr}->{play_game} == L4D() && $s_output =~ /^\(TONGUE\)\sTongue\sgrab\sstarting\.\s+Smoker:"(.+?(?:<.+?>)*?(?:|<setpos_exact ((?:|-)\d+?\.\d\d) ((?:|-)\d+?\.\d\d) ((?:|-)\d+?\.\d\d);.*?))"\.\s+Victim:"(.+?(?:<.+?>)*?(?:|<setpos_exact ((?:|-)\d+?\.\d\d) ((?:|-)\d+?\.\d\d) ((?:|-)\d+?\.\d\d);.*?))".*/) {
  2612. # Prototype: (TONGUE) Tongue grab starting. Smoker:"player". Victim:"victim".
  2613. # Matches:
  2614. # 11. Player Action
  2615.  
  2616. $ev_player = $1;
  2617. $ev_l4dXcoord = $2;
  2618. $ev_l4dYcoord = $3;
  2619. $ev_l4dZcoord = $4;
  2620. $ev_victim = $5;
  2621. $ev_l4dXcoordV = $6;
  2622. $ev_l4dYcoordV = $7;
  2623. $ev_l4dZcoordV = $8;
  2624.  
  2625. $playerinfo = &getPlayerInfo($ev_player, 1);
  2626. $victiminfo = &getPlayerInfo($ev_victim, 1);
  2627.  
  2628. $ev_type = 11;
  2629.  
  2630. if ($playerinfo) {
  2631. $ev_status = &doEvent_PlayerAction(
  2632. $playerinfo->{"userid"},
  2633. $playerinfo->{"uniqueid"},
  2634. "tongue_grab"
  2635. );
  2636. }
  2637. if ($playerinfo && $victiminfo) {
  2638. $ev_status = &doEvent_PlayerPlayerAction(
  2639. $playerinfo->{"userid"},
  2640. $playerinfo->{"uniqueid"},
  2641. $victiminfo->{"userid"},
  2642. $victiminfo->{"uniqueid"},
  2643. "tongue_grab",
  2644. $ev_l4dXcoord,
  2645. $ev_l4dYcoord,
  2646. $ev_l4dZcoord,
  2647. $ev_l4dXcoordV,
  2648. $ev_l4dYcoordV,
  2649. $ev_l4dZcoordV
  2650. );
  2651. }
  2652. } elsif ($s_output =~ /^
  2653. "(.+?(?:<.+?>)*?
  2654. )" # player string
  2655. \s(triggered(?:\sa)?)\s # verb (ex. attacked, killed, triggered)
  2656. "(.+?(?:<.+?>)*?
  2657. )" # player string as above or action name
  2658. \s[a-zA-Z]+\s # (ex. with, against)
  2659. "(.+?(?:<.+?>)*?
  2660. )" # player string as above or weapon name
  2661. (?:\s[a-zA-Z]+\s"(.+?)")? # weapon name on plyrplyr actions
  2662. (.*) #properties
  2663. /x)
  2664. {
  2665.  
  2666. # 10. Player-Player Actions
  2667.  
  2668. # no l4d/2 actions are logged with the locations (in fact, very few are logged period) so the l4d/2 location parsing can be skipped
  2669.  
  2670. $ev_player = $1;
  2671. $ev_verb = $2; # triggered or triggered a
  2672. $ev_obj_a = $3; # action
  2673. $ev_obj_b = $4; # victim
  2674. $ev_obj_c = $5; # weapon (optional)
  2675. $ev_properties = $6;
  2676. %ev_properties_hash = &getProperties($ev_properties);
  2677.  
  2678.  
  2679. if ($ev_verb eq "triggered") { # it's either 'triggered' or 'triggered a'
  2680.  
  2681. my $playerinfo = &getPlayerInfo($ev_player, 1);
  2682. my $victiminfo = &getPlayerInfo($ev_obj_b, 1);
  2683. $ev_type = 10;
  2684.  
  2685. if ($playerinfo) {
  2686. if ($victiminfo) {
  2687. $ev_status = &doEvent_PlayerPlayerAction(
  2688. $playerinfo->{"userid"},
  2689. $playerinfo->{"uniqueid"},
  2690. $victiminfo->{"userid"},
  2691. $victiminfo->{"uniqueid"},
  2692. $ev_obj_a,
  2693. undef,
  2694. undef,
  2695. undef,
  2696. undef,
  2697. undef,
  2698. undef,
  2699. &getProperties($ev_properties)
  2700. );
  2701. }
  2702.  
  2703. $ev_type = 11;
  2704.  
  2705. $ev_status = &doEvent_PlayerAction(
  2706. $playerinfo->{"userid"},
  2707. $playerinfo->{"uniqueid"},
  2708. $ev_obj_a,
  2709. undef,
  2710. undef,
  2711. undef,
  2712. &getProperties($ev_properties)
  2713. );
  2714. }
  2715. } else {
  2716. my $playerinfo = &getPlayerInfo($ev_player, 1);
  2717. $ev_type = 11;
  2718.  
  2719. if ($playerinfo) {
  2720. $ev_status = &doEvent_PlayerAction(
  2721. $playerinfo->{"userid"},
  2722. $playerinfo->{"uniqueid"},
  2723. $ev_obj_a,
  2724. undef,
  2725. undef,
  2726. undef,
  2727. &getProperties($ev_properties)
  2728. );
  2729. }
  2730. }
  2731. } elsif ( $s_output =~ /^(?:\[STATSME\] )?"(.+?(?:<.+?>)*)" triggered "(weaponstats\d{0,1})"(.*)$/ ) {
  2732. # Prototype: [STATSME] "player" triggered "weaponstats?"[properties]
  2733. # Matches:
  2734. # 501. Statsme weaponstats
  2735. # 502. Statsme weaponstats2
  2736.  
  2737. $ev_player = $1;
  2738. $ev_verb = $2; # weaponstats; weaponstats2
  2739. $ev_properties = $3;
  2740. %ev_properties = &getProperties($ev_properties);
  2741.  
  2742. if (like($ev_verb, "weaponstats")) {
  2743. $ev_type = 501;
  2744. my $playerinfo = &getPlayerInfo($ev_player, 0);
  2745.  
  2746. if ($playerinfo) {
  2747. my $playerId = $playerinfo->{"userid"};
  2748. my $playerUniqueId = $playerinfo->{"uniqueid"};
  2749. my $ingame = 0;
  2750.  
  2751. $ingame = 1 if (lookupPlayer($s_addr, $playerId, $playerUniqueId));
  2752.  
  2753. if (!$ingame) {
  2754. &getPlayerInfo($ev_player, 1);
  2755. }
  2756.  
  2757. $ev_status = &doEvent_Statsme(
  2758. $playerId,
  2759. $playerUniqueId,
  2760. $ev_properties{"weapon"},
  2761. $ev_properties{"shots"},
  2762. $ev_properties{"hits"},
  2763. $ev_properties{"headshots"},
  2764. $ev_properties{"damage"},
  2765. $ev_properties{"kills"},
  2766. $ev_properties{"deaths"}
  2767. );
  2768.  
  2769. if (!$ingame) {
  2770. &doEvent_Disconnect(
  2771. $playerId,
  2772. $playerUniqueId,
  2773. ""
  2774. );
  2775. }
  2776. }
  2777. } elsif (like($ev_verb, "weaponstats2")) {
  2778. $ev_type = 502;
  2779. my $playerinfo = &getPlayerInfo($ev_player, 0);
  2780.  
  2781. if ($playerinfo) {
  2782. my $playerId = $playerinfo->{"userid"};
  2783. my $playerUniqueId = $playerinfo->{"uniqueid"};
  2784. my $ingame = 0;
  2785.  
  2786. $ingame = 1 if (lookupPlayer($s_addr, $playerId, $playerUniqueId));
  2787.  
  2788. if (!$ingame) {
  2789. &getPlayerInfo($ev_player, 1);
  2790. }
  2791.  
  2792. $ev_status = &doEvent_Statsme2(
  2793. $playerId,
  2794. $playerUniqueId,
  2795. $ev_properties{"weapon"},
  2796. $ev_properties{"head"},
  2797. $ev_properties{"chest"},
  2798. $ev_properties{"stomach"},
  2799. $ev_properties{"leftarm"},
  2800. $ev_properties{"rightarm"},
  2801. $ev_properties{"leftleg"},
  2802. $ev_properties{"rightleg"}
  2803. );
  2804.  
  2805. if (!$ingame) {
  2806. &doEvent_Disconnect(
  2807. $playerId,
  2808. $playerUniqueId,
  2809. ""
  2810. );
  2811. }
  2812. }
  2813. }
  2814. } elsif ( $s_output =~ /^(?:\[STATSME\] )?"(.+?(?:<.+?>)*)" triggered "(latency|time)"(.*)$/ ) {
  2815. # Prototype: [STATSME] "player" triggered "latency|time"[properties]
  2816. # Matches:
  2817. # 503. Statsme latency
  2818. # 504. Statsme time
  2819.  
  2820. $ev_player = $1;
  2821. $ev_verb = $2; # latency; time
  2822. $ev_properties = $3;
  2823. %ev_properties = &getProperties($ev_properties);
  2824.  
  2825. if ($ev_verb eq "time") {
  2826. $ev_type = 504;
  2827. my $playerinfo = &getPlayerInfo($ev_player, 0);
  2828.  
  2829. if ($playerinfo) {
  2830. my ($min, $sec) = split(/:/, $ev_properties{"time"});
  2831. my $hour = sprintf("%d", $min / 60);
  2832.  
  2833. if ($hour) {
  2834. $min = $min % 60;
  2835. }
  2836.  
  2837. $ev_status = &doEvent_Statsme_Time(
  2838. $playerinfo->{"userid"},
  2839. $playerinfo->{"uniqueid"},
  2840. "$hour:$min:$sec"
  2841. );
  2842. }
  2843. } else { # latency
  2844. $ev_type = 503;
  2845. my $playerinfo = &getPlayerInfo($ev_player, 0);
  2846.  
  2847. if ($playerinfo) {
  2848. $ev_status = &doEvent_Statsme_Latency(
  2849. $playerinfo->{"userid"},
  2850. $playerinfo->{"uniqueid"},
  2851. $ev_properties{"ping"}
  2852. );
  2853. }
  2854. }
  2855. } elsif ($s_output =~ /^"(.+?(?:<.+?>)*?)" ([a-zA-Z,_\s]+) "(.+?)"(.*)$/) {
  2856. # Prototype: "player" verb "obj_a"[properties]
  2857. # Matches:
  2858. # 1. Connection
  2859. # 4. Suicides
  2860. # 5. Team Selection
  2861. # 6. Role Selection
  2862. # 7. Change Name
  2863. # 11. Player Objectives/Actions
  2864. # 14. a) Chat; b) Team Chat
  2865.  
  2866. $ev_player = $1;
  2867. $ev_verb = $2;
  2868. $ev_obj_a = $3;
  2869. $ev_properties = $4;
  2870. %ev_properties = &getProperties($ev_properties);
  2871.  
  2872. if ($ev_verb eq "connected, address") {
  2873. my $ipAddr = $ev_obj_a;
  2874. my $playerinfo;
  2875.  
  2876. if ($ipAddr =~ /([\d\.]+):(\d+)/) {
  2877. $ipAddr = $1;
  2878. }
  2879.  
  2880. $playerinfo = &getPlayerInfo($ev_player, 1, $ipAddr);
  2881.  
  2882. $ev_type = 1;
  2883.  
  2884. if ($playerinfo) {
  2885. if (($playerinfo->{"uniqueid"} =~ /UNKNOWN/) || ($playerinfo->{"uniqueid"} =~ /PENDING/) || ($playerinfo->{"uniqueid"} =~ /VALVE_ID_LAN/)) {
  2886. $ev_status = "(DELAYING CONNECTION): $s_output";
  2887.  
  2888. if ($g_mode ne "LAN") {
  2889. my $p_name = $playerinfo->{"name"};
  2890. my $p_userid = $playerinfo->{"userid"};
  2891. &printEvent("SERVER", "LATE CONNECT [$p_name/$p_userid] - STEAM_ID_PENDING");
  2892. $g_preconnect->{"$s_addr/$p_userid/$p_name"} = {
  2893. ipaddress => $ipAddr,
  2894. name => $p_name,
  2895. server => $s_addr,
  2896. timestamp => $ev_daemontime
  2897. };
  2898. }
  2899. } else {
  2900. $ev_status = &doEvent_Connect(
  2901. $playerinfo->{"userid"},
  2902. $playerinfo->{"uniqueid"},
  2903. $ipAddr
  2904. );
  2905. }
  2906. }
  2907. } elsif ($ev_verb eq "committed suicide with") {
  2908. my $playerinfo = &getPlayerInfo($ev_player, 1);
  2909.  
  2910. $ev_type = 4;
  2911.  
  2912. if ($playerinfo) {
  2913. $ev_status = &doEvent_Suicide(
  2914. $playerinfo->{"userid"},
  2915. $playerinfo->{"uniqueid"},
  2916. $ev_obj_a,
  2917. %ev_properties
  2918. );
  2919. }
  2920. } elsif ($ev_verb eq "joined team") {
  2921. my $playerinfo = &getPlayerInfo($ev_player, 1);
  2922.  
  2923. $ev_type = 5;
  2924.  
  2925. if ($playerinfo) {
  2926. $ev_status = &doEvent_TeamSelection(
  2927. $playerinfo->{"userid"},
  2928. $playerinfo->{"uniqueid"},
  2929. $ev_obj_a
  2930. );
  2931. }
  2932. } elsif ($ev_verb eq "changed role to") {
  2933. my $playerinfo = &getPlayerInfo($ev_player, 1);
  2934.  
  2935. $ev_type = 6;
  2936.  
  2937. if ($playerinfo) {
  2938. $ev_status = &doEvent_RoleSelection(
  2939. $playerinfo->{"userid"},
  2940. $playerinfo->{"uniqueid"},
  2941. $ev_obj_a
  2942. );
  2943. }
  2944. } elsif ($ev_verb eq "changed name to") {
  2945. my $playerinfo = &getPlayerInfo($ev_player, 1);
  2946.  
  2947. $ev_type = 7;
  2948.  
  2949. if ($playerinfo) {
  2950. $ev_status = &doEvent_ChangeName(
  2951. $playerinfo->{"userid"},
  2952. $playerinfo->{"uniqueid"},
  2953. $ev_obj_a
  2954. );
  2955. }
  2956. } elsif ($ev_verb eq "triggered") {
  2957.  
  2958. # in cs:s players dropp the bomb if they are the only ts
  2959. # and disconnect...the dropp the bomb after they disconnected :/
  2960. if ($ev_obj_a eq "Dropped_The_Bomb") {
  2961. $playerinfo = &getPlayerInfo($ev_player, 0);
  2962. } else {
  2963. $playerinfo = &getPlayerInfo($ev_player, 1);
  2964. }
  2965. if ($playerinfo) {
  2966. if ($ev_obj_a eq "player_changeclass" && defined($ev_properties{newclass})) {
  2967.  
  2968. $ev_type = 6;
  2969.  
  2970. $ev_status = &doEvent_RoleSelection(
  2971. $playerinfo->{"userid"},
  2972. $playerinfo->{"uniqueid"},
  2973. $ev_properties{newclass}
  2974. );
  2975. } else {
  2976. if ($g_servers{$s_addr}->{play_game} == TFC())
  2977. {
  2978. if ($ev_obj_a eq "Sentry_Destroyed")
  2979. {
  2980. $ev_obj_a = "Sentry_Dismantle";
  2981. }
  2982. elsif ($ev_obj_a eq "Dispenser_Destroyed")
  2983. {
  2984. $ev_obj_a = "Dispenser_Dismantle";
  2985. }
  2986. elsif ($ev_obj_a eq "Teleporter_Entrance_Destroyed")
  2987. {
  2988. $ev_obj_a = "Teleporter_Entrance_Dismantle"
  2989. }
  2990. elsif ($ev_obj_a eq "Teleporter_Exit_Destroyed")
  2991. {
  2992. $ev_obj_a = "Teleporter_Exit_Dismantle"
  2993. }
  2994. }
  2995.  
  2996. $ev_type = 11;
  2997.  
  2998. $ev_status = &doEvent_PlayerAction(
  2999. $playerinfo->{"userid"},
  3000. $playerinfo->{"uniqueid"},
  3001. $ev_obj_a,
  3002. undef,
  3003. undef,
  3004. undef,
  3005. %ev_properties
  3006. );
  3007. }
  3008. }
  3009. } elsif ($ev_verb eq "triggered a") {
  3010. my $playerinfo = &getPlayerInfo($ev_player, 1);
  3011.  
  3012. $ev_type = 11;
  3013.  
  3014. if ($playerinfo)
  3015. {
  3016. $ev_status = &doEvent_PlayerAction(
  3017. $playerinfo->{"userid"},
  3018. $playerinfo->{"uniqueid"},
  3019. $ev_obj_a,
  3020. undef,
  3021. undef,
  3022. undef,
  3023. %ev_properties
  3024. );
  3025. }
  3026. } elsif ($ev_verb eq "say" || $ev_verb eq "say_team" || $ev_verb eq "say_squad") {
  3027. my $playerinfo = &getPlayerInfo($ev_player, 1);
  3028.  
  3029. $ev_type = 14;
  3030.  
  3031. if ($playerinfo) {
  3032. $ev_status = &doEvent_Chat(
  3033. $ev_verb,
  3034. $playerinfo->{"userid"},
  3035. $playerinfo->{"uniqueid"},
  3036. $ev_obj_a
  3037. );
  3038. }
  3039. }
  3040. } elsif ($s_output =~ /^(?:Kick: )?"(.+?(?:<.+?>)*)" ([^\(]+)(.*)$/) {
  3041. # Prototype: "player" verb[properties]
  3042. # Matches:
  3043. # 2. Enter Game
  3044. # 3. Disconnection
  3045.  
  3046. $ev_player = $1;
  3047. $ev_verb = $2;
  3048. $ev_properties = $3;
  3049. %ev_properties = &getProperties($ev_properties);
  3050.  
  3051. if (like($ev_verb, "entered the game")) {
  3052. my $playerinfo = &getPlayerInfo($ev_player, 1);
  3053.  
  3054. if ($playerinfo) {
  3055. $ev_type = 2;
  3056. $ev_status = &doEvent_EnterGame($playerinfo->{"userid"}, $playerinfo->{"uniqueid"}, $ev_obj_a);
  3057. }
  3058. } elsif (like($ev_verb, "disconnected") || like($ev_verb, "was kicked")) {
  3059. my $playerinfo = &getPlayerInfo($ev_player, 0);
  3060.  
  3061. if ($playerinfo) {
  3062. $ev_type = 3;
  3063.  
  3064. $userid = $playerinfo->{userid};
  3065. $uniqueid = $playerinfo->{uniqueid};
  3066.  
  3067. $ev_status = &doEvent_Disconnect(
  3068. $playerinfo->{userid},
  3069. $playerinfo->{uniqueid},
  3070. $ev_properties
  3071. );
  3072. }
  3073. }
  3074. elsif (like($ev_verb, "STEAM USERID validated") || like($ev_verb, "VALVE USERID validated")) {
  3075. my $playerinfo = &getPlayerInfo($ev_player, 0);
  3076.  
  3077. if ($playerinfo) {
  3078. $ev_type = 1;
  3079.  
  3080. }
  3081. }
  3082. } elsif ($s_output =~ /^Team "(.+?)" ([^"\(]+) "([^"]+)"(.*)$/) {
  3083. # Prototype: Team "team" verb "obj_a"[properties]
  3084. # Matches:
  3085. # 12. Team Objectives/Actions
  3086. # 1200. Team Objective With Players involved
  3087. # 15. Team Alliances
  3088.  
  3089. $ev_team = $1;
  3090. $ev_verb = $2;
  3091. $ev_obj_a = $3;
  3092. $ev_properties = $4;
  3093. %ev_properties_hash = &getProperties($ev_properties);
  3094.  
  3095. if ($ev_obj_a eq "pointcaptured") {
  3096. $numcappers = $ev_properties_hash{numcappers};
  3097. if ($g_debug > 1) {
  3098. print "NumCappers = ".$numcappers."\n";
  3099. }
  3100. foreach ($i = 1; $i <= $numcappers; $i++) {
  3101. # reward each player involved in capturing
  3102. $player = $ev_properties_hash{"player".$i};
  3103. if ($g_debug > 1) {
  3104. print $i." -> ".$player."\n";
  3105. }
  3106. #$position = $ev_properties_hash{"position".$i};
  3107. my $playerinfo = &getPlayerInfo($player, 1);
  3108. if ($playerinfo) {
  3109. $ev_status = &doEvent_PlayerAction(
  3110. $playerinfo->{"userid"},
  3111. $playerinfo->{"uniqueid"},
  3112. $ev_obj_a,
  3113. "",
  3114. "",
  3115. "",
  3116. &getProperties($ev_properties)
  3117. );
  3118. }
  3119. }
  3120. }
  3121. if ($ev_obj_a eq "captured_loc") {
  3122. # $flag_name = $ev_properties_hash{flagname};
  3123. $player_a = $ev_properties_hash{player_a};
  3124. $player_b = $ev_properties_hash{player_b};
  3125.  
  3126. my $playerinfo_a = &getPlayerInfo($player_a, 1);
  3127. if ($playerinfo_a) {
  3128. $ev_status = &doEvent_PlayerAction(
  3129. $playerinfo_a->{"userid"},
  3130. $playerinfo_a->{"uniqueid"},
  3131. $ev_obj_a,
  3132. "",
  3133. "",
  3134. "",
  3135. &getProperties($ev_properties)
  3136. );
  3137. }
  3138.  
  3139. my $playerinfo_b = &getPlayerInfo($player_b, 1);
  3140. if ($playerinfo_b) {
  3141. $ev_status = &doEvent_PlayerAction(
  3142. $playerinfo_b->{"userid"},
  3143. $playerinfo_b->{"uniqueid"},
  3144. $ev_obj_a,
  3145. "",
  3146. "",
  3147. "",
  3148. &getProperties($ev_properties)
  3149. );
  3150. }
  3151. }
  3152.  
  3153. if (like($ev_verb, "triggered")) {
  3154. if ($ev_obj_a ne "captured_loc") {
  3155. $ev_type = 12;
  3156. $ev_status = &doEvent_TeamAction(
  3157. $ev_team,
  3158. $ev_obj_a,
  3159. &getProperties($ev_properties)
  3160. );
  3161. }
  3162. } elsif (like($ev_verb, "triggered a")) {
  3163. $ev_type = 12;
  3164. $ev_status = &doEvent_TeamAction(
  3165. $ev_team,
  3166. $ev_obj_a
  3167. );
  3168. }
  3169. } elsif ($s_output =~ /^(Rcon|Bad Rcon): "rcon [^"]+"([^"]+)"\s+(.+)" from "([0-9\.]+?):(\d+?)"(.*)$/) {
  3170. # Prototype: verb: "rcon ?..."obj_a" obj_b" from "obj_c"[properties]
  3171. # Matches:
  3172. # 20. HL1 a) Rcon; b) Bad Rcon
  3173.  
  3174. $ev_verb = $1;
  3175. $ev_obj_a = $2; # password
  3176. $ev_obj_b = $3; # command
  3177. $ev_obj_c = $4; # ip
  3178. $ev_obj_d = $5; # port
  3179. $ev_properties = $6;
  3180. %ev_properties = &getProperties($ev_properties);
  3181. if ($g_rcon_ignoreself == 0 || $ev_obj_c ne $s_ip) {
  3182. $ev_obj_b = substr($ev_obj_b, 0, 255);
  3183. if (like($ev_verb, "Rcon")) {
  3184. $ev_type = 20;
  3185. $ev_status = &doEvent_Rcon(
  3186. "OK",
  3187. $ev_obj_b,
  3188. "",
  3189. $ev_obj_c
  3190. );
  3191. } elsif (like($ev_verb, "Bad Rcon")) {
  3192. $ev_type = 20;
  3193. $ev_status = &doEvent_Rcon(
  3194. "BAD",
  3195. $ev_obj_b,
  3196. $ev_obj_a,
  3197. $ev_obj_c
  3198. );
  3199. }
  3200. } else {
  3201. $ev_status = "(IGNORED) Rcon from \"$ev_obj_a:$ev_obj_b\": \"$ev_obj_c\"";
  3202. }
  3203. } elsif ($s_output =~ /^rcon from "(.+?):(.+?)": (?:command "(.*)".*|(Bad) Password)$/) {
  3204. # Prototype: verb: "rcon ?..."obj_a" obj_b" from "obj_c"[properties]
  3205. # Matches:
  3206. # 20. a) Rcon;
  3207.  
  3208. $ev_obj_a = $1; # ip
  3209. $ev_obj_b = $2; # port
  3210. $ev_obj_c = $3; # command
  3211. $ev_isbad = $4; # if bad, "Bad"
  3212. if ($g_rcon_ignoreself == 0 || $ev_obj_a ne $s_ip) {
  3213. if ($ev_isbad ne "Bad") {
  3214. $ev_type = 20;
  3215. @cmds = split(/;/,$ev_obj_c);
  3216. foreach(@cmds)
  3217. {
  3218. $ev_status = &doEvent_Rcon(
  3219. "OK",
  3220. substr($_, 0, 255),
  3221. "",
  3222. $ev_obj_a
  3223. );
  3224. }
  3225. } else {
  3226. $ev_type = 20;
  3227. $ev_status = &doEvent_Rcon(
  3228. "BAD",
  3229. "",
  3230. "",
  3231. $ev_obj_a
  3232. );
  3233. }
  3234. } else {
  3235. $ev_status = "(IGNORED) Rcon from \"$ev_obj_a:$ev_obj_b\": \"$ev_obj_c\"";
  3236. }
  3237. } elsif ($s_output =~ /^\[(.+)\.(smx|amxx)\]\s*(.+)$/i) {
  3238. # Prototype: Cmd:[SM] obj_a
  3239. # Matches:
  3240. # Admin Mod messages
  3241.  
  3242. my $ev_plugin = $1;
  3243. my $ev_adminmod = $2;
  3244. $ev_obj_a = $3;
  3245. $ev_type = 500;
  3246. $ev_status = &doEvent_Admin(
  3247. (($ev_adminmod eq "smx")?"Sourcemod":"AMXX")." ($ev_plugin)",
  3248. substr($ev_obj_a, 0, 255)
  3249. );
  3250. } elsif ($s_output =~ /^([^"\(]+) "([^"]+)"(.*)$/) {
  3251. # Prototype: verb "obj_a"[properties]
  3252. # Matches:
  3253. # 13. World Objectives/Actions
  3254. # 19. a) Loading map; b) Started map
  3255. # 21. Server Name
  3256.  
  3257. $ev_verb = $1;
  3258. $ev_obj_a = $2;
  3259. $ev_properties = $3;
  3260. %ev_properties = &getProperties($ev_properties);
  3261.  
  3262. if (like($ev_verb, "World triggered")) {
  3263. $ev_type = 13;
  3264. if ($ev_obj_a eq "killlocation") {
  3265. $ev_status = &doEvent_Kill_Loc(
  3266. %ev_properties
  3267. );
  3268. } else {
  3269. $ev_status = &doEvent_WorldAction(
  3270. $ev_obj_a
  3271. );
  3272. if ($ev_obj_a eq "Round_Win" || $ev_obj_a eq "Mini_Round_Win") {
  3273. $ev_team = $ev_properties{"winner"};
  3274. $ev_status = &doEvent_TeamAction(
  3275. $ev_team,
  3276. $ev_obj_a
  3277. );
  3278. }
  3279. }
  3280. } elsif (like($ev_verb, "Loading map")) {
  3281. $ev_type = 19;
  3282. $ev_status = &doEvent_ChangeMap(
  3283. "loading",
  3284. $ev_obj_a
  3285. );
  3286. } elsif (like($ev_verb, "Started map")) {
  3287. $ev_type = 19;
  3288. $ev_status = &doEvent_ChangeMap(
  3289. "started",
  3290. $ev_obj_a
  3291. );
  3292. }
  3293. } elsif ($s_output =~ /^\[MANI_ADMIN_PLUGIN\]\s*(.+)$/) {
  3294. # Prototype: [MANI_ADMIN_PLUGIN] obj_a
  3295. # Matches:
  3296. # Mani-Admin-Plugin messages
  3297.  
  3298. $ev_obj_a = $1;
  3299. $ev_type = 500;
  3300. $ev_status = &doEvent_Admin(
  3301. "Mani Admin Plugin",
  3302. substr($ev_obj_a, 0, 255)
  3303. );
  3304. } elsif ($s_output =~ /^\[BeetlesMod\]\s*(.+)$/) {
  3305. # Prototype: Cmd:[BeetlesMod] obj_a
  3306. # Matches:
  3307. # Beetles Mod messages
  3308.  
  3309. $ev_obj_a = $1;
  3310. $ev_type = 500;
  3311. $ev_status = &doEvent_Admin(
  3312. "Beetles Mod",
  3313. substr($ev_obj_a, 0, 255)
  3314. );
  3315. } elsif ($s_output =~ /^\[ADMIN:(.+)\] ADMIN Command: \1 used command (.+)$/) {
  3316. # Prototype: [ADMIN] obj_a
  3317. # Matches:
  3318. # Admin Mod messages
  3319.  
  3320. $ev_obj_a = $1;
  3321. $ev_obj_b = $2;
  3322. $ev_type = 500;
  3323. $ev_status = &doEvent_Admin(
  3324. "Admin Mod",
  3325. substr($ev_obj_b, 0, 255),
  3326. $ev_obj_a
  3327. );
  3328. } elsif ($g_servers{$s_addr}->{play_game} == DYSTOPIA()) {
  3329. if ($s_output =~ /^weapon { steam_id: 'STEAM_\d+:(.+?)', weapon_id: (\d+), class: \d+, team: \d+, shots: \((\d+),(\d+)\), hits: \((\d+),(\d+)\), damage: \((\d+),(\d+)\), headshots: \((\d+),(\d+)\), kills: \(\d+,\d+\) }$/ && $g_mode eq "Normal") {
  3330.  
  3331. # Prototype: weapon { steam_id: 'STEAMID', weapon_id: X, class: X, team: X, shots: (X,X), hits: (X,X), damage: (X,X), headshots: (X,X), kills: (X,X) }
  3332. # Matches:
  3333. # 501. Statsme weaponstats (Dystopia)
  3334.  
  3335. my $steamid = $1;
  3336. my $weapon = $2;
  3337. my $shots = $3 + $4;
  3338. my $hits = $5 + $6;
  3339. my $damage = $7 + $8;
  3340. my $headshots = $9 + $10;
  3341. my $kills = $11 + $12;
  3342.  
  3343. $ev_type = 501;
  3344.  
  3345. my $weapcode = $dysweaponcodes{$weapon};
  3346.  
  3347. foreach $player (values(%g_players)) {
  3348. if ($player->{uniqueid} eq $steamid) {
  3349. $ev_status = &doEvent_Statsme(
  3350. $player->{"userid"},
  3351. $steamid,
  3352. $weapcode,
  3353. $shots,
  3354. $hits,
  3355. $headshots,
  3356. $damage,
  3357. $kills,
  3358. 0
  3359. );
  3360. last;
  3361. }
  3362. }
  3363. } elsif ($s_output =~ /^(?:join|change)_class { steam_id: 'STEAM_\d+:(.+?)', .* (?:new_|)class: (\d+), .* }$/ && $g_mode eq "Normal") {
  3364. # Prototype: join_class { steam_id: 'STEAMID', team: X, class: Y, time: ZZZZZZZZZ }
  3365. # Matches:
  3366. # 6. Role Selection (Dystopia)
  3367.  
  3368. my $steamid = $1;
  3369. my $role = $2;
  3370. $ev_type = 6;
  3371.  
  3372. foreach $player (values(%g_players)) {
  3373. if ($player->{uniqueid} eq $steamid) {
  3374. $ev_status = &doEvent_RoleSelection(
  3375. $player->{"userid"},
  3376. $steamid,
  3377. $role
  3378. );
  3379. last;
  3380. }
  3381. }
  3382. } elsif ($s_output =~ /^objective { steam_id: 'STEAM_\d+:(.+?)', class: \d+, team: \d+, objective: '(.+?)', time: \d+ }$/ && $g_mode eq "Normal") {
  3383. # Prototype: objective { steam_id: 'STEAMID', class: X, team: X, objective: 'TEXT', time: X }
  3384. # Matches:
  3385. # 11. Player Action (Dystopia Objectives)
  3386.  
  3387. my $steamid = $1;
  3388. my $action = $2;
  3389. foreach $player (values(%g_players)) {
  3390. if ($player->{uniqueid} eq $steamid) {
  3391. $ev_status = &doEvent_PlayerAction(
  3392. $player->{"userid"},
  3393. $steamid,
  3394. $action
  3395. );
  3396. last;
  3397. }
  3398. }
  3399. }
  3400. }
  3401.  
  3402. if ($ev_type) {
  3403. if ($g_debug > 2) {
  3404. print <<EOT
  3405. type = "$ev_type"
  3406. team = "$ev_team"
  3407. player = "$ev_player"
  3408. verb = "$ev_verb"
  3409. obj_a = "$ev_obj_a"
  3410. obj_b = "$ev_obj_b"
  3411. obj_c = "$ev_obj_c"
  3412. properties = "$ev_properties"
  3413. EOT
  3414. ;
  3415. while (my($key, $value) = each(%ev_properties)) {
  3416. print "property: \"$key\" = \"$value\"\n";
  3417. }
  3418.  
  3419. while (my($key, $value) = each(%ev_player)) {
  3420. print "player $key = \"$value\"\n";
  3421. }
  3422. }
  3423.  
  3424. if ($ev_status ne "") {
  3425. &printEvent($ev_type, $ev_status);
  3426. } else {
  3427. &printEvent($ev_type, "BAD DATA: $s_output");
  3428. }
  3429. } elsif (($s_output =~ /^Banid: "(.+?(?:<.+?>)*)" was (?:kicked and )?banned "for ([0-9]+).00 minutes" by "Console"$/) ||
  3430. ($s_output =~ /^Banid: "(.+?(?:<.+?>)*)" was (?:kicked and )?banned "(permanently)" by "Console"$/)) {
  3431.  
  3432. # Prototype: "player" verb[properties]
  3433. # Banid: huaaa<1804><STEAM_0:1:10769><>" was kicked and banned "permanently" by "Console"
  3434.  
  3435. $ev_player = $1;
  3436. $ev_bantime = $2;
  3437. my $playerinfo = &getPlayerInfo($ev_player, 1);
  3438.  
  3439. if ($ev_bantime eq "5") {
  3440. &printEvent("BAN", "Auto Ban - ignored");
  3441. } elsif ($playerinfo) {
  3442. if (($g_global_banning > 0) && ($g_servers{$s_addr}->{ignore_nextban}->{$playerinfo->{"uniqueid"}} == 1)) {
  3443. delete($g_servers{$s_addr}->{ignore_nextban}->{$playerinfo->{"uniqueid"}});
  3444. &printEvent("BAN", "Global Ban - ignored");
  3445. } elsif (!$g_servers{$s_addr}->{ignore_nextban}->{$playerinfo->{"uniqueid"}}) {
  3446. my $p_steamid = $playerinfo->{"uniqueid"};
  3447. my $player_obj = lookupPlayer($s_addr, $playerId, $p_steamid);
  3448. &printEvent("BAN", "Steamid: ".$p_steamid);
  3449.  
  3450. if ($player_obj) {
  3451. $player_obj->{"is_banned"} = 1;
  3452. }
  3453. if (($p_steamid ne "") && ($playerinfo->{"is_bot"} == 0) && ($playerinfo->{"userid"} > 0)) {
  3454. if ($g_global_banning > 0) {
  3455. if ($ev_bantime eq "permanently") {
  3456. &printEvent("BAN", "Hide player!");
  3457. &execNonQuery("UPDATE hlstats_Players SET hideranking=2 WHERE playerId IN (SELECT playerId FROM hlstats_PlayerUniqueIds WHERE uniqueId='".&quoteSQL($p_steamid)."')");
  3458. $ev_bantime = 0;
  3459. }
  3460. my $pl_steamid = $playerinfo->{"plain_uniqueid"};
  3461. while (my($addr, $server) = each(%g_servers)) {
  3462. if ($addr ne $s_addr) {
  3463. &printEvent("BAN", "Global banning on ".$addr);
  3464. $server->{ignore_nextban}->{$p_steamid} = 1;
  3465. $server->dorcon("banid ".$ev_bantime." $pl_steamid");
  3466. $server->dorcon("writeid");
  3467. }
  3468. }
  3469. }
  3470. }
  3471. }
  3472. } else {
  3473. &printEvent("BAN", "No playerinfo");
  3474. }
  3475. &printEvent("BAN", $s_output);
  3476. } else {
  3477. # Unrecognized event
  3478. # HELLRAISER
  3479. if ($g_debug > 1) {
  3480. &printEvent(999, "UNRECOGNIZED: " . $s_output);
  3481. }
  3482. }
  3483.  
  3484. if (!$g_stdin && defined($g_servers{$s_addr}) && $ev_daemontime > $g_servers{$s_addr}->{next_plyr_flush}) {
  3485. &printEvent("MYSQL", "Flushing player updates to database...",1);
  3486. if ($g_servers{$s_addr}->{"srv_players"}) {
  3487. while ( my($pl, $player) = each(%{$g_servers{$s_addr}->{"srv_players"}}) ) {
  3488. if ($player->{needsupdate}) {
  3489. $player->flushDB();
  3490. }
  3491. }
  3492. }
  3493. &printEvent("MYSQL", "Flushing player updates to database is complete.",1);
  3494.  
  3495. $g_servers{$s_addr}->{next_plyr_flush} = $ev_daemontime + 15+int(rand(15));
  3496. }
  3497.  
  3498. if (($g_stdin == 0) && defined($g_servers{$s_addr})) {
  3499. $s_lines = $g_servers{$s_addr}->{lines};
  3500. # get ping from players
  3501. if ($s_lines % 1000 == 0) {
  3502. $g_servers{$s_addr}->update_players_pings();
  3503. }
  3504.  
  3505. if ($g_servers{$s_addr}->{show_stats} == 1) {
  3506. # show stats
  3507. if ($s_lines % 2500 == 40) {
  3508. $g_servers{$s_addr}->dostats();
  3509. }
  3510. }
  3511.  
  3512. if ($s_lines > 500000) {
  3513. $g_servers{$s_addr}->set("lines", 0);
  3514. } else {
  3515. $g_servers{$s_addr}->increment("lines");
  3516. }
  3517. }
  3518. } else {
  3519. $s_addr = "";
  3520. }
  3521.  
  3522. while( my($server) = each(%g_servers))
  3523. {
  3524. if($g_servers{$server}->{next_timeout}<$ev_daemontime)
  3525. {
  3526. #print "checking $ev_unixtime\n";
  3527. # Clean up
  3528. # look
  3529. if($g_servers{$server}->{"srv_players"})
  3530. {
  3531. my %players_temp=%{$g_servers{$server}->{"srv_players"}};
  3532. while ( my($pl, $player) = each(%players_temp) ) {
  3533. my $timeout = 250; # 250;
  3534. if ($g_mode eq "LAN") {
  3535. $timeout = $timeout * 2;
  3536. }
  3537. #if (($player->{is_bot} == 0) && (($ev_unixtime - $player->get("timestamp")) > ($timeout - 20))) {
  3538. # &printEvent(400, $player->getInfoString()." is ".($ev_unixtime - $player->get("timestamp"))."s idle");
  3539. #}
  3540. my $userid = $player->{userid};
  3541. my $uniqueid = $player->{uniqueid};
  3542. if ( ($ev_daemontime - $player->{timestamp}) > $timeout ) {
  3543. #printf("%s - %s %s\n",$server, $player->{userid}, $player->{uniqueid});
  3544. # we delete any player who is inactive for over $timeout sec
  3545. # - they probably disconnected silently somehow.
  3546. if (($player->{is_bot} == 0) || ($g_stdin)) {
  3547. &printEvent(400, "Auto-disconnecting " . $player->getInfoString() ." for idling (" . ($ev_daemontime - $player->{timestamp}) . " sec) on server (".$server.")");
  3548. removePlayer($server, $userid, $uniqueid);
  3549. }
  3550. }
  3551. }
  3552. }
  3553. $g_servers{$server}->{next_timeout}=$ev_daemontime+30+rand(30);
  3554. }
  3555.  
  3556. if ($ev_daemontime > $g_servers{$server}->{next_flush}
  3557. && $g_servers{$server}->{needsupdate}
  3558. )
  3559. {
  3560. $g_servers{$server}->flushDB();
  3561. $g_servers{$server}->{next_flush} = $ev_daemontime + 20;
  3562. }
  3563. }
  3564.  
  3565. while ( my($pl, $player) = each(%g_preconnect) ) {
  3566. my $timeout = 600;
  3567. if ( ($ev_unixtime - $player->{"timestamp"}) > $timeout ) {
  3568. &printEvent(401, "Clearing pre-connect entry with key ".$pl);
  3569. delete($g_preconnect{$pl});
  3570. }
  3571. }
  3572.  
  3573. if ($g_stdin == 0) {
  3574. # Track the Trend
  3575. if ($g_track_stats_trend > 0) {
  3576. track_hlstats_trend();
  3577. }
  3578. while (my($addr, $server) = each(%g_servers)) {
  3579. if (defined($server)) {
  3580. $server->track_server_load();
  3581. }
  3582. }
  3583.  
  3584. while( my($table) = each(%g_eventtable_data))
  3585. {
  3586. if ($g_eventtable_data{$table}{lastflush} + 30 < $ev_daemontime)
  3587. {
  3588. flushEventTable($table);
  3589. }
  3590. }
  3591.  
  3592. if (defined($g_servers{$s_addr})) {
  3593. if (($g_servers{$s_addr}->{map} eq "") && (($timeout > 0) && ($timeout % 60 == 0))) {
  3594. $g_servers{$s_addr}->get_map();
  3595. }
  3596. }
  3597. }
  3598.  
  3599. $c++;
  3600. $c = 1 if ($c > 500000);
  3601. $import_logs_count++ if ($g_stdin);
  3602. }
  3603.  
  3604. $end_time = time();
  3605. if ($g_stdin) {
  3606. if ($import_logs_count > 0) {
  3607. print "\n";
  3608. }
  3609.  
  3610. &flushAll(1);
  3611. &execNonQuery("UPDATE hlstats_Players SET last_event=UNIX_TIMESTAMP();");
  3612. &printEvent("IMPORT", "Import of log file complete. Scanned ".$import_logs_count." lines in ".($end_time-$start_time)." seconds", 1, 1);
  3613. }
  3614.  
  3615. sub INT_handler
  3616. {
  3617. print "SIGINT received. Flushing data and shutting down...\n";
  3618. flushAll(1);
  3619. exit(0);
  3620. }
  3621.  
  3622. sub HUP_handler
  3623. {
  3624. print "SIGHUP received. Flushing data and reloading configuration...\n";
  3625. &reloadConfiguration;
  3626. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement