Location: PHPKode > projects > Kbot > kbot_1.3/rconBots/includes/nsRconBot.class.php
<?PHP 

/**
 *
 * $Id: nsRconBot.class.php 72 2005-12-29 08:24:06Z khaless $
 * $Revision: 72 $
 * $Author: khaless $
 * $Date: 2005-12-29 19:24:06 +1100 (Thu, 29 Dec 2005) $
 * 
 * Copyright (c) 2005 Mathew Rodley <hide@address.com>
 * 
 * Full GPL License: <http://www.gnu.org/licenses/gpl.txt>
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

class nsRconBot {
	public $dataArray;
	public $coreFuncs;
	public $matchAdminSteamID;
	public $pugLive;
	public $halfCount;
	public $roundsPlaye;
	public $hitTheCount;
	public $roundsToBePlayed;
	public $countTillEnd;
	public $endingProc;
	public $playerCount;
	public $playerInfoArray;
	public $positionTranslate;

	function nsRconBot($dataArray, $coreFuncs)
	{
		$this->dataArray = &$dataArray;
		$this->coreFuncs = &$coreFuncs;
		$this->matchAdminSteamID = NULL;
		$this->pugLive = TRUE; // pug is always LIVE due to Tournament Mode :)
		$this->halfCount = 0;
		$this->roundsPlayed = 0;
		$this->hitTheCount = FALSE;
		$this->roundsToBePlayed = $this->dataArray['rounds'];
		$this->countTillEnd = 0;
		$this->endingProc = FALSE;
		$this->playerCount = 0;
		$this->playerInfoArray = array();
		$this->positionTranslate = array(); // we need this because damage inflected in NS doesnt supply steam ID, rather somthing quite random :S


		// process the team string so we can display it adhearing to rcon say length limits.
		$teams = $this->dataArray['teams'];
		$members = $this->dataArray['teamMembers'];

		$this->teamRemindLines = array();
		foreach($teams as $index => $team)
		{
			array_push($this->teamRemindLines, $team.':');
			$lineString = '';
			foreach($members[$index] as $member)
			{
				if(strlen($lineString . $member) > 64)
				{
					array_push($this->teamRemindLines, substr($lineString,0,-2));
					$lineString = '';
				}
				$lineString .= $member .', ';
			}
			if($lineString != '') array_push($this->teamRemindLines, substr($lineString,0,-2));
		}
	}

	// use this to get their IP to report it to connect2
	function connection1($data)
	{
		$this->tempIPHold[$data[2]] = $data[4];
	}

	// handle when they connect to the server...
	function connection2($data)
	{
		//$this->coreFuncs->sendToIRC('MSG:'.chr(3).'5'. $data[1] .' has connected');
		$this->playerCount++;
		// add this name to the player info array
		$this->playerInfoArray[$data[3]]['name'] = $data[1];
		$this->playerInfoArray[$data[3]]['ip'] = $this->tempIPHold[$data[2]];
		$this->positionTranslate[$data[2]] = $data[3];

		if($this->playerCount >= ($this->dataArray['playerCount'] - 2) && $this->hitTheCount !== TRUE && $this->countTillEnd == 0)
		{
			$this->hitTheCount = TRUE;
		}
	}

	function changeName($data)
	{
		// account for a change of name and update the player info array
		//DEFINE('CHANGE_NAME',           '"(.*)<([0-9]+)><(.*)><([A-Za-z]{0,20})>" changed name to "(.*)"');
		if($this->playerInfoArray[$data[3]]) $this->playerInfoArray[$data[3]]['name'] = $data[5];
	}

	function disconnection($data)
	{
		$this->coreFuncs->sendToIRC('MSG:'.chr(3).'5* '. $data[1] .' has disconnected');
		$this->playerCount--;
		// unset the player info array for this player
		unset($this->playerInfoArray[$data[3]]);
		// organising val 2 get even teams...
		$val = ceil(2*$this->dataArray['playerCount']/3);
		if($val&1 == 1) $val++;
		// ok now we check for that 2/3 factor as we know the server has peaked with players....
		// make sure active players is not a blank string..
		if($this->playerCount < $val && $this->hitTheCount == TRUE && $this->countTillEnd == 0 && $this->endingProcess !== TRUE)
		{
			$this->coreFuncs->sendToIRC('MSG:'.chr(3).'05* PUG Ending due to insufficient active players in server. ['.$this->playerCount.' Active, '.$val.' Needed]');
			// give report on all players.
			$this->playerReport();
			$this->coreFuncs->rconCommand("say PUG Ending due to insufficient active players in server.");
			// ok we want to end the game here..
			$this->endingProcess = TRUE;
			$this->coreFuncs->sendToIRC('CMD:ENDPUG');
		}
		elseif ($this->playerCount >= $val && $this->playerCount < $this->dataArray['playerCount'])
		{
			$needMore = $this->dataArray['playerCount'] - $this->playerCount;
			$this->coreFuncs->sendToIRC('MSG:'.chr(3).'5* '.chr(31).'Player Left. Need '.$needMore.' more. Password is '.$this->dataArray['sv_password']);
		}
	}

	// handle when they change teams...
	function roleSelection($data)

	{

		//22:35:35 og L 01/03/2005 - 22:38:19: "s5. intrik<152><STEAM_0:0:46320><alien1team>" changed role to "skulk"

		if($data[4] == 'alien1team')
		{
			switch($data[5])
			{
				// leave this out ;) because whent they spawn they become a skulk!
				//case 'skulk':
				//	$name = 'Skulk';
				//break;
				case 'onos':
				if($this->playerInfoArray[$data[3]]['role2'] != 'onos') $name = 'Onos';
				break;
				case 'lerk':
				if($this->playerInfoArray[$data[3]]['role2'] != 'lerk') $name = 'Lerk';
				break;
				case 'gorge':
				if($this->playerInfoArray[$data[3]]['role2'] != 'gorge') $name = 'Gorge';
				break;
				case 'fade':
				if($this->playerInfoArray[$data[3]]['role2'] != 'fade') $name = 'Fade';
				break;
			}
			if($name) $this->coreFuncs->sendToIRC('MSG:'.chr(3).'5* '. chr(3).'04' . $data[1] . chr(3) .'05 gestated into a '.$data[5]);
		}
		elseif($data[4] == 'marine1team')
		{
			switch($data[5])
			{
				// None of this thanks :), unless they are hopping out of the chair.
				case 'soldier':
				if($this->playerInfoArray[$data[3]]['role'] == 'commander')
				$name = 'exited the Comm. Chair';
				break;
				case 'commander':
				$name = 'entered the Comm. Chair';
				break;
			}
			if($name) $this->coreFuncs->sendToIRC('MSG:'.chr(3).'5* '. chr(3).'12' . $data[1] . chr(3) .'05 has '.$name);
		}

		// set team
		$this->playerInfoArray[$data[3]]['team'] = $data[4];
		$this->playerInfoArray[$data[3]]['role2'] = $this->playerInfoArray[$data[3]]['role'];
		$this->playerInfoArray[$data[3]]['role'] = $data[5];

	}

	function damageCount($data)
	{
		// only track damage if PUG is live.
		if($this->pugLive !== TRUE) return;

		// this will be the amount of damage done.
		$damage = $data[10];
		$attacker = $this->positionTranslate[$data[2]];
		$target = $this->positionTranslate[$data[6]];

		$this->playerInfoArray[$attacker]['dmgInflicted'] += $damage;
		$this->playerInfoArray[$target]['dmgRecived']   += $damage;
	}

	function kill($data)
	{
		// only track kills if the PUG is live
		if($this->pugLive !== TRUE) return;

		// keep track of kill's / Deaths here...
		$this->playerInfoArray[$data[3]]['kills']++;
		$this->playerInfoArray[$data[7]]['deaths']++;


		$gunArray = array(
		// marine
		'grenade'  => 'a Grenade Launcher',
		'machinegun'=> 'a LMG',
		'pistol' => 'a Pistol',
		'shotgun' => 'a Shotgun',
		'heavymachinegun' => 'a HMG',
		'knife'	  => 'a Knife',
		'item_mine' => 'a Mine',
		//alien
		'bitegun' => 'Chomp',
		'leap' => 'Leap',
		'parasite' => 'Parasite',
		'divinewind' => 'Xeno',
		'bite2gun' => 'Bite',
		'sporegunprojectile' => 'Spores',
		'swipe' => 'Swipe',
		'acidrocket' => 'Acid Rocket',
		'claws' => 'Gore',
		'devour' => 'Devour',
		'charge' => 'Charge'
		);

		// do TK stuff here.
		if($data[4] == $data[8]) $tk = TRUE;
		else $tk = FALSE;


		if($data[4] == 'marine1team') $killer = chr(3).'12'.$data[1] . chr(3).'05';
		elseif($data[4] == 'alien1team') $killer = chr(3).'04'.$data[1] . chr(3).'05';


		if($data[8] == 'marine1team') $target = chr(3).'12'.$data[5] . chr(3).'5';
		elseif($data[8] == 'alien1team') $target = chr(3).'04'.$data[5] . chr(3).'05';

		if(!$tk)
		{
			// find gun.
			if(!$gun = $gunArray[$data[9]]) $gun = $data[9];

			if($data[9] == 'knife') $this->coreFuncs->sendToIRC('MSG:'.chr(3).'5* WTF!!! '.$killer.' KNIFED '.$target.', How Humiliating');
			else $this->coreFuncs->sendToIRC('MSG:'.chr(3).'5* '.$killer .' ('.$this->playerInfoArray[$data[3]]['role'].') killed '. $target.' ('.$this->playerInfoArray[$data[7]]['role'].') with '.$gun);

		}
		else
		{
			// ok count the Team Kills! give them 3
			$this->teamKillCount[trim(strtoupper($data[3]))]++;
			if($this->teamKillCount[trim(strtoupper($data[3]))] == 1)
			{
				$this->coreFuncs->rconCommand('Say '.$data[1].' Do Not Team Kill!');
				$this->coreFuncs->rconCommand('If you do this three more you will be banned for 2 days');
			}
			if($this->teamKillCount[trim(strtoupper($data[3]))] == 2)
			{
				$this->coreFuncs->rconCommand('Say '.$data[1].' Do Not Team Kill!');
				$this->coreFuncs->rconCommand('If you do this twice more you will be banned for 2 days');
			}
			if($this->teamKillCount[trim(strtoupper($data[3]))] == 3)
			{
				$this->coreFuncs->rconCommand('Say '.$data[1].' Do Not Team Kill!');
				$this->coreFuncs->rconCommand('Last warning. If you do this Once more you will be banned for 2 days');
			}
			if($this->teamKillCount[trim(strtoupper($data[3]))] == 4)
			{
				$additional = chr(2). ' Player has now been gifted a 2 day BAN!!!';
				$chan = unserialize($this->dataArray['msgTargetChannels']);
				$chan = $chan[0];
				// 2 day ban.
				$this->coreFuncs->rconCommand('banid 2880 '.$data[3]);
				$this->coreFuncs->rconCommand('writeid');
				$this->coreFuncs->rconCommand('kick # '.$data[3]);
				$host = gethostbyaddr($this->playerInfoArray[$data[3]]['ip']);
				$this->coreFuncs->sendToIRC('CMDBAN:'.serialize(array('host' => '*!*@'.$host, 'length' => 172800, 'time' => time(), 'channel' => $chan)));
			}

			$this->coreFuncs->sendToIRC('MSG:'.chr(3).'5* '.$killer .' Team Killed '. $target . $additional);
		}
	}

	function playerAction($data)
	{
		// could have build and research things here.. but NO.!

		// only find the action if the PUG is live.
		if($this->pugLive !== TRUE) return;

		// find the action...
		switch($data[5])
		{
		}
		// for finding actions // $this->coreFuncs->sendToIRC('MSG:'.serialize($data[5]));
	}

	function say($data)
	{
		// get the details.
		$name    = 	$data[1];
		$uid     =	$data[2];
		$steamID = 	$data[3];
		$team    = 	$data[4];
		$message = 	$data[5];
		if(!$this->matchAdminSteamID)
		{
			if($message == '!admin '.$this->dataArray['adminPassword'])
			{
				$this->matchAdminSteamID = $steamID;
				$this->coreFuncs->rconCommand('say PUG Admin given to '.$name.'');
			}
		}
		elseif($steamID == $this->matchAdminSteamID)
		{
			//admin access here
			if(substr($message,0,4) == '!irc') $this->coreFuncs->sendToIRC('MSG:'.chr(3).'5* '.chr(31).'Message From Ingame: '. substr($message,5));
			if(substr($message,0,9) == '!password')
			{
				// change pass
				$this->dataArray['sv_password'] = substr($message,10);
				$this->coreFuncs->rconCommand('say '.$name.' Has changed the server\'s password to '.$this->dataArray['sv_password']);
				$this->coreFuncs->rconCommand('sv_password "'.$this->dataArray['sv_password'].'"');
				$this->coreFuncs->sendToIRC('PASSWD:'.$this->dataArray['sv_password']);
			}
			else if($message == '!joinpug') $this->coreFuncs->sendToIRC('MSG:'.chr(3).'5* Could all players in the PUG please join the server.');
			else if(substr($message,0,5) == '!kick') $this->coreFuncs->rconCommand('kick "'.substr($message,6).'"');
			else if($message == '!killpug' || $message == '!endpug')
			{
				$this->coreFuncs->sendToIRC('MSG:'.chr(3).'5* PUG Ending due to admin command');
				$this->coreFuncs->rconCommand("say PUG Ending due to admin command.");
				// ok we want to end the game here..
				$this->endingProcess = TRUE;

				// give report on all players.
				$this->playerReport();

				$this->coreFuncs->sendToIRC('CMD:ENDPUG');
			}
			else if(substr($message,0,4) == '!say')
			{
				$this->coreFuncs->rconCommand('say PUG Message From '.$name.':');
				$this->coreFuncs->rconCommand('say '.substr($message,5));
			}
			else if(substr($message,0,4) == '!map')
			{
				// ok check that this is a valid map...
				if(in_array(substr($message,5),$this->dataArray['maps']))
				{
					// ok lets change the map
					$this->coreFuncs->rconCommand('say '.$name.' is changing map to '.substr($message,5));
					$this->coreFuncs->rconCommand('changelevel '.substr($message,5));
					sleep(2);
					// make sure the password is set..
					$this->coreFuncs->rconCommand('sv_password "'.$this->dataArray['sv_password'].'"');

					// ok reset rounds and everything!
					$this->pugLive = FALSE;
					$this->halfCount = 0;
					$this->roundsPlayed = 0;

					// set hit the count to false, as the server has to fill up again
					$this->hitTheCount = FALSE;

					// also exec to irc making the bot change the topic with the new map and
					// also the info in the variables..
					// and also notify the ppl in channel of the change :)
					$this->coreFuncs->sendToIRC('CMD:CNGMAP:'.trim(substr($message,5)));

				}
				else $this->coreFuncs->rconCommand('say '.substr($message,5) .' Is an invalid map!');
			}
			else if(substr($message,0,4) == '!get')
			{
				if ($this->playerCount >= $val && $this->playerCount < $this->dataArray['playerCount'])
				{
					$needMore = $this->dataArray['playerCount'] - $this->playerCount;
					$this->coreFuncs->sendToIRC('MSG:'.chr(3).'5* '.chr(31).'Player Left. Need '.$needMore.' more. Password is '.$this->dataArray['sv_password']);
					$this->coreFuncs->rconCommand('say "Requested '.$needMore.' more player'.(($needMore > 1) ? 's':'').' on irc."');
				}
				else $this->coreFuncs->rconCommand('say "PUG is full, not requesting players."');
			}
		}

		// this is the voteban command!
		if(substr($message,0,8) == '!voteban')
		{
			$tmp = trim(strtoupper(substr($message,9))); // what the user supplied.
			// if this is a steam id (STEAM_0:1:1234567)
			if(preg_match('/^STEAM_[0-9]{1}:[0-9]{1}:[0-9]+$/', $tmp))
			$voteTargetsteamID = $tmp;
			else {
				// try and find them.
				// if we come out with one match we can match them.. otherwise complain they need to be more specific.
				$matched = 0;
				foreach($this->playerInfoArray as $steamID => $playerData)
				{
					if(stristr($playerData['name'], $tmp))
					{
						// found a match
						$voteTargetsteamID = $steamID;
						$matched++;
					}
				}
				if($matched == 0)
				{
					$this->coreFuncs->rconCommand('say Sorry no match found for that name');
					return;
				} else if($matched > 1)
				{
					$this->coreFuncs->rconCommand('say Sorry you will have to be more specific');
					return;
				}
			}
			$voterSteamID = $steamID;
			// general purpose voting commands...
			if(!$this->voters[$voteTargetsteamID][$voterSteamID] && $this->playerInfoArray[$voteTargetsteamID])
			{
				// we have it, add a vote to this STEAM ID and IP
				$this->votes[$voteTargetsteamID]++;
				$this->voters[$voteTargetsteamID][$voterSteamID] = TRUE;
				$this->coreFuncs->rconCommand('say '.$this->votes[$voteTargetsteamID].'/'.(ceil($this->dataArray['playerCount'] / 2)) .' votes lodged for '.$this->playerInfoArray[$voteTargetsteamID]['name']);
				if($this->votes[$voteTargetsteamID] >= ceil($this->dataArray['playerCount'] / 2))
				{
					// find channel.
					$chan = unserialize($this->dataArray['msgTargetChannels']);
					$chan = $chan[0];
					// 2 day ban.
					$this->coreFuncs->rconCommand('banid 2880 '.$voteTargetsteamID);
					$this->coreFuncs->rconCommand('writeid');
					$this->coreFuncs->rconCommand('kick # '.$voteTargetsteamID);
					$host = gethostbyaddr($this->playerInfoArray[$voteTargetsteamID]['ip']);
					$this->coreFuncs->sendToIRC('CMDBAN:'.serialize(array('host' => '*!*@'.$host, 'length' => 172800, 'time' => time(), 'channel' => $chan)));
					// ok ban on server, kick, and irc
					//array('host' => '*!*@'.$data->host, 'time' => time(), 'channel' => $data->channel)
				}
			}
		}
		else if($message == '!teams')
		{
			// display the teams CT's and T's
			foreach($this->teamRemindLines as $element)
			{
				$this->coreFuncs->rconCommand('say '.$element);
			}
		}
	}

	function roundEnd($data)
	{
		// ok only go further if we are in a LIVE half :)
		if($this->pugLive === TRUE)
		{
			$this->halfCount++;
			$this->roundsPlayed++;

			$this->coreFuncs->sendToIRC('MSG:'.chr(3) . '5* Round '. $this->roundsPlayed .' out of '.($this->roundsToBePlayed).' Completed: '.ucfirst($data[4]).'s ('.$data[6].' players) Beat '.ucfirst($data[5]).'s ('.$data[7].' players) in '.$data[3].' on '.$data[1]);

			// in the rounds played for the BOT's stats :)
			$this->coreFuncs->sendToIRC('CMD:RNDREP');

			// check if that was final round.
			if($this->roundsPlayed >= $this->roundsToBePlayed && $this->halfCount >= 2 && $this->endingProc !== TRUE)
			{
				$this->countTillEnd = time();
				$this->endingProc = TRUE;
				$this->pugLive = FALSE;
				// increment over 3 so we cant !lo3
				$this->halfCount++;

				// server will end in 30 seconds :o
				$this->coreFuncs->rconCommand("say PUG Finished, Closing Down in 30 seconds...");
			}
			// give report on all players.
			$this->playerReport();
		}
	}
	function playerReport()
	{
		// this array will contain the maximum length of each field for the purpose of tabbing.
		$maxLengthArray = array();

		foreach($this->playerInfoArray as $steamID => $array)
		{
			$name = $array['name'];
			$team = $array['team'];
			$kills = $array['kills'] ? $array['kills'] : 0;
			$deaths = $array['deaths'] ? $array['deaths'] : 0;
			$damageInflicted = $array['dmgInflicted'] ? $array['dmgInflicted'] : 0;
			$damageRecived   = $array['dmgRecived'] ? $array['dmgRecived'] : 0;
			if($deaths > 0) $ratio = round($kills/$deaths,2).':1';
			else $ratio = $kills.':0';


			// now formulate this into a nice block.
			$presentationArray[$steamID]['team'] = $array['team'];

			$presentationArray[$steamID]['name'] = $name;
			if(strlen($name) > $maxLengthArray['name']) $maxLengthArray['name'] = strlen($name);

			$presentationArray[$steamID]['killsDeathsRatioStr'] = $kills.'-'.$deaths.' ('.$ratio.')';

			if(strlen($presentationArray[$steamID]['killsDeathsRatioStr']) > $maxLengthArray['killsDeathsRatioStr'])
			$maxLengthArray['killsDeathsRatioStr'] = strlen($presentationArray[$steamID]['killsDeathsRatioStr']);
			$presentationArray[$steamID]['damage'] = $damageInflicted.'-'.$damageRecived;
			if(strlen($presentationArray[$steamID]['damage']) > $maxLengthArray['damage'])
			$maxLengthArray['damage'] = strlen($presentationArray[$steamID]['damage']);
		}

		// make it.
		$this->coreFuncs->sendToIRC('MSG:'.chr(3).'5* Player Report:');
		foreach($presentationArray as $array)
		{
			if($array['team'] == 'marine1team') $colour = chr(3).'12';
			elseif($array['team'] == 'alien1team') $colour = chr(3).'04';
			else $colour = chr(3).'01';

			$string = chr(3).'5* '. $colour .  sprintf('%-'.($maxLengthArray['name']).'s', $array['name']) . chr(3).'05 * ';
			$string .= 'Score(K/D): '.sprintf('%-'.$maxLengthArray['killsDeathsRatioStr'].'s',  $array['killsDeathsRatioStr']) . chr(3).'05 * ';
			$string .= 'Damage (Inflicted/Received): '.sprintf('%-'.$maxLengthArray['damage'].'s',  $array['damage']) . chr(3).'05 *';
			// output string to IRC.
			$this->coreFuncs->sendToIRC('MSG:'.$string);
		}
	}

	function resetPlayerInfoArray()
	{
		$keys = array_keys($this->playerInfoArray);
		for($i=0;$i<count($keys);$i++)
		{
			$this->playerInfoArray[$keys[$i]]['kills'] = 0;
			$this->playerInfoArray[$keys[$i]]['deaths'] = 0;
			$this->playerInfoArray[$keys[$i]]['dmgInflicted'] = 0;
			$this->playerInfoArray[$keys[$i]]['dmgRecived'] = 0;
		}
	}
}
?>
Return current item: Kbot