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

1143 lines
45 KiB
PHP
Raw Normal View History

2022-06-26 17:43:44 +02:00
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
/**
* Dedimania plugin.
* Handles interaction with the Dedimania world database.
* Created by Xymph, based on FAST
*
* Dependencies: requires chat.dedimania.php, plugin.checkpoints.php
* used by chat.dedimania.php
* requires plugin.panels.php on TMF
*/
require_once('includes/GbxRemote.response.php');
require_once('includes/web_access.inc.php');
require_once('includes/xmlrpc_db.inc.php');
define('DEDICONFIG', 'dedimania.xml');
global $dedi_db, $dedi_db_defaults, $dedi_debug, $dedi_lastsent, $dedi_timeout,
$dedi_refresh, $dedi_minauth, $dedi_mintime, $dedi_webaccess;
// how many seconds before retrying connection
$dedi_timeout = 1800; // 30 mins
// how many seconds before reannouncing server
$dedi_refresh = 240; // 4 mins
// minimum author & finish times that are still accepted
$dedi_minauth = 8000; // 8 secs
$dedi_mintime = 6000; // 6 secs
$dedi_debug = 0; /* max debug level = 5:
1 +internal warnings
2 +main data structure, initial connection response, progress messages, dedicated callback data
3 +config defaults, XML config, full record lists, data in XML responses
4 +full XML responses
5 +record checkpoints
*/
// overrule these in dedimania.xml, don't change them here
$dedi_db_defaults = array(
'Name' => 'Dedimania',
'LogNews' => false,
'ShowWelcome' => true,
'ShowMinRecs' => 8,
'ShowRecsBefore' => 1,
'ShowRecsAfter' => 1,
'ShowRecsRange' => true,
'DisplayRecs' => true,
'RecsInWindow' => false,
'ShowRecLogins' => true,
'LimitRecs' => 10,
);
Aseco::registerEvent('onSync', 'dedimania_init');
Aseco::registerEvent('onEverySecond', 'dedimania_update');
Aseco::registerEvent('onPlayerConnect', 'dedimania_playerconnect');
Aseco::registerEvent('onPlayerDisconnect', 'dedimania_playerdisconnect');
Aseco::registerEvent('onNewChallenge', 'dedimania_newchallenge');
Aseco::registerEvent('onEndRace', 'dedimania_endrace');
Aseco::registerEvent('onPlayerFinish', 'dedimania_playerfinish');
// Initialize Dedimania subsystem
// called @ onSync
function dedimania_init($aseco) {
global $dedi_db, $dedi_db_defaults, $dedi_debug, $dedi_webaccess, $dedi_lastsent,
$checkpoints; // from plugin.checkpoints.php
// check for checkpoints plugin
if (!isset($checkpoints) || !is_array($checkpoints))
trigger_error('Dedimania system cannot find $checkpoints - include plugin.checkpoints.php in plugins.xml!', E_USER_ERROR);
// create web access
$dedi_webaccess = new Webaccess();
if ($dedi_debug > 2)
print_r($dedi_db_defaults);
// read & parse config file
$dedi_db = array();
if ($config = $aseco->xml_parser->parseXml(DEDICONFIG)) {
if ($dedi_debug > 2)
print_r($config);
// read the XML structure into array
if (isset($config['DEDIMANIA']['DATABASE']) && is_array($config['DEDIMANIA']['DATABASE']) &&
isset($config['DEDIMANIA']['MASTERSERVER_ACCOUNT']) && is_array($config['DEDIMANIA']['MASTERSERVER_ACCOUNT'])) {
$dbdata = &$config['DEDIMANIA']['DATABASE'][0];
if ($dedi_debug > 2)
print_r($dbdata);
if (isset($dbdata['URL'][0])) {
if (!is_array($dbdata['URL'][0]))
$dedi_db['Url'] = $dbdata['URL'][0];
else
trigger_error('Multiple URLs specified in your Dedimania config file!', E_USER_ERROR);
if (isset($dbdata['WELCOME'][0]))
$dedi_db['Welcome'] = $dbdata['WELCOME'][0];
else
$dedi_db['Welcome'] = '';
if (isset($dbdata['TIMEOUT'][0]))
$dedi_db['Timeout'] = $dbdata['TIMEOUT'][0];
else
$dedi_db['Timeout'] = '';
if (isset($dbdata['NAME'][0]))
$dedi_db['Name'] = $dbdata['NAME'][0];
else
$dedi_db['Name'] = $dedi_db_defaults['Name'];
if (isset($dbdata['LOG_NEWS'][0]))
$dedi_db['LogNews'] = (strtolower($dbdata['LOG_NEWS'][0]) == 'true');
else
$dedi_db['LogNews'] = $dedi_db_defaults['LogNews'];
if (isset($dbdata['SHOW_WELCOME'][0]))
$dedi_db['ShowWelcome'] = (strtolower($dbdata['SHOW_WELCOME'][0]) == 'true');
else
$dedi_db['ShowWelcome'] = $dedi_db_defaults['ShowWelcome'];
if (isset($dbdata['SHOW_MIN_RECS'][0]))
$dedi_db['ShowMinRecs'] = intval($dbdata['SHOW_MIN_RECS'][0]);
else
$dedi_db['ShowMinRecs'] = $dedi_db_defaults['ShowMinRecs'];
if (isset($dbdata['SHOW_RECS_BEFORE'][0]))
$dedi_db['ShowRecsBefore'] = intval($dbdata['SHOW_RECS_BEFORE'][0]);
else
$dedi_db['ShowRecsBefore'] = $dedi_db_defaults['ShowRecsBefore'];
if (isset($dbdata['SHOW_RECS_AFTER'][0]))
$dedi_db['ShowRecsAfter'] = intval($dbdata['SHOW_RECS_AFTER'][0]);
else
$dedi_db['ShowRecsAfter'] = $dedi_db_defaults['ShowRecsAfter'];
if (isset($dbdata['SHOW_RECS_RANGE'][0]))
$dedi_db['ShowRecsRange'] = (strtolower($dbdata['SHOW_RECS_RANGE'][0]) == 'true');
else
$dedi_db['ShowRecsRange'] = $dedi_db_defaults['ShowRecsRange'];
if (isset($dbdata['DISPLAY_RECS'][0]))
$dedi_db['DisplayRecs'] = (strtolower($dbdata['DISPLAY_RECS'][0]) == 'true');
else
$dedi_db['DisplayRecs'] = $dedi_db_defaults['DisplayRecs'];
if (isset($dbdata['RECS_IN_WINDOW'][0]))
$dedi_db['RecsInWindow'] = (strtolower($dbdata['RECS_IN_WINDOW'][0]) == 'true');
else
$dedi_db['RecsInWindow'] = $dedi_db_defaults['RecsInWindow'];
if (isset($dbdata['SHOW_REC_LOGINS'][0]))
$dedi_db['ShowRecLogins'] = (strtolower($dbdata['SHOW_REC_LOGINS'][0]) == 'true');
else
$dedi_db['ShowRecLogins'] = $dedi_db_defaults['ShowRecLogins'];
if (isset($dbdata['LIMIT_RECS'][0]))
$dedi_db['LimitRecs'] = intval($dbdata['LIMIT_RECS'][0]);
else
$dedi_db['LimitRecs'] = $dedi_db_defaults['LimitRecs'];
// set default MaxRank
$dedi_db['MaxRank'] = 30;
// check/initialise server configuration
$dbdata = &$config['DEDIMANIA']['MASTERSERVER_ACCOUNT'][0];
$dedi_db['Login'] = $dbdata['LOGIN'][0];
$dedi_db['Password'] = $dbdata['PASSWORD'][0];
$dedi_db['Nation'] = $dbdata['NATION'][0];
if ($dedi_db['Login'] == '' || $dedi_db['Login'] == 'YOUR_SERVER_LOGIN' ||
$dedi_db['Password'] == '' || $dedi_db['Password'] == 'YOUR_SERVER_PASSWORD' ||
$dedi_db['Nation'] == '' || $dedi_db['Nation'] == 'YOUR_SERVER_NATION')
trigger_error('Dedimania not configured! <masterserver_account> contains default or empty value(s)', E_USER_ERROR);
if ($aseco->server->getGame() == 'TMF' && strtolower($dedi_db['Login']) != $aseco->server->serverlogin)
trigger_error('Dedimania misconfigured! <masterserver_account><login> (' . $dedi_db['Login'] . ') is not the actual server login (' . $aseco->server->serverlogin . ')', E_USER_ERROR);
$dedi_db['Messages'] = &$config['DEDIMANIA']['MESSAGES'][0];
$dedi_db['RecsValid'] = false;
$dedi_db['BannedLogins'] = array();
$dedi_db['ModeList'] = array();
$dedi_db['ModeList'][Gameinfo::RNDS] = 'Rounds';
$dedi_db['ModeList'][Gameinfo::TA] = 'TA';
$dedi_db['ModeList'][Gameinfo::TEAM] = 'Rounds';
$dedi_db['ModeList'][Gameinfo::LAPS] = 'TA';
$dedi_db['ModeList'][Gameinfo::STNT] = '';
$dedi_db['ModeList'][Gameinfo::CUP] = 'Rounds';
} else {
trigger_error('No URL specified in your Dedimania config file!', E_USER_ERROR);
}
} else {
trigger_error('Structure error in your Dedimania config file!', E_USER_ERROR);
}
} else {
trigger_error('Could not read/parse Dedimania config file ' . DEDICONFIG . ' !', E_USER_ERROR);
}
if ($dedi_debug > 1)
print_r($dedi_db);
// connect to Dedimania server
$aseco->console('************* (Dedimania) *************');
dedimania_connect($aseco);
$aseco->console('------------- (Dedimania) -------------');
$dedi_lastsent = time();
} // dedimania_init
function dedimania_connect($aseco) {
global $dedi_db, $dedi_debug, $dedi_timeout, $dedi_webaccess;
$time = time();
// check for no or timed-out connection
if (!isset($dedi_db['XmlrpcDB']) &&
(!isset($dedi_db['XmlrpcDBbadTime']) || ($time - $dedi_db['XmlrpcDBbadTime']) > $dedi_timeout)) {
$aseco->console('* Dataserver connection on ' . $dedi_db['Name'] . ' ...');
$aseco->console('* Try connection on ' . $dedi_db['Url'] . ' ...');
// establish Dedimania connection and login
$xmlrpcdb = new XmlrpcDB($dedi_webaccess, $dedi_db['Url'],
$aseco->server->getGame(),
$dedi_db['Login'],
$dedi_db['Password'],
'XASECO', XASECO_VERSION,
$dedi_db['Nation'],
$aseco->server->packmask);
$response = $xmlrpcdb->RequestWait('dedimania.ValidateAccount');
if ($dedi_debug > 3)
$aseco->console_text('dedimania_connect - response' . CRLF . print_r($response, true));
elseif ($dedi_debug > 2)
$aseco->console_text('dedimania_connect - response[Data]' . CRLF . print_r($response['Data'], true));
// Reply a struct {'Status': boolean,
// 'Messages': array of struct {'Date': string, 'Text': string} }
// check response
if ($response === false) {
$aseco->console_text(' !!!' . CRLF . ' !!! Error bad database response !' . CRLF . ' !!!');
}
elseif (isset($response['Data']['params']['Status']) && $response['Data']['params']['Status']) {
// establish Dedimania connection and login
$xmlrpcdb = new XmlrpcDB($dedi_webaccess, $dedi_db['Url'],
$aseco->server->getGame(),
$dedi_db['Login'],
$dedi_db['Password'],
'XASECO', XASECO_VERSION,
$dedi_db['Nation'],
$aseco->server->packmask);
$dedi_db['XmlrpcDB'] = $xmlrpcdb;
$dedi_db['News'] = $response['Data']['params']['Messages'];
$aseco->console('* Connection and status ok! (' . $response['Headers']['server'][0] . ')');
if (($errors = dedi_iserror($response)) !== false)
$aseco->console_text(' !!!' . CRLF . ' !!! ...with authentication warning(s): ' . $errors);
}
elseif (($errors = dedi_iserror($response)) !== false) {
$aseco->console_text(' !!!' . CRLF . ' !!! Connection Error !!! (' . $response['Headers']['server'][0] . ')' . CRLF . $errors . CRLF . ' !!!');
}
elseif (!isset($response['Code'])) {
$aseco->console_text(' !!!' . CRLF . ' !!! Error no database response (' . $dedi_db['Url'] . ')' . CRLF . ' !!!');
}
else {
$aseco->console_text(' !!!' . CRLF . ' !!! Error bad database response or contents (' . $response['Headers']['server'][0] . ') ['
. $response['Code'] . ', ' . $response['Reason'] . ']' . CRLF . ' !!!');
if ($dedi_debug > 1) {
if ($response['Code'] == 200)
$aseco->console_text('dedimania_connect - response[Message]' . CRLF . $response['Message']);
elseif ($response['Code'] != 404)
$aseco->console_text('dedimania_connect - response' . CRLF . print_r($response, true));
}
}
// check for valid connection
if (isset($dedi_db['XmlrpcDB'])) {
// log Dedimania news
if ($dedi_db['LogNews'])
foreach ($dedi_db['News'] as $news)
$aseco->console('* NEWS (' . $dedi_db['Name'] . ', ' . $news['Date'] . '): ' . $news['Text']);
return;
}
// prepare for next connection attempt
$dedi_db['XmlrpcDBbadTime'] = $time;
}
} // dedimania_connect
function dedimania_announce() {
global $aseco, $dedi_db, $dedi_debug, $dedi_lastsent;
// check for valid track
if (isset($aseco->server->challenge->uid)) {
// check for valid connection
if (isset($dedi_db['XmlrpcDB']) && !$dedi_db['XmlrpcDB']->isBad()) {
if ($dedi_debug > 1)
$aseco->console('** Update server Dedimania info...');
// collect server & players info
$serverinfo = dedimania_serverinfo($aseco);
$players = dedimania_players($aseco);
$dedi_lastsent = time();
$callback = array('dedimania_announce_cb');
$dedi_db['XmlrpcDB']->addRequest($callback,
'dedimania.UpdateServerPlayers',
$aseco->server->getGame(),
$aseco->server->gameinfo->mode,
$serverinfo,
$players);
// UpdateServerPlayers(Game, Mode, SrvInfo, Players)
}
}
} // dedimania_announce
function dedimania_announce_cb($response) {
global $aseco, $dedi_debug;
// Reply true
if (($errors = dedi_iserror($response)) !== false) {
if ($dedi_debug > 3)
$aseco->console_text('dedimania_announce_cb - response' . CRLF . print_r($response, true));
elseif ($dedi_debug > 2)
$aseco->console_text('dedimania_announce_cb - response[Data]' . CRLF . print_r($response['Data'], true));
else
$aseco->console_text('dedimania_announce_cb - error(s): ' . $errors);
}
} // dedimania_announce_cb
// called @ onEverySecond
function dedimania_update($aseco) {
global $dedi_db, $dedi_lastsent, $dedi_timeout, $dedi_refresh, $dedi_webaccess;
// check for valid connection
if (isset($dedi_db['XmlrpcDB'])) {
// refresh DB every 4 mins after last DB update
if ($dedi_lastsent + $dedi_refresh < time())
dedimania_announce();
if ($dedi_db['XmlrpcDB']->isBad()) {
// retry after 30 mins of bad state
if ($dedi_db['XmlrpcDB']->badTime() > $dedi_timeout) {
$aseco->console('Dedimania retry to send after ' . round($dedi_timeout/60) . ' minutes...');
$dedi_db['XmlrpcDB']->retry();
}
} else {
$response = $dedi_db['XmlrpcDB']->sendRequests();
if (!$response) {
$message = '{#server}>> ' . formatText($dedi_db['Timeout'], round($dedi_timeout/60));
$aseco->client->query('ChatSendServerMessage', $aseco->formatColors($message));
trigger_error('Dedimania has consecutive connection errors!', E_USER_WARNING);
}
}
} else {
// reconnect to Dedimania server
dedimania_connect($aseco);
}
// trigger pending callbacks
$read = array();
$write = null;
$except = null;
$dedi_webaccess->select($read, $write, $except, 0);
} // dedimania_update
// called @ onPlayerConnect
function dedimania_playerconnect($aseco, $player) {
global $dedi_db, $dedi_debug;
if ($dedi_debug > 1)
$aseco->console_text('dedimania_playerconnect - ' . $player->login . ' : ' . stripColors($player->nickname, false));
// get player info & check for non-LAN login
if ($pinfo = dedimania_playerinfo($aseco, $player)) {
if ($dedi_debug > 1)
$aseco->console_text('dedimania_playerconnect - pinfo' . CRLF . print_r($pinfo, true));
// check for valid connection
if (isset($dedi_db['XmlrpcDB']) && !$dedi_db['XmlrpcDB']->isBad()) {
$callback = array('dedimania_playerconnect_cb', $player->login);
$dedi_db['XmlrpcDB']->addRequest($callback,
'dedimania.PlayerArrive',
$aseco->server->getGame(),
$player->login,
$player->nickname,
$pinfo['Nation'],
$pinfo['TeamName'],
$pinfo['Ranking'],
$pinfo['IsSpec'],
$pinfo['IsOff']);
// PlayerArrive(Game, Login, Nickname, Nation, TeamName, LadderRanking, IsSpectator, IsOfficial)
}
}
} // dedimania_playerconnect
function dedimania_playerconnect_cb($response, $login) {
global $aseco, $dedi_db, $dedi_debug;
// Reply a struct {'Login': string, 'TeamName': string, 'Nation': string,
// 'Options': array of struct {'Option': string, 'Value': string, 'Tool': string},
// 'Aliases': array of struct {'Alias': string, 'Text': string, 'Tool': string} }
if ($dedi_debug > 3)
$aseco->console_text('dedimania_playerconnect_cb - response' . CRLF . print_r($response, true));
elseif ($dedi_debug > 2)
$aseco->console_text('dedimania_playerconnect_cb - response[Data]' . CRLF . print_r($response['Data'], true));
elseif (($errors = dedi_iserror($response)) !== false)
$aseco->console_text('dedimania_playerconnect_cb - error(s): ' . $errors);
// check response
if (!$player = $aseco->server->players->getPlayer($login)) {
if ($dedi_debug > 0)
$aseco->console('dedimania_playerconnect_cb - ' . $login . ' does not exist!');
}
elseif (isset($response['Data']['params'])) {
// update nickname in record
if ($dedi_db['RecsValid'] && !empty($dedi_db['Challenge']['Records']) && isset($player->nickname)) {
foreach ($dedi_db['Challenge']['Records'] as &$rec) {
if ($rec['Login'] == $login && $rec['Game'] ==
($aseco->server->getGame() == 'TMF' ? 'TMU' : $aseco->server->getGame())) {
$rec['NickName'] = $player->nickname;
break;
}
}
}
// show welcome message
if ($dedi_db['ShowWelcome']) {
$message = '{#server}> ' . $dedi_db['Welcome'];
$message = str_replace('{br}', LF, $message); // split long message
// hyperlink Dedimania site on TMF
if ($aseco->server->getGame() == 'TMF')
$message = str_replace('www.dedimania.com', '$l[http://www.dedimania.com/]www.dedimania.com$l', $message);
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
}
// get player rank
$player->dedirank = $dedi_db['MaxRank'];
if (isset($response['Data']['params']['MaxRank']))
$player->dedirank = $response['Data']['params']['MaxRank']+0;
// check for banned player
if (!isset($response['Data']['params']['Status']))
trigger_error('Incomplete XASECO update - includes/xmlrpc_db.inc.php is out of date!', E_USER_ERROR);
if ($response['Data']['params']['Status'] % 2 == 1) {
// remember banned login
$dedi_db['BannedLogins'][] = $login;
// show chat message to all
$message = formatText($dedi_db['Messages']['BANNED_LOGIN'][0],
stripColors($player->nickname), $login);
$aseco->client->query('ChatSendServerMessage', $aseco->formatColors($message));
// log banned player
$aseco->console('[Dedimania] player {1} is banned - finishes ignored!', $login);
}
}
else {
if ($dedi_debug > 2)
$aseco->console('dedimania_playerconnect_cb - bad response!');
}
} // dedimania_playerconnect_cb
// called @ onPlayerDisconnect
function dedimania_playerdisconnect($aseco, $player) {
global $dedi_db, $dedi_debug;
if ($dedi_debug > 1)
$aseco->console_text('dedimania_playerdisconnect - ' . $player->login . ' : ' . stripColors($player->nickname, false));
// check for non-LAN login
if (!isLANLogin($player->login)) {
// check for valid connection
if (isset($dedi_db['XmlrpcDB']) && !$dedi_db['XmlrpcDB']->isBad()) {
$dedi_db['XmlrpcDB']->addRequest(null,
'dedimania.PlayerLeave',
$aseco->server->getGame(),
$player->login);
// PlayerLeave(Game, Login)
// ignore: Reply a struct {'Login': string}
}
}
// clear possible banned login
if (($i = array_search($player->login, $dedi_db['BannedLogins'])) !== false)
unset($dedi_db['BannedLogins'][$i]);
} // dedimania_playerdisconnect
// called @ onNewChallenge
function dedimania_newchallenge($aseco, $challenge) {
global $dedi_db, $dedi_debug, $dedi_minauth;
if ($dedi_debug > 1)
$aseco->console_text('dedimania_newchallenge - challenge' . CRLF . print_r($challenge, true));
// check for valid connection
$dedi_db['Challenge'] = array();
if (isset($dedi_db['XmlrpcDB']) && !$dedi_db['XmlrpcDB']->isBad()) {
// collect server & players info
$serverinfo = dedimania_serverinfo($aseco);
$players = dedimania_players($aseco);
$callback = array('dedimania_newchallenge_cb', $challenge);
$dedi_db['XmlrpcDB']->addRequest($callback,
'dedimania.CurrentChallenge',
$challenge->uid,
$challenge->name,
$challenge->environment,
$challenge->author,
$aseco->server->getGame(),
$aseco->server->gameinfo->mode,
$serverinfo,
$dedi_db['MaxRank'],
$players);
// CurrentChallenge(Uid, Name, Environment, Author, Game, Mode, SrvInfos, MaxGetTimes, Players)
}
$dedi_db['RecsValid'] = false;
$dedi_db['TrackValid'] = false;
$dedi_db['ServerMaxRank'] = $dedi_db['MaxRank'];
// check for Stunts mode
if ($aseco->server->gameinfo->mode == Gameinfo::STNT)
$aseco->console('[Dedimania] Stunts mode unsupported: records ignored');
// check for multilap track in TMF Rounds/Team/Cup modes
elseif ($aseco->server->getGame() == 'TMF' &&
$challenge->laprace && $challenge->forcedlaps != 0 &&
($aseco->server->gameinfo->mode == Gameinfo::RNDS ||
$aseco->server->gameinfo->mode == Gameinfo::TEAM ||
$aseco->server->gameinfo->mode == Gameinfo::CUP))
$aseco->console('[Dedimania] RoundForcedLaps != 0: records ignored');
// check for minimum author time
elseif ($challenge->authortime < $dedi_minauth)
$aseco->console('[Dedimania] Map\'s Author time < ' . ($dedi_minauth / 1000) . 's: records ignored');
else
$dedi_db['TrackValid'] = true;
} // dedimania_newchallenge
function dedimania_newchallenge_cb($response, $challenge) {
global $aseco, $dedi_db, $dedi_debug,
$checkpoints; // from plugin.checkpoints.php
// Reply a struct {'Uid': string, 'TotalRaces': int, 'TotalPlayers': int,
// 'TimeAttackRaces': int, 'TimeAttackPlayers': int,
// 'NumberOfChecks': int, 'ServerMaxRecords': int,
// 'Records': array of struct {'Login': string, 'NickName': string,
// 'Best': int, 'Rank': int,
// 'Checks': array of int, 'Vote': int} }
// if Stunts mode, bail out
if ($aseco->server->gameinfo->mode == Gameinfo::STNT) return;
if ($dedi_debug > 3)
$aseco->console_text('dedimania_newchallenge_cb - response' . CRLF . print_r($response, true));
elseif ($dedi_debug > 2)
$aseco->console_text('dedimania_newchallenge_cb - response[Data]' . CRLF . print_r($response['Data'], true));
elseif (($errors = dedi_iserror($response)) !== false)
$aseco->console_text('dedimania_newchallenge_cb - error(s): ' . $errors);
// check response
if (isset($response['Data']['params']) && $dedi_db['TrackValid']) {
$dedi_db['Challenge'] = $response['Data']['params'];
$dedi_db['RecsValid'] = true;
if (isset($response['Data']['params']['ServerMaxRecords']))
$dedi_db['ServerMaxRank'] = $response['Data']['params']['ServerMaxRecords']+0;
if ($dedi_debug > 1)
$aseco->console_text('dedimania_newchallenge_cb - records' . CRLF . print_r($dedi_db['Challenge']['Records'], true));
// check for records
if (!empty($dedi_db['Challenge']['Records'])) {
// strip line breaks in nicknames
foreach ($dedi_db['Challenge']['Records'] as &$rec) {
$rec['NickName'] = str_replace("\n", '', $rec['NickName']);
}
// set Dedimania record/checkpoints references
if ($aseco->settings['display_checkpoints']) {
foreach ($checkpoints as $login => $cp) {
$drec = $checkpoints[$login]->dedirec - 1;
// check for specific record
if ($drec+1 > 0) {
// if specific record unavailable, use last one
if ($drec > count($dedi_db['Challenge']['Records']) - 1)
$drec = count($dedi_db['Challenge']['Records']) - 1;
// store record/checkpoints reference
$checkpoints[$login]->best_fin = $dedi_db['Challenge']['Records'][$drec]['Best'];
$checkpoints[$login]->best_cps = $dedi_db['Challenge']['Records'][$drec]['Checks'];
}
elseif ($drec+1 == 0) {
// search for own/last record
$drec = 0;
while ($drec < count($dedi_db['Challenge']['Records'])) {
if ($dedi_db['Challenge']['Records'][$drec++]['Login'] == $login)
break;
}
$drec--;
// store record/checkpoints reference
$checkpoints[$login]->best_fin = $dedi_db['Challenge']['Records'][$drec]['Best'];
$checkpoints[$login]->best_cps = $dedi_db['Challenge']['Records'][$drec]['Checks'];
} // else -1
}
}
if ($dedi_debug > 4)
$aseco->console_text('dedimania_newchallenge_cb - checkpoints' . CRLF . print_r($checkpoints, true));
// notify records panel & update all panels
if ($aseco->server->getGame() == 'TMF') {
setRecordsPanel('dedi', ($aseco->server->gameinfo->mode == Gameinfo::STNT ?
str_pad($dedi_db['Challenge']['Records'][0]['Best'], 5, ' ', STR_PAD_LEFT) :
formatTime($dedi_db['Challenge']['Records'][0]['Best'])));
if (function_exists('update_allrecpanels'))
update_allrecpanels($aseco, null); // from plugin.panels.php
}
}
if ($dedi_db['ShowRecsBefore'] > 0)
show_dedirecs($aseco, $challenge->name, $challenge->uid,
$dedi_db['Challenge']['Records'], false, 1,
$dedi_db['ShowRecsBefore']); // from chat.dedimania.php
} else {
if ($dedi_debug > 2)
$aseco->console('dedimania_newchallenge_cb - bad response or track invalid!');
}
// throw 'Dedimania records loaded' event
$aseco->releaseEvent('onDediRecsLoaded', $dedi_db['RecsValid']);
} // dedimania_newchallenge_cb
// called @ onEndRace
function dedimania_endrace($aseco, $data) {
global $dedi_db, $dedi_debug, $dedi_lastsent, $dedi_mintime;
// notify records panel
if ($aseco->server->getGame() == 'TMF') {
setRecordsPanel('dedi', ($aseco->server->gameinfo->mode == Gameinfo::STNT ?
' ---' : ' --.--'));
}
// if Stunts mode, bail out
if ($aseco->server->gameinfo->mode == Gameinfo::STNT) return;
if ($dedi_debug > 1)
$aseco->console_text('dedimania_endrace - data' . CRLF . print_r($data, true));
// check for valid track
if (isset($data[1]['UId']) && isset($dedi_db['TrackValid']) && $dedi_db['TrackValid']) {
// check for valid connection
if (isset($dedi_db['XmlrpcDB']) && !$dedi_db['XmlrpcDB']->isBad()) {
// collect/sort new finish times & checkpoints
if ($dedi_db['RecsValid'] && !empty($dedi_db['Challenge']['Records'])) {
$times = array();
foreach ($dedi_db['Challenge']['Records'] as $rec) {
// check for valid, minimum finish time
if (isset($rec['NewBest']) && $rec['NewBest'] &&
$rec['Best'] >= $dedi_mintime)
$times[] = array('Login' => $rec['Login'], 'Best' => $rec['Best'],
'Checks' => implode(',', $rec['Checks']));
}
if (!empty($times))
usort($times, 'dedi_timecompare');
// compute number of checkpoints from best time
$numchecks = 0;
if (isset($times[0]['Checks']))
$numchecks = count(explode(',', $times[0]['Checks']));
if ($dedi_debug > 1) {
$aseco->console_text('dedimania_endrace - numchecks: ' . $numchecks);
$aseco->console_text('dedimania_endrace - times' . CRLF . print_r($times, true));
}
$dedi_lastsent = time();
$callback = array('dedimania_endrace_cb', $data[1]);
$dedi_db['XmlrpcDB']->addRequest($callback,
'dedimania.ChallengeRaceTimes',
$data[1]['UId'],
$data[1]['Name'],
$data[1]['Environnement'],
$data[1]['Author'],
$aseco->server->getGame(),
$aseco->server->gameinfo->mode,
$numchecks,
$dedi_db['MaxRank'],
$times);
// ChallengeRaceTimes(Uid, Name, Environment, Author, Game, Mode, MaxGetTimes, Times)
// Times is an array of struct {'Login': string, 'Best': int, 'Checks': array of int or comma-separated string of int}
}
}
}
} // dedimania_endrace
function dedimania_endrace_cb($response, $challenge) {
global $aseco, $dedi_db, $dedi_debug;
//Reply a struct {'Uid': string, 'TotalRaces': int, 'TotalPlayers': int,
// 'TimeAttackRaces': int, 'TimeAttackPlayers': int,
// 'NumberOfChecks': int, 'ServerMaxRecords': int,
// 'Records': array of struct {'Login': string, 'NickName': string,
// 'Best': int, 'Rank': int,
// 'Checks': array of int, 'NewBest': boolean} }
if ($dedi_debug > 3)
$aseco->console_text('dedimania_endrace_cb - response' . CRLF . print_r($response, true));
elseif ($dedi_debug > 2)
$aseco->console_text('dedimania_endrace_cb - response[Data]' . CRLF . print_r($response['Data'], true));
elseif (($errors = dedi_iserror($response)) !== false)
$aseco->console_text('dedimania_endrace_cb - error(s): ' . $errors);
// check response
if (isset($response['Data']['params'])) {
$dedi_db['Results'] = $response['Data']['params'];
// check for records
if (!empty($dedi_db['Results']['Records'])) {
// strip line breaks in nicknames
foreach ($dedi_db['Results']['Records'] as &$rec) {
$rec['NickName'] = str_replace("\n", '', $rec['NickName']);
}
if ($dedi_debug > 1)
$aseco->console_text('dedimania_endrace_cb - results' . CRLF . print_r($dedi_db['Results'], true));
if ($dedi_db['ShowRecsAfter'] > 0)
show_dedirecs($aseco, $challenge['Name'], $challenge['UId'],
$dedi_db['Results']['Records'], false, 3,
$dedi_db['ShowRecsAfter']); // from chat.dedimania.php
}
// check for banned players
if (isset($response['Data']['errors']) &&
preg_match('/Warning.+Player TM.+is banned on Dedimania/', $response['Data']['errors'])) {
// log banned players
$errors = explode("\n", $response['Data']['errors']);
foreach ($errors as $error) {
if (preg_match('/Warning.+Player TM[A-Z]+:(.+) is banned on Dedimania/', $error, $login))
$aseco->console('[Dedimania] player {1} is banned - record ignored!', $login[1]);
}
}
} else {
if ($dedi_debug > 2)
$aseco->console('dedimania_endrace_cb - bad response!');
}
} // dedimania_endrace_cb
// called @ onPlayerFinish
function dedimania_playerfinish($aseco, $finish_item) {
global $dedi_db, $dedi_debug,
$checkpoints; // from plugin.checkpoints.php
// if no Dedimania records, bail out - Stunts mode temporarily too
if (!$dedi_db['RecsValid'] || $aseco->server->gameinfo->mode == Gameinfo::STNT) return;
// if no actual finish, bail out immediately
if ($finish_item->score == 0) return;
// in Laps mode on real PlayerFinish event, bail out too
if ($aseco->server->gameinfo->mode == Gameinfo::LAPS && !$finish_item->new) return;
$login = $finish_item->player->login;
$nickname = stripColors($finish_item->player->nickname);
// if LAN login, bail out immediately
if (isLANLogin($login)) return;
// if banned login, notify player and bail out
if (in_array($login, $dedi_db['BannedLogins'])) {
$message = formatText($dedi_db['Messages']['BANNED_FINISH'][0]);
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
return;
}
if ($dedi_debug > 4)
$aseco->console_text('dedimania_playerfinish - checkpoints ' . $login . CRLF . print_r($checkpoints[$login], true));
// check finish/checkpoints consistency, unless Stunts mode
if ($aseco->server->gameinfo->mode != Gameinfo::STNT) {
if (($aseco->server->gameinfo->mode != Gameinfo::LAPS && $finish_item->score != $checkpoints[$login]->curr_fin) ||
$finish_item->score != end($checkpoints[$login]->curr_cps)) {
$aseco->console('[Dedimania] player ' . $login . ' inconsistent finish/checks, ignored: ' . $finish_item->score . CRLF . print_r($checkpoints[$login], true));
return;
}
}
// point to master records list
$dedi_recs = &$dedi_db['Challenge']['Records'];
$maxrank = max($dedi_db['ServerMaxRank'], $finish_item->player->dedirank);
// go through all records
for ($i = 0; $i < $maxrank; $i++) {
// check if no record, or player's time/score is better
if (!isset($dedi_recs[$i]) || ($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$finish_item->score > $dedi_recs[$i]['Best'] :
$finish_item->score < $dedi_recs[$i]['Best'])) {
// does player have a record already?
$cur_rank = -1;
$cur_score = 0;
for ($rank = 0; $rank < count($dedi_recs); $rank++) {
$rec = $dedi_recs[$rank];
if ($login == $rec['Login'] && $rec['Game'] ==
($aseco->server->getGame() == 'TMF' ? 'TMU' : $aseco->server->getGame())) {
// new record worse than old one
if ($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$finish_item->score < $rec['Best'] :
$finish_item->score > $rec['Best']) {
return;
// new record is better than or equal to old one
} else {
$cur_rank = $rank;
$cur_score = $rec['Best'];
break;
}
}
}
$finish_time = $finish_item->score;
if ($aseco->server->gameinfo->mode != Gameinfo::STNT)
$finish_time = formatTime($finish_time);
if ($cur_rank != -1) { // player has a record in topXX already
// compute difference to old record
if ($aseco->server->gameinfo->mode != Gameinfo::STNT) {
$diff = $cur_score - $finish_item->score;
$sec = floor($diff/1000);
$hun = ($diff - ($sec * 1000)) / 10;
} else { // Stunts
$diff = $finish_item->score - $cur_score;
}
// update the record if improved
if ($diff > 0) {
// ignore 'Rank' field - not used in /dedi* commands
$dedi_recs[$cur_rank]['Best'] = $finish_item->score;
$dedi_recs[$cur_rank]['Checks'] = $checkpoints[$login]->curr_cps;
$dedi_recs[$cur_rank]['NewBest'] = true;
}
// player moved up in Dedimania list
if ($cur_rank > $i) {
// move record to the new position
moveArrayElement($dedi_recs, $cur_rank, $i);
// do a player improved his/her Dedimania rank message
$message = formatText($dedi_db['Messages']['RECORD_NEW_RANK'][0],
$nickname,
$i+1,
($aseco->server->gameinfo->mode == Gameinfo::STNT ? 'Score' : 'Time'),
$finish_time,
$cur_rank+1,
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
'+' . $diff : sprintf('-%d.%02d', $sec, $hun)));
// show chat message to all or player
if ($dedi_db['DisplayRecs']) {
if ($i < $dedi_db['LimitRecs']) {
if ($dedi_db['RecsInWindow'] && function_exists('send_window_message'))
send_window_message($aseco, $message, false);
else
$aseco->client->query('ChatSendServerMessage', $aseco->formatColors($message));
} else {
$message = str_replace('{#server}>> ', '{#server}> ', $message);
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
}
}
} else {
if ($diff == 0) {
// do a player equaled his/her record message
$message = formatText($dedi_db['Messages']['RECORD_EQUAL'][0],
$nickname,
$cur_rank+1,
($aseco->server->gameinfo->mode == Gameinfo::STNT ? 'Score' : 'Time'),
$finish_time);
} else {
// do a player secured his/her record message
$message = formatText($dedi_db['Messages']['RECORD_NEW'][0],
$nickname,
$i+1,
($aseco->server->gameinfo->mode == Gameinfo::STNT ? 'Score' : 'Time'),
$finish_time,
$cur_rank+1,
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
'+' . $diff : sprintf('-%d.%02d', $sec, $hun)));
}
// show chat message to all or player
if ($dedi_db['DisplayRecs']) {
if ($i < $dedi_db['LimitRecs']) {
if ($dedi_db['RecsInWindow'] && function_exists('send_window_message'))
send_window_message($aseco, $message, false);
else
$aseco->client->query('ChatSendServerMessage', $aseco->formatColors($message));
} else {
$message = str_replace('{#server}>> ', '{#server}> ', $message);
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
}
}
}
} else { // player hasn't got a record yet
// if previously tracking own/last Dedi record, now track new one
if ($checkpoints[$login]->dedirec == 0) {
$checkpoints[$login]->best_fin = $checkpoints[$login]->curr_fin;
$checkpoints[$login]->best_cps = $checkpoints[$login]->curr_cps;
// store timestamp for sorting in case of equal bests
$checkpoints[$login]->best_time = microtime(true);
}
// insert new record at the specified position
// ignore 'Rank' field - not used in /dedi* commands
$record = array('Game' => ($aseco->server->getGame() == 'TMF' ? 'TMU' : $aseco->server->getGame()),
'Login' => $login,
'NickName' => $finish_item->player->nickname,
'Best' => $finish_item->score,
'Checks' => $checkpoints[$login]->curr_cps,
'NewBest' => true);
insertArrayElement($dedi_recs, $record, $i);
// do a player drove first record message
$message = formatText($dedi_db['Messages']['RECORD_FIRST'][0],
$nickname,
$i+1,
($aseco->server->gameinfo->mode == Gameinfo::STNT ? 'Score' : 'Time'),
$finish_time);
// show chat message to all or player
if ($dedi_db['DisplayRecs']) {
if ($i < $dedi_db['LimitRecs']) {
if ($dedi_db['RecsInWindow'] && function_exists('send_window_message'))
send_window_message($aseco, $message, false);
else
$aseco->client->query('ChatSendServerMessage', $aseco->formatColors($message));
} else {
$message = str_replace('{#server}>> ', '{#server}> ', $message);
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
}
}
}
// log a new Dedimania record (not an equalled one)
if (isset($dedi_recs[$i]['NewBest']) && $dedi_recs[$i]['NewBest']) {
// update all panels if new #1 record
if ($aseco->server->getGame() == 'TMF' && $i == 0) {
setRecordsPanel('dedi', ($aseco->server->gameinfo->mode == Gameinfo::STNT ?
str_pad($finish_item->score, 5, ' ', STR_PAD_LEFT) :
formatTime($finish_item->score)));
if (function_exists('update_allrecpanels'))
update_allrecpanels($aseco, null); // from plugin.panels.php
}
// log record message in console
$aseco->console('[Dedimania] player {1} finished with {2} and took the {3}. WR place!',
$login, $finish_item->score, $i+1);
// throw 'Dedimania record' event
$dedi_recs[$i]['Pos'] = $i+1;
$aseco->releaseEvent('onDedimaniaRecord', $dedi_recs[$i]);
}
if ($dedi_debug > 1)
$aseco->console_text('dedimania_playerfinish - dedi_recs' . CRLF . print_r($dedi_recs, true));
// got the record, now stop!
return;
}
}
} // dedimania_playerfinish
/*
* Support functions
*/
function dedimania_players($aseco) {
global $dedi_debug;
// collect all players
$players = array();
foreach ($aseco->server->players->player_list as $pl) {
$pinfo = dedimania_playerinfo($aseco, $pl);
if ($pinfo !== false)
$players[] = $pinfo;
}
if ($dedi_debug > 2 || ($dedi_debug > 1 && count($players) > 0))
$aseco->console_text('dedimania_players - players' . CRLF . print_r($players, true));
return $players;
} // dedimania_players
function dedimania_playerinfo($aseco, $player) {
// check for non-LAN login
if (!isLANLogin($player->login)) {
$aseco->client->resetError();
// get current player info
if ($aseco->server->getGame() == 'TMF') {
$aseco->client->query('GetDetailedPlayerInfo', $player->login);
$info = $aseco->client->getResponse();
if ($aseco->client->isError()) {
return false;
} else {
$nation = explode('|', $info['Path']);
if (isset($nation[1]))
$nation = mapCountry($nation[1]);
else
$nation = mapCountry('');
return array('Login' => $info['Login'],
'Nation' => $nation,
'TeamName' => $info['LadderStats']['TeamName'],
'TeamId' => -1,
'IsSpec' => $info['IsSpectator'],
'Ranking' => $info['LadderStats']['PlayerRankings'][0]['Ranking'],
'IsOff' => $info['IsInOfficialMode']
);
}
} else { // TMN/TMS/TMO
$aseco->client->query('GetPlayerInfo', $player->login);
$info = $aseco->client->getResponse();
if ($aseco->client->isError()) {
return false;
} else {
return array('Login' => $info['Login'],
'Nation' => $info['Nation'],
'TeamName' => $info['LadderStats']['TeamName'],
'TeamId' => -1,
'IsSpec' => $info['IsSpectator'],
'Ranking' => $info['LadderStats']['Ranking'],
'IsOff' => $info['IsInOfficialMode']
);
}
}
}
return false;
} // dedimania_playerinfo
function dedimania_serverinfo($aseco) {
global $dedi_debug;
// compute number of players and spectators
$numplayers = 0;
$numspecs = 0;
foreach ($aseco->server->players->player_list as $pl) {
if ($aseco->isSpectator($pl))
$numspecs++;
else
$numplayers++;
}
// get current server options
$aseco->client->query('GetServerOptions');
$options = $aseco->client->getResponse();
$serverinfo = array('SrvName' => $options['Name'],
'Comment' => $options['Comment'],
'Private' => ($options['Password'] != ''),
'SrvIP' => '',
'SrvPort' => 0,
'XmlrpcPort' => 0,
'NumPlayers' => $numplayers,
'MaxPlayers' => $options['CurrentMaxPlayers'],
'NumSpecs' => $numspecs,
'MaxSpecs' => $options['CurrentMaxSpectators'],
'LadderMode' => $options['CurrentLadderMode'],
'NextFiveUID' => dedi_getnextuid($aseco)
);
if ($dedi_debug > 1)
$aseco->console_text('dedimania_serverinfo - serverinfo' . CRLF . print_r($serverinfo, true));
return $serverinfo;
} // dedimania_serverinfo
function dedi_getnextuid($aseco) {
global $jukebox; // from plugin.rasp_jukebox.php
// check for jukeboxed track
if (isset($jukebox) && !empty($jukebox)) {
$jbtemp = $jukebox;
$track = array_shift($jbtemp);
$next = $track['uid'];
} else {
// check server for next track
if ($aseco->server->getGame() != 'TMF') {
$aseco->client->query('GetCurrentChallengeIndex');
$current = $aseco->client->getResponse();
$aseco->client->query('GetChallengeList', 1, ++$current);
$track = $aseco->client->getResponse();
if ($aseco->client->isError()) {
$aseco->client->query('GetChallengeList', 1, 0);
$track = $aseco->client->getResponse();
}
} else { // TMF
$aseco->client->query('GetNextChallengeIndex');
$next = $aseco->client->getResponse();
$aseco->client->query('GetChallengeList', 1, $next);
$track = $aseco->client->getResponse();
}
$next = $track[0]['UId'];
}
return $next;
} // dedi_getnextuid
function dedi_iserror(&$response) {
if (!isset($response))
return 'No response!';
if (isset($response['Error'])) {
if (is_string($response['Error']) && strlen($response['Error']) > 0)
return $response['Error'];
}
if (isset($response['Data']['errors'])) {
if (is_string($response['Data']['errors']) && strlen($response['Data']['errors']) > 0)
return $response['Data']['errors'];
if (is_array($response['Data']['errors']) && count($response['Data']['errors']) > 0)
return print_r($response['Data']['errors'], true);
}
return false;
} // dedi_iserror
// usort comparison function: return -1 if $a should be before $b, 1 if vice-versa
function dedi_timecompare($a, $b) {
global $checkpoints; // from plugin.checkpoints.php
// best a better than best b
if ($a['Best'] < $b['Best'])
return -1;
// best b better than best a
elseif ($a['Best'] > $b['Best'])
return 1;
// same best, use timestamp
else
return ($checkpoints[$a['Login']]->best_time < $checkpoints[$b['Login']]->best_time) ? -1 : 1;
} // dedi_timecompare
?>