/** * LVP's central database contains a shed load of information. Using * this method, we'll fetch whatever we may need from there and store it * in some variable until it's needed. At the moment this method only * fetches the crew status of a user. * * @param integer $nProfileId The unique ID of the user in the database. * @return boolean */ public function fetchInformation($nProfileId) { $this->m_nProfileId = $nProfileId; $db = LVPDatabase::getInstance(); $pResult = $db->query('SELECT u.level, u.is_developer FROM lvp_mainserver.users u WHERE u.user_id = ' . (int) $nProfileId); if ($pResult !== false && $pResult->num_rows != 0) { $aInformation = $pResult->fetch_assoc(); $this['Level'] = LVPCrewHandler::translateGamemodeLevel($aInformation['level'], $aInformation['is_developer']); $pResult->free(); } }
/** * Connects to the database with configuration defined from the config.php and sets correct modes. */ private function connectToDatabase() { $this->_pPDO = LVPDatabase::getInstance(); $this->_pPDO->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_WARNING); }
/** * Retrieves all the players from the gameserver using an UDP query and * sets up all objects for every player. * * @return boolean */ public function syncPlayers() { try { $pQuery = new QuerySAMPServer(LVP::GAMESERVER_IP, LVP::GAMESERVER_PORT, 0, 500000); $aPlayers = $pQuery->getDetailedPlayers(); $nPlayers = count($aPlayers); if ($nPlayers == 0) { // Sure? $aPlayers = $pQuery->getDetailedPlayers(); $nPlayers = count($aPlayers); } } catch (QueryServerException $pException) { return false; } $db = LVPDatabase::getInstance(); $oldPlayers = $this->m_aPlayers; $this->clearPlayers(); foreach ($aPlayers as $aPlayerInfo) { $nId = $aPlayerInfo['PlayerID']; // Check if we know the player already. if (isset($oldPlayers[$nId])) { // Yep we do. Put him back. $this->m_aPlayers[$nId] = $oldPlayers[$nId]; } $this->setPlayerKey($nId, 'Nickname', $aPlayerInfo['Nickname']); if ($this->getPlayerKey($nId, 'JoinTime') === 0) { $this->setPlayerKey($nId, 'JoinTime', time()); } if ($this->getPlayerKey($nId, 'Level') === 0) { $pResult = $db->query('SELECT u.user_id FROM lvp_mainserver.users_nickname n LEFT JOIN lvp_mainserver.users u ON u.user_id = n.user_id LEFT JOIN lvp_mainserver.users_mutable m ON m.user_id = u.user_id WHERE n.nickname = "' . $db->real_escape_string($aPlayerInfo['Nickname']) . '"'); if ($pResult !== false && $pResult->num_rows > 0) { list($nProfileId) = $pResult->fetch_row(); $pResult->free(); $this->getPlayer($nId)->fetchInformation($nProfileId); } } } return true; }
/** * Probably one of the most and most important commands of the whole bot * is handled in this quite large method. This command will determine * what kind of information is requested first. If given an IP or an IP * range, the command will return all nicknames found with that IP or in * that IP range. If given a nickname, it will simply return all the IPs * found with that nickname. Some additional parsing and adjustments in * the query make the output easily readable and understandable. * * @param integer $nLevel The level we're operating at. * @param string $sTrigger The trigger that set this in motion. * @param string $sParams All of the text following the trigger. * @param array $aParams Same as above, except split into an array. * @return string * * @todo Holy shit, future me, please refactor this. This method is way too loooooooooooooooooooooooooooooooong! */ private function handleIpInfo($nLevel, $sTrigger, $sParams, $aParams) { if ($sParams !== null) { $sParams = Util::stripFormat($sParams); } if ($sParams === null || $sParams === '' || count($aParams) > 1) { echo $sTrigger . ' IP // ' . $sTrigger . ' Name // ' . $sTrigger . ' ID'; return LVPCommand::OUTPUT_USAGE; } $bForceName = false; if (strpos($sTrigger, 'name') !== false) { $bForceName = true; } if ($this->isValidId($sParams)) { $pPlayer = $this->m_pModule['Players']->getPlayer($sParams); if ($pPlayer == null) { echo 'ID not found.'; return LVPCommand::OUTPUT_ERROR; } $sParams = $pPlayer['Nickname']; } $sOrigParams = $sParams; $nType = self::TYPE_NICKNAME; if (!$bForceName) { if ($this->isValidIpV6($sParams)) { echo 'Sorry, no IPv6 support.'; return LVPCommand::OUTPUT_ERROR; } else { if ($this->isValidIp($sParams)) { $nType = self::TYPE_IP; } else { if ($this->isRangedIp($sParams)) { $nType = self::TYPE_RANGED_IP; } } } } if ($nLevel < LVP::LEVEL_MANAGEMENT && ($nType == self::TYPE_NICKNAME || $nType == self::TYPE_IP)) { if ($nType == self::TYPE_NICKNAME) { $sTable = 'samp_address_hide_nickname'; $sField = 'nickname'; $sBindParam = 's'; $sQueryParam = $sParams; } else { if ($nType == self::TYPE_IP) { $sTable = 'samp_address_hide_ip'; $sField = 'ip_address'; $sBindParam = 'i'; $sQueryParam = $this->ip2long($sParams); } else { // ? echo 'Unknown error occurred.'; return LVPCommand::OUTPUT_ERROR; } } $pStatement = LVPDatabase::getInstance()->prepare('SELECT display_string FROM ' . $sTable . ' WHERE ' . $sField . ' = ?'); if ($pStatement === false) { echo 'There seems to be a problem with the database. Try again later.'; return LVPCommand::OUTPUT_ERROR; } $pStatement->bind_param($sBindParam, $sQueryParam); $pStatement->bind_result($sFakeString); $pStatement->execute(); $pStatement->store_result(); if ($pStatement->num_rows != 0) { $pStatement->fetch(); // Hide it. if ($sFakeString == null) { echo ModuleBase::COLOUR_TEAL . '* No results found.'; return LVPCommand::OUTPUT_NORMAL; } else { echo ModuleBase::COLOUR_TEAL . '* Result with "' . $sParams . '"'; echo ModuleBase::CLEAR . ': ' . $sFakeString; return LVPCommand::OUTPUT_NORMAL; } } $pStatement->close(); } $pDatabase = LVPDatabase::getInstance(); $sQuery = 'SELECT nickname, GROUP_CONCAT(DISTINCT ip_address) ip_address, COUNT(*) num, MAX(join_date) join_date, GROUP_CONCAT(DISTINCT country) country, GROUP_CONCAT(DISTINCT user_id) user_id FROM samp_addresses WHERE '; switch ($nType) { case self::TYPE_IP: $sQuery .= 'ip_address = ' . $this->ip2long($sParams); break; case self::TYPE_RANGED_IP: $nMinIp = $this->ip2long(str_replace('*', '0', $sParams)); $nMaxIp = $this->ip2long(str_replace('*', '255', $sParams)); $sQuery .= 'ip_address >= ' . $nMinIp . ' AND ip_address <= ' . $nMaxIp; break; case self::TYPE_NICKNAME: $sQuery .= 'nickname = "' . $pDatabase->real_escape_string($sParams) . '"'; break; } if ($nLevel < LVP::LEVEL_MANAGEMENT && ($nType == self::TYPE_NICKNAME || $nType == self::TYPE_IP)) { if ($nType == self::TYPE_NICKNAME) { $sTable = 'samp_address_hide_ip'; $sField = 'ip_address'; } else { if ($nType == self::TYPE_IP) { $sTable = 'samp_address_hide_nickname'; $sField = 'nickname'; } } $sQuery .= ' AND ' . $sField . ' NOT IN( SELECT ' . $sField . ' FROM ' . $sTable . ' )'; } $sQuery .= ' GROUP BY '; switch ($nType) { case self::TYPE_IP: case self::TYPE_RANGED_IP: $sQuery .= 'nickname'; break; case self::TYPE_NICKNAME: $sQuery .= 'ip_address'; break; } $sQuery .= ' ORDER BY join_date DESC LIMIT 15'; $pResult = $pDatabase->query($sQuery); if ($pResult === false) { echo 'There seems to be a problem with the database. Try again later.'; return LVPCommand::OUTPUT_ERROR; } if ($pResult->num_rows == 0) { echo ModuleBase::COLOUR_TEAL . '* No results found.'; return LVPCommand::OUTPUT_NORMAL; } echo ModuleBase::COLOUR_TEAL . '* '; switch ($nType) { case self::TYPE_IP: echo 'Names from IP'; break; case self::TYPE_RANGED_IP: echo 'Names in IP range'; break; case self::TYPE_NICKNAME: echo 'IPs with name'; break; } echo ' "' . $sOrigParams . '"' . ModuleBase::CLEAR . ': '; $nUnderlineDiff = 86400 * 3; $nTime = time(); $bFirst = true; $pLocation = null; // Inited when needed. while (($aRow = $pResult->fetch_assoc()) !== null) { if ($bFirst) { $bFirst = false; } else { echo ', '; } $bUnderline = false; if ($nTime - strtotime($aRow['join_date']) <= $nUnderlineDiff) { $bUnderline = true; echo ModuleBase::UNDERLINE; } switch ($nType) { case self::TYPE_NICKNAME: echo long2ip($aRow['ip_address']); break; case self::TYPE_IP: case self::TYPE_RANGED_IP: echo $aRow['nickname']; break; } if ($bUnderline) { // Reset echo ModuleBase::UNDERLINE; } if ($aRow['country'] == '') { if ($pLocation == null) { $pLocation = new Location(); } // Not yet retrieved, do it ourselves. $aInfo = $pLocation->lookup(long2ip(explode(',', $aRow['ip_address'])[0])); $aRow['country'] = $aInfo['country_2']; } if ($aRow['num'] > 1 || $aRow['country'] != '--' && $aRow['country'] != '') { // Darker color so that other information stays readable. echo ModuleBase::COLOUR_DARKGREY; if ($aRow['num'] > 1) { echo ' (x' . $aRow['num'] . ')'; } if ($aRow['country'] != '--' && $aRow['country'] != '') { echo ' (' . $aRow['country'] . ')'; } echo ModuleBase::CLEAR; } } return LVPCommand::OUTPUT_NORMAL; }
/** * This method updates the internal array with all the crew members. * After a successful update, an array with the number of members of * each rank is returned. */ public function handleUpdateCrew() { $db = LVPDatabase::getInstance(); $pResult = $db->query('SELECT u.user_id, u.username, u.level, u.is_developer FROM lvp_mainserver.users u LEFT JOIN lvp_website.users_links l ON l.samp_id = u.user_id LEFT JOIN lvp_website.website_settings s ON s.user_id = l.user_id WHERE u.level != "Player" OR u.is_developer <> 0 ORDER BY u.level DESC, u.username ASC'); if ($pResult === false || $pResult->num_rows == 0) { echo 'Could not fetch crew information from database.'; return LVPCommand::OUTPUT_ERROR; } $this->m_aManagement = array(); $this->m_aAdministrator = array(); $this->m_aDeveloper = array(); while ($aRow = $pResult->fetch_assoc()) { $aRow['level'] = self::translateGamemodeLevel($aRow['level'], $aRow['is_developer']); switch ($aRow['level']) { case LVP::LEVEL_MANAGEMENT: $aCrewMembers =& $this->m_aManagement; break; case LVP::LEVEL_ADMINISTRATOR: $aCrewMembers =& $this->m_aAdministrator; break; case LVP::LEVEL_DEVELOPER: $aCrewMembers =& $this->m_aDeveloper; break; default: continue; } $aCrewMembers[strtolower($aRow['username'])] = array('ProfileID' => $aRow['user_id'], 'Nickname' => $aRow['username']); } $aCounts = array(LVP::LEVEL_MANAGEMENT => count($this->m_aManagement), LVP::LEVEL_ADMINISTRATOR => count($this->m_aAdministrator), LVP::LEVEL_DEVELOPER => count($this->m_aDeveloper)); $sMessage = 'Crew updated. '; foreach ($aCounts as $nLevel => $nCount) { $sMessage .= $nCount . ' ' . $this->wrapLevelColor($nLevel, $this->getLevelName($nLevel)) . ', '; } echo substr($sMessage, 0, -2); return LVPCommand::OUTPUT_SUCCESS; }
/** * Registered players have the ability to log into their accounts in the * LVP server. We store the time they did that so we can see whether * they're registered or not. Also because we now know for sure that * this player is registered, we'll fetch the profile ID from the * database. This is used later on when the player leaves the game, to * store his/her session time. * * @param Bot $pBot The bot which received the message. * @param integer $nId The ID of the player. * @param string $sNickname The person who logged in. * @param array $aChunks The original message in chunks. */ private function handleLoggedIn(Bot $pBot, $nId, $sNickname, $aChunks) { $this->m_pModule['Players']->setPlayerKey($nId, 'LogInTime', time()); $nLevel = $this->m_pModule['Crew']->getLevel($sNickname); $this->m_pModule['Players']->setPlayerKey($nId, 'Level', $nLevel); $db = LVPDatabase::getInstance(); $pResult = $db->query('SELECT u.user_id FROM lvp_mainserver.users_nickname n LEFT JOIN lvp_mainserver.users u ON u.user_id = n.user_id LEFT JOIN lvp_mainserver.users_mutable m ON m.user_id = u.user_id WHERE n.nickname = "' . $db->real_escape_string($sNickname) . '"'); if ($pResult !== false && $pResult->num_rows > 0) { list($nProfileId) = $pResult->fetch_row(); $this->m_pModule->players[$nId]->fetchInformation($nProfileId); $pResult->free(); } if ($this->m_pModule->config->getDirective('parser', 'welcomemsg')) { $this->m_pModule->welcomemsg->handleWelcomeMessage($nId, $sNickname, $aChunks); } }
/** * I abuse the ArrayAccess interface to quickly add some getters for the * several handlers, manager and what not classes in this module. These * include but are not limited to: Database, Crew, Parser and Commands. * Those names seem pretty obvious to me as to what they'll return. If * not, look in the code below. * * @deprecated I don't like this pattern very much, so as of now, it's * deprecated in favor of the __get() style properties. * * @param mixed $mKey The key used when getting something from this class using array syntax. * @return mixed */ public function offsetGet($mKey) { switch ($mKey) { case 'db': case 'Database': return LVPDatabase::getInstance(); case 'config': case 'Configuration': return $this->m_pConfiguration; case 'crew': case 'Crew': return $this->m_pCrewHandler; case 'parser': case 'Parser': return $this->m_pMessageParser; case 'cmds': case 'Commands': return $this->m_pCommandHandler; case 'ip': case 'IP': return $this->m_pIpManager; case 'players': case 'Players': return $this->m_pPlayerManager; case 'welcomemsg': case 'WelcomeMessage': return $this->m_pWelcomeMessage; case 'radio': case 'Radio': return $this->m_pRadioHandler; } return false; }