docker-tmserver/xaseco/plugins/plugin.rasp.php

1066 lines
36 KiB
PHP

<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
/**
* RASP plugin.
* Provides rank & personal best handling, and related chat commands.
* Updated by Xymph
*
* Dependencies: requires plugin.rasp_karma.php
*/
Aseco::registerEvent('onStartup', 'event_onstartup');
Aseco::registerEvent('onSync', 'event_onsync');
Aseco::registerEvent('onNewChallenge2', 'event_newtrack'); // use 2nd event for logical ordering of rank/karma messages
Aseco::registerEvent('onEndRace', 'event_endrace');
Aseco::registerEvent('onPlayerFinish', 'event_finish');
Aseco::registerEvent('onPlayerConnect', 'event_playerjoin');
if (!INHIBIT_RECCMDS) {
Aseco::addChatCommand('pb', 'Shows your personal best on current track');
}
Aseco::addChatCommand('rank', 'Shows your current server rank');
Aseco::addChatCommand('top10', 'Displays top 10 best ranked players');
Aseco::addChatCommand('top100', 'Displays top 100 best ranked players');
Aseco::addChatCommand('topwins', 'Displays top 100 victorious players');
Aseco::addChatCommand('active', 'Displays top 100 most active players');
class Rasp {
var $aseco;
var $features;
var $ranks;
var $settings;
var $challenges;
var $responses;
var $maxrec;
var $playerlist;
function start($aseco_ext, $config_file) {
global $maxrecs;
$this->aseco = $aseco_ext;
$this->aseco->console('[RASP] Loading config file [' . $config_file . ']');
if (!$this->settings = $this->xmlparse($config_file)) {
trigger_error('{RASP_ERROR} Could not read/parse config file ' . $config_file . ' !', E_USER_ERROR);
} else {
$this->aseco->console('[RASP] Checking database structure...');
if (!$this->checkTables()) {
trigger_error('{RASP_ERROR} Table structure incorrect! Use localdb/rasp.sql to correct this', E_USER_ERROR);
}
$this->aseco->console('[RASP] ...Structure OK!');
$this->aseco->server->records->setLimit($maxrecs);
$this->cleanData();
}
} // start
function xmlparse($config_file) {
if ($settings = $this->aseco->xml_parser->parseXml($config_file)) {
$this->messages = $settings['RASP']['MESSAGES'][0];
return $settings;
} else {
return false;
}
} // xmlparse
function checkTables() {
// create rs_* tables if needed
$query = 'CREATE TABLE IF NOT EXISTS `rs_karma` (
`Id` int(11) NOT NULL auto_increment,
`ChallengeId` mediumint(9) NOT NULL default 0,
`PlayerId` mediumint(9) NOT NULL default 0,
`Score` tinyint(4) NOT NULL default 0,
PRIMARY KEY (`Id`),
UNIQUE KEY `PlayerId` (`PlayerId`,`ChallengeId`),
KEY `ChallengeId` (`ChallengeId`)
) ENGINE=MyISAM';
mysql_query($query);
$query = 'CREATE TABLE IF NOT EXISTS `rs_rank` (
`playerID` mediumint(9) NOT NULL default 0,
`avg` float NOT NULL default 0,
KEY `playerID` (`playerID`)
) ENGINE=MyISAM';
mysql_query($query);
$query = 'CREATE TABLE IF NOT EXISTS `rs_times` (
`ID` int(11) NOT NULL auto_increment,
`challengeID` mediumint(9) NOT NULL default 0,
`playerID` mediumint(9) NOT NULL default 0,
`score` int(11) NOT NULL default 0,
`date` int(10) unsigned NOT NULL default 0,
`checkpoints` text NOT NULL,
PRIMARY KEY (`ID`),
KEY `playerID` (`playerID`,`challengeID`),
KEY `challengeID` (`challengeID`)
) ENGINE=MyISAM';
mysql_query($query);
// check for rs_* tables
$tables = array();
$res = mysql_query('SHOW TABLES');
while ($row = mysql_fetch_row($res))
$tables[] = $row[0];
mysql_free_result($res);
$check = array();
$check[1] = in_array('rs_rank', $tables);
$check[2] = in_array('rs_times', $tables);
$check[3] = in_array('rs_karma', $tables);
// get list of rs_times columns
$fields = array();
$res = mysql_query('SHOW COLUMNS FROM rs_times');
while ($row = mysql_fetch_row($res))
$fields[] = $row[0];
mysql_free_result($res);
// rename column 'trackID' (v0.7) to 'challengeID' (v0.8+) if not yet done
if (in_array('trackID', $fields)) {
$this->aseco->console("[RASP] Rename 'rs_times' column 'trackID'...");
mysql_query('ALTER TABLE rs_times CHANGE trackID challengeID mediumint(9) NOT NULL default 0');
}
// add rs_times 'checkpoints' column
if (!in_array('checkpoints', $fields)) {
$this->aseco->console("[RASP] Add 'rs_times' column 'trackID'...");
mysql_query('ALTER TABLE rs_times ADD checkpoints text NOT NULL');
}
// enlarge rs_times 'ID' & 'score' columns
$res = mysql_query('DESC rs_times ID');
$row = mysql_fetch_row($res);
mysql_free_result($res);
if ($row[1] != 'int(11)') {
$this->aseco->console("[RASP] Alter 'rs_times' column 'ID'...");
mysql_query('ALTER TABLE rs_times MODIFY ID int(11) auto_increment');
}
$res = mysql_query('DESC rs_times score');
$row = mysql_fetch_row($res);
mysql_free_result($res);
if ($row[1] != 'int(11)') {
$this->aseco->console("[RASP] Alter 'rs_times' column 'score'...");
mysql_query('ALTER TABLE rs_times MODIFY score int(11) NOT NULL default 0');
}
// change rs_times old 'rs_times_player_track' key into new 'playerID' key
// and add rs_times new 'ChallengeId' key
$fields = array('rs_times_player_track' => 0, 'challengeID' => 0);
$result = mysql_query('SHOW INDEX FROM rs_times');
while ($row = mysql_fetch_row($result)) {
if (isset($fields[$row[2]]))
$fields[$row[2]]++;
}
mysql_free_result($result);
if ($fields['rs_times_player_track'] == 2 && $fields['challengeID'] == 0) {
$this->aseco->console("[RASP] Drop 'rs_times' key 'rs_times_player_track'...");
mysql_query("ALTER TABLE rs_times DROP KEY rs_times_player_track");
$this->aseco->console("[RASP] Add 'rs_times' key 'playerID'...");
mysql_query("ALTER TABLE rs_times ADD KEY playerID (playerID, challengeID)");
$this->aseco->console("[RASP] Add 'rs_times' key 'challengeID'...");
mysql_query("ALTER TABLE rs_times ADD KEY challengeID (challengeID)");
}
// reduce rs_karma 'Score' column
$res = mysql_query('DESC rs_karma Score');
$row = mysql_fetch_row($res);
mysql_free_result($res);
if ($row[1] != 'tinyint(4)') {
$this->aseco->console("[RASP] Alter 'rs_karma' column 'score'...");
mysql_query('ALTER TABLE rs_karma MODIFY Score tinyint(4) NOT NULL default 0');
}
return ($check[1] && $check[2] && $check[3]);
} // checkTables
function cleanData () {
global $prune_records_times;
$this->aseco->console('[RASP] Cleaning up unused data');
$sql = "DELETE FROM challenges WHERE uid=''";
mysql_query($sql);
$sql = "DELETE FROM players WHERE login=''";
mysql_query($sql);
if (!$prune_records_times) return;
// prune records and rs_times entries for players & challenges deleted from database
$deletelist = array();
$sql = 'SELECT DISTINCT r.ChallengeId,c.Id FROM records r LEFT JOIN challenges c ON (r.ChallengeId=c.Id) WHERE c.Id IS NULL';
$res = mysql_query($sql);
if (mysql_num_rows($res) > 0) {
while ($row = mysql_fetch_row($res))
$deletelist[] = $row[0];
$this->aseco->console('[RASP] ...Deleting records for deleted challenges: ' . implode(',', $deletelist));
$sql = 'DELETE FROM records WHERE ChallengeId IN (' . implode(',', $deletelist) . ')';
mysql_query($sql);
}
mysql_free_result($res);
$deletelist = array();
$sql = 'SELECT DISTINCT r.PlayerId,p.Id FROM records r LEFT JOIN players p ON (r.PlayerId=p.Id) WHERE p.Id IS NULL';
$res = mysql_query($sql);
if (mysql_num_rows($res) > 0) {
while ($row = mysql_fetch_row($res))
$deletelist[] = $row[0];
$this->aseco->console('[RASP] ...Deleting records for deleted players: ' . implode(',', $deletelist));
$sql = 'DELETE FROM records WHERE PlayerId IN (' . implode(',', $deletelist) . ')';
mysql_query($sql);
}
mysql_free_result($res);
$deletelist = array();
$sql = 'SELECT DISTINCT r.challengeID,c.Id FROM rs_times r LEFT JOIN challenges c ON (r.challengeID=c.Id) WHERE c.Id IS NULL';
$res = mysql_query($sql);
if (mysql_num_rows($res) > 0) {
while ($row = mysql_fetch_row($res))
$deletelist[] = $row[0];
$this->aseco->console('[RASP] ...Deleting rs_times for deleted challenges: ' . implode(',', $deletelist));
$sql = 'DELETE FROM rs_times WHERE challengeID IN (' . implode(',', $deletelist) . ')';
mysql_query($sql);
}
mysql_free_result($res);
$deletelist = array();
$sql = 'SELECT DISTINCT r.playerID,p.Id FROM rs_times r LEFT JOIN players p ON (r.playerID=p.Id) WHERE p.Id IS NULL';
$res = mysql_query($sql);
if (mysql_num_rows($res) > 0) {
while ($row = mysql_fetch_row($res))
$deletelist[] = $row[0];
$this->aseco->console('[RASP] ...Deleting rs_times for deleted players: ' . implode(',', $deletelist));
$sql = 'DELETE FROM rs_times WHERE playerID IN (' . implode(',', $deletelist) . ')';
mysql_query($sql);
}
mysql_free_result($res);
} // cleanData
function getChallenges() {
// get new/cached list of tracks
$newlist = getChallengesCache($this->aseco); // from rasp.funcs.php
foreach ($newlist as $row) {
$tid = $this->aseco->getChallengeId($row['UId']);
// insert in case it wasn't in the database yet
if ($tid == 0) {
$query = 'INSERT INTO challenges (Uid, Name, Author, Environment)
VALUES (' . quotedString($row['UId']) . ', ' . quotedString($row['Name']) . ', '
. quotedString($row['Author']) . ', ' . quotedString($row['Environnement']) . ')';
mysql_query($query);
if (mysql_affected_rows() != 1) {
trigger_error('{RASP_ERROR} Could not insert challenge! (' . mysql_error() . ')' . CRLF . 'sql = ' . $query, E_USER_WARNING);
} else {
$tid = mysql_insert_id();
}
}
if ($tid != 0)
$tlist[] = $tid;
}
// check for missing challenge list
if (empty($tlist)) {
trigger_error('{RASP_ERROR} Cannot obtain challenge list from server and/or database - check configuration files!', E_USER_ERROR);
}
$this->challenges = $tlist;
} // getChallenges
// called @ onSync
function onSync($aseco, $data) {
global $tmxdir, $tmxtmpdir, $feature_tmxadd;
$sepchar = substr($aseco->server->trackdir, -1, 1);
if ($sepchar == '\\') {
$tmxdir = str_replace('/', $sepchar, $tmxdir);
}
if (!file_exists($aseco->server->trackdir . $tmxdir)) {
if (!mkdir($aseco->server->trackdir . $tmxdir)) {
$aseco->console_text('{RASP_ERROR} TMX Directory (' . $aseco->server->trackdir . $tmxdir . ') cannot be created');
}
}
if (!is_writeable($aseco->server->trackdir . $tmxdir)) {
$aseco->console_text('{RASP_ERROR} TMX Directory (' . $aseco->server->trackdir . $tmxdir . ') cannot be written to');
}
// check if user /add votes are enabled
if ($feature_tmxadd) {
if (!file_exists($aseco->server->trackdir . $tmxtmpdir)) {
if (!mkdir($aseco->server->trackdir . $tmxtmpdir)) {
$aseco->console_text('{RASP_ERROR} TMXtmp Directory (' . $aseco->server->trackdir . $tmxtmpdir . ') cannot be created');
$feature_tmxadd = false;
}
}
if (!is_writeable($aseco->server->trackdir . $tmxtmpdir)) {
$aseco->console_text('{RASP_ERROR} TMXtmp Directory (' . $aseco->server->trackdir . $tmxtmpdir . ') cannot be written to');
$feature_tmxadd = false;
}
}
} // onSync
function resetRanks() {
global $maxrecs, $minrank;
$players = array();
$this->aseco->console('[RASP] Calculating ranks...');
$this->getChallenges();
$tracks = $this->challenges;
$total = count($tracks);
// erase old average data
mysql_query('TRUNCATE TABLE rs_rank');
// get list of players with at least $minrecs records (possibly unranked)
$query = 'SELECT PlayerId, COUNT(*) AS cnt
FROM records
GROUP BY PlayerId
HAVING cnt >=' . $minrank;
$res = mysql_query($query);
while ($row = mysql_fetch_object($res)) {
$players[$row->PlayerId] = array(0, 0); // sum, count
}
mysql_free_result($res);
if (!empty($players)) {
// get ranked records for all tracks
$order = ($this->aseco->server->gameinfo->mode == Gameinfo::STNT ? 'DESC' : 'ASC');
foreach ($tracks as $track) {
$query = 'SELECT PlayerId FROM records
WHERE challengeid=' . $track . '
ORDER BY score ' . $order . ', date ASC
LIMIT ' . $maxrecs;
$res = mysql_query($query);
if (mysql_num_rows($res) > 0) {
$i = 1;
while ($row = mysql_fetch_object($res)) {
$pid = $row->PlayerId;
if (isset($players[$pid])) {
$players[$pid][0] += $i;
$players[$pid][1] ++;
}
$i++;
}
}
mysql_free_result($res);
}
// one-shot insert for queries up to 1 MB (default max_allowed_packet),
// or about 75K rows at 14 bytes/row (avg)
$query = 'INSERT INTO rs_rank VALUES ';
// compute each player's new average score
foreach ($players as $player => $ranked) {
// ranked tracks sum + $maxrecs rank for all remaining tracks
$avg = ($ranked[0] + ($total - $ranked[1]) * $maxrecs) / $total;
$query .= '(' . $player . ',' . round($avg * 10000) . '),';
}
$query = substr($query, 0, strlen($query)-1); // strip trailing ','
mysql_query($query);
if (mysql_affected_rows() < 1) {
trigger_error('{RASP_ERROR} Could not insert any player averages! (' . mysql_error() . ')', E_USER_WARNING);
} elseif (mysql_affected_rows() != count($players)) {
trigger_error('{RASP_ERROR} Could not insert all ' . count($players) . ' player averages! (' . mysql_error() . ')', E_USER_WARNING);
// increase MySQL's max_allowed_packet setting
}
}
$this->aseco->console('[RASP] ...Done!');
} // resetRanks
// called @ onPlayerConnect
function onPlayerjoin($aseco, $player) {
global $feature_ranks, $feature_stats, $always_show_pb;
if ($feature_ranks)
$this->showRank($player->login);
if ($feature_stats)
$this->showPb($player, $aseco->server->challenge->id, $always_show_pb);
} // onPlayerjoin
function showPb($player, $track, $always_show) {
global $maxrecs, $maxavg;
$found = false;
// find ranked record
for ($i = 0; $i < $maxrecs; $i++) {
if (($rec = $this->aseco->server->records->getRecord($i)) !== false) {
if ($rec->player->login == $player->login) {
$ret['time'] = $rec->score;
$ret['rank'] = $i + 1;
$found = true;
break;
}
} else {
break;
}
}
// check whether to show PB (e.g. for /pb)
if (!$always_show) {
// check for ranked record that's already shown at track start,
// or for player's records panel showing it
if (($found && $this->aseco->settings['show_recs_before'] == 2) ||
$player->panels['records'] != '')
return;
}
if (!$found) {
// find unranked time/score
$order = ($this->aseco->server->gameinfo->mode == Gameinfo::STNT ? 'DESC' : 'ASC');
$query2 = 'SELECT score FROM rs_times
WHERE playerID=' . $player->id . ' AND challengeID=' . $track . '
ORDER BY score ' . $order . ' LIMIT 1';
$res2 = mysql_query($query2);
if (mysql_num_rows($res2) > 0) {
$row = mysql_fetch_object($res2);
$ret['time'] = $row->score;
$ret['rank'] = '$nUNRANKED$m';
$found = true;
}
mysql_free_result($res2);
}
// compute average time of last $maxavg times
$query = 'SELECT score FROM rs_times
WHERE playerID=' . $player->id . ' AND challengeID=' . $track . '
ORDER BY date DESC LIMIT ' . $maxavg;
$res = mysql_query($query);
$size = mysql_num_rows($res);
if ($size > 0) {
$total = 0;
while ($row = mysql_fetch_object($res)) {
$total += $row->score;
}
$avg = floor($total / $size);
if ($this->aseco->server->gameinfo->mode != Gameinfo::STNT)
$avg = formatTime($avg);
} else {
$avg = 'No Average';
}
mysql_free_result($res);
if ($found) {
$message = formatText($this->messages['PB'][0],
($this->aseco->server->gameinfo->mode == Gameinfo::STNT ?
$ret['time'] : formatTime($ret['time'])),
$ret['rank'], $avg);
$message = $this->aseco->formatColors($message);
$this->aseco->client->query('ChatSendServerMessageToLogin', $message, $player->login);
} else {
$message = $this->messages['PB_NONE'][0];
$message = $this->aseco->formatColors($message);
$this->aseco->client->query('ChatSendServerMessageToLogin', $message, $player->login);
}
} // showPb
function getPb($login, $track) {
global $maxrecs;
$found = false;
// find ranked record
for ($i = 0; $i < $maxrecs; $i++) {
if (($rec = $this->aseco->server->records->getRecord($i)) !== false) {
if ($rec->player->login == $login) {
$ret['time'] = $rec->score;
$ret['rank'] = $i + 1;
$found = true;
break;
}
} else {
break;
}
}
if (!$found) {
$pid = $this->aseco->getPlayerId($login);
// find unranked time/score
$order = ($this->aseco->server->gameinfo->mode == Gameinfo::STNT ? 'DESC' : 'ASC');
$query2 = 'SELECT score FROM rs_times
WHERE playerID=' . $pid . ' AND challengeID=' . $track . '
ORDER BY score ' . $order . ' LIMIT 1';
$res2 = mysql_query($query2);
if (mysql_num_rows($res2) > 0) {
$row = mysql_fetch_object($res2);
$ret['time'] = $row->score;
$ret['rank'] = '$nUNRANKED$m';
} else {
$ret['time'] = 0;
$ret['rank'] = '$nNONE$m';
}
mysql_free_result($res2);
}
return $ret;
} // getPb
function showRank($login) {
global $minrank;
$pid = $this->aseco->getPlayerId($login);
$query = 'SELECT avg FROM rs_rank
WHERE playerID=' . $pid;
$res = mysql_query($query);
if (mysql_num_rows($res) > 0) {
$row = mysql_fetch_array($res);
$query2 = 'SELECT playerid FROM rs_rank ORDER BY avg ASC';
$res2 = mysql_query($query2);
$rank = 1;
while ($row2 = mysql_fetch_array($res2)) {
if ($row2['playerid'] == $pid) break;
$rank++;
}
$message = formatText($this->messages['RANK'][0],
$rank, mysql_num_rows($res2),
sprintf("%4.1F", $row['avg'] / 10000));
$message = $this->aseco->formatColors($message);
$this->aseco->client->query('ChatSendServerMessageToLogin', $message, $login);
mysql_free_result($res2);
} else {
$message = formatText($this->messages['RANK_NONE'][0], $minrank);
$message = $this->aseco->formatColors($message);
$this->aseco->client->query('ChatSendServerMessageToLogin', $message, $login);
}
mysql_free_result($res);
} // showRank
function getRank($login) {
$pid = $this->aseco->getPlayerId($login);
$query = 'SELECT avg FROM rs_rank
WHERE playerID=' . $pid;
$res = mysql_query($query);
if (mysql_num_rows($res) > 0) {
$row = mysql_fetch_array($res);
$query2 = 'SELECT playerid FROM rs_rank ORDER BY avg ASC';
$res2 = mysql_query($query2);
$rank = 1;
while ($row2 = mysql_fetch_array($res2)) {
if ($row2['playerid'] == $pid) break;
$rank++;
}
$message = formatText('{1}/{2} Avg: {3}',
$rank, mysql_num_rows($res2),
sprintf("%4.1F", $row['avg'] / 10000));
mysql_free_result($res2);
} else {
$message = 'None';
}
mysql_free_result($res);
return $message;
} // getRank
// called @ onPlayerFinish
function onFinish($aseco, $finish_item) {
global $feature_stats,
$checkpoints; // from plugin.checkpoints.php
// check for actual finish & no Laps mode
if ($feature_stats && $finish_item->score > 0 && $aseco->server->gameinfo->mode != Gameinfo::LAPS) {
$this->insertTime($finish_item, isset($checkpoints[$finish_item->player->login]) ?
implode(',', $checkpoints[$finish_item->player->login]->curr_cps) : '');
}
} // onFinish
// called @ onNewChallenge2
function onNewtrack($aseco, $challenge) {
global $feature_karma, $feature_stats, $always_show_pb, $karma_show_start, $karma_show_votes;
if ($feature_stats && !$aseco->server->isrelay) {
foreach ($aseco->server->players->player_list as $pl)
$this->showPb($pl, $challenge->id, $always_show_pb);
}
if ($feature_karma && $karma_show_start &&
function_exists('rasp_karma')) {
// show players' actual votes, or global karma message?
if ($karma_show_votes) {
// send individual player messages
foreach ($aseco->server->players->player_list as $pl)
rasp_karma($challenge->id, $pl->login);
} else {
// send more efficient global message
rasp_karma($challenge->id, false);
}
}
} // onNewtrack
function insertTime($time, $cps) {
$pid = $time->player->id;
if ($pid != 0) {
$query = 'INSERT INTO rs_times (playerID, challengeID, score, date, checkpoints)
VALUES (' . $pid . ', ' . $time->challenge->id . ', ' . $time->score . ', '
. quotedString(time()) . ', ' . quotedString($cps) . ')';
mysql_query($query);
if (mysql_affected_rows() != 1) {
trigger_error('{RASP_ERROR} Could not insert time! (' . mysql_error() . ')' . CRLF . 'sql = ' . $query, E_USER_WARNING);
}
} else {
trigger_error('{RASP_ERROR} Could not get Player ID for ' . $time->player->login . ' !', E_USER_WARNING);
}
} // insertTime
function deleteTime($cid, $pid) {
$query = 'DELETE FROM rs_times WHERE challengeID=' . $cid . ' AND playerID=' . $pid;
mysql_query($query);
if (mysql_affected_rows() <= 0) {
trigger_error('{RASP_ERROR} Could not remove time(s)! (' . mysql_error() . ')' . CRLF . 'sql = ' . $query, E_USER_WARNING);
}
} // deleteTime
// called @ onEndRace
function onEndrace($aseco, $data) {
global $feature_ranks, $tmxplayed;
// check for relay server
if ($aseco->server->isrelay) return;
if ($feature_ranks) {
if (!$tmxplayed) {
$this->resetRanks();
}
if ($aseco->server->getGame() != 'TMF' || !$aseco->settings['sb_stats_panels']) {
foreach ($aseco->server->players->player_list as $pl)
$this->showRank($pl->login);
}
}
} // onEndrace
} // class Rasp
// These functions pass the callback data to the Rasp class...
function event_onsync($aseco, $data) { global $rasp; $rasp->onSync($aseco, $data); }
function event_finish($aseco, $data) { global $rasp; $rasp->onFinish($aseco, $data); }
function event_newtrack($aseco, $data) { global $rasp; $rasp->onNewtrack($aseco, $data); }
function event_endrace($aseco, $data) { global $rasp; $rasp->onEndrace($aseco, $data); }
function event_playerjoin($aseco, $data) { global $rasp; $rasp->onPlayerjoin($aseco, $data); }
// Chat commands...
function chat_pb($aseco, $command) {
global $rasp, $feature_stats;
// check for relay server
if ($aseco->server->isrelay) {
$message = formatText($aseco->getChatMessage('NOTONRELAY'));
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $command['author']->login);
return;
}
if ($feature_stats) {
$rasp->showPb($command['author'], $aseco->server->challenge->id, true);
}
} // chat_pb
function chat_rank($aseco, $command) {
global $rasp, $feature_ranks;
if ($feature_ranks) {
$rasp->showRank($command['author']->login);
}
} // chat_rank
function chat_top10($aseco, $command) {
$player = $command['author'];
if ($aseco->server->getGame() == 'TMN') {
$recs = 'Current TOP 10 Players:';
$top = 10;
$bgn = '{#black}'; // nickname begin
$end = '$z'; // ... & end colors
} elseif ($aseco->server->getGame() == 'TMF') {
$header = 'Current TOP 10 Players:';
$recs = array();
$top = 10;
$bgn = '{#black}'; // nickname begin
} else { // TMS/TMO
$recs = '{#server}> Current TOP 4 Players:{#highlite}';
$top = 4;
$bgn = '{#highlite}';
$end = '{#highlite}';
}
$query = 'SELECT p.NickName, r.avg FROM players p
LEFT JOIN rs_rank r ON (p.Id=r.PlayerId)
WHERE r.avg!=0 ORDER BY r.avg ASC LIMIT ' . $top;
$res = mysql_query($query);
if (mysql_num_rows($res) == 0) {
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No ranked players found!'), $player->login);
mysql_free_result($res);
return;
}
if ($aseco->server->getGame() == 'TMN') {
$i = 1;
while ($row = mysql_fetch_object($res)) {
$nick = $row->NickName;
if (!$aseco->settings['lists_colornicks'])
$nick = stripColors($nick);
$recs .= LF . $i . '. ' . $bgn . str_pad($nick, 20)
. $end . ' - ' . sprintf("%4.1F", $row->avg / 10000);
$i++;
}
// display popup message
$aseco->client->query('SendDisplayServerMessageToLogin', $player->login, $aseco->formatColors($recs), 'OK', '', 0);
} elseif ($aseco->server->getGame() == 'TMF') {
$i = 1;
while ($row = mysql_fetch_object($res)) {
$nick = $row->NickName;
if (!$aseco->settings['lists_colornicks'])
$nick = stripColors($nick);
$recs[] = array($i . '.',
$bgn . $nick,
sprintf("%4.1F", $row->avg / 10000));
$i++;
}
// reserve extra width for $w tags
$extra = ($aseco->settings['lists_colornicks'] ? 0.2 : 0);
// display ManiaLink message
display_manialink($player->login, $header, array('BgRaceScore2', 'LadderRank'), $recs, array(0.7+$extra, 0.1, 0.45+$extra, 0.15), 'OK');
} else { // TMS/TMO
$i = 1;
while ($row = mysql_fetch_object($res)) {
$recs .= LF . $i . '. ' . $bgn . str_pad(stripColors($row->NickName), 15)
. $end . ' - ' . sprintf("%4.1F", $row->avg / 10000);
$i++;
}
// show chat message
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($recs), $player->login);
}
mysql_free_result($res);
} // chat_top10
function chat_top100($aseco, $command) {
$player = $command['author'];
if ($aseco->server->getGame() == 'TMN') {
$head = 'Current TOP 100 Players:';
$top = 100;
$bgn = '{#black}'; // nickname begin
$end = '$z'; // ... & end colors
} elseif ($aseco->server->getGame() == 'TMF') {
$head = 'Current TOP 100 Players:';
$top = 100;
$bgn = '{#black}'; // nickname begin
} else { // TMS/TMO
$message = '{#server}> {#error}Command unavailable, use {#highlite}$i/top10 {#error}instead.';
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $player->login);
return;
}
$query = 'SELECT p.NickName, r.avg FROM players p
LEFT JOIN rs_rank r ON (p.Id=r.PlayerId)
WHERE r.avg!=0 ORDER BY r.avg ASC LIMIT ' . $top;
$res = mysql_query($query);
if (mysql_num_rows($res) == 0) {
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No ranked players found!'), $player->login);
mysql_free_result($res);
return;
}
if ($aseco->server->getGame() == 'TMN') {
$recs = '';
$lines = 0;
$player->msgs = array();
$player->msgs[0] = 1;
$i = 1;
while ($row = mysql_fetch_object($res)) {
$nick = $row->NickName;
if (!$aseco->settings['lists_colornicks'])
$nick = stripColors($nick);
$recs .= LF . str_pad($i, 2, '0', STR_PAD_LEFT) . '. ' . $bgn
. str_pad($nick, 20) . $end . ' - '
. sprintf("%4.1F", $row->avg / 10000);
$i++;
if (++$lines > 9) {
$player->msgs[] = $aseco->formatColors($head . $recs);
$lines = 0;
$recs = '';
}
}
// add if last batch exists
if ($recs != '')
$player->msgs[] = $aseco->formatColors($head . $recs);
// display popup message
if (count($player->msgs) == 2) {
$aseco->client->query('SendDisplayServerMessageToLogin', $player->login, $player->msgs[1], 'OK', '', 0);
} elseif (count($player->msgs) > 2) {
$aseco->client->query('SendDisplayServerMessageToLogin', $player->login, $player->msgs[1], 'Close', 'Next', 0);
} // == 1, no message
} elseif ($aseco->server->getGame() == 'TMF') {
$recs = array();
$lines = 0;
$player->msgs = array();
// reserve extra width for $w tags
$extra = ($aseco->settings['lists_colornicks'] ? 0.2 : 0);
$player->msgs[0] = array(1, $head, array(0.7+$extra, 0.1, 0.45+$extra, 0.15), array('BgRaceScore2', 'LadderRank'));
$i = 1;
while ($row = mysql_fetch_object($res)) {
$nick = $row->NickName;
if (!$aseco->settings['lists_colornicks'])
$nick = stripColors($nick);
$recs[] = array(str_pad($i, 2, '0', STR_PAD_LEFT) . '.',
$bgn . $nick,
sprintf("%4.1F", $row->avg / 10000));
$i++;
if (++$lines > 14) {
$player->msgs[] = $recs;
$lines = 0;
$recs = array();
}
}
// add if last batch exists
if (!empty($recs))
$player->msgs[] = $recs;
// display ManiaLink message
display_manialink_multi($player);
}
mysql_free_result($res);
} // chat_top100
function chat_topwins($aseco, $command) {
$player = $command['author'];
if ($aseco->server->getGame() == 'TMN') {
$head = 'Current TOP 100 Victors:';
$top = 100;
$bgn = '{#black}'; // nickname begin
$end = '$z'; // ... & end colors
} elseif ($aseco->server->getGame() == 'TMF') {
$head = 'Current TOP 100 Victors:';
$top = 100;
$bgn = '{#black}'; // nickname begin
} else {
$head = '{#server}> Current TOP 4 Victors:{#highlite}';
$top = 4;
$bgn = '{#highlite}';
$end = '{#highlite}';
}
$query = 'SELECT NickName, Wins FROM players ORDER BY Wins DESC LIMIT ' . $top;
$res = mysql_query($query);
if ($aseco->server->getGame() == 'TMN') {
$wins = '';
$i = 1;
$lines = 0;
$player->msgs = array();
$player->msgs[0] = 1;
if (mysql_num_rows($res) > 0) {
while ($row = mysql_fetch_object($res)) {
$nick = $row->NickName;
if (!$aseco->settings['lists_colornicks'])
$nick = stripColors($nick);
$wins .= LF . str_pad($i, 2, '0', STR_PAD_LEFT) . '. ' . $bgn
. str_pad($nick, 20) . $end . ' - ' . $row->Wins;
$i++;
if (++$lines > 9) {
$player->msgs[] = $aseco->formatColors($head . $wins);
$lines = 0;
$wins = '';
}
}
}
// add if last batch exists
if ($wins != '')
$player->msgs[] = $aseco->formatColors($head . $wins);
// display popup message
if (count($player->msgs) == 2) {
$aseco->client->query('SendDisplayServerMessageToLogin', $player->login, $player->msgs[1], 'OK', '', 0);
} elseif (count($player->msgs) > 2) {
$aseco->client->query('SendDisplayServerMessageToLogin', $player->login, $player->msgs[1], 'Close', 'Next', 0);
} // == 1, no message
} elseif ($aseco->server->getGame() == 'TMF') {
$wins = array();
$i = 1;
$lines = 0;
$player->msgs = array();
// reserve extra width for $w tags
$extra = ($aseco->settings['lists_colornicks'] ? 0.2 : 0);
$player->msgs[0] = array(1, $head, array(0.7+$extra, 0.1, 0.45+$extra, 0.15), array('BgRaceScore2', 'LadderRank'));
if (mysql_num_rows($res) > 0) {
while ($row = mysql_fetch_object($res)) {
$nick = $row->NickName;
if (!$aseco->settings['lists_colornicks'])
$nick = stripColors($nick);
$wins[] = array(str_pad($i, 2, '0', STR_PAD_LEFT) . '.',
$bgn . $nick,
$row->Wins);
$i++;
if (++$lines > 14) {
$player->msgs[] = $wins;
$lines = 0;
$wins = array();
}
}
}
// add if last batch exists
if (!empty($wins))
$player->msgs[] = $wins;
// display ManiaLink message
display_manialink_multi($player);
} else { // TMS/TMO
$wins = $head;
$i = 1;
while ($row = mysql_fetch_object($res)) {
$wins .= LF . $i . '. ' . $bgn . str_pad(stripColors($row->NickName), 15)
. $end . ' - ' . $row->Wins;
$i++;
}
// show chat message
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($wins), $player->login);
}
mysql_free_result($res);
} // chat_topwins
function chat_active($aseco, $command) {
$player = $command['author'];
if ($aseco->server->getGame() == 'TMN') {
$head = 'TOP 100 Most Active Players:';
$top = 100;
$bgn = '{#black}'; // nickname begin
$end = '$z'; // ... & end colors
} elseif ($aseco->server->getGame() == 'TMF') {
$head = 'TOP 100 Most Active Players:';
$top = 100;
$bgn = '{#black}'; // nickname begin
} else { // TMS/TMO
$head = '{#server}> Most Active Players:{#highlite}';
$top = 4;
$bgn = '{#highlite}';
$end = '{#highlite}';
}
$query = 'SELECT NickName, TimePlayed FROM players ORDER BY TimePlayed DESC LIMIT ' . $top;
$res = mysql_query($query);
if ($aseco->server->getGame() == 'TMN') {
$active = '';
$i = 1;
$lines = 0;
$player->msgs = array();
$player->msgs[0] = 1;
while ($row = mysql_fetch_object($res)) {
$nick = $row->NickName;
if (!$aseco->settings['lists_colornicks'])
$nick = stripColors($nick);
$active .= LF . str_pad($i, 2, '0', STR_PAD_LEFT) . '. ' . $bgn
. str_pad($nick, 20) . $end . ' - '
. formatTimeH($row->TimePlayed * 1000, false);
$i++;
if (++$lines > 9) {
$player->msgs[] = $aseco->formatColors($head . $active);
$lines = 0;
$active = '';
}
}
// add if last batch exists
if ($active != '')
$player->msgs[] = $aseco->formatColors($head . $active);
// display popup message
if (count($player->msgs) == 2) {
$aseco->client->query('SendDisplayServerMessageToLogin', $player->login, $player->msgs[1], 'OK', '', 0);
} elseif (count($player->msgs) > 2) {
$aseco->client->query('SendDisplayServerMessageToLogin', $player->login, $player->msgs[1], 'Close', 'Next', 0);
} // == 1, no message
} elseif ($aseco->server->getGame() == 'TMF') {
$active = array();
$i = 1;
$lines = 0;
$player->msgs = array();
// reserve extra width for $w tags
$extra = ($aseco->settings['lists_colornicks'] ? 0.2 : 0);
$player->msgs[0] = array(1, $head, array(0.8+$extra, 0.1, 0.45+$extra, 0.25), array('BgRaceScore2', 'LadderRank'));
while ($row = mysql_fetch_object($res)) {
$nick = $row->NickName;
if (!$aseco->settings['lists_colornicks'])
$nick = stripColors($nick);
$active[] = array(str_pad($i, 2, '0', STR_PAD_LEFT) . '.',
$bgn . $nick,
formatTimeH($row->TimePlayed * 1000, false));
$i++;
if (++$lines > 14) {
$player->msgs[] = $active;
$lines = 0;
$active = array();
}
}
// add if last batch exists
if (!empty($active))
$player->msgs[] = $active;
// display ManiaLink message
display_manialink_multi($player);
} else { // TMS/TMO
$active = $head;
$i = 1;
while ($row = mysql_fetch_object($res)) {
$active .= LF . $i . '. ' . $bgn . str_pad(stripColors($row->NickName), 15)
. $end . ' - ' . formatTimeH($row->TimePlayed * 1000, false);
$i++;
}
// show chat message
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($active), $player->login);
}
mysql_free_result($res);
} // chat_active
// Starts the rasp plugin...
// called @ onStartup
function event_onstartup($aseco) {
global $rasp, $prune_records_times;
$rasp = new Rasp();
$rasp->start($aseco, 'rasp.xml');
// prune records and rs_times entries for tracks deleted from server
if ($prune_records_times) {
$aseco->console('[RASP] Pruning records/rs_times for deleted tracks');
$rasp->getChallenges();
$tracks = $rasp->challenges;
// get list of challenge IDs with records in the database
$query = 'SELECT DISTINCT ChallengeId FROM records';
$res = mysql_query($query);
while ($row = mysql_fetch_row($res)) {
$track = $row[0];
// delete records & rs_times if it's not in server's challenge list
if (!in_array($track, $tracks)) {
$aseco->console('[RASP] ...challengeID: ' . $track);
$query = 'DELETE FROM records WHERE ChallengeId=' . $track;
mysql_query($query);
$query = 'DELETE FROM rs_times WHERE challengeID=' . $track;
mysql_query($query);
}
}
mysql_free_result($res);
}
} // event_onstartup
?>