Compare commits

...

8 Commits

Author SHA1 Message Date
fanyx fcf04ef651 Rework image to use s6-overlay to run TMServer and Xaseco
- Adds S6-Overlay
- Bundles Xaseco into the image
2022-06-26 18:06:29 +02:00
fanyx 6ff45af1cb New specification 2022-06-26 18:06:19 +02:00
fanyx f1b87b46b3 Add backup SQL files
Not needed due to patches done to plugin.localdatabase.php in the past.
2022-06-26 18:05:50 +02:00
fanyx 8cd2157816 Add Xaseco scripts 2022-06-26 18:05:50 +02:00
fanyx e5ec30e564 Add Xaseco as s6-rc service 2022-06-26 18:05:50 +02:00
fanyx ffaeb8a8dc Add Xaseco base files 2022-06-26 18:05:50 +02:00
fanyx 53f162c802 Rework Trackmania server 2022-06-26 18:05:50 +02:00
fanyx d6eb6e7ad9 Clean up environment 2022-06-26 18:05:47 +02:00
165 changed files with 44359 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.gitignore
/.tmp
.env
.env-mysql

24
Dockerfile Normal file
View File

@ -0,0 +1,24 @@
FROM php:5.6-alpine
ARG S6_OVERLAY_VERSION=3.1.1.1
RUN apk add --no-cache pwgen gettext xmlstarlet bash xz
RUN docker-php-ext-install mysql
COPY tmserver/ /var/lib/tmserver
COPY xaseco/ /var/lib/xaseco
RUN addgroup -S trackmania && adduser -D -H -S trackmania -G trackmania
RUN chown -R trackmania:trackmania /var/lib/tmserver /var/lib/xaseco
EXPOSE 5000
CMD ["/var/lib/tmserver/entrypoint.sh"]
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz
ENTRYPOINT ["/init"]
RUN touch /etc/s6-overlay/s6-rc.d/user/contents.d/xaseco
COPY services.d/xaseco/ /etc/s6-overlay/s6-rc.d/xaseco/

37
docker-compose.yml Normal file
View File

@ -0,0 +1,37 @@
version: '3.8'
services:
tmserver:
image: fanyx/tmserver:2.0.0
container_name: trackmania_tmserver
depends_on:
- db
restart: always
env_file: .env
volumes:
- ./tracks:/var/lib/tmserver/GameData/Tracks/Challenges/Custom # => Custom tracks
- ./blacklist:/var/lib/xaseco/blacklist # => Plugin blacklist
- ./plugins:/var/lib/xaseco/plugins/custom # => Custom plugins
- ./config:/var/lib/xaseco/config # => Custom configuration files
ports:
- "2350:2350/udp"
- "3450:3450/udp"
db:
image: mysql:5
container_name: trackmania_db
restart: always
env_file: .env-mysql
volumes:
- trackmania-db:/var/lib/mysql
pma:
image: phpmyadmin/phpmyadmin
container_name: trackmania_phpmyadmin
depends_on:
- db
environment:
- PMA_HOST=db
restart: always
ports:
- "8080:80"
volumes:
trackmania-db:

18
services.d/xaseco/run Executable file
View File

@ -0,0 +1,18 @@
#!/command/with-contenv bash
set -e
[[ "$(id -u)" == 0 ]] && s6-setuidgid trackmania "$0"
cd /var/lib/xaseco
# Parse environment to configuration files
./bin/eval_env.sh
# Link custom configuration files
./bin/config.sh
# Parse plugin list
./bin/plugins.sh
exec "php" "aseco.php"

1
services.d/xaseco/type Normal file
View File

@ -0,0 +1 @@
longrun

View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8" ?>
<dedicated>
<authorization_levels>
<level>
<name>SuperAdmin</name>
<password>@SERVER_SA_PASSWORD@</password>
</level>
<level>
<name>Admin</name>
<password>@SERVER_ADM_PASSWORD@</password>
</level>
<level>
<name>User</name>
<password>User</password>
</level>
</authorization_levels>
<masterserver_account>
<login>@SERVER_LOGIN@</login>
<password>@SERVER_LOGIN_PASSWORD@</password>
<validation_key>@VALIDATION_KEY@</validation_key>
</masterserver_account>
<server_options>
<name>@SERVER_NAME@</name>
<comment>@SERVER_COMMENT@</comment>
<hide_server>@HIDE_SERVER@</hide_server> <!-- value is 0 (always shown), 1 (always hidden), 2 (hidden from nations) -->
<max_players>@MAX_PLAYERS@</max_players>
<password>@SERVER_PASSWORD@</password>
<max_spectators>32</max_spectators>
<password_spectator></password_spectator>
<ladder_mode>forced</ladder_mode> <!-- value between 'inactive', 'forced' (or '0', '1') -->
<ladder_serverlimit_min>0</ladder_serverlimit_min> <!-- Those values will be clamped to the limits authorized on http://official.trackmania.com/tmf-ladderserver/ -->
<ladder_serverlimit_max>50000</ladder_serverlimit_max>
<enable_p2p_upload>True</enable_p2p_upload>
<enable_p2p_download>True</enable_p2p_download>
<callvote_timeout>60000</callvote_timeout>
<callvote_ratio>0.5</callvote_ratio> <!-- default ratio. value in [0..1], or -1 to forbid. -->
<callvote_ratios>
<voteratio command="Ban" ratio="0.65"/>
<!-- commands can be "Ban", "Kick", "ChallengeRestart", "NextChallenge", ... -->
</callvote_ratios>
<allow_challenge_download>True</allow_challenge_download>
<autosave_replays>False</autosave_replays>
<autosave_validation_replays>False</autosave_validation_replays>
<referee_password></referee_password>
<referee_validation_mode>0</referee_validation_mode> <!-- value is 0 (only validate top3 players), 1 (validate all players) -->
<use_changing_validation_seed>False</use_changing_validation_seed>
</server_options>
<system_config>
<connection_uploadrate>512</connection_uploadrate> <!-- Kbps (kilo bits per second) -->
<connection_downloadrate>8192</connection_downloadrate> <!-- Kbps -->
<force_ip_address></force_ip_address>
<server_port>$SERVER_PORT</server_port>
<server_p2p_port>$SERVER_P2P_PORT</server_p2p_port>
<client_port>0</client_port>
<bind_ip_address></bind_ip_address>
<use_nat_upnp></use_nat_upnp>
<p2p_cache_size>600</p2p_cache_size>
<xmlrpc_port>5000</xmlrpc_port>
<xmlrpc_allowremote>True</xmlrpc_allowremote> <!-- If you specify an ip adress here, it'll be the only accepted adress. this will improve security. -->
<blacklist_url></blacklist_url>
<guestlist_filename></guestlist_filename>
<blacklist_filename></blacklist_filename>
<packmask>stadium</packmask>
<allow_spectator_relays>False</allow_spectator_relays>
<!-- <minimum_client_build>2009-10-01</minimum_client_build> -->
<!-- <disable_coherence_checks>laps</disable_coherence_checks> -->
<use_proxy>False</use_proxy>
<proxy_login></proxy_login>
<proxy_password></proxy_password>
</system_config>
</dedicated>

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8" ?>
<playlist>
<gameinfos>
<game_mode>@GAMEMODE@</game_mode>
<chat_time>@CHATTIME@</chat_time>
<finishtimeout>@FINISHTIMEOUT@</finishtimeout>
<allwarmupduration>0</allwarmupduration>
<disablerespawn>@DISABLERESPAWN@</disablerespawn>
<forceshowallopponents>0</forceshowallopponents>
<rounds_pointslimit>@ROUNDS_POINTSLIMIT@</rounds_pointslimit>
<rounds_usenewrules>0</rounds_usenewrules>
<rounds_forcedlaps>0</rounds_forcedlaps>
<rounds_pointslimitnewrules>5</rounds_pointslimitnewrules>
<team_pointslimit>@TEAM_POINTSLIMIT@</team_pointslimit>
<team_maxpoints>@TEAM_MAXPOINTS@</team_maxpoints>
<team_usenewrules>0</team_usenewrules>
<team_pointslimitnewrules>5</team_pointslimitnewrules>
<timeattack_limit>@TIMEATTACK_LIMIT@</timeattack_limit>
<timeattack_synchstartperiod>0</timeattack_synchstartperiod>
<laps_nblaps>@LAPS_NBLAPS@</laps_nblaps>
<laps_timelimit>@LAPS_TIMELIMIT@</laps_timelimit>
<cup_pointslimit>@CUP_POINTSLIMIT@</cup_pointslimit>
<cup_roundsperchallenge>@CUP_ROUNDSPERCHALLENGE@</cup_roundsperchallenge>
<cup_nbwinners>@CUP_NBWINNERS@</cup_nbwinners>
<cup_warmupduration>@CUP_WARMUPDURATION@</cup_warmupduration>
</gameinfos>
<filter>
<is_lan>1</is_lan>
<is_internet>1</is_internet>
<is_solo>0</is_solo>
<is_hotseat>0</is_hotseat>
<sort_index>7</sort_index>
<random_map_order>0</random_map_order>
<force_default_gamemode>0</force_default_gamemode>
</filter>
<startindex>0</startindex>
</playlist>

72
tmserver/bin/eval_env.sh Executable file
View File

@ -0,0 +1,72 @@
#!/command/with-contenv bash
config=( )
playlist=( )
# Mandatory
SERVER_SA_PASSWORD=${SERVER_SA_PASSWORD:?ERROR | SuperAdminPassword needs to be set. Generate with pwgen if needed.} && \
config+=( "SERVER_SA_PASSWORD" )
SERVER_ADM_PASSWORD=${SERVER_ADM_PASSWORD:?ERROR | AdminPassword needs to be set. Generate with pwgen if needed.} && \
config+=( "SERVER_ADM_PASSWORD" )
SERVER_LOGIN=${SERVER_LOGIN?:ERROR | ServerLogin is missing. Server cannot start.} && \
config+=( "SERVER_LOGIN" )
SERVER_LOGIN_PASSWORD=${SERVER_LOGIN_PASSWORD?:ERROR | ServerLoginPassword is missing. Server cannot start.} && \
config+=( "SERVER_LOGIN_PASSWORD" )
# Optional
SERVER_PORT=${SERVER_PORT:-2350} && config+=( "SERVER_PORT" )
echo "INFO | SERVER_PORT: ${SERVER_PORT}"
SERVER_P2P_PORT=${SERVER_P2P_PORT:-3450} && config+=( "SERVER_P2P_PORT" )
echo "INFO | SERVER_P2P_PORT: ${SERVER_P2P_PORT}"
SERVER_NAME=${SERVER_NAME:-Trackmania Server} && config+=( "SERVER_NAME" )
echo "INFO | SERVER_NAME: ${SERVER_NAME}"
SERVER_COMMENT=${SERVER_COMMENT:-This is a Trackmania Server} && config+=( "SERVER_COMMENT" )
echo "INFO | SERVER_COMMENT: ${SERVER_COMMENT}"
SERVER_PASSWORD=${SERVER_PASSWORD} && config+=( "SERVER_PASSWORD" )
echo "INFO | SERVER_PASSWORD: ${SERVER_PASSWORD}"
HIDE_SERVER=${HIDE_SERVER:-0} && config+=( "HIDE_SERVER" )
echo "INFO | HIDE_SERVER: ${HIDE_SERVER}"
MAX_PLAYERS=${MAX_PLAYERS:-32} && config+=( "MAX_PLAYERS" )
echo "INFO | MAX_PLAYERS: ${MAX_PLAYERS}"
# Game Config
GAMEMODE=${GAMEMODE:-1} && playlist+=( "GAMEMODE" )
echo "INFO | GAMEMODE: ${GAMEMODE} | 1 = TimeAttack"
CHATTIME=${CHATTIME:-10000} && playlist+=( "CHATTIME" )
echo "INFO | CHATTTIME: ${CHATTIME} ms"
FINISHTIMEOUT=${FINISHTIMEOUT:-1} && playlist+=( "FINISHTIMEOUT" )
echo "INFO | FINISHTIMEOUT: ${FINISHTIMEOUT}"
DISABLERESPAWN=${DISABLERESPAWN:-0} && playlist+=( "DISABLERESPAWN" )
echo "INFO | DISABLERESPAWN: ${DISABLERESPAWN}"
ROUNDS_POINTSLIMIT=${ROUNDS_POINTSLIMIT:-30} && playlist+=( "ROUNDS_POINTSLIMIT" )
echo "INFO | ROUNDS_POINTSLIMIT: ${ROUNDS_POINTSLIMIT}"
TIMEATTACK_LIMIT=${TIMEATTACK_LIMIT:-180000} && playlist+=( "TIMEATTACK_LIMIT" )
echo "INFO | TIMEATTACK_LIMIT: ${TIMEATTACK_LIMIT} ms"
TEAM_POINTSLIMIT=${TEAM_POINTSLIMIT:-50} && playlist+=( "TEAM_POINTSLIMIT" )
echo "INFO | TEAM_POINTSLIMIT: ${TEAM_POINTSLIMIT}"
TEAM_MAXPOINTS=${TEAM_MAXPOINTS:-6} && playlist+=( "TEAM_MAXPOINTS" )
echo "INFO | TEAM_MAXPOINTS: ${TEAM_MAXPOINTS}"
LAPS_NBLAPS=${LAPS_NBLAPS:-5} && playlist+=( "LAPS_NBLAPS" )
echo "INFO | LAPS_NBLAPS: ${LAPS_NBLAPS}"
LAPS_TIMELIMIT=${LAPS_TIMELIMIT:-0} && playlist+=( "LAPS_TIMELIMIT" )
echo "INFO | LAPS_TIMELIMIT: ${LAPS_TIMELIMIT}"
CUP_POINTSLIMIT=${CUP_POINTSLIMIT:-100} && playlist+=( "CUP_POINTSLIMIT" )
echo "INFO | CUP_POINTSLIMIT: ${CUP_POINTSLIMIT}"
CUP_ROUNDSPERCHALLENGE=${CUP_ROUNDSPERCHALLENGE:-5} && playlist+=( "CUP_ROUNDSPERCHALLENGE" )
echo "INFO | CUP_ROUNDSPERCHALLENGE: ${CUP_ROUNDSPERCHALLENGE}"
CUP_NBWINNERS=${CUP_NBWINNERS:-3} && playlist+=( "CUP_NBWINNERS" )
echo "INFO | CUP_NBWINNERS: ${CUP_NBWINNERS}"
CUP_WARMUPDURATION=${CUP_WARMUPDURATION:-2} && playlist+=( "CUP_WARMUPDURATION" )
echo "INFO | CUP_WARMUPDURATION: ${CUP_WARMUPDURATION}"
# Parse config.xml
for idx in "${!config[@]}"; do
arg=${config[$idx]}
sed -i -e "s/@$arg@/${!arg}/g" GameData/Config/config.xml
done
# Parse playlist.xml
for idx in "${!playlist[@]}"; do
arg=${playlist[$idx]}
sed -i -e "s/@$arg@/${!arg}/g" GameData/Tracks/MatchSettings/playlist.xml
done

20
tmserver/bin/eval_playlist.sh Executable file
View File

@ -0,0 +1,20 @@
#!/command/with-contenv bash
echo "INFO | Parsing custom playlist..."
CUSTOM_PLAYLIST=${CUSTOM_PLAYLIST:-playlist.txt}
PLAYLIST_FILE='GameData/Tracks/MatchSettings/playlist.xml'
if [[ -f "${CUSTOM_PLAYLIST}" ]]; then
count=1
while read l; do
xmlstarlet ed -L -s /playlist -t elem -n challenge $PLAYLIST_FILE
xmlstarlet ed -L -s "/playlist/challenge[${count}]" -t elem -n file -v "${l}" $PLAYLIST_FILE
count=$((count+1))
done < $CUSTOM_PLAYLIST
else
xmlstarlet ed -L -s /playlist -t elem -n challenge $PLAYLIST_FILE
xmlstarlet ed -L -s "playlist/challenge[1]" -t elem -n file -v "Challenges/Nadeo/A01-Race.Challenge.Gbx" $PLAYLIST_FILE
fi
echo "INFO | Finished parsing playlist files."

15
tmserver/entrypoint.sh Executable file
View File

@ -0,0 +1,15 @@
#!/command/with-contenv bash
set -e
[[ "$(id -u)" == 0 ]] && s6-setuidgid trackmania "$0"
cd /var/lib/tmserver
# Parse config files
./bin/eval_env.sh
# Parse playlist files
./bin/eval_playlist.sh
exec "./TrackmaniaServer" "/nodaemon" "/internet" "/game_settings=MatchSettings/playlist.xml" "/dedicated_cfg=config.xml"

93
xaseco/access.xml Normal file
View File

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8" ?>
<access>
<!-- For a detailed explanation of this three-pass access control system,
see: http://httpd.apache.org/docs/2.0/mod/mod_access.html
The first pass checks all Allow or Deny entries, as specified by the
Order directive. The second pass checks all Deny or Allow entries.
The third pass applies to nations/zones which didn't match before.
The last value in Order thus controls the default behavior when no
match is found among Allow and Deny, and when both sections match.
From values are case-sensitive, upper- and lowercase characters must
match exactly with the players' nations/zones you want to control.
Special From value 'all' matches all nations/zones, and can be used
only once in either the Allow or Deny section, with no other From
fields in that section.
Examples of some common situations:
1. On TMN allow only players from the Benelux:
<order>Deny,Allow</order>
<allow>
<from>BEL</from>
<from>NED</from>
<from>LUX</from>
</allow>
<deny>
<from>all</from>
</deny>
2. The same policy can also be configured like this (note that
because Deny is last, it cannot contain 'all' or the allowed
nations would be denied as yet):
<order>Allow,Deny</order>
<allow>
<from>BEL</from>
<from>NED</from>
<from>LUX</from>
</allow>
<deny>
<from></from>
</deny>
3. On TMF allow all players except from France and Great Britain:
<order>Deny,Allow</order>
<allow>
<from></from>
</allow>
<deny>
<from>France</from>
<from>United Kingdom</from>
</deny>
And a more complex one:
On TMF allow only players from Germany and Austria, except Berlin,
Vienna, and the entire Bavaria region:
<order>Allow,Deny</order>
<allow>
<from>Germany</from>
<from>Austria</from>
</allow>
<deny>
<from>Germany|Berlin</from>
<from>Germany|Bavaria</from>
<from>Austria|Vienna</from>
</deny>
Configure your server's access policies below:
-->
<order>Deny,Allow</order>
<allow>
<from>all</from>
</allow>
<deny>
<from></from>
</deny>
<messages>
<denied>{#server}>> {#message}Player {#highlite}{1}$z$s{#message} denied access from {2} {#highlite}{3}{#message} [{#error}Kicked $z$s{#message}]</denied>
<dialog>{#message}Access from zone {#highlite}{1} {#message}denied$z</dialog>
<reload>{#server}> Player access control reloaded from {#highlite}access.xml</reload>
<xmlerr>{#server}> {#highlite}access.xml {#error}config error, player access control disabled!</xmlerr>
<missing>{#server}> {#error}Missing parameter, use {#highlite}$i /admin access help {#error}!</missing>
</messages>
</access>

318
xaseco/adminops.xml Normal file
View File

@ -0,0 +1,318 @@
<?xml version="1.0" encoding="utf-8" ?>
<lists>
<titles>
<masteradmin>MasterAdmin</masteradmin>
<admin>Admin</admin>
<operator>Operator</operator>
</titles>
<!-- format:
<admins>
<tmlogin>YOUR_ADMIN_LOGIN</tmlogin> <ipaddress></ipaddress>
</admins>
-->
<operators>
<!-- format:
<tmlogin>YOUR_OPERATOR_LOGIN</tmlogin> <ipaddress></ipaddress>
-->
</operators>
<admin_abilities>
<help>true</help>
<helpall>true</helpall>
<setservername>false</setservername>
<setcomment>false</setcomment>
<setpwd>true</setpwd>
<setspecpwd>true</setspecpwd>
<setrefpwd>true</setrefpwd>
<setmaxplayers>false</setmaxplayers>
<setmaxspecs>false</setmaxspecs>
<setgamemode>true</setgamemode>
<setrefmode>true</setrefmode>
<nextmap>true</nextmap>
<next>true</next>
<skipmap>true</skipmap>
<skip>true</skip>
<previous>true</previous>
<prev>true</prev>
<nextenv>true</nextenv>
<restartmap>true</restartmap>
<res>true</res>
<replaymap>true</replaymap>
<replay>true</replay>
<dropjukebox>true</dropjukebox>
<djb>true</djb>
<clearjukebox>true</clearjukebox>
<cjb>true</cjb>
<clearhist>true</clearhist>
<add>true</add>
<addthis>true</addthis>
<addlocal>true</addlocal>
<warn>true</warn>
<kick>true</kick>
<kickghost>true</kickghost>
<ban>true</ban>
<unban>true</unban>
<banip>true</banip>
<unbanip>true</unbanip>
<black>true</black>
<unblack>true</unblack>
<addguest>true</addguest>
<removeguest>true</removeguest>
<pass>true</pass>
<cancel>true</cancel>
<can>true</can>
<endround>true</endround>
<er>true</er>
<players>true</players>
<showbanlist>true</showbanlist>
<listbans>true</listbans>
<showiplist>true</showiplist>
<listips>true</listips>
<showblacklist>true</showblacklist>
<listblacks>true</listblacks>
<showguestlist>true</showguestlist>
<listguests>true</listguests>
<writeiplist>true</writeiplist>
<readiplist>true</readiplist>
<writeblacklist>true</writeblacklist>
<readblacklist>true</readblacklist>
<writeguestlist>true</writeguestlist>
<readguestlist>true</readguestlist>
<cleanbanlist>false</cleanbanlist>
<cleaniplist>false</cleaniplist>
<cleanblacklist>false</cleanblacklist>
<cleanguestlist>false</cleanguestlist>
<mergegbl>false</mergegbl>
<access>false</access>
<writetracklist>true</writetracklist>
<readtracklist>true</readtracklist>
<shuffle>true</shuffle>
<shufflemaps>true</shufflemaps>
<listdupes>true</listdupes>
<remove>true</remove>
<erase>true</erase>
<removethis>true</removethis>
<erasethis>true</erasethis>
<mute>true</mute>
<ignore>true</ignore>
<unmute>true</unmute>
<unignore>true</unignore>
<mutelist>true</mutelist>
<ignorelist>true</ignorelist>
<listmutes>true</listmutes>
<listignores>true</listignores>
<cleanmutes>false</cleanmutes>
<cleanignores>false</cleanignores>
<addadmin>false</addadmin>
<removeadmin>false</removeadmin>
<addop>true</addop>
<removeop>true</removeop>
<listmasters>true</listmasters>
<listadmins>true</listadmins>
<listops>true</listops>
<adminability>false</adminability>
<opability>false</opability>
<listabilities>true</listabilities>
<writeabilities>true</writeabilities>
<readabilities>true</readabilities>
<wall>true</wall>
<mta>true</mta>
<delrec>false</delrec>
<prunerecs>false</prunerecs>
<rpoints>true</rpoints>
<match>true</match>
<acdl>false</acdl>
<autotime>true</autotime>
<disablerespawn>true</disablerespawn>
<forceshowopp>true</forceshowopp>
<scorepanel>true</scorepanel>
<roundsfinish>true</roundsfinish>
<forceteam>true</forceteam>
<forcespec>true</forcespec>
<specfree>true</specfree>
<panel>true</panel>
<style>false</style>
<admpanel>false</admpanel>
<donpanel>false</donpanel>
<recpanel>false</recpanel>
<votepanel>false</votepanel>
<coppers>true</coppers>
<pay>false</pay>
<relays>true</relays>
<server>true</server>
<pm>true</pm>
<pmlog>true</pmlog>
<call>false</call>
<unlock>true</unlock>
<debug>false</debug>
<shutdown>false</shutdown>
<shutdownall>false</shutdownall>
<uptodate>true</uptodate>
<chat_pma>true</chat_pma>
<chat_bestworst>true</chat_bestworst>
<chat_statsip>true</chat_statsip>
<chat_settings>true</chat_settings>
<chat_summary>true</chat_summary>
<chat_jukebox>true</chat_jukebox>
<chat_jb_multi>true</chat_jb_multi>
<chat_jb_recent>true</chat_jb_recent>
<chat_add_tref>false</chat_add_tref>
<chat_match>false</chat_match>
<chat_tc_listen>false</chat_tc_listen>
<chat_jfreu>true</chat_jfreu>
<chat_musicadmin>true</chat_musicadmin>
<noidlekick_play>false</noidlekick_play>
<noidlekick_spec>true</noidlekick_spec>
<server_coppers>true</server_coppers>
</admin_abilities>
<operator_abilities>
<help>true</help>
<helpall>true</helpall>
<setservername>false</setservername>
<setcomment>false</setcomment>
<setpwd>false</setpwd>
<setspecpwd>false</setspecpwd>
<setrefpwd>false</setrefpwd>
<setmaxplayers>false</setmaxplayers>
<setmaxspecs>false</setmaxspecs>
<setgamemode>false</setgamemode>
<setrefmode>false</setrefmode>
<nextmap>true</nextmap>
<next>true</next>
<skipmap>true</skipmap>
<skip>true</skip>
<previous>true</previous>
<prev>true</prev>
<nextenv>true</nextenv>
<restartmap>true</restartmap>
<res>true</res>
<replaymap>true</replaymap>
<replay>true</replay>
<dropjukebox>true</dropjukebox>
<djb>true</djb>
<clearjukebox>true</clearjukebox>
<cjb>true</cjb>
<clearhist>false</clearhist>
<add>false</add>
<addthis>false</addthis>
<addlocal>false</addlocal>
<warn>true</warn>
<kick>true</kick>
<kickghost>true</kickghost>
<ban>false</ban>
<unban>false</unban>
<banip>false</banip>
<unbanip>false</unbanip>
<black>false</black>
<unblack>false</unblack>
<addguest>true</addguest>
<removeguest>true</removeguest>
<pass>true</pass>
<cancel>true</cancel>
<can>true</can>
<endround>true</endround>
<er>true</er>
<players>true</players>
<showbanlist>true</showbanlist>
<listbans>true</listbans>
<showiplist>true</showiplist>
<listips>true</listips>
<showblacklist>true</showblacklist>
<listblacks>true</listblacks>
<showguestlist>true</showguestlist>
<listguests>true</listguests>
<writeiplist>false</writeiplist>
<readiplist>false</readiplist>
<writeblacklist>false</writeblacklist>
<readblacklist>false</readblacklist>
<writeguestlist>false</writeguestlist>
<readguestlist>false</readguestlist>
<cleanbanlist>false</cleanbanlist>
<cleaniplist>false</cleaniplist>
<cleanblacklist>false</cleanblacklist>
<cleanguestlist>false</cleanguestlist>
<mergegbl>false</mergegbl>
<access>false</access>
<writetracklist>false</writetracklist>
<readtracklist>false</readtracklist>
<shuffle>false</shuffle>
<shufflemaps>false</shufflemaps>
<listdupes>false</listdupes>
<remove>false</remove>
<erase>false</erase>
<removethis>false</removethis>
<erasethis>false</erasethis>
<mute>true</mute>
<ignore>true</ignore>
<unmute>true</unmute>
<unignore>true</unignore>
<mutelist>true</mutelist>
<ignorelist>true</ignorelist>
<listmutes>true</listmutes>
<listignores>true</listignores>
<cleanmutes>false</cleanmutes>
<cleanignores>false</cleanignores>
<addadmin>false</addadmin>
<removeadmin>false</removeadmin>
<addop>false</addop>
<removeop>false</removeop>
<listmasters>true</listmasters>
<listadmins>true</listadmins>
<listops>true</listops>
<adminability>false</adminability>
<opability>false</opability>
<listabilities>true</listabilities>
<writeabilities>false</writeabilities>
<readabilities>false</readabilities>
<wall>false</wall>
<mta>false</mta>
<delrec>false</delrec>
<prunerecs>false</prunerecs>
<rpoints>false</rpoints>
<match>false</match>
<acdl>false</acdl>
<autotime>false</autotime>
<disablerespawn>false</disablerespawn>
<forceshowopp>false</forceshowopp>
<scorepanel>true</scorepanel>
<roundsfinish>true</roundsfinish>
<forceteam>true</forceteam>
<forcespec>true</forcespec>
<specfree>true</specfree>
<panel>true</panel>
<style>false</style>
<admpanel>false</admpanel>
<donpanel>false</donpanel>
<recpanel>false</recpanel>
<votepanel>false</votepanel>
<coppers>false</coppers>
<pay>false</pay>
<relays>true</relays>
<server>false</server>
<pm>false</pm>
<pmlog>false</pmlog>
<call>false</call>
<unlock>true</unlock>
<debug>false</debug>
<shutdown>false</shutdown>
<shutdownall>false</shutdownall>
<uptodate>false</uptodate>
<chat_pma>false</chat_pma>
<chat_bestworst>false</chat_bestworst>
<chat_statsip>false</chat_statsip>
<chat_settings>true</chat_settings>
<chat_summary>false</chat_summary>
<chat_jukebox>false</chat_jukebox>
<chat_jb_multi>false</chat_jb_multi>
<chat_jb_recent>false</chat_jb_recent>
<chat_add_tref>false</chat_add_tref>
<chat_match>false</chat_match>
<chat_tc_listen>false</chat_tc_listen>
<chat_jfreu>false</chat_jfreu>
<chat_musicadmin>false</chat_musicadmin>
<noidlekick_play>false</noidlekick_play>
<noidlekick_spec>true</noidlekick_spec>
<server_coppers>false</server_coppers>
</operator_abilities>
</lists>

2562
xaseco/aseco.php Normal file

File diff suppressed because it is too large Load Diff

15
xaseco/autotime.xml Normal file
View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<autotime>
<!-- Set the multiplicator for author time (e.g. 6x author -->
<!-- time = new timelimit); set to 0 to disable function -->
<multiplicator>6</multiplicator>
<!-- Set minimum timelimit in minutes -->
<mintime>3.5</mintime>
<!-- Set maximum timelimit in minutes -->
<maxtime>7</maxtime>
<!-- Set default timelimit in minutes if any error occurs -->
<defaulttime>5</defaulttime>
<!-- 2 = in TMF message window, 1 = in chat, 0 = none -->
<display>1</display>
<message>{#server}>> Set {1} timelimit for {#highlite}{2}{#server} : {#highlite}{3}{#server} (Author time: {#highlite}{4}{#server})</message>
</autotime>

6
xaseco/bannedips.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<ban_list>
<!-- format:
<ipaddress>xx.xx.xx.xx</ipaddress>
-->
</ban_list>

10
xaseco/bin/config.sh Executable file
View File

@ -0,0 +1,10 @@
#!/command/with-contenv bash
if ls config/* &>/dev/null; then
echo "INFO | Linking custom configuration files..."
for i in config/*
do
ln -sf $i .
done
echo "INFO | Custom configuration done."
fi

44
xaseco/bin/eval_env.sh Executable file
View File

@ -0,0 +1,44 @@
#!/command/with-contenv bash
config=( )
database=( )
# Config
MASTERADMIN_LOGIN=${MASTERADMIN_LOGIN:?ERROR | One player needs to be assigned the MasterAdmin role.} && \
config+=( "MASTERADMIN_LOGIN" )
SERVER_SA_PASSWORD=${SERVER_SA_PASSWORD:?ERROR | SuperAdminPassword was not given. Please refer to your TMServer configuration.} && \
config+=( "SERVER_SA_PASSWORD" )
# Optional
TMSERVER_HOST=${TMSERVER_HOST:-localhost} && \
config+=( "TMSERVER_HOST" )
echo "INFO | TMSERVER_HOST: ${TMSERVER_HOST}"
TMSERVER_PORT=${TMSERVER_PORT:-5000} && \
config+=( "TMSERVER_PORT" )
echo "INFO | TMSERVER_PORT: ${TMSERVER_PORT}"
# Local Database
MYSQL_HOST=${MYSQL_HOST:-db} && \
database+=( "MYSQL_HOST" )
echo "INFO | MYSQL_HOST: ${MYSQL_HOST}"
MYSQL_LOGIN=${MYSQL_LOGIN:?ERROR | MySQL username was not given...} && \
database+=( "MYSQL_LOGIN" )
echo "INFO | MYSQL_LOGIN: ${MYSQL_LOGIN}"
MYSQL_PASSWORD=${MYSQL_PASSWORD:?ERROR | MySQL password was not given...} && \
database+=( "MYSQL_PASSWORD" )
echo "INFO | MYSQL_PASSWORD: ${MYSQL_PASSWORD}"
MYSQL_DATABASE=${MYSQL_DATABASE:-trackmania} && \
database+=( "MYSQL_DATABASE" )
echo "INFO | MYSQL_DATABASE: ${MYSQL_DATABASE}"
# Parse config.xml
for idx in "${!config[@]}"; do
arg=${config[$idx]}
sed -i -e "s/@$arg@/${!arg}/g" config.xml
done
# Parse localdatabase.xml
for idx in "${!database[@]}"; do
arg=${database[$idx]}
sed -i -e "s/@$arg@/${!arg}/g" localdatabase.xml
done

44
xaseco/bin/plugins.sh Executable file
View File

@ -0,0 +1,44 @@
#!/command/with-contenv bash
cd /var/lib/xaseco
XML_HEADER='<?xml version="1.0" encoding="utf-8" ?>\n<aseco_plugins>\n'
XML_FOOTER='</aseco_plugins>'
if ls plugins/custom/* &>/dev/null; then
for i in plugins/custom/*
do
ln -sf ${i#*/} plugins/
done
fi
PLUGINS_LIST=($(ls -d plugins/*.php | sed -e 's/plugins\///g'))
[[ -r ./blacklist ]] && \
PLUGINS_LIST=($(echo ${PLUGINS_LIST[@]} | tr ' ' '\n' | grep -vFf blacklist))
{
# open with header -- \n interpreted
printf "%b" "$XML_HEADER"
# main block -- parse plugin list
{
[[ "${PLUGINS_LIST[@]}" =~ "plugin.localdatabase.php" ]] && printf " <plugin>plugin.localdatabase.php</plugin>\n"
for plugin in "${PLUGINS_LIST[@]}"
do
case "${plugin}" in
"plugin.localdatabase.php")
;;
"plugin.records_eyepiece.php")
;;
*)
printf " <plugin>%s</plugin>\n" "${plugin}"
;;
esac
done
[[ "${PLUGINS_LIST[@]}" =~ "plugin.records_eyepiece.php" ]] && printf " <plugin>plugin.records_eyepiece.php</plugin>\n"
}
# finish with footer -- \n interpreted
printf "%b" "$XML_FOOTER"
} > plugins.xml

219
xaseco/config.xml Normal file
View File

@ -0,0 +1,219 @@
<?xml version="1.0" encoding="utf-8" ?>
<settings>
<aseco>
<masteradmins>
<!-- /ip:port in tmlogin only needed when joining server over LAN -->
<!-- ipaddress can be specified to guard each login against -->
<!-- unauthorized use of admin commands from other IP addresses -->
<tmlogin>@MASTERADMIN_LOGIN@</tmlogin> <ipaddress></ipaddress>
</masteradmins>
<colors>
<error>$f00$i</error>
<welcome>$f00</welcome>
<server>$ff0</server>
<highlite>$fff</highlite>
<timelite>$bbb</timelite>
<record>$0f3</record>
<emotic>$fa0</emotic>
<music>$d80</music>
<message>$39f</message>
<rank>$ff3</rank>
<vote>$f8f</vote>
<karma>$ff0</karma>
<donate>$f0f</donate>
<admin>$ff0</admin>
<black>$000</black>
<grey>$888</grey>
<login>$00f</login>
<logina>$0c0</logina>
<nick>$f00</nick>
<interact>$ff0$i</interact>
<dedimsg>$28b</dedimsg>
<dedirec>$0b3</dedirec>
</colors>
<messages>
<!-- init messages -->
<startup>{#server}*** XASECO {#highlite}v{1}{#server} running on {#highlite}{2}{#server}:{#highlite}{3}{#server} ***</startup>
<welcome>{#welcome}Welcome {#highlite}{1}{#welcome} to {#highlite}{2}$z$s{br}{#welcome}This server uses {#highlite}XASECO v{3}{#welcome} to manage your records.</welcome>
<warning>$s{#welcome}This is an administrative warning.{br}{br}$gWhatever you wrote is against our server's{br}policy. Not respecting other players, or{br}using offensive language might result in a{br}{#welcome}kick, or ban {#message}the next time.{br}{br}$gThe server administrators.</warning>
<!-- record messages -->
<record_current>{#server}>> {#message}Current record on {#highlite}{1}{#message} is {#highlite}{2}{#message} by {#highlite}{3}</record_current>
<record_none>{#server}>> {#message}Currently no record on {#highlite}{1}{#message} ...</record_none>
<record_error>{#server}>> {#error}Could not get records from database... No records this round!</record_error>
<!-- ranking messages -->
<ranking>{#server}>> {#message}Local Record rankings on {#highlite}{1}{#message} {2} this round:</ranking>
<ranking_range>{#server}>> {#message}Local Record rankings on {#highlite}{1}{#message} {2} this round (range {#highlite}{3}{#message}):</ranking_range>
<ranking_new>{#server}>> {#message}Local Record rankings on {#highlite}{1}{#message} {2} this round ({#highlite}{3}{#message} new):</ranking_new>
<ranking_nonew>{#server}>> {#message}Local Record rankings on {#highlite}{1}{#message} {2} this round: none new so far</ranking_nonew>
<ranking_none>{#server}>> {#message}Local Record rankings on {#highlite}{1}{#message} {2} this round: no records!</ranking_none>
<!-- record misc. messages -->
<ranking_record_new_on>{#rank}{1}{#message}.$i{#highlite}{2}{#message}[{#highlite}{3}{#message}]$i, </ranking_record_new_on>
<ranking_record_new>{#rank}{1}{#message}.{#highlite}{2}{#message}[{#highlite}{3}{#message}], </ranking_record_new>
<ranking_record_on>{#rank}{1}{#message}.$i{#timelite}{2}{#message}[{#timelite}{3}{#message}]$i, </ranking_record_on>
<ranking_record>{#rank}{1}{#message}.{#timelite}{2}{#message}[{#timelite}{3}{#message}], </ranking_record>
<ranking_record2>{#rank}{1}{#message}.{#timelite}{2}{#message}, </ranking_record2>
<!-- record relation messages -->
<first_record>{#server}> {#record}The first Local record is: </first_record>
<last_record>{#server}> {#record}The last Local record is: </last_record>
<diff_record>{#server}> {#record}Difference between {1}{#record} and {2}{#record} is: {#highlite}{3}</diff_record>
<summary>{#server}> {#highlite}{1} $z$s{#record}has {#highlite}{2}{#record} Local record{3}, the top {4} being: </summary>
<sum_entry>{#highlite}{1} {#record}rec{2} #{#rank}{3}{#record}, </sum_entry>
<!-- win messages -->
<wins>{#server}> {#record}You have already won {#highlite}{1}{#record} race{2}</wins>
<win_new>{#server}> {#record}Congratulations, you won your {#highlite}{1}{#record}. race!</win_new>
<win_multi>{#server}>> {#record}Congratulations, {#highlite}{1}{#record} won his/her {#highlite}{2}{#record}. race!</win_multi>
<!-- muting messages -->
<mute>{#server}> Player {#highlite}{1}$z$s{#server} is muted!</mute>
<unmute>{#server}> Player {#highlite}{1}$z$s{#server} is unmuted!</unmute>
<muted>{#server}> {#highlite}{1}{#error} disabled because you are on the global mute list!</muted>
<!-- donate/pay messages -->
<donation>{#donate} Donate {#highlite}{1}{#donate} coppers to {#highlite}{2}$z</donation>
<thanks_all>{#server}>> {#highlite}{1}$z$s{#donate} received a donation of {#highlite}{2}{#donate} coppers from {#highlite}{3}$z$s{#donate}. Thank You!</thanks_all>
<thanks_you>{#server}> {#donate}You made a donation of {#highlite}{1}{#donate} coppers. Thank You!</thanks_you>
<donate_minimum>{#server}> {#error}Minimum donation amount is {#highlite}$i {1}{#error} coppers!</donate_minimum>
<donate_help>{#server}> {#error}Use {#highlite}$i /donate &lt;number&gt;{#error} to donate coppers to the server</donate_help>
<payment>{#donate} Send {#highlite}{1}{#donate} coppers to {#highlite}{2}$z</payment>
<pay_insuff>{#server}> {#error}Insufficient server coppers: {#highlite}$i {1}{#error}!</pay_insuff>
<pay_server>{#server}> {#error}Cannot pay this server itself!</pay_server>
<pay_confirm>{#server}> {#donate}Payment of {#highlite}{1}{#donate} coppers to {#highlite}{2}{#donate} confirmed! Remaining coppers: {#highlite}{3}</pay_confirm>
<pay_cancel>{#server}> {#donate}Payment to {#highlite}{1}{#donate} cancelled!</pay_cancel>
<pay_help>{#server}> {#error}Use {#highlite}$i /admin pay &lt;login&gt; $m&lt;number&gt;{#error} to send server coppers to a login</pay_help>
<!-- playtime/track messages -->
<playtime>{#server}> Current track {#highlite}{1}{#server} has been played for {#highlite}{2}</playtime>
<playtime_finish>{#server}>> Current track {#highlite}{1}{#server} finished after {#highlite}{2}</playtime_finish>
<playtime_replay> {#server}({#highlite}{1}{#server} replay{2}, total {#highlite}{3}{#server})</playtime_replay>
<track>{#server}> Current track {#highlite}{1} {#server}by {#highlite}{2} {#server}Author: {#highlite}{3} {#server}Gold: {#highlite}{4} {#server}Silver: {#highlite}{5} {#server}Bronze: {#highlite}{6} {#server}Cost: {#highlite}{7}</track>
<current_track>{#server}>> Current track {#highlite}{1} {#server}by {#highlite}{2} {#server}Author: {#highlite}{3}</current_track>
<!-- rounds points messages -->
<rpoints_named>{#server}> {1}Custom points system set to {#highlite}{2}{3}: {#highlite}{4},...</rpoints_named>
<rpoints_nameless>{#server}> {1}Custom points system set to: {#highlite}{2},...</rpoints_nameless>
<no_rpoints>{#server}> {1}No custom Rounds points system defined!</no_rpoints>
<!-- relay messages -->
<no_relays>{#server}> {#error}No relay servers connected</no_relays>
<relaymaster>{#server}> This server relays master server: {#highlite}{1}{#server} ({#highlite}{2}{#server})</relaymaster>
<notonrelay>{#server}> {#error}Command unavailable on relay server</notonrelay>
<!-- uptodate messages -->
<uptodate_ok>{#server}>> {#message}This XASECO version {#highlite}{1}{#message} is up to date</uptodate_ok>
<uptodate_new>{#server}>> {#message}New XASECO version {#highlite}{1}{#message} available from {#highlite}{2}</uptodate_new>
<!-- connection messages -->
<banip_dialog>{#welcome}Your IP was banned from this server.$z</banip_dialog>
<banip_error>{#welcome}Could not connect:{br}{br}Your IP was banned from this server!</banip_error>
<client_dialog>{#welcome}Obsolete client version, please $l[http://www.tm-forum.com/viewtopic.php?p=139752#p139752]upgrade$l.$z</client_dialog>
<client_error>{#welcome}Obsolete client version!{br}Please upgrade to the $l[http://www.tm-forum.com/viewtopic.php?p=139752#p139752]latest version$l.</client_error>
<connect_dialog>{#welcome}Connection problem, please retry.$z</connect_dialog>
<connect_error>{#welcome}$sThis is an administrative notice.$z{br}{br}XASECO encountered a very rare player connection{br}problem. Please re-join the server to correct it.{br}Apologies for the inconvenience.{br}{br}$sThe server administrators.</connect_error>
<!-- idlekick messages -->
<idlekick_play>{#server}>> IdleKick player {#highlite}{1}$z$s{#server} after {#highlite}{2}{#server} challenge{3}!</idlekick_play>
<idlespec_play>{#server}>> IdleSpec player {#highlite}{1}$z$s{#server} after {#highlite}{2}{#server} challenge{3}</idlespec_play>
<idlekick_spec>{#server}>> IdleKick spectator {#highlite}{1}$z$s{#server} after {#highlite}{2}{#server} challenge{3}!</idlekick_spec>
<!-- miscellaneous messages -->
<song>{#server}> Track {#highlite}{1} {#server}plays song: {#highlite}{2}</song>
<mod>{#server}> Track {#highlite}{1} {#server}uses mod: {#highlite}{2} {#server}({#highlite}{3}{#server})</mod>
<coppers>{#server}> Server {#highlite}{1}$z$s {#server}owns {#highlite}{2} {#server}coppers!</coppers>
<time>{#server}> {#interact}Current Server Time: {#highlite}$i {1}{#interact} on {#highlite}$i {2}</time>
<tmxrec>{#server}>> {#record}TMX World Record: {#highlite}{1}{#record} by {#highlite}{2}</tmxrec>
<round>$n{#message}R{#highlite}{1}{#message}> </round>
<no_cpsspec>{#server}> {#highlite}/cpsspec{#server} is not currently enabled on this server.</no_cpsspec>
<no_admin>{#server}> {#error}You have to be in admin list to do that!</no_admin>
<help_explanation>{#server}> Press the {#highlite}C{#server} key to see the whole list, and use {#highlite}/helpall{#server} for details</help_explanation>
<united_only>{#server}> {#error}This requires a TM United Forever {1}!</united_only>
<forever_only>{#server}> {#error}Command only available on TM Forever!</forever_only>
</messages>
<welcome_msg_window>False</welcome_msg_window>
<lock_password></lock_password>
<log_all_chat>False</log_all_chat>
<chatpmlog_times>True</chatpmlog_times>
<cheater_action>0</cheater_action>
<script_timeout>60</script_timeout>
<show_min_recs>8</show_min_recs>
<!-- 2 = full report, 1 = only track record, 0 = none -->
<!-- add 4 to show the report in the TMF message -->
<!-- window instead of the main chat window -->
<show_recs_before>2</show_recs_before>
<!-- 2 = full report, 1 = only top-5, 0 = none -->
<!-- add 4 to show the report in the TMF message -->
<!-- window instead of the main chat window -->
<show_recs_after>2</show_recs_after>
<show_recs_range>True</show_recs_range>
<!-- 2 = in TMF message window, 1 = in chat, 0 = none -->
<show_tmxrec>1</show_tmxrec>
<!-- 2 = in TMF message window, 1 = in chat, 0 = none -->
<show_playtime>1</show_playtime>
<!-- 2 = in TMF message window, 1 = in chat, 0 = none -->
<show_curtrack>0</show_curtrack>
<default_tracklist>tracklist.txt</default_tracklist>
<writetracklist_random>True</writetracklist_random>
<help_explanation>False</help_explanation>
<lists_colornicks>True</lists_colornicks>
<lists_colortracks>True</lists_colortracks>
<topclans_minplayers>2</topclans_minplayers>
<global_win_multiple>50</global_win_multiple>
<display_checkpoints>True</display_checkpoints>
<enable_cpsspec>False</enable_cpsspec>
<auto_enable_cps>True</auto_enable_cps>
<auto_enable_dedicps>False</auto_enable_dedicps>
<auto_admin_addip>True</auto_admin_addip>
<adminops_file>adminops.xml</adminops_file>
<bannedips_file>bannedips.xml</bannedips_file>
<blacklist_file>blacklist.txt</blacklist_file>
<guestlist_file>guestlist.txt</guestlist_file>
<trackhist_file>trackhist.txt</trackhist_file>
<!-- the remaining settings are for TMF only -->
<admin_client_version>2.11.19</admin_client_version>
<player_client_version></player_client_version>
<default_rpoints></default_rpoints>
<afk_force_spec>True</afk_force_spec>
<clickable_lists>True</clickable_lists>
<show_rec_logins>True</show_rec_logins>
<!-- True = in TMF message window, False = in chat -->
<recs_in_window>False</recs_in_window>
<!-- True = in TMF message window, False = in chat -->
<rounds_in_window>False</rounds_in_window>
<!-- timeout of the TMF message window in seconds -->
<window_timeout>6</window_timeout>
<!-- True = display individual stats panels for each -->
<!-- player during the scoreboard & disable the rank -->
<!-- chat messages, False = show only rank messages -->
<sb_stats_panels>False</sb_stats_panels>
<!-- default windows style: none for old TMN-like, or -->
<!-- filename that should be at styles/<filename>.xml -->
<window_style>DarkBlur</window_style>
<!-- default panels: empty for no panel, otherwise a -->
<!-- filename that should be at panels/<filename>.xml -->
<admin_panel>AdminBelowChat</admin_panel>
<donate_panel>DonateBelowCPList</donate_panel>
<records_panel>RecordsRightBottom</records_panel>
<vote_panel>VoteBelowChat</vote_panel>
</aseco>
<tmserver>
<login>SuperAdmin</login>
<password>@SERVER_SA_PASSWORD@</password>
<ip>@TMSERVER_HOST@</ip>
<port>@TMSERVER_PORT@</port>
<timeout>180</timeout>
</tmserver>
</settings>

74
xaseco/dedimania.xml Normal file
View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" ?>
<dedimania>
<database>
<welcome>{#welcome}Welcome to the Dedimania world record system at www.dedimania.com - see {#highlite}/helpdedi</welcome>
<timeout>{#dedimsg}Dedimania system timed out - retrying in {#highlite}{1}{#dedimsg} minutes</timeout>
<name>Dedimania</name>
<!-- Dedimania communication link, do not modify this! -->
<url>http://dedimania.net:8002/Dedimania</url>
<!-- Log Dedimania news in logfile? -->
<log_news>True</log_news>
<!-- Show welcome message in chat? -->
<show_welcome>True</show_welcome>
<!-- Minimum number of records you'd always like to see at -->
<!-- the start and end of tracks, and with /dedilive (-2) -->
<show_min_recs>8</show_min_recs>
<!-- Display existing records at start of a new track? -->
<!-- 2 = in message window, 1 = in chat, 0 = none -->
<show_recs_before>1</show_recs_before>
<!-- Display (possibly) updated records at end of a track? -->
<!-- 2 = in message window, 1 = in chat, 0 = none -->
<show_recs_after>1</show_recs_after>
<!-- Display records range if there are no new records? -->
<show_recs_range>True</show_recs_range>
<!-- Do you want XASECO to display newly driven records? -->
<display_recs>True</display_recs>
<!-- Show logins on TMF for players in /dedirecs ? -->
<show_rec_logins>True</show_rec_logins>
<!-- Display records in TMF message window or in chat? -->
<recs_in_window>False</recs_in_window>
<!-- Limit the highest record that will be displayed to -->
<!-- all? Only the pertaining player sees higher records, -->
<!-- so typically this is the same as your Server MaxRank -->
<limit_recs>30</limit_recs>
</database>
<!-- Dedimania server registration, copy these from dedicated.cfg -->
<!-- Instead of password you can also use the community code for -->
<!-- your server by using the login/password on the official site -->
<!-- For the nation, please use the 3-character abbreviation from -->
<!-- http://en.wikipedia.org/wiki/List_of_IOC_country_codes -->
<masterserver_account>
<login>YOUR_SERVER_LOGIN</login>
<password>YOUR_SERVER_PASSWORD</password>
<nation>YOUR_SERVER_NATION</nation>
</masterserver_account>
<messages>
<!-- ranking messages -->
<ranking>{#server}>> {#dedimsg}Dedimania Record rankings on {#highlite}{1}{#dedimsg} {2} this round:</ranking>
<ranking_range>{#server}>> {#dedimsg}Dedimania Record rankings on {#highlite}{1}{#dedimsg} {2} this round (range {#highlite}{3}{#dedimsg}):</ranking_range>
<ranking_new>{#server}>> {#dedimsg}Dedimania Record rankings on {#highlite}{1}{#dedimsg} {2} this round ({#highlite}{3}{#dedimsg} new):</ranking_new>
<ranking_nonew>{#server}>> {#dedimsg}Dedimania Record rankings on {#highlite}{1}{#dedimsg} {2} this round: none new so far</ranking_nonew>
<ranking_none>{#server}>> {#dedimsg}Dedimania Record rankings on {#highlite}{1}{#dedimsg} {2} this round: no records!</ranking_none>
<!-- record messages -->
<record_new>{#server}>> {#highlite}{1}{#dedirec} secured his/her {#rank}{2}{#dedirec}. Dedimania Record! {3}: {#highlite}{4}{#dedirec} $n({#rank}{5}{#highlite}{6}{#dedirec})</record_new>
<record_equal>{#server}>> {#highlite}{1}{#dedirec} equaled his/her {#rank}{2}{#dedirec}. Dedimania Record! {3}: {#highlite}{4}</record_equal>
<record_new_rank>{#server}>> {#highlite}{1}{#dedirec} gained the {#rank}{2}{#dedirec}. Dedimania Record! {3}: {#highlite}{4}{#dedirec} $n({#rank}{5}{#highlite}{6}{#dedirec})</record_new_rank>
<record_first>{#server}>> {#highlite}{1}{#dedirec} claimed the {#rank}{2}{#dedirec}. Dedimania Record! {3}: {#highlite}{4}</record_first>
<!-- record relation messages -->
<first_record>{#server}> {#dedirec}The first Dedimania record is: </first_record>
<last_record>{#server}> {#dedirec}The last Dedimania record is: </last_record>
<diff_record>{#server}> {#dedirec}Difference between {1}{#dedirec} and {2}{#dedirec} is: {#highlite}{3}</diff_record>
<!-- pb messages -->
<pb>{#server}> {#dedirec}Dedimania Personal Best: {#highlite}{1}{#dedirec}({#rank}{2}{#dedirec})</pb>
<pb_none>{#server}> {#error}You don't have a Dedimania record on this track yet...</pb_none>
<!-- ban messages -->
<banned_login>{#server}>> {#highlite}{1} {#server}({#highlite}{2}{#server}) {#error}is banned from Dedimania!</banned_login>
<banned_finish>{#server}> {#error}Finish ignored by Dedimania as you are banned!</banned_finish>
</messages>
</dedimania>

View File

@ -0,0 +1,891 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
/*
IXR - The Incutio XML-RPC Library - (c) Incutio Ltd 2002
Version 1.61 - Simon Willison, 11th July 2003 (htmlentities -> htmlspecialchars)
Site: http://scripts.incutio.com/xmlrpc/
Manual: http://scripts.incutio.com/xmlrpc/manual.php
Errors: http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php
Made available under the Artistic License: http://www.opensource.org/licenses/artistic-license.php
Modified to support protocol 'GbxRemote 2' ('GbxRemote 1')
This version is for BigEndian machines. For LittleEndian (e.g. Intel PC)
machines use the original GbxRemote.inc.php instead.
Release 2007-09-22 - Slig:
Modified to support >256KB received data (and now >2MB data produce a specific error message)
Modified readCB() to wait the initial timeout only before first read packet
Modified readCB() to return true if there is data to get with getCBResponses()
Modified to support amd64 (for $recvhandle)
Modified IXR_ClientMulticall_Gbx::addCall() to fit Aseco 0.6.1
Added IXR_Client_Gbx::bytes_sent & bytes_received counters
Fix for a changed feature since php5.1.1 about reference parameter assignment (was used in stream_select)
Workaround for stream_select return value bug with amd64
Release 2008-01-20 - Slig / Xymph / Assembler Maniac:
Workaround for fread delay bug in some cases
Added IXR_Client_Gbx::resetError() method (by Xymph)
Some comments and strings code cleanup (by Xymph)
Fix stream_set_timeout($this->socket,...) (thx to CavalierDeVache)
Added a default timeout value to IXR_Client_Gbx::readCB($timeout)
Changed calls with timeout on a stream to use microseconds instead of seconds (by AM)
Removed IXR_Client_Gbx::bytes_sent & bytes_received counters - not used (by AM)
Release 2008-02-05 - Slig:
Changed some socket read/write timeouts back to seconds to avoid 'transport error'
Changed max data received from 2MB to 4MB
Release 2008-05-20 - Xymph:
Prevented unpack() warnings in IXR_Client_Gbx::query() when the connection dies
Changed IXR_Client_Gbx::resetError() to assign 'false' for correct isError()
Tweaked some 'transport error' messages
Release 2009-04-08 - Gou1:
Added method IXR_Client_Gbx::queryIgnoreResult()
Added methods IXR_Client_Gbx::sendRequest() & IXR_Client_Gbx::getResult()
IXR_Client_Gbx::queryIgnoreResult checks if the request is larger than 512KB to avoid errors
If larger than 512KB and method is system.multicall, try to divide the request into
two separate requests with two separate IXR_Client_Gbx::queryIgnoreResult() calls
Release 2009-06-03 - Xymph:
Suppress possible repetitive CRT warning at stream_select
Release 2011-04-09 - Xymph / La beuze:
Added optional timeout mechanism to IXR_Client_Gbx::InitWithIp()
Release 2011-05-22 - Xymph:
Added non-error (true) return status to IXR_Client_Gbx::queryIgnoreResult()
Updated status codes and messages for transport/endian errors
Prevented possible PHP warning in IXR_Client_Gbx::getErrorCode() and getErrorMessage()
Release 2011-12-04 - Xymph:
Prevented possible PHP warning in IXR_Value::calculateType
Release 2013-02-18 - Xymph:
Removed unnecessary breaks after returns in IXR_Value::getXml() switch
*/
if (!defined('LF')) {
define('LF', "\n");
}
class IXR_Value {
public $data;
public $type;
function IXR_Value ($data, $type = false) {
$this->data = $data;
if (!$type) {
$type = $this->calculateType();
}
$this->type = $type;
if ($type == 'struct') {
// Turn all the values in the array into new IXR_Value objects
foreach ($this->data as $key => $value) {
$this->data[$key] = new IXR_Value($value);
}
}
if ($type == 'array') {
for ($i = 0, $j = count($this->data); $i < $j; $i++) {
$this->data[$i] = new IXR_Value($this->data[$i]);
}
}
}
function calculateType() {
if ($this->data === true || $this->data === false) {
return 'boolean';
}
if (is_integer($this->data)) {
return 'int';
}
if (is_double($this->data)) {
return 'double';
}
// Deal with IXR object types base64 and date
if (is_object($this->data) && ($this->data instanceof IXR_Date)) {
return 'date';
}
if (is_object($this->data) && ($this->data instanceof IXR_Base64)) {
return 'base64';
}
// If it is a normal PHP object convert it into a struct
if (is_object($this->data)) {
$this->data = get_object_vars($this->data);
return 'struct';
}
if (!is_array($this->data)) {
return 'string';
}
// We have an array - is it an array or a struct?
if ($this->isStruct($this->data)) {
return 'struct';
} else {
return 'array';
}
}
function getXml() {
// Return XML for this value
switch ($this->type) {
case 'boolean':
return '<boolean>' . ($this->data ? '1' : '0') . '</boolean>';
case 'int':
return '<int>' . $this->data . '</int>';
case 'double':
return '<double>' . $this->data . '</double>';
case 'string':
return '<string>' . htmlspecialchars($this->data) . '</string>';
case 'array':
$return = '<array><data>' . LF;
foreach ($this->data as $item) {
$return .= ' <value>' . $item->getXml() . '</value>' . LF;
}
$return .= '</data></array>';
return $return;
case 'struct':
$return = '<struct>' . LF;
foreach ($this->data as $name => $value) {
$return .= ' <member><name>' . $name . '</name><value>';
$return .= $value->getXml() . '</value></member>' . LF;
}
$return .= '</struct>';
return $return;
case 'date':
case 'base64':
return $this->data->getXml();
}
return false;
}
function isStruct($array) {
// Nasty function to check if an array is a struct or not
$expected = 0;
foreach ($array as $key => $value) {
if ((string)$key != (string)$expected) {
return true;
}
$expected++;
}
return false;
}
}
class IXR_Message {
public $message;
public $messageType; // methodCall / methodResponse / fault
public $faultCode;
public $faultString;
public $methodName;
public $params;
// Current variable stacks
protected $_arraystructs = array(); // Stack to keep track of the current array/struct
protected $_arraystructstypes = array(); // Stack to keep track of whether things are structs or array
protected $_currentStructName = array(); // A stack as well
protected $_param;
protected $_value;
protected $_currentTag;
protected $_currentTagContents;
// The XML parser
protected $_parser;
function IXR_Message ($message) {
$this->message = $message;
}
function parse() {
// first remove the XML declaration
$this->message = preg_replace('/<\?xml(.*)?\?'.'>/', '', $this->message);
if (trim($this->message) == '') {
return false;
}
$this->_parser = xml_parser_create();
// Set XML parser to take the case of tags into account
xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);
// Set XML parser callback functions
xml_set_object($this->_parser, $this);
xml_set_element_handler($this->_parser, 'tag_open', 'tag_close');
xml_set_character_data_handler($this->_parser, 'cdata');
if (!xml_parse($this->_parser, $this->message)) {
/* die(sprintf('GbxRemote XML error: %s at line %d',
xml_error_string(xml_get_error_code($this->_parser)),
xml_get_current_line_number($this->_parser))); */
return false;
}
xml_parser_free($this->_parser);
// Grab the error messages, if any
if ($this->messageType == 'fault') {
$this->faultCode = $this->params[0]['faultCode'];
$this->faultString = $this->params[0]['faultString'];
}
return true;
}
function tag_open($parser, $tag, $attr) {
$this->currentTag = $tag;
switch ($tag) {
case 'methodCall':
case 'methodResponse':
case 'fault':
$this->messageType = $tag;
break;
// Deal with stacks of arrays and structs
case 'data': // data is to all intents and purposes more interesting than array
$this->_arraystructstypes[] = 'array';
$this->_arraystructs[] = array();
break;
case 'struct':
$this->_arraystructstypes[] = 'struct';
$this->_arraystructs[] = array();
break;
}
}
function cdata($parser, $cdata) {
$this->_currentTagContents .= $cdata;
}
function tag_close($parser, $tag) {
$valueFlag = false;
switch ($tag) {
case 'int':
case 'i4':
$value = (int)trim($this->_currentTagContents);
$this->_currentTagContents = '';
$valueFlag = true;
break;
case 'double':
$value = (double)trim($this->_currentTagContents);
$this->_currentTagContents = '';
$valueFlag = true;
break;
case 'string':
$value = (string)trim($this->_currentTagContents);
$this->_currentTagContents = '';
$valueFlag = true;
break;
case 'dateTime.iso8601':
$value = new IXR_Date(trim($this->_currentTagContents));
// $value = $iso->getTimestamp();
$this->_currentTagContents = '';
$valueFlag = true;
break;
case 'value':
// If no type is indicated, the type is string
if (trim($this->_currentTagContents) != '') {
$value = (string)$this->_currentTagContents;
$this->_currentTagContents = '';
$valueFlag = true;
}
break;
case 'boolean':
$value = (boolean)trim($this->_currentTagContents);
$this->_currentTagContents = '';
$valueFlag = true;
break;
case 'base64':
$value = base64_decode($this->_currentTagContents);
$this->_currentTagContents = '';
$valueFlag = true;
break;
// Deal with stacks of arrays and structs
case 'data':
case 'struct':
$value = array_pop($this->_arraystructs);
array_pop($this->_arraystructstypes);
$valueFlag = true;
break;
case 'member':
array_pop($this->_currentStructName);
break;
case 'name':
$this->_currentStructName[] = trim($this->_currentTagContents);
$this->_currentTagContents = '';
break;
case 'methodName':
$this->methodName = trim($this->_currentTagContents);
$this->_currentTagContents = '';
break;
}
if ($valueFlag) {
/*
if (!is_array($value) && !is_object($value)) {
$value = trim($value);
}
*/
if (count($this->_arraystructs) > 0) {
// Add value to struct or array
if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {
// Add to struct
$this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value;
} else {
// Add to array
$this->_arraystructs[count($this->_arraystructs)-1][] = $value;
}
} else {
// Just add as a paramater
$this->params[] = $value;
}
}
}
}
class IXR_Request {
public $method;
public $args;
public $xml;
function IXR_Request($method, $args) {
$this->method = $method;
$this->args = $args;
$this->xml = '<?xml version="1.0" encoding="utf-8" ?><methodCall><methodName>' . $this->method . '</methodName><params>';
foreach ($this->args as $arg) {
$this->xml .= '<param><value>';
$v = new IXR_Value($arg);
$this->xml .= $v->getXml();
$this->xml .= '</value></param>' . LF;
}
$this->xml .= '</params></methodCall>';
}
function getLength() {
return strlen($this->xml);
}
function getXml() {
return $this->xml;
}
}
class IXR_Error {
public $code;
public $message;
function IXR_Error($code, $message) {
$this->code = $code;
$this->message = $message;
}
function getXml() {
$xml = <<<EOD
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>{$this->code}</int></value>
</member>
<member>
<name>faultString</name>
<value><string>{$this->message}</string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>
EOD;
return $xml;
}
}
class IXR_Date {
public $year;
public $month;
public $day;
public $hour;
public $minute;
public $second;
function IXR_Date($time) {
// $time can be a PHP timestamp or an ISO one
if (is_numeric($time)) {
$this->parseTimestamp($time);
} else {
$this->parseIso($time);
}
}
function parseTimestamp($timestamp) {
$this->year = date('Y', $timestamp);
$this->month = date('Y', $timestamp);
$this->day = date('Y', $timestamp);
$this->hour = date('H', $timestamp);
$this->minute = date('i', $timestamp);
$this->second = date('s', $timestamp);
}
function parseIso($iso) {
$this->year = substr($iso, 0, 4);
$this->month = substr($iso, 4, 2);
$this->day = substr($iso, 6, 2);
$this->hour = substr($iso, 9, 2);
$this->minute = substr($iso, 12, 2);
$this->second = substr($iso, 15, 2);
}
function getIso() {
return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second;
}
function getXml() {
return '<dateTime.iso8601>'.$this->getIso().'</dateTime.iso8601>';
}
function getTimestamp() {
return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year);
}
}
class IXR_Base64 {
public $data;
function IXR_Base64($data) {
$this->data = $data;
}
function getXml() {
return '<base64>'.base64_encode($this->data).'</base64>';
}
}
//////////////////////////////////////////////////////////
// Nadeo modifications //
// (many thanks to slig for adding callback support) //
//////////////////////////////////////////////////////////
class IXR_Client_Gbx {
public $socket;
public $message = false;
public $cb_message = array();
public $reqhandle;
public $protocol = 0;
// Storage place for an error message
public $error = false;
function bigEndianTest() {
list($endiantest) = array_values(unpack('L1L', pack('V', 1)));
if ($endiantest == 1) {
echo "Machine reports itself as LittleEndian, float handling will work correctly with unpack\r\n";
echo "Use original GbxRemote.inc.php instead of GbxRemote.bem.php\r\n";
die('App Terminated');
return false;
}
return true;
} // bigEndianTest
function IXR_Client_Gbx() {
$this->socket = false;
$this->reqhandle = 0x80000000;
}
function InitWithIp($ip, $port, $timeout = null) {
if (!$this->bigEndianTest()) {
$this->error = new IXR_Error(-31999, 'endian error - script doesn\'t match machine type');
return false;
}
// open connection, with timeout if specified
if (!isset($timeout)) {
$this->socket = @fsockopen($ip, $port, $errno, $errstr);
} else {
$init_time = microtime(true);
$init_timeout = 5; // retry every 5s
while (true) {
$this->socket = @fsockopen($ip, $port, $errno, $errstr, $init_timeout);
if ($this->socket || (microtime(true) - $init_time >= $timeout))
break;
}
}
if (!$this->socket) {
$this->error = new IXR_Error(-32300, "transport error - could not open socket (error: $errno, $errstr)");
return false;
}
// handshake
$array_result = big_endian_unpack('Vsize', fread($this->socket, 4));
$size = $array_result['size'];
if ($size > 64) {
$this->error = new IXR_Error(-32300, 'transport error - wrong lowlevel protocol header');
return false;
}
$handshake = fread($this->socket, $size);
if ($handshake == 'GBXRemote 1') {
$this->protocol = 1;
} else if ($handshake == 'GBXRemote 2') {
$this->protocol = 2;
} else {
$this->error = new IXR_Error(-32300, 'transport error - wrong lowlevel protocol version');
return false;
}
return true;
}
function Init($port) {
return $this->InitWithIp('localhost', $port);
}
function Terminate() {
if ($this->socket) {
fclose($this->socket);
$this->socket = false;
}
}
protected function sendRequest(IXR_Request $request) {
$xml = $request->getXml();
@stream_set_timeout($this->socket, 20); // timeout 20s (to write the request)
// send request
$this->reqhandle++;
if ($this->protocol == 1) {
$bytes = pack('Va*', strlen($xml), $xml);
} else {
$bytes = pack('VVa*', strlen($xml), $this->reqhandle, $xml);
}
$bytes_to_write = strlen($bytes);
while ($bytes_to_write > 0) {
$r = @fwrite($this->socket, $bytes);
if ($r === false || $r == 0) {
// connection interrupted
return false; // or die?
}
$bytes_to_write -= $r;
if ($bytes_to_write == 0)
break;
$bytes = substr($bytes, $r);
}
return true;
}
protected function getResult() {
$contents = '';
$contents_length = 0;
do {
$size = 0;
$recvhandle = 0;
@stream_set_timeout($this->socket, 20); // timeout 20s (to read the reply header)
// Get result
if ($this->protocol == 1) {
$contents = fread($this->socket, 4);
if (strlen($contents) == 0) {
$this->error = new IXR_Error(-32300, 'transport error - cannot read size');
return false;
}
$array_result = big_endian_unpack('Vsize', $contents);
$size = $array_result['size'];
$recvhandle = $this->reqhandle;
} else {
$contents = fread($this->socket, 8);
if (strlen($contents) == 0) {
$this->error = new IXR_Error(-32300, 'transport error - cannot read size/handle');
return false;
}
$array_result = big_endian_unpack('Vsize/Vhandle', $contents);
$size = $array_result['size'];
$recvhandle = $array_result['handle'];
// -- amd64 support --
$bits = sprintf('%b', $recvhandle);
if (strlen($bits) == 64) {
$recvhandle = bindec(substr($bits, 32));
}
}
if ($recvhandle == 0 || $size == 0) {
$this->error = new IXR_Error(-32300, 'transport error - connection interrupted!');
return false;
}
if ($size > 4096*1024) {
$this->error = new IXR_Error(-32300, "transport error - response too large ($size)");
return false;
}
$contents = '';
$contents_length = 0;
@stream_set_timeout($this->socket, 0, 10000); // timeout 10 ms (for successive reads until end)
while ($contents_length < $size) {
$contents .= fread($this->socket, $size-$contents_length);
$contents_length = strlen($contents);
}
if (($recvhandle & 0x80000000) == 0) {
// this is a callback, not our answer! handle= $recvhandle, xml-rpc= $contents
// just add it to the message list for the user to read
$new_cb_message = new IXR_Message($contents);
if ($new_cb_message->parse() && $new_cb_message->messageType != 'fault') {
array_push($this->cb_message, array($new_cb_message->methodName, $new_cb_message->params));
}
}
} while ((int)$recvhandle != (int)$this->reqhandle);
$this->message = new IXR_Message($contents);
if (!$this->message->parse()) {
// XML error
$this->error = new IXR_Error(-32700, 'parse error. not well formed');
return false;
}
// Is the message a fault?
if ($this->message->messageType == 'fault') {
$this->error = new IXR_Error($this->message->faultCode, $this->message->faultString);
return false;
}
// Message must be OK
return true;
}
function query() {
$args = func_get_args();
$method = array_shift($args);
if (!$this->socket || $this->protocol == 0) {
$this->error = new IXR_Error(-32300, 'transport error - client not initialized');
return false;
}
$request = new IXR_Request($method, $args);
// Check if request is larger than 512 Kbytes
if (($size = $request->getLength()) > 512*1024-8) {
$this->error = new IXR_Error(-32300, "transport error - request too large ($size)");
return false;
}
// Send request
$ok = $this->sendRequest($request);
if (!$ok) {
$this->error = new IXR_Error(-32300, 'transport error - connection interrupted!');
return false;
}
// Get result
return $this->getResult();
}
// Non-blocking query method: doesn't read the response
function queryIgnoreResult() {
$args = func_get_args();
$method = array_shift($args);
if (!$this->socket || $this->protocol == 0) {
$this->error = new IXR_Error(-32300, 'transport error - client not initialized');
return false;
}
$request = new IXR_Request($method, $args);
// Check if the request is greater than 512 Kbytes to avoid errors
// If the method is system.multicall, make two calls (possibly recursively)
if (($size = $request->getLength()) > 512*1024-8) {
if ($method = 'system.multicall' && isset($args[0])) {
$count = count($args[0]);
// If count is 1, query cannot be reduced
if ($count < 2) {
$this->error = new IXR_Error(-32300, "transport error - request too large ($size)");
return false;
}
$length = floor($count/2);
$args1 = array_slice($args[0], 0, $length);
$args2 = array_slice($args[0], $length, ($count-$length));
$res1 = $this->queryIgnoreResult('system.multicall', $args1);
$res2 = $this->queryIgnoreResult('system.multicall', $args2);
return ($res1 && $res2);
}
// If the method is not a multicall, just stop
else {
$this->error = new IXR_Error(-32300, "transport error - request too large ($size)");
return false;
}
}
// Send request
$ok = $this->sendRequest($request);
if (!$ok) {
$this->error = new IXR_Error(-32300, 'transport error - connection interrupted!');
return false;
}
return true;
}
function readCB($timeout = 2000) { // timeout 2 ms
if (!$this->socket || $this->protocol == 0) {
$this->error = new IXR_Error(-32300, 'transport error - client not initialized');
return false;
}
if ($this->protocol == 1)
return false;
$something_received = count($this->cb_message)>0;
$contents = '';
$contents_length = 0;
@stream_set_timeout($this->socket, 0, 10000); // timeout 10 ms (to read available data)
// (assignment in arguments is forbidden since php 5.1.1)
$read = array($this->socket);
$write = NULL;
$except = NULL;
$nb = @stream_select($read, $write, $except, 0, $timeout);
// workaround for stream_select bug with amd64
if ($nb !== false)
$nb = count($read);
while ($nb !== false && $nb > 0) {
$timeout = 0; // we don't want to wait for the full time again, just flush the available data
$size = 0;
$recvhandle = 0;
// Get result
$contents = fread($this->socket, 8);
if (strlen($contents) == 0) {
$this->error = new IXR_Error(-32300, 'transport error - cannot read size/handle');
return false;
}
$array_result = big_endian_unpack('Vsize/Vhandle', $contents);
$size = $array_result['size'];
$recvhandle = $array_result['handle'];
if ($recvhandle == 0 || $size == 0) {
$this->error = new IXR_Error(-32300, 'transport error - connection interrupted!');
return false;
}
if ($size > 4096*1024) {
$this->error = new IXR_Error(-32300, "transport error - response too large ($size)");
return false;
}
$contents = '';
$contents_length = 0;
while ($contents_length < $size) {
$contents .= fread($this->socket, $size-$contents_length);
$contents_length = strlen($contents);
}
if (($recvhandle & 0x80000000) == 0) {
// this is a callback. handle= $recvhandle, xml-rpc= $contents
//echo 'CALLBACK('.$contents_length.')[ '.$contents.' ]' . LF;
$new_cb_message = new IXR_Message($contents);
if ($new_cb_message->parse() && $new_cb_message->messageType != 'fault') {
array_push($this->cb_message, array($new_cb_message->methodName, $new_cb_message->params));
}
$something_received = true;
}
// (assignment in arguments is forbidden since php 5.1.1)
$read = array($this->socket);
$write = NULL;
$except = NULL;
$nb = @stream_select($read, $write, $except, 0, $timeout);
// workaround for stream_select bug with amd64
if ($nb !== false)
$nb = count($read);
}
return $something_received;
}
function getResponse() {
// methodResponses can only have one param - return that
return $this->message->params[0];
}
function getCBResponses() {
// (look at the end of basic.php for an example)
$messages = $this->cb_message;
$this->cb_message = array();
return $messages;
}
function isError() {
return is_object($this->error);
}
function resetError() {
$this->error = false;
}
function getErrorCode() {
if ($this->isError())
return $this->error->code;
else
return 0;
}
function getErrorMessage() {
if ($this->isError())
return $this->error->message;
else
return '';
}
}
class IXR_ClientMulticall_Gbx extends IXR_Client_Gbx {
public $calls = array();
function addCall($methodName, $args) {
$struct = array('methodName' => $methodName, 'params' => $args);
$this->calls[] = $struct;
return (count($this->calls) - 1);
}
function multiquery($ignoreResult = false) {
// Prepare multicall, then call the parent::query() (or queryIgnoreResult) method
if ($ignoreResult) {
$result = parent::queryIgnoreResult('system.multicall', $this->calls);
} else {
$result = parent::query('system.multicall', $this->calls);
}
$this->calls = array(); // reset for next calls
return $result;
}
}
/**
* The following code is a workaround for php's unpack function which
* does not have the capability of unpacking double precision floats
* that were packed in the opposite byte order of the current machine.
*/
function big_endian_unpack($format, $data) {
$ar = unpack($format, $data);
$vals = array_values($ar);
$f = explode('/', $format);
$i = 0;
foreach ($f as $f_k => $f_v) {
$repeater = intval(substr($f_v, 1));
if ($repeater == 0)
$repeater = 1;
if ($f_v{1} == '*') {
$repeater = count($ar) - $i;
}
if ($f_v{0} != 'd') {
$i += $repeater;
continue;
}
$j = $i + $repeater;
for ($a = $i; $a < $j; ++$a) {
$p = pack('d', $vals[$i]);
$p = strrev($p);
list($vals[$i]) = array_values(unpack('d1d', $p));
++$i;
}
}
$a = 0;
foreach ($ar as $ar_k => $ar_v) {
$ar[$ar_k] = $vals[$a];
++$a;
}
return $ar;
}
?>

View File

@ -0,0 +1,854 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
/*
IXR - The Incutio XML-RPC Library - (c) Incutio Ltd 2002
Version 1.61 - Simon Willison, 11th July 2003 (htmlentities -> htmlspecialchars)
Site: http://scripts.incutio.com/xmlrpc/
Manual: http://scripts.incutio.com/xmlrpc/manual.php
Errors: http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php
Made available under the Artistic License: http://www.opensource.org/licenses/artistic-license.php
Modified to support protocol 'GbxRemote 2' ('GbxRemote 1')
This version is for LittleEndian (e.g. Intel PC) machines. For BegEndian
machines use GbxRemote.bem.php as GbxRemote.inc.php instead.
Release 2007-09-22 - Slig:
Modified to support >256KB received data (and now >2MB data produce a specific error message)
Modified readCB() to wait the initial timeout only before first read packet
Modified readCB() to return true if there is data to get with getCBResponses()
Modified to support amd64 (for $recvhandle)
Modified IXR_ClientMulticall_Gbx::addCall() to fit Aseco 0.6.1
Added IXR_Client_Gbx::bytes_sent & bytes_received counters
Fix for a changed feature since php5.1.1 about reference parameter assignment (was used in stream_select)
Workaround for stream_select return value bug with amd64
Release 2008-01-20 - Slig / Xymph / Assembler Maniac:
Workaround for fread delay bug in some cases
Added IXR_Client_Gbx::resetError() method (by Xymph)
Some comments and strings code cleanup (by Xymph)
Fix stream_set_timeout($this->socket,...) (thx to CavalierDeVache)
Added a default timeout value to IXR_Client_Gbx::readCB($timeout)
Changed calls with timeout on a stream to use microseconds instead of seconds (by AM)
Removed IXR_Client_Gbx::bytes_sent & bytes_received counters - not used (by AM)
Release 2008-02-05 - Slig:
Changed some socket read/write timeouts back to seconds to avoid 'transport error'
Changed max data received from 2MB to 4MB
Release 2008-05-20 - Xymph:
Prevented unpack() warnings in IXR_Client_Gbx::query() when the connection dies
Changed IXR_Client_Gbx::resetError() to assign 'false' for correct isError()
Tweaked some 'transport error' messages
Release 2009-04-08 - Gou1:
Added method IXR_Client_Gbx::queryIgnoreResult()
Added methods IXR_Client_Gbx::sendRequest() & IXR_Client_Gbx::getResult()
IXR_Client_Gbx::queryIgnoreResult checks if the request is larger than 512KB to avoid errors
If larger than 512KB and method is system.multicall, try to divide the request into
two separate requests with two separate IXR_Client_Gbx::queryIgnoreResult() calls
Release 2009-06-03 - Xymph:
Suppress possible repetitive CRT warning at stream_select
Release 2011-04-09 - Xymph / La beuze:
Added optional timeout mechanism to IXR_Client_Gbx::InitWithIp()
Release 2011-05-22 - Xymph:
Added non-error (true) return status to IXR_Client_Gbx::queryIgnoreResult()
Updated status codes and messages for transport/endian errors
Prevented possible PHP warning in IXR_Client_Gbx::getErrorCode() and getErrorMessage()
Release 2011-12-04 - Xymph:
Prevented possible PHP warning in IXR_Value::calculateType
Release 2013-02-18 - Xymph:
Removed unnecessary breaks after returns in IXR_Value::getXml() switch
*/
if (!defined('LF')) {
define('LF', "\n");
}
class IXR_Value {
public $data;
public $type;
function IXR_Value ($data, $type = false) {
$this->data = $data;
if (!$type) {
$type = $this->calculateType();
}
$this->type = $type;
if ($type == 'struct') {
// Turn all the values in the array into new IXR_Value objects
foreach ($this->data as $key => $value) {
$this->data[$key] = new IXR_Value($value);
}
}
if ($type == 'array') {
for ($i = 0, $j = count($this->data); $i < $j; $i++) {
$this->data[$i] = new IXR_Value($this->data[$i]);
}
}
}
function calculateType() {
if ($this->data === true || $this->data === false) {
return 'boolean';
}
if (is_integer($this->data)) {
return 'int';
}
if (is_double($this->data)) {
return 'double';
}
// Deal with IXR object types base64 and date
if (is_object($this->data) && ($this->data instanceof IXR_Date)) {
return 'date';
}
if (is_object($this->data) && ($this->data instanceof IXR_Base64)) {
return 'base64';
}
// If it is a normal PHP object convert it into a struct
if (is_object($this->data)) {
$this->data = get_object_vars($this->data);
return 'struct';
}
if (!is_array($this->data)) {
return 'string';
}
// We have an array - is it an array or a struct?
if ($this->isStruct($this->data)) {
return 'struct';
} else {
return 'array';
}
}
function getXml() {
// Return XML for this value
switch ($this->type) {
case 'boolean':
return '<boolean>' . ($this->data ? '1' : '0') . '</boolean>';
case 'int':
return '<int>' . $this->data . '</int>';
case 'double':
return '<double>' . $this->data . '</double>';
case 'string':
return '<string>' . htmlspecialchars($this->data) . '</string>';
case 'array':
$return = '<array><data>' . LF;
foreach ($this->data as $item) {
$return .= ' <value>' . $item->getXml() . '</value>' . LF;
}
$return .= '</data></array>';
return $return;
case 'struct':
$return = '<struct>' . LF;
foreach ($this->data as $name => $value) {
$return .= ' <member><name>' . $name . '</name><value>';
$return .= $value->getXml() . '</value></member>' . LF;
}
$return .= '</struct>';
return $return;
case 'date':
case 'base64':
return $this->data->getXml();
}
return false;
}
function isStruct($array) {
// Nasty function to check if an array is a struct or not
$expected = 0;
foreach ($array as $key => $value) {
if ((string)$key != (string)$expected) {
return true;
}
$expected++;
}
return false;
}
}
class IXR_Message {
public $message;
public $messageType; // methodCall / methodResponse / fault
public $faultCode;
public $faultString;
public $methodName;
public $params;
// Current variable stacks
protected $_arraystructs = array(); // Stack to keep track of the current array/struct
protected $_arraystructstypes = array(); // Stack to keep track of whether things are structs or array
protected $_currentStructName = array(); // A stack as well
protected $_param;
protected $_value;
protected $_currentTag;
protected $_currentTagContents;
// The XML parser
protected $_parser;
function IXR_Message ($message) {
$this->message = $message;
}
function parse() {
// first remove the XML declaration
$this->message = preg_replace('/<\?xml(.*)?\?'.'>/', '', $this->message);
if (trim($this->message) == '') {
return false;
}
$this->_parser = xml_parser_create();
// Set XML parser to take the case of tags into account
xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);
// Set XML parser callback functions
xml_set_object($this->_parser, $this);
xml_set_element_handler($this->_parser, 'tag_open', 'tag_close');
xml_set_character_data_handler($this->_parser, 'cdata');
if (!xml_parse($this->_parser, $this->message)) {
/* die(sprintf('GbxRemote XML error: %s at line %d',
xml_error_string(xml_get_error_code($this->_parser)),
xml_get_current_line_number($this->_parser))); */
return false;
}
xml_parser_free($this->_parser);
// Grab the error messages, if any
if ($this->messageType == 'fault') {
$this->faultCode = $this->params[0]['faultCode'];
$this->faultString = $this->params[0]['faultString'];
}
return true;
}
function tag_open($parser, $tag, $attr) {
$this->currentTag = $tag;
switch ($tag) {
case 'methodCall':
case 'methodResponse':
case 'fault':
$this->messageType = $tag;
break;
// Deal with stacks of arrays and structs
case 'data': // data is to all intents and purposes more interesting than array
$this->_arraystructstypes[] = 'array';
$this->_arraystructs[] = array();
break;
case 'struct':
$this->_arraystructstypes[] = 'struct';
$this->_arraystructs[] = array();
break;
}
}
function cdata($parser, $cdata) {
$this->_currentTagContents .= $cdata;
}
function tag_close($parser, $tag) {
$valueFlag = false;
switch ($tag) {
case 'int':
case 'i4':
$value = (int)trim($this->_currentTagContents);
$this->_currentTagContents = '';
$valueFlag = true;
break;
case 'double':
$value = (double)trim($this->_currentTagContents);
$this->_currentTagContents = '';
$valueFlag = true;
break;
case 'string':
$value = (string)trim($this->_currentTagContents);
$this->_currentTagContents = '';
$valueFlag = true;
break;
case 'dateTime.iso8601':
$value = new IXR_Date(trim($this->_currentTagContents));
// $value = $iso->getTimestamp();
$this->_currentTagContents = '';
$valueFlag = true;
break;
case 'value':
// If no type is indicated, the type is string
if (trim($this->_currentTagContents) != '') {
$value = (string)$this->_currentTagContents;
$this->_currentTagContents = '';
$valueFlag = true;
}
break;
case 'boolean':
$value = (boolean)trim($this->_currentTagContents);
$this->_currentTagContents = '';
$valueFlag = true;
break;
case 'base64':
$value = base64_decode($this->_currentTagContents);
$this->_currentTagContents = '';
$valueFlag = true;
break;
// Deal with stacks of arrays and structs
case 'data':
case 'struct':
$value = array_pop($this->_arraystructs);
array_pop($this->_arraystructstypes);
$valueFlag = true;
break;
case 'member':
array_pop($this->_currentStructName);
break;
case 'name':
$this->_currentStructName[] = trim($this->_currentTagContents);
$this->_currentTagContents = '';
break;
case 'methodName':
$this->methodName = trim($this->_currentTagContents);
$this->_currentTagContents = '';
break;
}
if ($valueFlag) {
/*
if (!is_array($value) && !is_object($value)) {
$value = trim($value);
}
*/
if (count($this->_arraystructs) > 0) {
// Add value to struct or array
if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') {
// Add to struct
$this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value;
} else {
// Add to array
$this->_arraystructs[count($this->_arraystructs)-1][] = $value;
}
} else {
// Just add as a paramater
$this->params[] = $value;
}
}
}
}
class IXR_Request {
public $method;
public $args;
public $xml;
function IXR_Request($method, $args) {
$this->method = $method;
$this->args = $args;
$this->xml = '<?xml version="1.0" encoding="utf-8" ?><methodCall><methodName>' . $this->method . '</methodName><params>';
foreach ($this->args as $arg) {
$this->xml .= '<param><value>';
$v = new IXR_Value($arg);
$this->xml .= $v->getXml();
$this->xml .= '</value></param>' . LF;
}
$this->xml .= '</params></methodCall>';
}
function getLength() {
return strlen($this->xml);
}
function getXml() {
return $this->xml;
}
}
class IXR_Error {
public $code;
public $message;
function IXR_Error($code, $message) {
$this->code = $code;
$this->message = $message;
}
function getXml() {
$xml = <<<EOD
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>{$this->code}</int></value>
</member>
<member>
<name>faultString</name>
<value><string>{$this->message}</string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>
EOD;
return $xml;
}
}
class IXR_Date {
public $year;
public $month;
public $day;
public $hour;
public $minute;
public $second;
function IXR_Date($time) {
// $time can be a PHP timestamp or an ISO one
if (is_numeric($time)) {
$this->parseTimestamp($time);
} else {
$this->parseIso($time);
}
}
function parseTimestamp($timestamp) {
$this->year = date('Y', $timestamp);
$this->month = date('Y', $timestamp);
$this->day = date('Y', $timestamp);
$this->hour = date('H', $timestamp);
$this->minute = date('i', $timestamp);
$this->second = date('s', $timestamp);
}
function parseIso($iso) {
$this->year = substr($iso, 0, 4);
$this->month = substr($iso, 4, 2);
$this->day = substr($iso, 6, 2);
$this->hour = substr($iso, 9, 2);
$this->minute = substr($iso, 12, 2);
$this->second = substr($iso, 15, 2);
}
function getIso() {
return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second;
}
function getXml() {
return '<dateTime.iso8601>'.$this->getIso().'</dateTime.iso8601>';
}
function getTimestamp() {
return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year);
}
}
class IXR_Base64 {
public $data;
function IXR_Base64($data) {
$this->data = $data;
}
function getXml() {
return '<base64>'.base64_encode($this->data).'</base64>';
}
}
//////////////////////////////////////////////////////////
// Nadeo modifications //
// (many thanks to slig for adding callback support) //
//////////////////////////////////////////////////////////
class IXR_Client_Gbx {
public $socket;
public $message = false;
public $cb_message = array();
public $reqhandle;
public $protocol = 0;
// Storage place for an error message
public $error = false;
function bigEndianTest() {
list($endiantest) = array_values(unpack('L1L', pack('V', 1)));
if ($endiantest != 1) {
echo "Machine reports itself as BigEndian, float handling must be altered\r\n";
echo "Overwrite GbxRemote.inc.php with GbxRemote.bem.php\r\n";
die('App Terminated');
return false;
}
return true;
} // bigEndianTest
function IXR_Client_Gbx() {
$this->socket = false;
$this->reqhandle = 0x80000000;
}
function InitWithIp($ip, $port, $timeout = null) {
if (!$this->bigEndianTest()) {
$this->error = new IXR_Error(-31999, 'endian error - script doesn\'t match machine type');
return false;
}
// open connection, with timeout if specified
if (!isset($timeout)) {
$this->socket = @fsockopen($ip, $port, $errno, $errstr);
} else {
$init_time = microtime(true);
$init_timeout = 5; // retry every 5s
while (true) {
$this->socket = @fsockopen($ip, $port, $errno, $errstr, $init_timeout);
if ($this->socket || (microtime(true) - $init_time >= $timeout))
break;
}
}
if (!$this->socket) {
$this->error = new IXR_Error(-32300, "transport error - could not open socket (error: $errno, $errstr)");
return false;
}
// handshake
$array_result = unpack('Vsize', fread($this->socket, 4));
$size = $array_result['size'];
if ($size > 64) {
$this->error = new IXR_Error(-32300, 'transport error - wrong lowlevel protocol header');
return false;
}
$handshake = fread($this->socket, $size);
if ($handshake == 'GBXRemote 1') {
$this->protocol = 1;
} else if ($handshake == 'GBXRemote 2') {
$this->protocol = 2;
} else {
$this->error = new IXR_Error(-32300, 'transport error - wrong lowlevel protocol version');
return false;
}
return true;
}
function Init($port) {
return $this->InitWithIp('localhost', $port);
}
function Terminate() {
if ($this->socket) {
fclose($this->socket);
$this->socket = false;
}
}
protected function sendRequest(IXR_Request $request) {
$xml = $request->getXml();
@stream_set_timeout($this->socket, 20); // timeout 20s (to write the request)
// send request
$this->reqhandle++;
if ($this->protocol == 1) {
$bytes = pack('Va*', strlen($xml), $xml);
} else {
$bytes = pack('VVa*', strlen($xml), $this->reqhandle, $xml);
}
$bytes_to_write = strlen($bytes);
while ($bytes_to_write > 0) {
$r = @fwrite($this->socket, $bytes);
if ($r === false || $r == 0) {
// connection interrupted
return false; // or die?
}
$bytes_to_write -= $r;
if ($bytes_to_write == 0)
break;
$bytes = substr($bytes, $r);
}
return true;
}
protected function getResult() {
$contents = '';
$contents_length = 0;
do {
$size = 0;
$recvhandle = 0;
@stream_set_timeout($this->socket, 20); // timeout 20s (to read the reply header)
// Get result
if ($this->protocol == 1) {
$contents = fread($this->socket, 4);
if (strlen($contents) == 0) {
$this->error = new IXR_Error(-32300, 'transport error - cannot read size');
return false;
}
$array_result = unpack('Vsize', $contents);
$size = $array_result['size'];
$recvhandle = $this->reqhandle;
} else {
$contents = fread($this->socket, 8);
if (strlen($contents) == 0) {
$this->error = new IXR_Error(-32300, 'transport error - cannot read size/handle');
return false;
}
$array_result = unpack('Vsize/Vhandle', $contents);
$size = $array_result['size'];
$recvhandle = $array_result['handle'];
// -- amd64 support --
$bits = sprintf('%b', $recvhandle);
if (strlen($bits) == 64) {
$recvhandle = bindec(substr($bits, 32));
}
}
if ($recvhandle == 0 || $size == 0) {
$this->error = new IXR_Error(-32300, 'transport error - connection interrupted!');
return false;
}
if ($size > 4096*1024) {
$this->error = new IXR_Error(-32300, "transport error - response too large ($size)");
return false;
}
$contents = '';
$contents_length = 0;
@stream_set_timeout($this->socket, 0, 10000); // timeout 10 ms (for successive reads until end)
while ($contents_length < $size) {
$contents .= fread($this->socket, $size-$contents_length);
$contents_length = strlen($contents);
}
if (($recvhandle & 0x80000000) == 0) {
// this is a callback, not our answer! handle= $recvhandle, xml-rpc= $contents
// just add it to the message list for the user to read
$new_cb_message = new IXR_Message($contents);
if ($new_cb_message->parse() && $new_cb_message->messageType != 'fault') {
array_push($this->cb_message, array($new_cb_message->methodName, $new_cb_message->params));
}
}
} while ((int)$recvhandle != (int)$this->reqhandle);
$this->message = new IXR_Message($contents);
if (!$this->message->parse()) {
// XML error
$this->error = new IXR_Error(-32700, 'parse error. not well formed');
return false;
}
// Is the message a fault?
if ($this->message->messageType == 'fault') {
$this->error = new IXR_Error($this->message->faultCode, $this->message->faultString);
return false;
}
// Message must be OK
return true;
}
function query() {
$args = func_get_args();
$method = array_shift($args);
if (!$this->socket || $this->protocol == 0) {
$this->error = new IXR_Error(-32300, 'transport error - client not initialized');
return false;
}
$request = new IXR_Request($method, $args);
// Check if request is larger than 512 Kbytes
if (($size = $request->getLength()) > 512*1024-8) {
$this->error = new IXR_Error(-32300, "transport error - request too large ($size)");
return false;
}
// Send request
$ok = $this->sendRequest($request);
if (!$ok) {
$this->error = new IXR_Error(-32300, 'transport error - connection interrupted!');
return false;
}
// Get result
return $this->getResult();
}
// Non-blocking query method: doesn't read the response
function queryIgnoreResult() {
$args = func_get_args();
$method = array_shift($args);
if (!$this->socket || $this->protocol == 0) {
$this->error = new IXR_Error(-32300, 'transport error - client not initialized');
return false;
}
$request = new IXR_Request($method, $args);
// Check if the request is greater than 512 Kbytes to avoid errors
// If the method is system.multicall, make two calls (possibly recursively)
if (($size = $request->getLength()) > 512*1024-8) {
if ($method = 'system.multicall' && isset($args[0])) {
$count = count($args[0]);
// If count is 1, query cannot be reduced
if ($count < 2) {
$this->error = new IXR_Error(-32300, "transport error - request too large ($size)");
return false;
}
$length = floor($count/2);
$args1 = array_slice($args[0], 0, $length);
$args2 = array_slice($args[0], $length, ($count-$length));
$res1 = $this->queryIgnoreResult('system.multicall', $args1);
$res2 = $this->queryIgnoreResult('system.multicall', $args2);
return ($res1 && $res2);
}
// If the method is not a multicall, just stop
else {
$this->error = new IXR_Error(-32300, "transport error - request too large ($size)");
return false;
}
}
// Send request
$ok = $this->sendRequest($request);
if (!$ok) {
$this->error = new IXR_Error(-32300, 'transport error - connection interrupted!');
return false;
}
return true;
}
function readCB($timeout = 2000) { // timeout 2 ms
if (!$this->socket || $this->protocol == 0) {
$this->error = new IXR_Error(-32300, 'transport error - client not initialized');
return false;
}
if ($this->protocol == 1)
return false;
$something_received = count($this->cb_message)>0;
$contents = '';
$contents_length = 0;
@stream_set_timeout($this->socket, 0, 10000); // timeout 10 ms (to read available data)
// (assignment in arguments is forbidden since php 5.1.1)
$read = array($this->socket);
$write = NULL;
$except = NULL;
$nb = @stream_select($read, $write, $except, 0, $timeout);
// workaround for stream_select bug with amd64
if ($nb !== false)
$nb = count($read);
while ($nb !== false && $nb > 0) {
$timeout = 0; // we don't want to wait for the full time again, just flush the available data
$size = 0;
$recvhandle = 0;
// Get result
$contents = fread($this->socket, 8);
if (strlen($contents) == 0) {
$this->error = new IXR_Error(-32300, 'transport error - cannot read size/handle');
return false;
}
$array_result = unpack('Vsize/Vhandle', $contents);
$size = $array_result['size'];
$recvhandle = $array_result['handle'];
if ($recvhandle == 0 || $size == 0) {
$this->error = new IXR_Error(-32300, 'transport error - connection interrupted!');
return false;
}
if ($size > 4096*1024) {
$this->error = new IXR_Error(-32300, "transport error - response too large ($size)");
return false;
}
$contents = '';
$contents_length = 0;
while ($contents_length < $size) {
$contents .= fread($this->socket, $size-$contents_length);
$contents_length = strlen($contents);
}
if (($recvhandle & 0x80000000) == 0) {
// this is a callback. handle= $recvhandle, xml-rpc= $contents
//echo 'CALLBACK('.$contents_length.')[ '.$contents.' ]' . LF;
$new_cb_message = new IXR_Message($contents);
if ($new_cb_message->parse() && $new_cb_message->messageType != 'fault') {
array_push($this->cb_message, array($new_cb_message->methodName, $new_cb_message->params));
}
$something_received = true;
}
// (assignment in arguments is forbidden since php 5.1.1)
$read = array($this->socket);
$write = NULL;
$except = NULL;
$nb = @stream_select($read, $write, $except, 0, $timeout);
// workaround for stream_select bug with amd64
if ($nb !== false)
$nb = count($read);
}
return $something_received;
}
function getResponse() {
// methodResponses can only have one param - return that
return $this->message->params[0];
}
function getCBResponses() {
// (look at the end of basic.php for an example)
$messages = $this->cb_message;
$this->cb_message = array();
return $messages;
}
function isError() {
return is_object($this->error);
}
function resetError() {
$this->error = false;
}
function getErrorCode() {
if ($this->isError())
return $this->error->code;
else
return 0;
}
function getErrorMessage() {
if ($this->isError())
return $this->error->message;
else
return '';
}
}
class IXR_ClientMulticall_Gbx extends IXR_Client_Gbx {
public $calls = array();
function addCall($methodName, $args) {
$struct = array('methodName' => $methodName, 'params' => $args);
$this->calls[] = $struct;
return (count($this->calls) - 1);
}
function multiquery($ignoreResult = false) {
// Prepare multicall, then call the parent::query() (or queryIgnoreResult) method
if ($ignoreResult) {
$result = parent::queryIgnoreResult('system.multicall', $this->calls);
} else {
$result = parent::query('system.multicall', $this->calls);
}
$this->calls = array(); // reset for next calls
return $result;
}
}
?>

View File

@ -0,0 +1,218 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
////////////////////////////////////////////////////////////////
//
// File: Adds to GbxRemote.inc.php
// Date: 10.07.2007
// Author: Gilles Masson
// Updated: Xymph
//
// xmlrpc_request() or RequestStd class to build a methodCall request (xml-rpc string)
// xmlrpc_response() or ResponseStd class to build a method response (xml-rpc string)
// xmlrpc_error() to build an error response (xml-rpc string)
// rpc_method() to build a method array (for method call in a multicall)
// rpc_response() to build a response array (for method response in a multicall)
// rpc_error() to build an error array (for method error in a multicall)
// xml_decode_rpc() to build a data array from a xml-rpc string
// -----------------------------------------------------------------------
// Build a methodCall request in xml-rpc format from methodname and array params
// (methodname + array_params ==> xml-rpc string)
// -----------------------------------------------------------------------
// $method is the method name.
// $params should be an array containing the method parameters.
// For system.multicall, there should be one parameter, which is an array
// containing the methods structs {'methodName':string, 'params':array}
// -----------------------------------------------------------------------
function xmlrpc_request($method, $params = null) {
// in case the first level array was forgotten in a multicall then add it...
if ($method == 'system.multicall' && isset($params[0]['methodName']))
$params = array($params);
$xml = '<?xml version="1.0" encoding="UTF-8" ?>'
. "\n<methodCall>\n<methodName>$method</methodName>\n<params>\n";
if (is_array($params)) {
foreach ($params as $param) {
$xml .= "<param>\n<value>";
$v = new IXR_Value($param);
$xml .= $v->getXml();
$xml .= "</value>\n</param>\n";
}
}
$xml .= "</params>\n</methodCall>";
return $xml;
}
// -----------------------------------------------------------------------
// Build a methodCall response in xml-rpc format from arrays args
// (array_args ==> xml-rpc string)
// -----------------------------------------------------------------------
// $args should be an array containing the response
// -----------------------------------------------------------------------
function xmlrpc_response($args = null) {
$xml = '<?xml version="1.0" encoding="UTF-8" ?>'
. "\n<methodResponse>\n<params>\n<param>\n";
if ($args !== null) {
$xml .= '<value>';
$v = new IXR_Value($args);
$xml .= $v->getXml();
$xml .= "</value>\n";
}
$xml .= "</param>\n</params>\n</methodResponse>";
return $xml;
}
// -----------------------------------------------------------------------
// Build an error response in xml-rpc format
// (error code + message ==> xml-rpc string)
// -----------------------------------------------------------------------
// $code is the error code
// $message is the error string
// -----------------------------------------------------------------------
function xmlrpc_error($code, $message) {
$xml = '<?xml version="1.0" encoding="UTF-8" ?>'
. "\n<methodResponse>\n<fault>\n"
. "<value><struct>\n"
. "<member><name>faultCode</name><value><int>$code</int></value>\n</member>\n"
. "<member><name>faultString</name><value><string>$message</string></value></member>\n"
. "</struct></value>\n"
. "</fault>\n</methodResponse>";
return $xml;
}
// -----------------------------------------------------------------------
// Build a method struct (array) usable for a method call in a multicall
// (method name + params array ==> method struct array
// -----------------------------------------------------------------------
// $name is the method name
// $params is the params array
// -----------------------------------------------------------------------
function rpc_method($name, $params = null) {
if (!is_array($params))
$params = array();
return array('methodName' => $name, 'params' => $params);
}
// -----------------------------------------------------------------------
// Build a response struct (array) usable as a reply for a method in a multicall
// (method name + params array ==> method struct array
// -----------------------------------------------------------------------
// $params is the methode response array
// -----------------------------------------------------------------------
function rpc_response($response = null) {
if (!is_array($response))
$response = array();
return array($response);
}
// -----------------------------------------------------------------------
// Build an error struct (array) usable as an error for a method in a multicall
// (error code + message ==> error struct array)
// -----------------------------------------------------------------------
// $code is the error code
// $message is the error string
// -----------------------------------------------------------------------
function rpc_error($code, $message) {
return array('faultCode' => $code, 'faultString' => $message);
}
// -----------------------------------------------------------------------
// Build a data array from a text xml-rpc
// (xml-rpc string ==> array)
// -----------------------------------------------------------------------
// $xml is the xml-rpc text to decode
// If there is an error then null is returned, you can then look infos
// in global $_xmlrpc_decode_obj
// -----------------------------------------------------------------------
function xml_decode_rpc($xml) {
global $_xmlrpc_decode_obj;
$_xmlrpc_decode_obj = new IXR_Message($xml);
if (!$_xmlrpc_decode_obj->parse() || !isset($_xmlrpc_decode_obj->params[0]))
return null;
return $_xmlrpc_decode_obj->params[0];
}
// -----------------------------------------------------------------------
// use this class constructor to build a methodCall request
// -----------------------------------------------------------------------
class IXR_RequestStd {
var $method;
var $params;
var $xml;
// see xmlrpc_request
function IXR_RequestStd($method, $params = null) {
$this->method = $method;
// in case the first level array was forgotten in a multicall then add it...
if ($method == 'system.multicall' && isset($params[0]['methodName']))
$this->params = array($params);
else
$this->params = $params;
$this->xml = xmlrpc_request($this->method, $this->params);
}
function getLength() {
return strlen($this->xml);
}
function getXml() {
return $this->xml;
}
}
// -----------------------------------------------------------------------
// use this class constructor to build a methodCall response
// -----------------------------------------------------------------------
class IXR_ResponseStd {
var $args;
var $xml;
// see xmlrpc_response
function IXR_ResponseStd($args) {
$this->args = $args;
$this->xml = xmlrpc_response($this->args);
}
function getLength() {
return strlen($this->xml);
}
function getXml() {
return $this->xml;
}
}
?>

View File

@ -0,0 +1,830 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
// Updated by Xymph
/**
* Writes a logfile of all output messages, either in a single big file
* or in monthly chunks inside the logs/ directory.
*/
function doLog($text) {
global $logfile;
if (MONTHLY_LOGSDIR) {
// create logs/ directory if needed
$dir = './logs';
if (!file_exists($dir)) mkdir($dir);
// define monthly file inside dir
$file = $dir . '/logfile-' . date('Ym') . '.txt';
// if new monthly file, close old logfile
if (!file_exists($file) && $logfile) {
fclose($logfile);
$logfile = false;
}
} else {
// original single big file
$file = 'logfile.txt';
}
if (!$logfile) {
$logfile = fopen($file, 'a+');
}
fwrite($logfile, $text);
} // doLog
/**
* Case-insensitive file_exists replacement function.
* Returns matching path, otherwise false.
* Created by Xymph
*/
function file_exists_nocase($filepath) {
// try case-sensitive path first
if (file_exists($filepath)) return $filepath;
// extract directory path
if (DIRECTORY_SEPARATOR == '/')
preg_match('|^(.+/)([^/]+)$|', $filepath, $paths);
else // '\'
preg_match('|^(.+\\\\)([^\\\\]+)$|', $filepath, $paths);
$dirpath = $paths[1];
// $filename = $paths[2];
// collect all files inside directory
$checkpaths = glob($dirpath . '*');
if ($checkpaths === false || empty($checkpaths)) return false;
// check case-insensitive paths
foreach ($checkpaths as $path)
if (strtolower($filepath) == strtolower($path))
return $path;
return false;
} // file_exists_nocase
/**
* Puts an element at a specific position into an array.
* Increases original size by one element.
*/
function insertArrayElement(&$array, $value, $pos) {
// get current size
$size = count($array);
// if position is in array range
if ($pos < 0 && $pos >= $size) {
return false;
}
// shift values down
for ($i = $size-1; $i >= $pos; $i--) {
$array[$i+1] = $array[$i];
}
// now put in the new element
$array[$pos] = $value;
return true;
} // insertArrayElement
/**
* Removes an element from a specific position in an array.
* Decreases original size by one element.
*/
function removeArrayElement(&$array, $pos) {
// get current size
$size = count($array);
// if position is in array range
if ($pos < 0 && $pos >= $size) {
return false;
}
// remove specified element
unset($array[$pos]);
// shift values up
$array = array_values($array);
return true;
} // removeArrayElement
/**
* Moves an element from one position to the other.
* All items between are shifted down or up as needed.
*/
function moveArrayElement(&$array, $from, $to) {
// get current size
$size = count($array);
// destination and source have to be among the array borders!
if ($from < 0 || $from >= $size || $to < 0 || $to >= $size) {
return false;
}
// backup the element we have to move
$moving_element = $array[$from];
if ($from > $to) {
// shift values between downwards
for ($i = $from-1; $i >= $to; $i--) {
$array[$i+1] = $array[$i];
}
} else { // $from < $to
// shift values between upwards
for ($i = $from; $i <= $to; $i++) {
$array[$i] = $array[$i+1];
}
}
// now put in the element which was to move
$array[$to] = $moving_element;
return true;
} // moveArrayElement
/**
* Formats a string from the format sssshh0
* into the format mmm:ss.hh (or mmm:ss if $hsec is false)
*/
function formatTime($MwTime, $hsec = true) {
if ($MwTime == -1) {
return '???';
} else {
$minutes = floor($MwTime/(1000*60));
$seconds = floor(($MwTime - $minutes*60*1000)/1000);
$hseconds = substr($MwTime, strlen($MwTime)-3, 2);
if ($hsec) {
$tm = sprintf('%02d:%02d.%02d', $minutes, $seconds, $hseconds);
} else {
$tm = sprintf('%02d:%02d', $minutes, $seconds);
}
}
if ($tm[0] == '0') {
$tm = substr($tm, 1);
}
return $tm;
} // formatTime
/**
* Formats a string from the format sssshh0
* into the format hh:mm:ss.hh (or hh:mm:ss if $hsec is false)
*/
function formatTimeH($MwTime, $hsec = true) {
if ($MwTime == -1) {
return '???';
} else {
$hseconds = substr($MwTime, strlen($MwTime)-3, 2);
$MwTime = substr($MwTime, 0, strlen($MwTime)-3);
$hours = floor($MwTime / 3600);
$MwTime = $MwTime - ($hours * 3600);
$minutes = floor($MwTime / 60);
$MwTime = $MwTime - ($minutes * 60);
$seconds = floor($MwTime);
if ($hsec) {
return sprintf('%02d:%02d:%02d.%02d', $hours, $minutes, $seconds, $hseconds);
} else {
return sprintf('%02d:%02d:%02d', $hours, $minutes, $seconds);
}
}
} // formatTimeH
/**
* Formats a text.
* Replaces parameters in the text which are marked with {n}
*/
function formatText($text) {
// get all function's parameters
$args = func_get_args();
// first parameter is the text to format
$text = array_shift($args);
// further parameters will be replaced in the text
$i = 1;
foreach ($args as $param)
$text = str_replace('{' . $i++ . '}', $param, $text);
// and return the modified text
return $text;
} // formatText
/**
* Make String for SQL use that single quoted & got special chars replaced.
*/
function quotedString($input) {
return "'" . mysql_real_escape_string($input) . "'";
} // quotedString
/**
* Check login string for LAN postfix (pre/post v2.11.21).
*/
function isLANLogin($login) {
$n="(25[0-5]|2[0-4]\d|[01]?\d\d|\d)";
return (preg_match("/(\/{$n}\\.{$n}\\.{$n}\\.{$n}:\d+)$/", $login) ||
preg_match("/(_{$n}\\.{$n}\\.{$n}\\.{$n}_\d+)$/", $login));
} // isLANLogin
/**
* Summary: Strips all display formatting from an input string, suitable for display
* within the game ('$$' escape pairs are preserved) and for logging
* Params : $input - The input string to strip formatting from
* $for_tm - Optional flag to double up '$' into '$$' (default, for TM) or not (for logs, etc)
* Returns: The content portions of $input without formatting
* Authors: Bilge/Assembler Maniac/Xymph/Slig
*
* "$af0Brat$s$fffwurst" will become "Bratwurst".
* 2007-08-27 Xymph - replaced with Bilge/AM's code (w/o the H&L tags bit)
* http://www.tm-forum.com/viewtopic.php?p=55867#p55867
* 2008-04-24 Xymph - extended to handle the H/L/P tags for TMF
* http://www.tm-forum.com/viewtopic.php?p=112856#p112856
* 2009-05-16 Slig - extended to emit non-TM variant & handle incomplete colors
* http://www.tm-forum.com/viewtopic.php?p=153368#p153368
* 2010-10-05 Slig - updated to handle incomplete colors & tags better
* http://www.tm-forum.com/viewtopic.php?p=183410#p183410
* 2010-10-09 Xymph - updated to handle $[ and $] properly
* http://www.tm-forum.com/viewtopic.php?p=183410#p183410
*/
function stripColors($input, $for_tm = true) {
return
//Replace all occurrences of a null character back with a pair of dollar
//signs for displaying in TM, or a single dollar for log messages etc.
str_replace("\0", ($for_tm ? '$$' : '$'),
//Replace links (introduced in TMU)
preg_replace(
'/
#Strip TMF H, L & P links by stripping everything between each square
#bracket pair until another $H, $L or $P sequence (or EoS) is found;
#this allows a $H to close a $L and vice versa, as does the game
\\$[hlp](.*?)(?:\\[.*?\\](.*?))*(?:\\$[hlp]|$)
/ixu',
//Keep the first and third capturing groups if present
'$1$2',
//Replace various patterns beginning with an unescaped dollar
preg_replace(
'/
#Match a single dollar sign and any of the following:
\\$
(?:
#Strip color codes by matching any hexadecimal character and
#any other two characters following it (except $)
[0-9a-f][^$][^$]
#Strip any incomplete color codes by matching any hexadecimal
#character followed by another character (except $)
|[0-9a-f][^$]
#Strip any single style code (including an invisible UTF8 char)
#that is not an H, L or P link or a bracket ($[ and $])
|[^][hlp]
#Strip the dollar sign if it is followed by [ or ], but do not
#strip the brackets themselves
|(?=[][])
#Strip the dollar sign if it is at the end of the string
|$
)
#Ignore alphabet case, ignore whitespace in pattern & use UTF-8 mode
/ixu',
//Replace any matches with nothing (i.e. strip matches)
'',
//Replace all occurrences of dollar sign pairs with a null character
str_replace('$$', "\0", $input)
)
)
)
;
} // stripColors
/**
* Strips only size tags from TM strings.
* "$w$af0Brat$n$fffwurst" will become "$af0Brat$fffwurst".
* 2009-03-27 Xymph - derived from stripColors above
* http://www.tm-forum.com/viewtopic.php?f=127&t=20602
* 2009-05-16 Slig - extended to emit non-TM variant
* http://www.tm-forum.com/viewtopic.php?p=153368#p153368
*/
function stripSizes($input, $for_tm = true) {
return
//Replace all occurrences of a null character back with a pair of dollar
//signs for displaying in TM, or a single dollar for log messages etc.
str_replace("\0", ($for_tm ? '$$' : '$'),
//Replace various patterns beginning with an unescaped dollar
preg_replace(
'/
#Match a single dollar sign and any of the following:
\\$
(?:
#Strip any size code
[nwo]
#Strip the dollar sign if it is at the end of the string
|$
)
#Ignore alphabet case, ignore whitespace in pattern & use UTF-8 mode
/ixu',
//Replace any matches with nothing (i.e. strip matches)
'',
//Replace all occurrences of dollar sign pairs with a null character
str_replace('$$', "\0", $input)
)
)
;
} // stripSizes
/**
* Strips only newlines from TM strings.
*/
function stripNewlines($input) {
return str_replace(array("\n\n", "\r", "\n"),
array(' ', '', ''), $input);
} // stripNewlines
/**
* Univeral show help for user, admin & Jfreu commands.
* Created by Xymph
*
* $width is the width of the first column in the ManiaLink window on TMF
*/
function showHelp($player, $chat_commands, $head,
$showadmin = false, $dispall = false, $width = 0.3) {
global $aseco;
// display full help for TMN
if ($aseco->server->getGame() == 'TMN' && $dispall) {
$head = "Currently supported $head commands:" . LF;
if (!empty($chat_commands)) {
// define admin or non-admin padding string
$pad = ($showadmin ? '$f00... ' : '$f00/');
$help = '';
$lines = 0;
$player->msgs = array();
$player->msgs[0] = 1;
// create list of chat commands
foreach ($chat_commands as $cc) {
// collect either admin or non-admin commands
if ($cc->isadmin == $showadmin) {
$help .= $pad . $cc->name . ' $000' . $cc->help . LF;
if (++$lines > 14) {
$player->msgs[] = $head . $help;
$lines = 0;
$help = '';
}
}
}
// add if last batch exists
if ($help != '')
$player->msgs[] = $head . $help;
// display popup message
if (count($player->msgs) == 2) {
$aseco->client->query('SendDisplayServerMessageToLogin', $player->login, $player->msgs[1], 'OK', '', 0);
} else { // > 2
$aseco->client->query('SendDisplayServerMessageToLogin', $player->login, $player->msgs[1], 'Close', 'Next', 0);
}
}
// display full help for TMF
} elseif ($aseco->server->getGame() == 'TMF' && $dispall) {
$head = "Currently supported $head commands:";
if (!empty($chat_commands)) {
// define admin or non-admin padding string
$pad = ($showadmin ? '$f00... ' : '$f00/');
$help = array();
$lines = 0;
$player->msgs = array();
$player->msgs[0] = array(1, $head, array(1.3, $width, 1.3 - $width), array('Icons64x64_1', 'TrackInfo', -0.01));
// create list of chat commands
foreach ($chat_commands as $cc) {
// collect either admin or non-admin commands
if ($cc->isadmin == $showadmin) {
$help[] = array($pad . $cc->name, $cc->help);
if (++$lines > 14) {
$player->msgs[] = $help;
$lines = 0;
$help = array();
}
}
}
// add if last batch exists
if (!empty($help))
$player->msgs[] = $help;
// display ManiaLink message
display_manialink_multi($player);
}
// show help for TMS or TMO, and plain help for TMF/TMN
} else {
$head = "Currently supported $head commands:" . LF;
$help = $aseco->formatColors('{#interact}' . $head);
foreach ($chat_commands as $cc) {
// collect either admin or non-admin commands
if ($cc->isadmin == $showadmin) {
$help .= $cc->name . ', ';
}
}
// show chat message
$help = substr($help, 0, strlen($help) - 2); // strip trailing ", "
$aseco->client->query('ChatSendToLogin', $help, $player->login);
}
} // showHelp
/**
* Map country names to 3-letter Nation abbreviations
* Created by Xymph
* Based on http://en.wikipedia.org/wiki/List_of_IOC_country_codes
* See also http://en.wikipedia.org/wiki/Comparison_of_IOC,_FIFA,_and_ISO_3166_country_codes
*/
function mapCountry($country) {
$nations = array(
'Afghanistan' => 'AFG',
'Albania' => 'ALB',
'Algeria' => 'ALG',
'Andorra' => 'AND',
'Angola' => 'ANG',
'Argentina' => 'ARG',
'Armenia' => 'ARM',
'Aruba' => 'ARU',
'Australia' => 'AUS',
'Austria' => 'AUT',
'Azerbaijan' => 'AZE',
'Bahamas' => 'BAH',
'Bahrain' => 'BRN',
'Bangladesh' => 'BAN',
'Barbados' => 'BAR',
'Belarus' => 'BLR',
'Belgium' => 'BEL',
'Belize' => 'BIZ',
'Benin' => 'BEN',
'Bermuda' => 'BER',
'Bhutan' => 'BHU',
'Bolivia' => 'BOL',
'Bosnia&Herzegovina' => 'BIH',
'Botswana' => 'BOT',
'Brazil' => 'BRA',
'Brunei' => 'BRU',
'Bulgaria' => 'BUL',
'Burkina Faso' => 'BUR',
'Burundi' => 'BDI',
'Cambodia' => 'CAM',
'Cameroon' => 'CAR', // actually CMR
'Canada' => 'CAN',
'Cape Verde' => 'CPV',
'Central African Republic' => 'CAF',
'Chad' => 'CHA',
'Chile' => 'CHI',
'China' => 'CHN',
'Chinese Taipei' => 'TPE',
'Colombia' => 'COL',
'Congo' => 'CGO',
'Costa Rica' => 'CRC',
'Croatia' => 'CRO',
'Cuba' => 'CUB',
'Cyprus' => 'CYP',
'Czech Republic' => 'CZE',
'Czech republic' => 'CZE',
'DR Congo' => 'COD',
'Denmark' => 'DEN',
'Djibouti' => 'DJI',
'Dominica' => 'DMA',
'Dominican Republic' => 'DOM',
'Ecuador' => 'ECU',
'Egypt' => 'EGY',
'El Salvador' => 'ESA',
'Eritrea' => 'ERI',
'Estonia' => 'EST',
'Ethiopia' => 'ETH',
'Fiji' => 'FIJ',
'Finland' => 'FIN',
'France' => 'FRA',
'Gabon' => 'GAB',
'Gambia' => 'GAM',
'Georgia' => 'GEO',
'Germany' => 'GER',
'Ghana' => 'GHA',
'Greece' => 'GRE',
'Grenada' => 'GRN',
'Guam' => 'GUM',
'Guatemala' => 'GUA',
'Guinea' => 'GUI',
'Guinea-Bissau' => 'GBS',
'Guyana' => 'GUY',
'Haiti' => 'HAI',
'Honduras' => 'HON',
'Hong Kong' => 'HKG',
'Hungary' => 'HUN',
'Iceland' => 'ISL',
'India' => 'IND',
'Indonesia' => 'INA',
'Iran' => 'IRI',
'Iraq' => 'IRQ',
'Ireland' => 'IRL',
'Israel' => 'ISR',
'Italy' => 'ITA',
'Ivory Coast' => 'CIV',
'Jamaica' => 'JAM',
'Japan' => 'JPN',
'Jordan' => 'JOR',
'Kazakhstan' => 'KAZ',
'Kenya' => 'KEN',
'Kiribati' => 'KIR',
'Korea' => 'KOR',
'Kuwait' => 'KUW',
'Kyrgyzstan' => 'KGZ',
'Laos' => 'LAO',
'Latvia' => 'LAT',
'Lebanon' => 'LIB',
'Lesotho' => 'LES',
'Liberia' => 'LBR',
'Libya' => 'LBA',
'Liechtenstein' => 'LIE',
'Lithuania' => 'LTU',
'Luxembourg' => 'LUX',
'Macedonia' => 'MKD',
'Malawi' => 'MAW',
'Malaysia' => 'MAS',
'Mali' => 'MLI',
'Malta' => 'MLT',
'Mauritania' => 'MTN',
'Mauritius' => 'MRI',
'Mexico' => 'MEX',
'Moldova' => 'MDA',
'Monaco' => 'MON',
'Mongolia' => 'MGL',
'Montenegro' => 'MNE',
'Morocco' => 'MAR',
'Mozambique' => 'MOZ',
'Myanmar' => 'MYA',
'Namibia' => 'NAM',
'Nauru' => 'NRU',
'Nepal' => 'NEP',
'Netherlands' => 'NED',
'New Zealand' => 'NZL',
'Nicaragua' => 'NCA',
'Niger' => 'NIG',
'Nigeria' => 'NGR',
'Norway' => 'NOR',
'Oman' => 'OMA',
'Other Countries' => 'OTH',
'Pakistan' => 'PAK',
'Palau' => 'PLW',
'Palestine' => 'PLE',
'Panama' => 'PAN',
'Paraguay' => 'PAR',
'Peru' => 'PER',
'Philippines' => 'PHI',
'Poland' => 'POL',
'Portugal' => 'POR',
'Puerto Rico' => 'PUR',
'Qatar' => 'QAT',
'Romania' => 'ROM', // actually ROU
'Russia' => 'RUS',
'Rwanda' => 'RWA',
'Samoa' => 'SAM',
'San Marino' => 'SMR',
'Saudi Arabia' => 'KSA',
'Senegal' => 'SEN',
'Serbia' => 'SCG', // actually SRB
'Sierra Leone' => 'SLE',
'Singapore' => 'SIN',
'Slovakia' => 'SVK',
'Slovenia' => 'SLO',
'Somalia' => 'SOM',
'South Africa' => 'RSA',
'Spain' => 'ESP',
'Sri Lanka' => 'SRI',
'Sudan' => 'SUD',
'Suriname' => 'SUR',
'Swaziland' => 'SWZ',
'Sweden' => 'SWE',
'Switzerland' => 'SUI',
'Syria' => 'SYR',
'Taiwan' => 'TWN',
'Tajikistan' => 'TJK',
'Tanzania' => 'TAN',
'Thailand' => 'THA',
'Togo' => 'TOG',
'Tonga' => 'TGA',
'Trinidad and Tobago' => 'TRI',
'Tunisia' => 'TUN',
'Turkey' => 'TUR',
'Turkmenistan' => 'TKM',
'Tuvalu' => 'TUV',
'Uganda' => 'UGA',
'Ukraine' => 'UKR',
'United Arab Emirates' => 'UAE',
'United Kingdom' => 'GBR',
'United States of America' => 'USA',
'Uruguay' => 'URU',
'Uzbekistan' => 'UZB',
'Vanuatu' => 'VAN',
'Venezuela' => 'VEN',
'Vietnam' => 'VIE',
'Yemen' => 'YEM',
'Zambia' => 'ZAM',
'Zimbabwe' => 'ZIM',
);
if (array_key_exists($country, $nations)) {
$nation = $nations[$country];
} else {
$nation = 'OTH';
if ($country != '')
trigger_error('Could not map country: ' . $country, E_USER_WARNING);
}
return $nation;
} // mapCountry
/**
* Find TMX data for the given track
* Created by Xymph
*/
require_once('includes/tmxinfofetcher.inc.php'); // provides access to TMX info
function findTMXdata($uid, $envir, $exever, $records = false) {
// determine likely search order
if ($envir == 'Stadium') {
// check for old TMN
if (strcmp($exever, '0.1.8.0') < 0)
$sections = array('TMN', 'TMNF', 'TMU');
// check for new TMF
elseif (strcmp($exever, '2.11.0') >= 0)
$sections = array('TMNF', 'TMU');
else
$sections = array('TMU'); // TMNF section opened after TMF beta
} elseif ($envir == 'Bay' || $envir == 'Coast' || $envir == 'Island') {
// check for old TMS
if (strcmp($exever, '0.1.5.0') <= 0)
$sections = array('TMS', 'TMU');
else
$sections = array('TMU'); // TMS section closed after TMU release
} else { // $envir == 'Alpine' || 'Snow' || 'Desert' || 'Speed' || 'Rally'
// check for old TMO
if (strcmp($exever, '0.1.5.0') <= 0)
$sections = array('TMO', 'TMU');
else
$sections = array('TMU'); // TMO section closed after TMU release
}
// search TMX for track
foreach ($sections as $section) {
$tmxdata = new TMXInfoFetcher($section, $uid, $records);
if ($tmxdata->name) {
return $tmxdata;
}
}
return false;
} // findTMXdata
/**
* Simple HTTP Get function with timeout
* ok: return string || error: return false || timeout: return -1
* if $openonly == true, don't read data but return true upon connect
*/
function http_get_file($url, $openonly = false) {
global $aseco;
$url = parse_url($url);
$port = isset($url['port']) ? $url['port'] : 80;
$query = isset($url['query']) ? '?' . $url['query'] : '';
$fp = @fsockopen($url['host'], $port, $errno, $errstr, 4);
if (!$fp)
return false;
if ($openonly) {
fclose($fp);
return true;
}
$uri = '';
foreach (explode('/', $url['path']) as $subpath)
$uri .= rawurlencode($subpath) . '/';
$uri = substr($uri, 0, strlen($uri)-1); // strip trailing '/'
fwrite($fp, 'GET ' . $uri . $query . " HTTP/1.0\r\n" .
'Host: ' . $url['host'] . "\r\n" .
'User-Agent: XASECO-' . XASECO_VERSION . ' (' . PHP_OS . '; ' .
$aseco->server->game . ")\r\n\r\n");
stream_set_timeout($fp, 2);
$res = '';
$info['timed_out'] = false;
while (!feof($fp) && !$info['timed_out']) {
$res .= fread($fp, 512);
$info = stream_get_meta_data($fp);
}
fclose($fp);
if ($info['timed_out']) {
return -1;
} else {
if (substr($res, 9, 3) != '200')
return false;
$page = explode("\r\n\r\n", $res, 2);
return $page[1];
}
} // http_get_file
/**
* Return valid UTF-8 string, replacing faulty byte values with a given string
* Created by (OoR-F)~fuckfish (fish@stabb.de)
* http://www.tm-forum.com/viewtopic.php?p=117639#p117639
* Based on the original tm_substr function by Slig (slig@free.fr)
* Updated by Xymph; More info: http://en.wikipedia.org/wiki/UTF-8
*/
function validateUTF8String($input, $invalidRepl = '') {
$str = (string) $input;
$len = strlen($str); // byte string length
$pos = 0; // current byte pos in string
$new = '';
while ($pos < $len) {
$co = ord($str[$pos]);
// 4-6 bytes UTF8 => unsupported
if ($co >= 240) {
// bad multibyte char
$new .= $invalidRepl;
$pos++;
// 3 bytes UTF8 => 1110bbbb 10bbbbbb 10bbbbbb
} elseif ($co >= 224) {
if (($pos+2 < $len) &&
(ord($str[$pos+1]) >= 128 && ord($str[$pos+1]) < 192) &&
(ord($str[$pos+2]) >= 128 && ord($str[$pos+2]) < 192)) {
// ok, it was 1 character, increase counters
$new .= substr($str, $pos, 3);
$pos += 3;
} else {
// bad multibyte char
$new .= $invalidRepl;
$pos++;
}
// 2 bytes UTF8 => 110bbbbb 10bbbbbb
} elseif ($co >= 194) {
if (($pos+1 < $len) &&
(ord($str[$pos+1]) >= 128 && ord($str[$pos+1]) < 192)) {
// ok, it was 1 character, increase counters
$new .= substr($str, $pos, 2);
$pos += 2;
} else {
// bad multibyte char
$new .= $invalidRepl;
$pos++;
}
// 2 bytes overlong encoding => unsupported
} elseif ($co >= 192) {
// bad multibyte char 1100000b
$new .= $invalidRepl;
$pos++;
// 1 byte ASCII => 0bbbbbbb, or invalid => 10bbbbbb or 11111bbb
} else { // $co < 192
// erroneous middle multibyte char?
if ($co >= 128 || $co == 0)
$new .= $invalidRepl;
else
$new .= $str[$pos];
$pos++;
}
}
return $new;
} // validateUTF8String
/**
* Convert php.ini memory shorthand string to integer bytes
* http://www.php.net/manual/en/function.ini-get.php#96996
*/
function shorthand2bytes($size_str) {
switch (substr($size_str, -1)) {
case 'M': case 'm': return (int)$size_str * 1048576;
case 'K': case 'k': return (int)$size_str * 1024;
case 'G': case 'g': return (int)$size_str * 1073741824;
default: return (int)$size_str;
}
} // return_bytes
/**
* Convert boolean value to text string
*/
function bool2text($boolval) {
if ($boolval)
return 'True';
else
return 'False';
} // bool2text
?>

View File

@ -0,0 +1,131 @@
<?php
// vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2:
require('GbxRemote.inc.php');
/**
* GBXChallInfo - Return GetCurrentChallengeInfo data for TrackMania tracks
* Created by Xymph <tm@gamers.org>
*
* v1.2: Allow getting info from tracks already on the server w/o removing them
* v1.1: Fix PHP notices about redefinition of constants
* v1.0: Initial release
*/
class GBXChallengeInfo {
public $name, $uid, $filename, $author, $envir, $mood,
$bronzetm, $silvertm, $goldtm, $authortm,
$coppers, $laprace, $nblaps, $nbcps, $nbrcps, $error;
/**
* Fetches current ChallengeInfo for a GBX challenge
* Loads track into server, selects it, gets info, and removes it from server
*
* @param String $filename
* The challenge filename (must be a path below .../GameData/Tracks/)
* @return GBXChallengeInfo
* If $uid is empty, GBX data couldn't be extracted and $error contains
* an error message
*/
public function GBXChallengeInfo($filename) {
$ip = 'localhost';
$port = 5000;
$user = 'SuperAdmin';
$pass = 'YOUR_SUPERADMIN_PASSWORD';
$this->uid = '';
$this->error = '';
$client = new IXR_Client_Gbx;
// connect to the server
if (!$client->InitWithIp($ip, $port)) {
$this->error = 'Connection failed - Error ' . $client->getErrorCode() . ': ' . $client->getErrorMessage();
return false;
}
// log into the server
if (!$client->query('Authenticate', $user, $pass)) {
$this->error = 'Login failed - Error ' . $client->getErrorCode() . ': ' . $client->getErrorMessage();
} else {
// add the challenge
$ret = $client->query('AddChallenge', $filename);
$already = ($client->getErrorMessage() == 'Challenge already added.');
if (!$ret && !$already) {
$this->error = 'AddChallenge failed - Error ' . $client->getErrorCode() . ': ' . $client->getErrorMessage();
} else {
// select the challenge
if (!$client->query('ChooseNextChallenge', $filename)) {
$this->error = 'ChooseNextChallenge failed - Error ' . $client->getErrorCode() . ': ' . $client->getErrorMessage();
// switch to our challenge
} elseif (!$client->query('NextChallenge')) {
$this->error = 'NextChallenge 1 failed - Error ' . $client->getErrorCode() . ': ' . $client->getErrorMessage();
} else {
// allow for challenge switch but time out after 5 seconds
$retry = 5;
while (true) {
sleep(1);
// obtain challenge details
if ($client->query('GetCurrentChallengeInfo')) {
$info = $client->getResponse();
// check for our challenge
if ($info['FileName'] == $filename) {
break;
}
} else {
$this->error = 'GetCurrentChallengeInfo failed - Error ' . $client->getErrorCode() . ': ' . $client->getErrorMessage();
break;
}
if ($retry-- == 0) {
$this->error = 'GetCurrentChallengeInfo timed out after 5 seconds';
break;
}
}
// extract our challenge details
if ($this->error == '') {
$this->name = $info['Name'];
$this->uid = $info['UId'];
$this->filename = $info['FileName'];
$this->author = $info['Author'];
$this->envir = $info['Environnement'];
$this->mood = $info['Mood'];
$this->bronzetm = $info['BronzeTime'];
$this->silvertm = $info['SilverTime'];
$this->goldtm = $info['GoldTime'];
$this->authortm = $info['AuthorTime'];
$this->coppers = $info['CopperPrice'];
$this->laprace = $info['LapRace'];
$this->nblaps = $info['NbLaps'];
$this->nbcps = $info['NbCheckpoints'];
$this->nbrcps = $info['NbCheckpoints'];
if ($this->laprace && $this->nblaps > 1)
$this->nbrcps *= $this->nblaps;
}
}
}
// check if challenge wasn't already there
if (!$already) {
// remove the challenge
if (!$client->query('RemoveChallenge', $filename)) {
$this->error = 'RemoveChallenge failed - Error ' . $client->getErrorCode() . ': ' . $client->getErrorMessage();
}
// switch away from our challenge
sleep(5);
if (!$client->query('NextChallenge')) {
$this->error = 'NextChallenge 2 failed - Error ' . $client->getErrorCode() . ': ' . $client->getErrorMessage();
}
}
}
$client->Terminate();
}
}
?>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,136 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
/**
* Jfreu's plugin 0.13d
* Configuration settings.
* This file is included by jfreu.plugin.php or jfreu.lite.php, so don't
* list it in plugins.xml!
* Updated by Xymph
*/
//-> paths to config, vip/vip_team & bans files
$conf_file = 'plugins/jfreu/jfreu.config.xml';
$vips_file = 'plugins/jfreu/jfreu.vips.xml';
$bans_file = 'plugins/jfreu/jfreu.bans.xml';
//-> Server's base name: (ex: '$000Jfreu')
// Max. length: 26 chars (incl. colors & tags, and optional "TopXXX")
$servername = 'YOUR SERVER NAME';
//-> Word between the servername and the limit (usually " Top")
$top = ' $449TOP';
//-> Change the servername when the limit changes: "Servername TopXXX" (0 = OFF, 1 = ON)
$autochangename = 0;
//-> ranklimit: ranklimiting default state (0 = OFF, 1 = ON)
$ranklimit = 0;
//-> limit: ranklimit default value (when autorank is OFF)
$limit = 500000;
//-> spec ranklimit
$hardlimit = 1000000;
//-> autorank: autorank default state (0 = OFF, 1 = ON)
$autorank = 0;
//-> offset (average + offset = Auto-RankLimit)
$offset = 999;
//-> autorankminplayers (autorank disabled when not enough players)
$autorankminplayers = 10;
//-> autorankvip: include VIP/unSpec in autorank calculation (0 = OFF, 1 = ON)
$autorankvip = 0;
//-> kick hirank when server is full and new player arrives (0 = OFF, 1 = ON)
$kickhirank = 0;
//-> maxplayers value for kickhirank (must be less than server's <max_players>)
$maxplayers = 20;
//-> allow user /unspec vote (0 = OFF, 1 = ON)
$unspecvote = 1;
//-> player join/leave messages
$player_join = '{#server}>> {1}: {#highlite}{2}$z$s{#message} Nation: {#highlite}{3}{#message} Ladder: {#highlite}{4}';
$player_joins = '{#server}>> {1}: {#highlite}{2}$z$s{#message} Nation: {#highlite}{3}{#message} Ladder: {#highlite}{4}{#message} Server: {#highlite}{5}';
$player_left = '{#server}>> {#highlite}{1}$z$s{#message} has left the game. Played: {#highlite}{2}';
//-> random info messages at the end of the race (0 = OFF, 1 = in chat, 2 = in TMF message window)
$infomessages = 1;
//-> prefix for info messages
$message_start = '$z$s$ff0>> [$f00INFO$ff0] $fff';
//-> random information messages (if you add a message don't forget to change the number) (999 messages max :-P)
// $message1 = 'Jfreu\'s plugin: "http://reload.servegame.com/plugin/"';
$message1 = 'Information about and download of this XASECO on ' . XASECO_TMN;
$message2 = 'Use "/list" -> "/jukebox ##" to add a track in the jukebox.';
$message3 = 'Please don\'t sound your horn throughout the entire track.';
$message4 = 'When going AFK, please set your car to Spectator mode.';
$message5 = 'Don\'t use Enter to skip intros - instead use Space & Enter';
$message6 = 'For player & server info use the "/stats" and "/server" commands.';
$message7 = 'Looking for the name of this server? Use the "/server" command.';
$message8 = 'Use "/list nofinish" to find tracks you haven\'t completed yet, then /jukebox them!';
$message9 = 'Use "/list norank" to find tracks you aren\'t ranked on, then /jukebox them!';
$message10 = 'Can you beat the Gold time on all tracks? Use "/list nogold" to find out!';
$message11 = 'Can you beat the Author time on all tracks? Use "/list noauthor" to find out!';
$message12 = 'Wondering which tracks you haven\'t played recently? Use "/list norecent" to find out!';
$message13 = 'Use the "/best" & "/worst" commands to find your best and worst records!';
$message14 = 'Use the "/clans" & "/topclans" commands to see clan members and ranks!';
$message15 = 'Use the "/ranks" commands to see the server ranks of all online players!';
$message16 = 'Who is the most victorious player? Use "/topwins" to find out!';
$message17 = 'Who has the most ranked records? Use "/toprecs" to find out!';
$message18 = 'Wondering what tracks were played recently? Use the "/history" command.';
$message19 = 'Looking for the next better ranked record to beat? Use "/nextrec"!';
$message20 = 'Find the difference between your personal best and the track record with the "/diffrec" command!';
$message21 = 'Check how many records were driven on the current track with the "/newrecs" command!';
$message22 = 'Check how many records, and the 3 best ones, you have with the "/summary" command!';
$message23 = 'Who has the most top-3 ranked records? Use "/topsums" to find out!';
$message24 = 'Jukeboxed the wrong track? Use "/jukebox drop" to remove it!';
$message25 = 'Forgot what someone said? Use "/chatlog" to check the chat history!';
$message26 = 'Forgot what someone pm-ed you? Use "/pmlog" to check your PM history!';
$message27 = 'Looking for the next better ranked player to beat? Use "/nextrank"!';
$message28 = 'Use "/list newest <#>" to find the newest tracks added to the server, then /jukebox them!';
$message29 = 'Find the longest and shortest tracks with the "/list longest / shortest" commands!';
$message30 = 'Use "/mute" and "/unmute" to mute / unmute other players, and "/mutelist" to list them!';
$message31 = 'Wondering when a player was last online? Use "/laston <login>" to find out!';
$message32 = 'Looking for any player\'s world stats? Use the "/statsall <login>" command!';
$message33 = 'Use checkpoints tracking in Rounds/Team/Cup modes with the "/cps" command!';
$message34 = 'Find the TMX info & records for a track with the "/tmxinfo" & "/tmxrecs" commands!';
$message35 = 'Looking for the name of the current track\'s song? Use the "/song" command!';
$message36 = 'Looking for the name of the current track\'s mod? Use the "/mod" command!';
$message37 = 'Use the "/style" command to select your personal window style!';
$message38 = 'Use the "/recpanel" command to select your personal records panel!';
$message39 = 'Use the "/votepanel" command to select your personal vote panel!';
$message40 = 'Find out all about the Dedimania world records system with "/helpdedi"!';
$message41 = 'Check out the XASECO[2] site at ' . XASECO_ORG . ' !';
global $feature_votes;
if ($feature_votes) {
$message42 = 'Find out all about the chat-based voting commands with "/helpvote"!';
}
if (function_exists('send_window_message')) {
$message43 = 'Missed a system message? Use "/msglog" to check the message history!';
}
//-> Badwords checking (0 = OFF, 1 = ON)
$badwords = 0;
//-> Badwords banning (0 = OFF, 1 = ON)
$badwordsban = 0;
//-> Number of badwords allowed
$badwordsnum = 3;
//-> Banning period (minutes)
$badwordstime = 10;
//-> Badwords to check for
$badwordslist = array(
'putain','ptain','klote','kIote','kanker','kenker',
'arschl','wichs','fick','fikk','salop','siktirgit','gvd',
'hitler','nutte','dick','cock','faitchier','bordel','shit',
'encul','sucks','a.q','conerie','scheise','scheiße','scheis',
'baskasole','cocugu','kodugumun','cazo','hoer','bitch',
'penis','fotze','maul','frese','pizda','gay','fuck','tyfus',
'sugi','cacat','pisat','labagiu','gaozar','muist','orospu',
'pédé','cunt','godve','godfe','kut','kudt','lul','iui');
//-> novote (auto-cancel votes) (0 = OFF, 1 = ON)
$novote = 0;
?>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,154 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
/**
* Ogg_Comments - Extract comments (ID3 tags) from .ogg files
* Created by Xymph <tm@gamers.org>
* Derived from Ogg.class.php v1.3e by Nicolas Ricquemaque <f1iqf@hotmail.com>
* For more info see: http://opensource.grisambre.net/ogg/
* Ogg format: http://en.wikipedia.org/wiki/Ogg
* Comments : http://www.xiph.org/vorbis/doc/v-comment.html
*
* v1.1: Improved get_1block URL parsing; added User-Agent to the GET request
* v1.0: Initial release
*/
define('BLOCKSIZE', 512);
class Ogg_Comments {
public $comments = array();
/**
* Fetches all comments from a .ogg local file or URL
*
* @param String $path
* The .ogg file or URL
* @param Boolean $utf8
* If true, return fields in UTF-8, otherwise in ASCII/ISO-8859-1
* @return Ogg_Comments
* If $comments is empty, no .ogg file
*/
public function Ogg_Comments($path, $utf8 = false) {
// check for local file or URL
if (strpos($path, '://') === false) {
if (!$fp = @fopen($path, 'rb'))
return false;
$file = fread($fp, BLOCKSIZE);
fclose($fp);
if ($file === false)
return false;
} else {
$file = $this->get_1block($path);
if ($file === false || $file == -1)
return false;
}
// read OGG pages
for ($pos = 0; ($pos = strpos($file, 'OggS', $pos)) !== false; $pos++) {
// check stream version
if (ord($file[$pos+4]) != 0)
continue;
// compute offset of packet after header
$offset = $pos + 27 + ord($file[$pos+26]);
// check for second (== comments) packet
if ($this->read_intle($file, $pos+18) == 1) {
// check for vorbis comments
if (ord($file[$offset]) != 0x03 ||
substr($file, $offset+1, 6) != 'vorbis')
continue;
// read vendor string
$offset += 7;
$vndlen = $this->read_intle($file, $offset);
$this->comments['VENDOR'] = $this->decode_field(substr($file, $offset+4, $vndlen), $utf8);
// read comments count
$offset += 4 + $vndlen;
$cmtcnt = $this->read_intle($file, $offset);
// read/parse all comment fields
$offset += 4;
for ($i = 0; $i < $cmtcnt; $i++) {
$cmtlen = $this->read_intle($file, $offset);
$comment = substr($file, $offset+4, $cmtlen);
$offset += 4 + $cmtlen;
// store field name=value pair
$comment = explode('=', $comment, 2);
// check for repeated field & append
if (isset($this->comments[strtoupper($comment[0])]))
$this->comments[strtoupper($comment[0])] .= ', ' . $this->decode_field($comment[1], $utf8);
else
$this->comments[strtoupper($comment[0])] = $this->decode_field($comment[1], $utf8);
}
}
// check whether done
if (isset($this->comments['VENDOR']))
break;
}
} // Ogg_Comments
// Read 32-bits Little Endian integer
private function read_intle(&$buf, $pos) {
return (ord($buf[$pos+0]) + (ord($buf[$pos+1]) << 8) +
(ord($buf[$pos+2]) << 16) + (ord($buf[$pos+3]) << 24));
} // read_intle
// Decode comment field into specified charset
private function decode_field($str, $utf8) {
if ($utf8)
// return UTF8 or encode it in UTF8
return ((utf8_encode(utf8_decode($str)) == $str) ?
$str : utf8_encode($str));
else
// decode it to ASCII or return ASCII
return ((utf8_encode(utf8_decode($str)) == $str) ?
utf8_decode($str) : $str);
} // decode_field
// Simple HTTP Get 1 Block function with timeout
// ok: return string || error: return false || timeout: return -1
private function get_1block($url) {
$url = parse_url($url);
$port = isset($url['port']) ? $url['port'] : 80;
$query = isset($url['query']) ? '?' . $url['query'] : '';
$fp = @fsockopen($url['host'], $port, $errno, $errstr, 4);
if (!$fp)
return false;
$uri = '';
foreach (explode('/', $url['path']) as $subpath)
$uri .= rawurlencode($subpath) . '/';
$uri = substr($uri, 0, strlen($uri)-1); // strip trailing '/'
fwrite($fp, 'GET ' . $uri . $query . " HTTP/1.0\r\n" .
'Host: ' . $url['host'] . "\r\n" .
'User-Agent: Ogg_Comments (' . PHP_OS . ")\r\n\r\n");
stream_set_timeout($fp, 2);
$res = '';
$info['timed_out'] = false;
for ($i = 0; $i < 2; $i++)
if (feof($fp) || $info['timed_out']) {
break;
} else {
$res .= fread($fp, BLOCKSIZE);
$info = stream_get_meta_data($fp);
}
fclose($fp);
if ($info['timed_out']) {
return -1;
} else {
if (substr($res, 9, 3) != '200')
return false;
$page = explode("\r\n\r\n", $res, 2);
return trim($page[1]);
}
} // get_1block
} // class Ogg_Comments
?>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,290 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
/**
* TMNDataFetcher - Fetch TMN ladder/nation/server stats for a login
* Created by (OoR-F)~fuckfish (fish@stabb.de)
* Updated by Xymph <tm@gamers.org>
*
* v1.8: Improved error reporting via $error; fixed file check in extendedInfo
* processing; added magic __set_state function to support var_export()
* v1.7: Improve handling of empty API responses
* v1.6: Fixed $teamrank type into int; added User-Agent to the GET request
* v1.5: Tweaked initial get_file return value check
* v1.4: Fixed get_file return value checks; fixed $nationrank check
* v1.3: Optimized get_file URL parsing
* v1.2: Added get_file function to handle master server timeouts
* v1.1: General code cleanup; added more comments; added $lastmatch,
* $totalplayers, $nationplayers, $nationpos, $nationpoints,
* $totalnations, $servernick, $serverdesc, $servernation;
* renamed $actualserver to $serverlogin
* v1.0: Initial release
*/
class TMNDataFetcher {
public $version, $extended, $error,
$login, $nickname, $worldrank, $totalplayers,
$points, $lastmatch, $wins, $losses, $draws,
$stars, $stardays, $teamname, $teamrank, $totalteams,
$nation, $nationrank, $nationplayers,
$nationpos, $nationpoints, $totalnations,
$online, $serverlogin, $servernick, $serverdesc, $servernation;
/**
* Fetches a hell of a lot of data about a TMN login
*
* @param String $login
* The TMN login to search for
* @param Boolean $extendedInfo
* If true, the script also searches for the server that the
* player is on at the moment (also determines online-state)
* @return TMNDataFetcher
* If $nickname is empty, login was not found
*/
function TMNDataFetcher($login, $extendedInfo) {
$this->version = '0.1.7.9';
$this->error = '';
$this->extended = $extendedInfo;
$this->login = strtolower($login);
$this->getData();
} // TMNDataFetcher
public static function __set_state($import) {
$tmn = new TMNDataFetcher('', true);
$tmn->version = $import['version'];
$tmn->extended = $import['extended'];
$tmn->error = '';
$tmn->login = $import['login'];
$tmn->nickname = $import['nickname'];
$tmn->worldrank = $import['worldrank'];
$tmn->totalplayers = $import['totalplayers'];
$tmn->points = $import['points'];
$tmn->lastmatch = $import['lastmatch'];
$tmn->wins = $import['wins'];
$tmn->losses = $import['losses'];
$tmn->draws = $import['draws'];
$tmn->stars = $import['stars'];
$tmn->stardays = $import['stardays'];
$tmn->teamname = $import['teamname'];
$tmn->teamrank = $import['teamrank'];
$tmn->totalteams = $import['totalteams'];
$tmn->nation = $import['nation'];
$tmn->nationrank = $import['nationrank'];
$tmn->nationplayers = $import['nationplayers'];
$tmn->nationpos = $import['nationpos'];
$tmn->nationpoints = $import['nationpoints'];
$tmn->totalnations = $import['totalnations'];
$tmn->online = $import['online'];
$tmn->serverlogin = $import['serverlogin'];
$tmn->servernick = $import['servernick'];
$tmn->serverdesc = $import['serverdesc'];
$tmn->servernation = $import['servernation'];
return $tmn;
} // __set_state
private function getData() {
$url = 'http://game.trackmanianations.com/online_game/getplayerinfos.php?ver=' . $this->version . '&lang=en&login=' . $this->login;
$line = $this->get_file($url);
if ($line === false) {
$this->error = 'Connection or response error on ' . $url;
return;
} else if ($line === -1) {
$this->error = 'Timed out while reading data from ' . $url;
return;
} else if ($line == '' || strpos($line, '<br>') !== false) {
$this->error = 'No data returned from ' . $url;
return;
}
$array = explode(';', $line);
if (!isset($array[6])) {
$this->error = 'Cannot parse data returned data from ' . $url;
return;
}
// 0 = $array[0];
// login = $array[1];
$this->nickname = urldecode($array[2]);
$this->nation = $array[3];
// empty = $array[4];
$this->stars = $array[5];
$this->stardays = $array[6];
$url = 'http://ladder.trackmanianations.com/ladder/getstats.php?ver=' . $this->version . '&laddertype=g&login=' . $this->login;
$line = $this->get_file($url);
if ($line === false) {
$this->error = 'Connection or response error on ' . $url;
return;
} else if ($line === -1) {
$this->error = 'Timed out while reading data from ' . $url;
return;
} else if ($line == '' || strpos($line, '<br>') !== false) {
$this->error = 'No data returned from ' . $url;
return;
}
$array = explode(';', $line);
if (!isset($array[10])) {
$this->error = 'Cannot parse data returned data from ' . $url;
return;
}
// 0 = $array[0];
$this->totalplayers = $array[1];
$this->wins = $array[2];
$this->losses = $array[3];
$this->draws = $array[4];
$this->worldrank = $array[5];
$this->points = $array[6];
$this->lastmatch = $array[7];
$this->teamname = urldecode($array[8]);
$this->teamrank = (int)$array[9];
$this->totalteams = $array[10];
$url = 'http://ladder.trackmanianations.com/ladder/getstats.php?ver=' . $this->version . '&laddertype=g&login=' . $this->login . '&country=' . $this->nation;
$line = $this->get_file($url);
if ($line === false) {
$this->error = 'Connection or response error on ' . $url;
return;
} else if ($line === -1) {
$this->error = 'Timed out while reading data from ' . $url;
return;
} else if ($line == '' || strpos($line, '<br>') !== false) {
$this->error = 'No data returned from ' . $url;
return;
}
$array = explode(';', $line);
// 0 = $array[1];
if (isset($array[5]))
$this->nationrank = $array[5];
else
$this->nationrank = '';
// the remaining fields are the same as the world stats above
$url = 'http://ladder.trackmanianations.com/ladder/getrankings.php?ver=' . $this->version . '&laddertype=g&start=0&limit=0&country=' . $this->nation;
$line = $this->get_file($url);
if ($line === false) {
$this->error = 'Connection or response error on ' . $url;
return;
} else if ($line === -1) {
$this->error = 'Timed out while reading data from ' . $url;
return;
} else if ($line == '' || strpos($line, '<br>') !== false) {
$this->error = 'No data returned from ' . $url;
return;
}
$array = explode(';', $line);
if (!isset($array[1])) {
$this->error = 'Cannot parse data returned data from ' . $url;
return;
}
// 0 = $array[1];
$this->nationplayers = $array[1];
// 1;login;nickname;nation;points = $array[2-6];
// 2;login;nickname;nation;points = $array[7-11]; etc.etc.
$url = 'http://ladder.trackmanianations.com/ladder/getcountriesrankings.php?ver=' . $this->version . '&laddertype=g&lang=en&start=0&limit=100';
$line = $this->get_file($url);
if ($line === false) {
$this->error = 'Connection or response error on ' . $url;
return;
} else if ($line === -1) {
$this->error = 'Timed out while reading data from ' . $url;
return;
} else if ($line == '' || strpos($line, '<br>') !== false) {
$this->error = 'No data returned from ' . $url;
return;
}
$array = explode(';', $line);
if (!isset($array[1])) {
$this->error = 'Cannot parse data returned data from ' . $url;
return;
}
// 0 = $array[1];
$this->totalnations = $array[1];
// 1;nation;points = $array[2-4];
// 2;nation;points = $array[5-7]; etc.etc.
$i = 2;
while (isset($array[$i]) && $array[$i] != '') {
if ($array[$i+1] == $this->nation) {
$this->nationpos = $array[$i];
$this->nationpoints = $array[$i+2];
break;
}
$i += 3;
}
$this->online = false;
// check online status too?
if ($this->extended) {
$page = $this->get_file('http://game.trackmanianations.com/online_game/www_serverslist.php');
if ($page === false || $page == -1 || $page == '')
// no error message if main info was already fetched
return;
$lines = explode('<host>', $page);
foreach ($lines as $line) {
if (stripos($line, '<player>' . $this->login . '</player>') !== false) {
$this->online = true;
$this->serverlogin = substr($line, 0, strpos($line, '</host>'));
break;
}
}
if ($this->online) {
$page = $this->get_file('http://game.trackmanianations.com/online_game/browse_top.php?ver=0.1.7.9&lang=en&key=XXXX-XXXX-XXXX-XXXX-XXX&nb=100&page=1&flatall=1');
if ($page === false || $page == -1 || $page == '')
// no error message if main info was already fetched
return;
$server = $this->serverlogin; // can't use object member inside pattern
if (preg_match("/^${server};([^;]+);([^;]+);([A-Z]+);/m", $page, $fields)) {
$this->serverdesc = $fields[1];
$this->servernick = ($fields[2] != 'x' ? urldecode($fields[2]) : '');
$this->servernation = $fields[3];
}
}
}
} // getData
// Simple HTTP Get function with timeout
// ok: return string || error: return false || timeout: return -1
private function get_file($url) {
$url = parse_url($url);
$port = isset($url['port']) ? $url['port'] : 80;
$query = isset($url['query']) ? "?" . $url['query'] : "";
$fp = @fsockopen($url['host'], $port, $errno, $errstr, 4);
if (!$fp)
return false;
fwrite($fp, 'GET ' . $url['path'] . $query . " HTTP/1.0\r\n" .
'Host: ' . $url['host'] . "\r\n" .
'User-Agent: TMNDataFetcher (' . PHP_OS . ")\r\n\r\n");
stream_set_timeout($fp, 2);
$res = '';
$info['timed_out'] = false;
while (!feof($fp) && !$info['timed_out']) {
$res .= fread($fp, 512);
$info = stream_get_meta_data($fp);
}
fclose($fp);
if ($info['timed_out']) {
return -1;
} else {
if (substr($res, 9, 3) != '200')
return false;
$page = explode("\r\n\r\n", $res, 2);
return trim($page[1]);
}
} // get_file
} // class TMNDataFetcher
?>

View File

@ -0,0 +1,297 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
/**
* TMXInfoFetcher - Fetch TMX info/records for TMO/TMS/TMN/TMU(F)/TMNF tracks
* Created by Xymph <tm@gamers.org>
* Inspired by TMNDataFetcher & "Stats for TMN Aseco+RASP"
*
* v1.19: Allowed 24-char UIDs too
* v1.18: Added 'replayurl' to the entries in $recordslist
* v1.17: Allowed 25-char UIDs too
* v1.16: Improved error reporting via $error; parsed more comment formatting
* v1.15: Added User-Agent to the GET request
* v1.14: Fixed PHP Strict level warning
* v1.13: Fixed handling of empty API responses
* v1.12: Renamed $worldrec into $custimg to correct its meaning; fixed
* $replayid check
* v1.11: Added another check for valid $replayid
* v1.10: Added magic __set_state function to support var_export()
* v1.9: Fixed fetch records run-time warnings
* v1.8: Optimized get_file URL parsing
* v1.7: Added $worldrec (boolean); changed $visible to boolean; renamed
* $download to $dloadurl, $imgurl to $imageurl & $imgurlsmall to
* $thumburl; minor tweaks
* v1.6: Added check for valid $replayid & $replayurl
* v1.5: Added TMNF compatibility
* v1.4: Added get_file function to handle TMX site timeouts
* v1.3: Added $awards, $comments, $replayid, $replayurl; renamed $comment to
* $acomment (to better distinguish author comment from # of comments)
* v1.2: Allowed 26-char UIDs too; added $pageurl
* v1.1: Allowed TMX IDs too
* v1.0: Initial release
*/
class TMXInfoFetcher {
public $section, $prefix, $uid, $id, $records, $error,
$name, $userid, $author, $uploaded, $updated,
$visible, $type, $envir, $mood, $style, $routes,
$length, $diffic, $lbrating, $awards, $comments, $custimg,
$game, $acomment, $pageurl, $replayid, $replayurl,
$imageurl, $thumburl, $dloadurl, $recordlist;
/**
* Fetches a hell of a lot of data about a TMX track
*
* @param String $game
* TMX section for 'TMO', 'TMS', 'TMN', 'TMU', 'TMNF'
* @param String $id
* The challenge UID to search for (if a 24-27 char alphanum string),
* otherwise the TMX ID to search for (if a number)
* @param Boolean $records
* If true, the script also returns the world records (max. 10)
* @return TMXInfoFetcher
* If $error is not an empty string, it's an error message
*/
public function TMXInfoFetcher($game, $id, $records) {
$this->section = $game;
switch ($game) {
case 'TMO':
$this->prefix = 'original';
break;
case 'TMS':
$this->prefix = 'sunrise';
break;
case 'TMN':
$this->prefix = 'nations';
break;
case 'TMU':
$this->prefix = 'united';
break;
case 'TMNF':
$this->prefix = 'tmnforever';
break;
default:
$this->prefix = '';
return;
}
$this->error = '';
$this->records = $records;
// check for UID string
if (preg_match('/^\w{24,27}$/', $id)) {
$this->uid = $id;
$this->getData(true);
// check for TMX ID
} elseif (is_numeric($id) && $id > 0) {
$this->id = floor($id);
$this->getData(false);
}
} // TMXInfoFetcher
public static function __set_state($import) {
$tmx = new TMXInfoFetcher('', 0, false);
$tmx->section = $import['section'];
$tmx->prefix = $import['prefix'];
$tmx->uid = $import['uid'];
$tmx->id = $import['id'];
$tmx->records = $import['records'];
$tmx->error = '';
$tmx->name = $import['name'];
$tmx->userid = $import['userid'];
$tmx->author = $import['author'];
$tmx->uploaded = $import['uploaded'];
$tmx->updated = $import['updated'];
$tmx->visible = $import['visible'];
$tmx->type = $import['type'];
$tmx->envir = $import['envir'];
$tmx->mood = $import['mood'];
$tmx->style = $import['style'];
$tmx->routes = $import['routes'];
$tmx->length = $import['length'];
$tmx->diffic = $import['diffic'];
$tmx->lbrating = $import['lbrating'];
$tmx->awards = $import['awards'];
$tmx->comments = $import['comments'];
$tmx->custimg = $import['custimg'];
$tmx->game = $import['game'];
$tmx->acomment = $import['acomment'];
$tmx->pageurl = $import['pageurl'];
$tmx->replayid = $import['replayid'];
$tmx->replayurl = $import['replayurl'];
$tmx->imageurl = $import['imageurl'];
$tmx->thumburl = $import['thumburl'];
$tmx->dloadurl = $import['dloadurl'];
$tmx->recordlist = null;
return $tmx;
} // __set_state
private function getData($isuid) {
// get main track info
$url = 'http://' . $this->prefix . '.tm-exchange.com/apiget.aspx?action=apitrackinfo&' . ($isuid ? 'u' : '') . 'id=' . ($isuid ? $this->uid : $this->id);
$file = $this->get_file($url);
if ($file === false) {
$this->error = 'Connection or response error on ' . $url;
return;
} else if ($file === -1) {
$this->error = 'Timed out while reading data from ' . $url;
return;
} else if ($file == '') {
$this->error = 'No data returned from ' . $url;
return;
}
// check for API error message
if (strpos($file, chr(27)) !== false) {
$this->error = 'Cannot decode main track info';
return;
}
// separate columns on Tabs
$fields = explode(chr(9), $file);
if ($isuid)
$this->id = $fields[0];
$this->name = $fields[1];
$this->userid = $fields[2];
$this->author = $fields[3];
$this->uploaded = $fields[4];
$this->updated = $fields[5];
$this->visible = (strtolower($fields[6]) == 'true');
$this->type = $fields[7];
$this->envir = $fields[8];
$this->mood = $fields[9];
$this->style = $fields[10];
$this->routes = $fields[11];
$this->length = $fields[12];
$this->diffic = $fields[13];
$this->lbrating = ($fields[14] > 0 ? $fields[14] : 'Classic!');
$this->game = $fields[15];
$search = array(chr(31), '[b]', '[/b]', '[i]', '[/i]', '[u]', '[/u]', '[url]', '[/url]');
$replace = array('<br/>', '<b>', '</b>', '<i>', '</i>', '<u>', '</u>', '<i>', '</i>');
$this->acomment = str_ireplace($search, $replace, $fields[16]);
$this->acomment = preg_replace('/\[url=".*"\]/', '<i>', $this->acomment);
$this->pageurl = 'http://' . $this->prefix . '.tm-exchange.com/main.aspx?action=trackshow&id=' . $this->id;
$this->imageurl = 'http://' . $this->prefix . '.tm-exchange.com/get.aspx?action=trackscreen&id=' . $this->id;
$this->thumburl = 'http://' . $this->prefix . '.tm-exchange.com/get.aspx?action=trackscreensmall&id=' . $this->id;
$this->dloadurl = 'http://' . $this->prefix . '.tm-exchange.com/get.aspx?action=trackgbx&id=' . $this->id;
$this->awards = 0;
$this->comments = 0;
$this->custimg = false;
$this->replayid = 0;
$this->replayurl = '';
// get misc. track info
$url = 'http://' . $this->prefix . '.tm-exchange.com/apiget.aspx?action=apisearch&trackid=' . $this->id;
$file = $this->get_file($url);
if ($file === false || $file === -1 || $file == '')
// no error message if main info was already fetched
return;
// check for API error message
if (strpos($file, chr(27)) !== false)
return;
// separate columns on Tabs
$fields = explode(chr(9), $file);
// id = $fields[0];
// name = $fields[1];
// userid = $fields[2];
// author = $fields[3];
// type = $fields[4];
// envir = $fields[5];
// mood = $fields[6];
// style = $fields[7];
// routes = $fields[8];
// length = $fields[9];
// diffic = $fields[10];
// lbrating = ($fields[11] > 0 ? $fields[11] : 'Classic!');
$this->awards = $fields[12];
$this->comments = $fields[13];
$this->custimg = (strtolower($fields[14]) == 'true');
// game = $fields[15];
$this->replayid = $fields[16];
// unknown = $fields[17-21];
// uploaded = $fields[22];
// updated = $fields[23];
if ($this->replayid > 0) {
$this->replayurl = 'http://' . $this->prefix . '.tm-exchange.com/get.aspx?action=recordgbx&id=' . $this->replayid;
}
// fetch records too?
$this->recordlist = array();
if ($this->records) {
$url = 'http://' . $this->prefix . '.tm-exchange.com/apiget.aspx?action=apitrackrecords&id=' . $this->id;
$file = $this->get_file($url);
if ($file === false || $file === -1 || $file == '')
// no error message if main info was already fetched
return;
$file = explode("\r\n", $file);
$i = 0;
while ($i < 10 && isset($file[$i]) && $file[$i] != '') {
// separate columns on Tabs
$fields = explode(chr(9), $file[$i]);
$this->recordlist[$i++] = array(
'replayid' => $fields[0],
'userid' => $fields[1],
'name' => $fields[2],
'time' => $fields[3],
'replayat' => $fields[4],
'trackat' => $fields[5],
'approved' => $fields[6],
'score' => $fields[7],
'expires' => $fields[8],
'lockspan' => $fields[9],
'replayurl'=> 'http://' . $this->prefix . '.tm-exchange.com/get.aspx?action=recordgbx&id=' . $fields[0],
);
}
}
} // getData
// Simple HTTP Get function with timeout
// ok: return string || error: return false || timeout: return -1
private function get_file($url) {
$url = parse_url($url);
$port = isset($url['port']) ? $url['port'] : 80;
$query = isset($url['query']) ? "?" . $url['query'] : "";
$fp = @fsockopen($url['host'], $port, $errno, $errstr, 4);
if (!$fp)
return false;
fwrite($fp, 'GET ' . $url['path'] . $query . " HTTP/1.0\r\n" .
'Host: ' . $url['host'] . "\r\n" .
'User-Agent: TMXInfoFetcher (' . PHP_OS . ")\r\n\r\n");
stream_set_timeout($fp, 2);
$res = '';
$info['timed_out'] = false;
while (!feof($fp) && !$info['timed_out']) {
$res .= fread($fp, 512);
$info = stream_get_meta_data($fp);
}
fclose($fp);
if ($info['timed_out']) {
return -1;
} else {
if (substr($res, 9, 3) != '200')
return false;
$page = explode("\r\n\r\n", $res, 2);
return trim($page[1]);
}
} // get_file
} // class TMXInfoFetcher
?>

View File

@ -0,0 +1,288 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
/**
* TMXInfoSearcher - Search TMX info for TMO/TMS/TMN/TMU(F)/TMNF tracks
* Created by Xymph <tm@gamers.org>
* Based on TMXInfoFetcher & http://united.tm-exchange.com/main.aspx?action=threadshow&id=619302
*
* v1.7: Added Countable interface to searcher class
* v1.6: Fixed an error checking bug
* v1.5: Improved error reporting via $error
* v1.4: Added User-Agent to the GET request
* v1.3: Renamed $worldrec into $custimg to correct its meaning; fixed
* $replayid check
* v1.2: Fixed TMXInfo processing false $track
* v1.1: Optimized get_file URL parsing
* v1.0: Initial release
*/
class TMXInfoSearcher implements Iterator,Countable {
public $error;
protected $tracks = array();
private $section;
private $prefix;
/**
* Searches TMX for tracks matching name, author and/or environment;
* or search TMX for the 10 most recent tracks
*
* @param String $game
* TMX section for 'TMO', 'TMS', 'TMN', 'TMU', 'TMNF'
* @param String $name
* The track name to search for (partial, case-insensitive match)
* @param String $author
* The track author to search for (partial, case-insensitive match)
* @param String $env
* The environment to search for (exact case-insensitive match
* from: Desert, Snow, Rally, Bay, Coast, Island, Stadium);
* ignored when searching TMN or TMNF
* @param Boolean $recent
* If true, ignore search parameters and just return 10 newest tracks
* (max. one per author)
* @return TMXInfoSearcher
* If ->valid() is false, no matching track was found;
* otherwise, an iterator of TMXInfo objects for a 'foreach' loop.
* Returns at most 500 tracks ($maxpage * 20) for TMNF/TMU(F),
* and at most 20 tracks for the other TMX sections.
*/
public function __construct($game, $name, $author, $env, $recent) {
$this->section = $game;
switch ($game) {
case 'TMO':
$this->prefix = 'original';
break;
case 'TMS':
$this->prefix = 'sunrise';
break;
case 'TMN':
$this->prefix = 'nations';
$env = ''; // ignore possible environment
break;
case 'TMU':
$this->prefix = 'united';
break;
case 'TMNF':
$this->prefix = 'tmnforever';
$env = ''; // ignore possible environment
break;
default:
$this->prefix = '';
$this->error = 'Unknown TMX section: ' . $game;
return;
}
$this->error = '';
if ($recent) {
$this->tracks = $this->getRecent();
} else {
$this->tracks = $this->getList($name, $author, $env);
}
} // __construct
// define standard Iterator functions
public function rewind() {
reset($this->tracks);
}
public function current() {
return new TMXInfo($this->section, $this->prefix, current($this->tracks));
}
public function next() {
return new TMXInfo($this->section, $this->prefix, next($this->tracks));
}
public function key() {
return key($this->tracks);
}
public function valid() {
return (current($this->tracks) !== false);
}
// define standard Countable function
public function count() {
return count($this->tracks);
}
private function getRecent() {
// get 10 most recent tracks
$url = 'http://' . $this->prefix . '.tm-exchange.com/apiget.aspx?action=apirecent';
$file = $this->get_file($url);
if ($file === false) {
$this->error = 'Connection or response error on ' . $url;
return array();
} else if ($file === -1) {
$this->error = 'Timed out while reading data from ' . $url;
return array();
} else if ($file == '') {
$this->error = 'No data returned from ' . $url;
return array();
}
// check for API error message
if (strpos($file, chr(27)) !== false) {
$this->error = 'Cannot decode recent track info from ' . $url;
return array();
}
// return list of tracks as array of strings
return explode("\r\n", $file);
} // getRecent
private function getList($name, $author, $env) {
// older TMX sections don't support multi-page results :(
$maxpage = 1;
if ($this->prefix == 'tmnforever' || $this->prefix == 'united')
$maxpage = 25; // max. 500 tracks
// compile search URL
$url = 'http://' . $this->prefix . '.tm-exchange.com/apiget.aspx?action=apisearch';
if ($name != '')
$url .= '&track=' . $name;
if ($author != '')
$url .= '&author=' . $author;
if ($env != '')
$url .= '&env=' . $env;
$url .= '&page=';
$tracks = '';
$page = 0;
$done = false;
// get results 20 tracks at a time
while ($page < $maxpage && !$done) {
$file = $this->get_file($url . $page);
if ($file === false) {
$this->error = 'Connection or response error on ' . $url;
return array();
} else if ($file === -1) {
$this->error = 'Timed out while reading data from ' . $url;
return array();
} else if ($file == '') {
if ($tracks == '') {
$this->error = 'No data returned from ' . $url;
return array();
} else {
break;
}
}
// check for API error message
if (strpos($file, chr(27)) !== false) {
$this->error = 'Cannot decode searched track info from ' . $url;
return array();
}
// check for results
if ($file != '') {
// no line break before first page
$tracks .= ($page++ > 0 ? "\r\n" : '') . $file;
} else {
$done = true;
}
}
// return list of tracks as array of strings
if ($tracks != '')
return explode("\r\n", $tracks);
else
return array();
} // getList
// Simple HTTP Get function with timeout
// ok: return string || error: return false || timeout: return -1
private function get_file($url) {
$url = parse_url($url);
$port = isset($url['port']) ? $url['port'] : 80;
$query = isset($url['query']) ? "?" . $url['query'] : "";
$fp = @fsockopen($url['host'], $port, $errno, $errstr, 4);
if (!$fp)
return false;
fwrite($fp, 'GET ' . $url['path'] . $query . " HTTP/1.0\r\n" .
'Host: ' . $url['host'] . "\r\n" .
'User-Agent: TMXInfoSearcher (' . PHP_OS . ")\r\n\r\n");
stream_set_timeout($fp, 2);
$res = '';
$info['timed_out'] = false;
while (!feof($fp) && !$info['timed_out']) {
$res .= fread($fp, 512);
$info = stream_get_meta_data($fp);
}
fclose($fp);
if ($info['timed_out']) {
return -1;
} else {
if (substr($res, 9, 3) != '200')
return false;
$page = explode("\r\n\r\n", $res, 2);
return trim($page[1]);
}
} // get_file
} // class TMXInfoSearcher
class TMXInfo {
public $section, $prefix, $id, $name, $userid, $author,
$type, $envir, $mood, $style, $routes, $length, $diffic,
$lbrating, $awards, $comments, $custimg, $game, $uploaded, $updated,
$pageurl, $replayid, $replayurl, $imageurl, $thumburl, $dloadurl;
/**
* Returns track object with a hell of a lot of data from TMX track string
*
* @param String $section
* TMX section
* @param String $prefix
* TMX URL prefix
* @param String $track
* The TMX track string from TMXInfoSearcher
* @return TMXInfo
*/
public function TMXInfo($section, $prefix, $track) {
$this->section = $section;
$this->prefix = $prefix;
if ($track) {
// separate columns on Tabs
$fields = explode(chr(9), $track);
$this->id = $fields[0];
$this->name = $fields[1];
$this->userid = $fields[2];
$this->author = $fields[3];
$this->type = $fields[4];
$this->envir = $fields[5];
$this->mood = $fields[6];
$this->style = $fields[7];
$this->routes = $fields[8];
$this->length = $fields[9];
$this->diffic = $fields[10];
$this->lbrating = ($fields[11] > 0 ? $fields[11] : 'Classic!');
$this->awards = $fields[12];
$this->comments = $fields[13];
$this->custimg = (strtolower($fields[14]) == 'true');
$this->game = $fields[15];
$this->replayid = $fields[16];
// unknown = $fields[17-21];
$this->uploaded = $fields[22];
$this->updated = $fields[23];
$this->pageurl = 'http://' . $prefix . '.tm-exchange.com/main.aspx?action=trackshow&id=' . $this->id;
$this->imageurl = 'http://' . $prefix . '.tm-exchange.com/get.aspx?action=trackscreen&id=' . $this->id;
$this->thumburl = 'http://' . $prefix . '.tm-exchange.com/get.aspx?action=trackscreensmall&id=' . $this->id;
$this->dloadurl = 'http://' . $prefix . '.tm-exchange.com/get.aspx?action=trackgbx&id=' . $this->id;
if ($this->replayid > 0) {
$this->replayurl = 'http://' . $prefix . '.tm-exchange.com/get.aspx?action=recordgbx&id=' . $this->replayid;
} else {
$this->replayurl = '';
}
}
} // TMXInfo
} // class TMXInfo
?>

View File

@ -0,0 +1,511 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
// Updated by Xymph
/**
* Structure of a Record.
*/
class Record {
var $player;
var $challenge;
var $score;
var $date;
var $checks;
var $new;
var $pos;
} // class Record
/**
* Manages a list of records.
* Add records to the list and remove them.
*/
class RecordList {
var $record_list;
var $max;
// instantiates a record list with max $limit records
function RecordList($limit) {
$this->record_list = array();
$this->max = $limit;
}
function setLimit($limit) {
$this->max = $limit;
}
function getRecord($rank) {
if (isset($this->record_list[$rank]))
return $this->record_list[$rank];
else
return false;
}
function setRecord($rank, $record) {
if (isset($this->record_list[$rank])) {
return $this->record_list[$rank] = $record;
} else {
return false;
}
}
function moveRecord($from, $to) {
moveArrayElement($this->record_list, $from, $to);
}
function addRecord($record, $rank = -1) {
// if no rank was set for this record, then put it to the end of the list
if ($rank == -1) {
$rank = count($this->record_list);
}
// do not insert a record behind the border of the list
if ($rank >= $this->max) return;
// do not insert a record with no score
if ($record->score <= 0) return;
// if the given object is a record
if (get_class($record) == 'Record') {
// if records are getting too much, drop the last from the list
if (count($this->record_list) >= $this->max) {
array_pop($this->record_list);
}
// insert the record at the specified position
return insertArrayElement($this->record_list, $record, $rank);
}
}
function delRecord($rank = -1) {
// do not remove a record outside the current list
if ($rank < 0 || $rank >= count($this->record_list)) return;
// remove the record from the specified position
return removeArrayElement($this->record_list, $rank);
}
function count() {
return count($this->record_list);
}
function clear() {
$this->record_list = array();
}
} // class RecordList
/**
* Structure of a Player.
* Can be instantiated with an RPC 'GetPlayerInfo' or
* 'GetDetailedPlayerInfo' response.
*/
class Player {
var $id;
var $pid;
var $login;
var $nickname;
var $teamname;
var $ip;
var $client;
var $ipport;
var $zone;
var $nation;
var $prevstatus;
var $isspectator;
var $isofficial;
var $rights;
var $language;
var $avatar;
var $teamid;
var $unlocked;
var $ladderrank;
var $ladderscore;
var $created;
var $wins;
var $newwins;
var $timeplayed;
var $tracklist;
var $playerlist;
var $msgs;
var $pmbuf;
var $mutelist;
var $mutebuf;
var $style;
var $panels;
var $speclogin;
var $dedirank;
function getWins() {
return $this->wins + $this->newwins;
}
function getTimePlayed() {
return $this->timeplayed + $this->getTimeOnline();
}
function getTimeOnline() {
return $this->created > 0 ? time() - $this->created : 0;
}
// instantiates the player with an RPC response
function Player($rpc_infos = null) {
$this->id = 0;
if ($rpc_infos) {
$this->pid = $rpc_infos['PlayerId'];
$this->login = $rpc_infos['Login'];
$this->nickname = $rpc_infos['NickName'];
$this->ipport = $rpc_infos['IPAddress'];
$this->ip = preg_replace('/:\d+/', '', $rpc_infos['IPAddress']); // strip port
$this->prevstatus = false;
$this->isspectator = $rpc_infos['IsSpectator'];
$this->isofficial = $rpc_infos['IsInOfficialMode'];
$this->teamname = $rpc_infos['LadderStats']['TeamName'];
if (isset($rpc_infos['Nation'])) { // TMN (TMS/TMO?)
$this->zone = $rpc_infos['Nation'];
$this->nation = $rpc_infos['Nation'];
$this->ladderrank = $rpc_infos['LadderStats']['Ranking'];
$this->ladderscore = $rpc_infos['LadderStats']['Score'];
$this->client = '';
$this->rights = false;
$this->language = '';
$this->avatar = '';
$this->teamid = 0;
} else { // TMF
$this->zone = substr($rpc_infos['Path'], 6); // strip 'World|'
$this->nation = explode('|', $rpc_infos['Path']);
if (isset($this->nation[1]))
$this->nation = $this->nation[1];
else
$this->nation = '';
$this->ladderrank = $rpc_infos['LadderStats']['PlayerRankings'][0]['Ranking'];
$this->ladderscore = round($rpc_infos['LadderStats']['PlayerRankings'][0]['Score'], 2);
$this->client = $rpc_infos['ClientVersion'];
$this->rights = ($rpc_infos['OnlineRights'] == 3); // United = true
$this->language = $rpc_infos['Language'];
$this->avatar = $rpc_infos['Avatar']['FileName'];
$this->teamid = $rpc_infos['TeamId'];
}
$this->created = time();
} else {
// set defaults
$this->pid = 0;
$this->login = '';
$this->nickname = '';
$this->ipport = '';
$this->ip = '';
$this->prevstatus = false;
$this->isspectator = false;
$this->isofficial = false;
$this->teamname = '';
$this->zone = '';
$this->nation = '';
$this->ladderrank = 0;
$this->ladderscore = 0;
$this->rights = false;
$this->created = 0;
}
$this->wins = 0;
$this->newwins = 0;
$this->timeplayed = 0;
$this->unlocked = false;
$this->pmbuf = array();
$this->mutelist = array();
$this->mutebuf = array();
$this->style = array();
$this->panels = array();
$this->speclogin = '';
$this->dedirank = 0;
}
} // class Player
/**
* Manages players on the server.
* Add player and remove them.
*/
class PlayerList {
var $player_list;
// instantiates the empty player list
function PlayerList() {
$this->player_list = array();
}
function nextPlayer() {
if (is_array($this->player_list)) {
$player_item = current($this->player_list);
next($this->player_list);
return $player_item;
} else {
$this->resetPlayers();
return false;
}
}
function resetPlayers() {
if (is_array($this->player_list)) {
reset($this->player_list);
}
}
function addPlayer($player) {
if (get_class($player) == 'Player' && $player->login != '') {
$this->player_list[$player->login] = $player;
return true;
} else {
return false;
}
}
function removePlayer($login) {
if (isset($this->player_list[$login])) {
$player = $this->player_list[$login];
unset($this->player_list[$login]);
} else {
$player = false;
}
return $player;
}
function getPlayer($login) {
if (isset($this->player_list[$login]))
return $this->player_list[$login];
else
return false;
}
} // class PlayerList
/**
* Can store challenge information.
* You can instantiate with an RPC 'GetChallengeInfo' response.
*/
class Challenge {
var $id;
var $name;
var $uid;
var $filename;
var $author;
var $environment;
var $mood;
var $bronzetime;
var $silvertime;
var $goldtime;
var $authortime;
var $copperprice;
var $laprace;
var $forcedlaps;
var $nblaps;
var $nbchecks;
var $score;
var $starttime;
var $gbx;
var $tmx;
// instantiates the challenge with an RPC response
function Challenge($rpc_infos = null) {
$this->id = 0;
if ($rpc_infos) {
$this->name = stripNewlines($rpc_infos['Name']);
$this->uid = $rpc_infos['UId'];
$this->filename = $rpc_infos['FileName'];
$this->author = $rpc_infos['Author'];
$this->environment = $rpc_infos['Environnement'];
$this->mood = $rpc_infos['Mood'];
$this->bronzetime = $rpc_infos['BronzeTime'];
$this->silvertime = $rpc_infos['SilverTime'];
$this->goldtime = $rpc_infos['GoldTime'];
$this->authortime = $rpc_infos['AuthorTime'];
$this->copperprice = $rpc_infos['CopperPrice'];
$this->laprace = $rpc_infos['LapRace'];
$this->forcedlaps = 0;
if (isset($rpc_infos['NbLaps']))
$this->nblaps = $rpc_infos['NbLaps'];
else
$this->nblaps = 0;
if (isset($rpc_infos['NbCheckpoints']))
$this->nbchecks = $rpc_infos['NbCheckpoints'];
else
$this->nbchecks = 0;
} else {
// set defaults
$this->name = 'undefined';
}
}
} // class Challenge
/**
* Contains information about an RPC call.
*/
class RPCCall {
var $index;
var $id;
var $callback;
var $call;
// instantiates the RPC call with the parameters
function RPCCall($id, $index, $callback, $call) {
$this->id = $id;
$this->index = $index;
$this->callback = $callback;
$this->call = $call;
}
} // class RPCCall
/**
* Contains information about a chat command.
*/
class ChatCommand {
var $name;
var $help;
var $isadmin;
// instantiates the chat command with the parameters
function ChatCommand($name, $help, $isadmin) {
$this->name = $name;
$this->help = $help;
$this->isadmin = $isadmin;
}
} // class ChatCommand
/**
* Stores basic information of the server XASECO is running on.
*/
class Server {
var $id;
var $name;
var $game;
var $serverlogin;
var $nickname;
var $zone;
var $rights;
var $ip;
var $port;
var $timeout;
var $version;
var $build;
var $packmask;
var $laddermin;
var $laddermax;
var $login;
var $pass;
var $maxplay;
var $maxspec;
var $challenge;
var $records;
var $players;
var $mutelist;
var $gamestate;
var $gameinfo;
var $gamedir;
var $trackdir;
var $votetime;
var $voterate;
var $uptime;
var $starttime;
var $isrelay;
var $relaymaster;
var $relayslist;
// game states
const RACE = 'race';
const SCORE = 'score';
function getGame() {
switch ($this->game) {
case 'TmForever':
return 'TMF';
case 'TmNationsESWC':
return 'TMN';
case 'TmSunrise':
return 'TMS';
case 'TmOriginal':
return 'TMO';
default: // TMU was never supported
return 'Unknown';
}
}
// instantiates the server with default parameters
function Server($ip, $port, $login, $pass) {
$this->ip = $ip;
$this->port = $port;
$this->login = $login;
$this->pass = $pass;
$this->starttime = time();
}
} // class Server
/**
* Contains information to the current game which is played.
*/
class Gameinfo {
var $mode;
var $numchall;
var $rndslimit;
var $timelimit;
var $teamlimit;
var $lapslimit;
var $cuplimit;
var $forcedlaps;
const RNDS = 0;
const TA = 1;
const TEAM = 2;
const LAPS = 3;
const STNT = 4;
const CUP = 5;
// returns current game mode as string
function getMode() {
switch ($this->mode) {
case self::RNDS:
return 'Rounds';
case self::TA:
return 'TimeAttack';
case self::TEAM:
return 'Team';
case self::LAPS:
return 'Laps';
case self::STNT:
return 'Stunts';
case self::CUP:
return 'Cup';
default:
return 'Undefined';
}
}
// instantiates the game info with an RPC response
function Gameinfo($rpc_infos = null) {
if ($rpc_infos) {
$this->mode = $rpc_infos['GameMode'];
$this->numchall = $rpc_infos['NbChallenge'];
if (isset($rpc_infos['RoundsUseNewRules']) && $rpc_infos['RoundsUseNewRules'])
$this->rndslimit = $rpc_infos['RoundsPointsLimitNewRules'];
else
$this->rndslimit = $rpc_infos['RoundsPointsLimit'];
$this->timelimit = $rpc_infos['TimeAttackLimit'];
if (isset($rpc_infos['TeamUseNewRules']) && $rpc_infos['TeamUseNewRules'])
$this->teamlimit = $rpc_infos['TeamPointsLimitNewRules'];
else
$this->teamlimit = $rpc_infos['TeamPointsLimit'];
$this->lapslimit = $rpc_infos['LapsTimeLimit'];
if (isset($rpc_infos['CupPointsLimit']))
$this->cuplimit = $rpc_infos['CupPointsLimit'];
if (isset($rpc_infos['RoundsForcedLaps']))
$this->forcedlaps = $rpc_infos['RoundsForcedLaps'];
else
$this->forcedlaps = 0;
} else {
$this->mode = -1;
}
}
} // class Gameinfo
?>

View File

@ -0,0 +1,26 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
// Alternative base64 url compatible decode and encode functions
// Written by Ferdinand Dosser
// Updated by Xymph
// urlsafe base64 alternative encode
function urlsafe_base64_encode($string) {
$data = base64_encode($string);
$data = str_replace(array('+','/','='), array('-','_',''), $data);
return $data;
} // urlsafe_base64_encode
// urlsafe base64 alternative decode
function urlsafe_base64_decode($string) {
$data = str_replace(array('-','_'), array('+','/'), $string);
$mod4 = strlen($data) % 4;
if ($mod4) {
$data .= substr('====', $mod4);
}
return base64_decode($data);
} // urlsafe_base64_decode
?>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,121 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
/**
* Builds an easy structured array out of a xml file.
* Element names will be the keys and the data the values.
*
* Updated by Xymph
*/
class Examsly {
var $data;
var $struct;
var $parser;
var $stack;
var $utf8enc;
/**
* Parses a XML structure into an array.
*/
function parseXml($source, $isfile = true, $utf8enc = false) {
// clear last results
$this->stack = array();
$this->struct = array();
$this->utf8enc = $utf8enc;
// create the parser
$this->parser = xml_parser_create();
xml_set_object($this->parser, $this);
xml_set_element_handler($this->parser, 'openTag', 'closeTag');
xml_set_character_data_handler($this->parser, 'tagData');
// load the xml file
if ($isfile)
$this->data = @file_get_contents($source);
else
$this->data = $source;
// escape '&' characters
$this->data = str_replace('&', '<![CDATA[&]]>', $this->data);
// parse xml file
$parsed = xml_parse($this->parser, $this->data);
// display errors
if (!$parsed) {
$code = xml_get_error_code($this->parser);
$err = xml_error_string($code);
$line = xml_get_current_line_number($this->parser);
trigger_error("[XML Error $code] $err on line $line", E_USER_WARNING);
return false;
}
return $this->struct;
}
private function openTag($parser, $name, $attributes) {
$this->stack[] = $name;
$this->struct[$name] = '';
}
private function tagData($parser, $data) {
if (trim($data)) {
$index = $this->stack[count($this->stack)-1];
// use raw, don't decode '+' into space
if ($this->utf8enc)
$this->struct[$index] .= rawurldecode($data);
else
$this->struct[$index] .= utf8_decode(rawurldecode($data));
}
}
private function closeTag($parser, $name) {
if (count($this->stack) > 1) {
$from = array_pop($this->stack);
$to = $this->stack[count($this->stack)-1];
$this->struct[$to][$from][] = $this->struct[$from];
unset($this->struct[$from]);
}
}
/**
* Parses an array into an XML structure.
*/
function parseArray($array) {
$xmlstring = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
$xmlstring .= $this->parseArrayElements($array);
return $xmlstring;
}
private function parseArrayElements($array, $opt_tag = '') {
// read each element of the array
for ($i = 0; $i < count($array); $i++) {
// check if array is associative
if (is_numeric(key($array))) {
$xml .= '<'.$opt_tag.'>';
if (is_array(current($array))) {
$xml .= $this->parseArrayElements(current($array), key($array));
} else {
// use raw, don't encode space into '+'
$xml .= rawurlencode(utf8_encode(current($array)));
}
$xml .= '</'.$opt_tag.'>';
} else {
if (is_array(current($array))) {
$xml .= $this->parseArrayElements(current($array), key($array));
} else {
$xml .= '<'.key($array).'>';
// use raw, don't encode space into '+'
$xml .= rawurlencode(utf8_encode(current($array)));
$xml .= '</'.key($array).'>';
}
}
next($array);
}
return $xml;
}
}
?>

View File

@ -0,0 +1,385 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
////////////////////////////////////////////////////////////////
//
// File: XMLRPC DB ACCESS 2.1
// Date: 28.05.2009
// Author: Gilles Masson
// Updated: Xymph
//
////////////////////////////////////////////////////////////////
class XmlrpcDB {
//-----------------------------
// Fields
//-----------------------------
var $_webaccess;
var $_url;
var $_server;
var $_callbacks;
var $_requests;
var $_auth_cb;
var $_bad;
var $_bad_time;
var $_debug;
//-----------------------------
// Methods
//-----------------------------
function XmlrpcDB($webaccess, $url, $game, $login, $password, $tool, $version, $nation, $packmask = '') {
$this->_debug = 0; // max debug level = 3
$this->_webaccess = $webaccess;
$this->_url = $url;
$this->_server = array('Game' => $game,
'Login' => $login,
'Password' => $password,
'Tool' => $tool,
'Version' => $version,
'Nation' => $nation,
'Packmask' => $packmask,
'PlayersGame' => true
);
$this->_auth_cb = array('xmlrpc_auth_cb');
$this->_bad = false;
$this->_bad_time = -1;
// in case webaccess URL connection was previously in error, ask to retry
$this->_webaccess->retry($this->_url);
// prepare to add requests
$this->_initRequest();
} // XmlrpcDB
// change the packmask value
function setPackmask($packmask = '') {
$this->_server['Packmask'] = $packmask;
} // setPackmask
// is the connection in recurrent error?
function isBad() {
return $this->_bad;
} // isBad
// get time since the error state was set
function badTime() {
return (time() - $this->_bad_time);
} // badTime
// stop the bad state: will try again at next RequestWait(),
// sendRequestsWait() or sendRequests()
function retry() {
$this->_bad = false;
$this->_bad_time = -1;
// set webaccess object to retry on that URL too
$this->_webaccess->retry($this->_url);
} // retry
// clear all requests, and get them if asked
function clearRequests($get_requests = false) {
if ($get_requests) {
$return = array($_requests, $_callbacks);
$this->_initRequest();
return $return;
}
$this->_initRequest();
} // clearRequests
// add a request
function addRequest($callback, $method) {
$args = func_get_args();
$callback = array_shift($args);
$method = array_shift($args);
return $this->addRequestArray($callback, $method, $args);
} // addRequest
// add a request
function addRequestArray($callback, $method, $args) {
$this->_callbacks[] = $callback;
$this->_requests[] = array('methodName' => $method, 'params' => $args);
return count($this->_requests) - 1;
} // addRequestArray
// send added requests, callbacks will be called when response come
function sendRequests() {
global $aseco;
if (count($this->_callbacks) > 1) {
$this->addRequest(null, 'dedimania.WarningsAndTTR');
$webdatas = $this->_makeXMLdatas();
$response = $this->_webaccess->request($this->_url,
array( array($this, '_callCB'), $this->_callbacks, $this->_requests),
$webdatas, true);
$this->_initRequest();
if ($response === false) {
if (!$this->_bad) {
$this->_bad = true;
$this->_bad_time = time();
}
if ($this->_debug > 2)
$aseco->console_text('XmlrpcDB->sendRequests - this' . CRLF . print_r($this, true));
return false;
}
}
return true;
} // sendRequests
// send added requests, wait response, then call callbacks
function sendRequestsWait() {
global $aseco;
if (count($this->_callbacks) > 1) {
$this->addRequest(null, 'dedimania.WarningsAndTTR');
$webdatas = $this->_makeXMLdatas();
$response = $this->_webaccess->request($this->_url,
null, $webdatas, true);
if ($response === false) {
if (!$this->_bad) {
$this->_bad = true;
$this->_bad_time = time();
}
if ($this->_debug > 0)
$aseco->console_text('XmlrpcDB->sendRequestsWait - this' . CRLF . print_r($this, true));
$this->_initRequest();
return false;
} else {
$this->_callCB($response, $this->_callbacks, $this->_requests);
$this->_initRequest();
}
}
return true;
} // sendRequestsWait
// send a request, wait response, and return the response
function RequestWait($method) {
$args = func_get_args();
$method = array_shift($args);
return $this->RequestWaitArray($method, $args);
} // RequestWait
// send a request, wait response, and return the response
function RequestWaitArray($method, $args) {
global $aseco;
if ($this->sendRequestsWait() === false) {
if (!$this->_bad) {
$this->_bad = true;
$this->_bad_time = time();
}
return false;
}
$reqnum = $this->addRequestArray(null, $method, $args);
$this->addRequest(null, 'dedimania.WarningsAndTTR');
$webdatas = $this->_makeXMLdatas();
$response = $this->_webaccess->request($this->_url, null, $webdatas, true);
if (isset($response['Message']) && is_string($response['Message'])) {
if ($this->_debug > 1)
$aseco->console_text('XmlrpcDB->RequestWaitArray() - response[Message]' . CRLF . print_r($response['Message'], true));
$xmlrpc_message = new IXR_Message($response['Message']);
if ($xmlrpc_message->parse() && $xmlrpc_message->messageType != 'fault') {
if ($this->_debug > 1) {
$aseco->console_text('XmlrpcDB->RequestWaitArray() - message' . CRLF . print_r($xmlrpc_message->message, true));
$aseco->console_text('XmlrpcDB->RequestWaitArray() - params' . CRLF . print_r($xmlrpc_message->params, true));
}
//$datas = array('methodName' => $xmlrpc_message->methodName, 'params' => $xmlrpc_message->params);
$datas = $this->_makeResponseDatas($xmlrpc_message->methodName, $xmlrpc_message->params, $this->_requests);
} else {
if ($this->_debug > 0)
$aseco->console_text('XmlrpcDB->RequestWaitArray() - message fault' . CRLF . print_r($xmlrpc_message->message, true));
$datas = array();
}
} else {
$datas = array();
}
if ($this->_debug > 0)
$aseco->console_text('XmlrpcDB->RequestWaitArray() - datas' . CRLF . print_r($datas, true));
if (isset($datas['params']) && isset($datas['params'][$reqnum])) {
$response['Data'] = $datas['params'][$reqnum];
$param_end = end($datas['params']);
if (isset($param_end['globalTTR']) && !isset($response['Data']['globalTTR']))
$response['Data']['globalTTR'] = $param_end['globalTTR'];
} else {
$response['Data'] = $datas;
}
if ($this->_debug > 0)
$aseco->console_text('XmlrpcDB->RequestWaitArray() - response[Data]' . CRLF . print_r($response['Data'], true));
$this->_initRequest();
return $response;
} // RequestWaitArray
// init the request and callback array
function _initRequest() {
$this->_callbacks = array();
$this->_requests = array();
$this->addRequest($this->_auth_cb, 'dedimania.Authenticate', $this->_server);
} // _initRequest
// make the xmlrpc string, encode it in base64, and pass it as value
// to xmlrpc URL post parameter
function _makeXMLdatas() {
global $aseco;
$xmlrpc_request = new IXR_RequestStd('system.multicall', $this->_requests);
if ($this->_debug > 1)
$aseco->console_text('XmlrpcDB->_makeXMLdatas() - getXml()' . CRLF . print_r($xmlrpc_request->getXml(), true));
return $xmlrpc_request->getXml();
} // _makeXMLdatas
function _callCB($response, $callbacks, $requests) {
global $aseco;
$globalTTR = 0;
if (isset($response['Message']) && is_string($response['Message'])) {
$xmlrpc_message = new IXR_Message($response['Message']);
if ($xmlrpc_message->parse() && $xmlrpc_message->messageType != 'fault') {
if ($this->_debug > 1)
$aseco->console_text('XmlrpcDB->_callCB() - message' . CRLF . print_r($xmlrpc_message->message, true));
//$datas = array('methodName' => $xmlrpc_message->methodName, 'params' => $xmlrpc_message->params);
$datas = $this->_makeResponseDatas($xmlrpc_message->methodName, $xmlrpc_message->params, $requests);
if (isset($datas['params']) && is_array($datas['params'])) {
$param_end = end($datas['params']);
if (isset($param_end['globalTTR']))
$globalTTR = $param_end['globalTTR'];
} else {
if ($this->_debug > 0)
$aseco->console_text('XmlrpcDB->_callCB() - message fault' . CRLF . print_r($xmlrpc_message->message, true));
$datas = array();
}
} else {
if ($this->_debug > 0)
$aseco->console_text('XmlrpcDB->_callCB() - message fault' . CRLF . print_r($xmlrpc_message->message, true));
$datas = array();
}
} else {
if (!isset($response['Error']) ||
(strpos($response['Error'], 'connection failed') === false && strpos($response['Error'], 'Request timeout') === false)) {
$infos = array('Url'=>$this->_url, 'Requests'=>$requests, 'Callbacks'=>$callbacks, 'Response'=>$response);
$serinfos = serialize($infos);
$aseco->console_text('XmlrpcDB->_callCB() - no response message:' . CRLF . $serinfos);
}
$datas = array();
}
for ($i = 0; $i < count($callbacks); $i++) {
if ($callbacks[$i] != null) {
$callback = $callbacks[$i][0];
if (isset($datas['params']) && isset($datas['params'][$i])) {
$response['Data'] = $datas['params'][$i];
if (!isset($response['Data']['globalTTR']))
$response['Data']['globalTTR'] = $globalTTR;
} else {
$response['Data'] = $datas;
}
$callbacks[$i][0] = $response;
call_user_func_array($callback, $callbacks[$i]);
}
}
} // _callCB
// build the datas array from XAseco or Dedimania server
// remove the first array level into params if needed
// add methodResponse name if needed
// rename sub responses params array from [0] to ['params'] if needed
function _makeResponseDatas($methodname, $params, $requests) {
global $aseco;
if (is_array($params) && count($params) == 1 && is_array($params[0]))
$params = $params[0];
if ($this->_debug > 2)
$aseco->console_text('XmlrpcDB->_makeResponseDatas() - requests' . CRLF . print_r($requests, true));
if ($this->_debug > 1)
$aseco->console_text('XmlrpcDB->_makeResponseDatas() - params' . CRLF . print_r($params, true));
if (is_array($params) && is_array($params[0]) && !isset($params[0]['methodResponse'])) {
$params2 = array();
foreach ($params as $key => $param) {
$errors = null;
if (isset($param['faultCode'])) {
$errors[] = array('Code' => $param['faultCode'], 'Message' => $param['faultString']);
}
if (isset($requests[$key]['methodName']))
$methodresponse = $requests[$key]['methodName'];
else
$methodresponse = 'Unknown';
$ttr = 0.000001;
if (isset($param[0]))
$param = $param[0];
else
$param = array();
$params2[$key] = array('methodResponse' => $methodresponse,
'params' => $param,
'errors' => $errors,
'TTR' => $ttr,
'globalTTR' => $ttr
);
if ($methodresponse == 'dedimania.WarningsAndTTR') {
if ($this->_debug > 1) {
$aseco->console_text('XmlrpcDB->_makeResponseDatas() - param' . CRLF . print_r($param, true));
$aseco->console_text('XmlrpcDB->_makeResponseDatas() - params2' . CRLF . print_r($params2, true));
}
$globalTTR = $param['globalTTR'];
foreach ($param['methods'] as $key3 => $param3) {
$key2 = 0;
while ($key2 < count($params2) && $params2[$key2]['methodResponse'] != $param3['methodName']) {
$params2[$key2]['globalTTR'] = $globalTTR;
$key2++;
}
if ($this->_debug > 1)
$aseco->console_text("XmlrpcDB->_makeResponseDatas() - key2=$key2 - key3=$key3 - param3" . CRLF . print_r($param3, true));
if ($key2 < count($params2)) {
$params2[$key2]['errors'] = $param3['errors'];
$params2[$key2]['TTR'] = $param3['TTR'];
$params2[$key2]['globalTTR'] = $globalTTR;
}
}
}
}
if ($this->_debug > 1)
$aseco->console_text('XmlrpcDB->_makeResponseDatas() - params2' . CRLF . print_r($params2, true));
return array('methodName' => $methodname, 'params' => $params2);
} else {
return array('methodName' => $methodname, 'params' => $params);
}
} // _makeResponseDatas
} // class XmlrpcDB
// Dedimania.Authenticate callback used to catch errors
function xmlrpc_auth_cb($response) {
global $aseco;
if (isset($response['Data']['errors']) && $response['Data']['errors'] != '') {
if (is_array($response['Data']['errors']))
$aseco->console('xmlrpc_auth_cb() - response[Data][errors]' . CRLF . print_r($response['Data']['errors'], true));
else
$aseco->console('xmlrpc_auth_cb() - response[Data][errors]' . CRLF . print_r($response, true));
}
} // xmlrpc_auth_cb
?>

22
xaseco/localdatabase.xml Normal file
View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8" ?>
<settings>
<!-- MySQL Server Settings -->
<mysql_server>@MYSQL_HOST@</mysql_server>
<mysql_login>@MYSQL_LOGIN@</mysql_login>
<mysql_password>@MYSQL_PASSWORD@</mysql_password>
<mysql_database>@MYSQL_DATABASE@</mysql_database>
<!-- Do you want XASECO to display newly driven records? -->
<display>true</display>
<!-- Limit the highest record that will be displayed to all? -->
<!-- If lower than $maxrecs, records above this limit will -->
<!-- only be displayed to the pertaining player -->
<limit>50</limit>
<messages>
<!-- record messages -->
<record_new>{#server}>> {#highlite}{1}{#record} secured his/her {#rank}{2}{#record}. Local Record! {3}: {#highlite}{4}{#record} $n({#rank}{5}{#highlite}{6}{#record})</record_new>
<record_equal>{#server}>> {#highlite}{1}{#record} equaled his/her {#rank}{2}{#record}. Local Record! {3}: {#highlite}{4}</record_equal>
<record_new_rank>{#server}>> {#highlite}{1}{#record} gained the {#rank}{2}{#record}. Local Record! {3}: {#highlite}{4}{#record} $n({#rank}{5}{#highlite}{6}{#record})</record_new_rank>
<record_first>{#server}>> {#highlite}{1}{#record} claimed the {#rank}{2}{#record}. Local Record! {3}: {#highlite}{4}</record_first>
</messages>
</settings>

60
xaseco/localdb/aseco.sql Normal file
View File

@ -0,0 +1,60 @@
-- Database: `aseco`
--
-- --------------------------------------------------------
--
-- Tablestructure for Table `challenges`
--
CREATE TABLE IF NOT EXISTS `challenges` (
`Id` mediumint(9) NOT NULL auto_increment,
`Uid` varchar(27) NOT NULL default '',
`Date` datetime NOT NULL default '1970-01-01 00:00:00',
`Name` varchar(100) NOT NULL default '',
`Author` varchar(30) NOT NULL default '',
`Environment` varchar(10) NOT NULL default '',
`BronzeTime` int(11) NOT NULL default 0,
`SilverTime` int(11) NOT NULL default 0,
`GoldTime` int(11) NOT NULL default 0,
`AuthorTime` int(11) NOT NULL default 0,
PRIMARY KEY (`Id`),
UNIQUE KEY `Uid` (`Uid`)
) ENGINE=MyISAM;
-- --------------------------------------------------------
--
-- Tablestructure for Table `players`
--
CREATE TABLE IF NOT EXISTS `players` (
`Id` mediumint(9) NOT NULL auto_increment,
`Login` varchar(50) NOT NULL default '',
`Game` varchar(3) NOT NULL default '',
`NickName` varchar(100) NOT NULL default '',
`Nation` varchar(3) NOT NULL default '',
`UpdatedAt` datetime NOT NULL default '1970-01-01 00:00:00',
`Wins` mediumint(9) NOT NULL default 0,
`TimePlayed` int(10) unsigned NOT NULL default 0,
`TeamName` char(60) NOT NULL default '',
PRIMARY KEY (`Id`),
UNIQUE KEY `Login` (`Login`),
KEY `Game` (`Game`)
) ENGINE=MyISAM;
-- --------------------------------------------------------
--
-- Tablestructure for Table `records`
--
CREATE TABLE IF NOT EXISTS `records` (
`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` datetime NOT NULL default '1970-01-01 00:00:00',
`Checkpoints` text NOT NULL,
PRIMARY KEY (`Id`),
UNIQUE KEY `PlayerId` (`PlayerId`,`ChallengeId`),
KEY `ChallengeId` (`ChallengeId`)
) ENGINE=MyISAM;

18
xaseco/localdb/extra.sql Normal file
View File

@ -0,0 +1,18 @@
-- Database: `aseco`
--
-- --------------------------------------------------------
--
-- Tablestructure for Table `players_extra`
--
CREATE TABLE IF NOT EXISTS `players_extra` (
`playerID` mediumint(9) NOT NULL default 0,
`cps` smallint(3) NOT NULL default -1,
`dedicps` smallint(3) NOT NULL default -1,
`donations` mediumint(9) NOT NULL default 0,
`style` varchar(20) NOT NULL default '',
`panels` varchar(255) NOT NULL default '',
PRIMARY KEY (`playerID`),
KEY `donations` (`donations`)
) ENGINE=MyISAM;

47
xaseco/localdb/rasp.sql Normal file
View File

@ -0,0 +1,47 @@
-- Database: `aseco`
--
-- --------------------------------------------------------
--
-- Tablestructure for Table `rs_karma`
--
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;
-- --------------------------------------------------------
--
-- Tablestructure for Table `rs_rank`
--
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;
-- --------------------------------------------------------
--
-- Tablestructure for Table `rs_times`
--
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;

View File

@ -0,0 +1,34 @@
Panels
======
This directory holds Admin, Donate, Records and Vote panel templates,
managed by plugin.panels.php.
Templates define the complete ManiaLink panel with position, size and
fonts, so you have full control to develop custom panels.
To create a new template, copy an existing one to a new filename and
edit that, as the existing set will be overwritten in future releases.
Use ManiaLink http://smurf1.free.fr/mle/index.xml
and webpage http://smurf1.free.fr/mle/list.php
to select styles and fonts. Note that not every (sub)style and font
fits everywhere due to size variations.
New Admin templates must stick to manialink id="3" and preserve
action="21" through action="27" for the buttons.
New Donate templates must stick to manialink id="6" and preserve
action="30" through action="36" for the buttons.
New Record templates must stick to manialink id="4" and preserve the
mapping of text="%PB%" to action="7", "%LCL%" to "8", "%DED%" to "9"
and "%TMX%" to "10".
New Vote templates must stick to manialink id="5" and preserve
action="18" for the Yes button and action="19" for the No button.
If you create a nice template for any panel that's sufficiently distinct
from the standard ones, send it to me and I might include it in the
next XAseco release. :)
Xymph

View File

@ -0,0 +1,10 @@
<manialink id="3"><frame posn="48.1 -28.5 0">
<quad sizen="16 3.5" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="0.8 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="ClipRewind" action="21"/>
<quad posn="2.9 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="ClipPause" action="22"/>
<quad posn="5.0 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="ClipPlay" action="23"/>
<quad posn="7.1 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="Refresh" action="24"/>
<quad posn="9.0 -0.2 2" sizen="2.4 3.3" style="Icons64x64_1" substyle="ArrowGreen" action="25"/>
<quad posn="10.8 -0.2 2" sizen="2.4 3.3" style="Icons64x64_1" substyle="ArrowRed" action="26"/>
<quad posn="12.9 -0.3 2" sizen="2.4 3" style="Icons64x64_1" substyle="Buddy" action="27"/>
</frame></manialink>

View File

@ -0,0 +1,10 @@
<manialink id="3"><frame posn="34.1 -27.8 0">
<quad sizen="30 4.2" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="1 -0.6 2" sizen="3 3" style="Icons64x64_1" substyle="ClipRewind" action="21"/>
<quad posn="5 -0.6 2" sizen="3 3" style="Icons64x64_1" substyle="ClipPause" action="22"/>
<quad posn="9 -0.6 2" sizen="3 3" style="Icons64x64_1" substyle="ClipPlay" action="23"/>
<quad posn="13 -0.6 2" sizen="3 3.1" style="Icons64x64_1" substyle="Refresh" action="24"/>
<quad posn="17 -0.2 2" sizen="3.5 4" style="Icons64x64_1" substyle="ArrowGreen" action="25"/>
<quad posn="21 -0.2 2" sizen="3.5 4" style="Icons64x64_1" substyle="ArrowRed" action="26"/>
<quad posn="25.2 -0.2 2" sizen="3.5 4" style="Icons64x64_1" substyle="Buddy" action="27"/>
</frame></manialink>

View File

@ -0,0 +1,10 @@
<manialink id="3"><frame posn="-56 -29.2 0">
<quad sizen="16 3.5" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="0.8 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="ClipRewind" action="21"/>
<quad posn="2.9 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="ClipPause" action="22"/>
<quad posn="5.0 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="ClipPlay" action="23"/>
<quad posn="7.1 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="Refresh" action="24"/>
<quad posn="9.0 -0.2 2" sizen="2.4 3.3" style="Icons64x64_1" substyle="ArrowGreen" action="25"/>
<quad posn="10.8 -0.2 2" sizen="2.4 3.3" style="Icons64x64_1" substyle="ArrowRed" action="26"/>
<quad posn="12.9 -0.3 2" sizen="2.4 3" style="Icons64x64_1" substyle="Buddy" action="27"/>
</frame></manialink>

View File

@ -0,0 +1,10 @@
<manialink id="3"><frame posn="-56 -28.5 0">
<quad sizen="30 4.2" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="1 -0.6 2" sizen="3 3" style="Icons64x64_1" substyle="ClipRewind" action="21"/>
<quad posn="5 -0.6 2" sizen="3 3" style="Icons64x64_1" substyle="ClipPause" action="22"/>
<quad posn="9 -0.6 2" sizen="3 3" style="Icons64x64_1" substyle="ClipPlay" action="23"/>
<quad posn="13 -0.6 2" sizen="3 3.1" style="Icons64x64_1" substyle="Refresh" action="24"/>
<quad posn="17 -0.2 2" sizen="3.5 4" style="Icons64x64_1" substyle="ArrowGreen" action="25"/>
<quad posn="21 -0.2 2" sizen="3.5 4" style="Icons64x64_1" substyle="ArrowRed" action="26"/>
<quad posn="25.2 -0.2 2" sizen="3.5 4" style="Icons64x64_1" substyle="Buddy" action="27"/>
</frame></manialink>

View File

@ -0,0 +1,10 @@
<manialink id="3"><frame posn="54.4 -33.5 0">
<quad sizen="10.4 6.5" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="0.8 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="ClipRewind" action="21"/>
<quad posn="2.9 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="ClipPause" action="22"/>
<quad posn="5.0 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="ClipPlay" action="23"/>
<quad posn="7.1 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="Refresh" action="24"/>
<quad posn="1.0 -2.9 2" sizen="2.5 3.4" style="Icons64x64_1" substyle="ArrowGreen" action="25"/>
<quad posn="3.5 -2.9 2" sizen="2.5 3.4" style="Icons64x64_1" substyle="ArrowRed" action="26"/>
<quad posn="6.6 -3.0 2" sizen="2.3 3" style="Icons64x64_1" substyle="Buddy" action="27"/>
</frame></manialink>

View File

@ -0,0 +1,11 @@
<!-- admin panel made by nouseforname -->
<manialink id="3"><frame posn="48.6 -35 5">
<quad sizen="16 3.5" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="0.8 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="ClipRewind" action="21"/>
<quad posn="2.9 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="ClipPause" action="22"/>
<quad posn="5.0 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="ClipPlay" action="23"/>
<quad posn="7.1 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="Refresh" action="24"/>
<quad posn="9.0 -0.2 2" sizen="2.4 3.3" style="Icons64x64_1" substyle="ArrowGreen" action="25"/>
<quad posn="10.8 -0.2 2" sizen="2.4 3.3" style="Icons64x64_1" substyle="ArrowRed" action="26"/>
<quad posn="12.8 -0.3 2" sizen="2.4 3" style="Icons64x64_1" substyle="Buddy" action="27"/>
</frame></manialink>

View File

@ -0,0 +1,10 @@
<manialink id="3"><frame posn="-33.5 -45.6 0">
<quad sizen="21.8 2.5" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="1 -0.4 2" sizen="1.7 1.7" style="Icons64x64_1" substyle="ClipRewind" action="21"/>
<quad posn="4 -0.4 2" sizen="1.7 1.7" style="Icons64x64_1" substyle="ClipPause" action="22"/>
<quad posn="7 -0.4 2" sizen="1.7 1.7" style="Icons64x64_1" substyle="ClipPlay" action="23"/>
<quad posn="10 -0.4 2" sizen="1.7 1.8" style="Icons64x64_1" substyle="Refresh" action="24"/>
<quad posn="13 0.1 2" sizen="2.1 2.8" style="Icons64x64_1" substyle="ArrowGreen" action="25"/>
<quad posn="16 0.1 2" sizen="2.1 2.8" style="Icons64x64_1" substyle="ArrowRed" action="26"/>
<quad posn="19.1 -0.2 2" sizen="1.8 2.1" style="Icons64x64_1" substyle="Buddy" action="27"/>
</frame></manialink>

View File

@ -0,0 +1,10 @@
<manialink id="3"><frame posn="-7.9 -34.9 0">
<quad sizen="16 3.5" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="0.8 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="ClipRewind" action="21"/>
<quad posn="2.9 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="ClipPause" action="22"/>
<quad posn="5.0 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="ClipPlay" action="23"/>
<quad posn="7.1 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="Refresh" action="24"/>
<quad posn="9.0 -0.2 2" sizen="2.4 3.3" style="Icons64x64_1" substyle="ArrowGreen" action="25"/>
<quad posn="10.8 -0.2 2" sizen="2.4 3.3" style="Icons64x64_1" substyle="ArrowRed" action="26"/>
<quad posn="12.9 -0.3 2" sizen="2.4 3" style="Icons64x64_1" substyle="Buddy" action="27"/>
</frame></manialink>

View File

@ -0,0 +1,10 @@
<manialink id="3"><frame posn="-11.4 -34.2 0">
<quad sizen="23 4.2" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="1 -0.6 2" sizen="3 3" style="Icons64x64_1" substyle="ClipRewind" action="21"/>
<quad posn="4 -0.6 2" sizen="3 3" style="Icons64x64_1" substyle="ClipPause" action="22"/>
<quad posn="7 -0.6 2" sizen="3 3" style="Icons64x64_1" substyle="ClipPlay" action="23"/>
<quad posn="10 -0.6 2" sizen="3 3.1" style="Icons64x64_1" substyle="Refresh" action="24"/>
<quad posn="13 -0.1 2" sizen="3.5 4" style="Icons64x64_1" substyle="ArrowGreen" action="25"/>
<quad posn="16 -0.1 2" sizen="3.5 4" style="Icons64x64_1" substyle="ArrowRed" action="26"/>
<quad posn="19.2 -0.2 2" sizen="3.2 3.5" style="Icons64x64_1" substyle="Buddy" action="27"/>
</frame></manialink>

View File

@ -0,0 +1,10 @@
<manialink id="3"><frame posn="-64.8 37.1 0">
<quad sizen="14.1 3.5" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="1.1 -0.8 2" sizen="1.7 1.8" style="Icons64x64_1" substyle="ClipRewind" action="21"/>
<quad posn="3.0 -0.8 2" sizen="1.7 1.8" style="Icons64x64_1" substyle="ClipPause" action="22"/>
<quad posn="4.9 -0.8 2" sizen="1.7 1.8" style="Icons64x64_1" substyle="ClipPlay" action="23"/>
<quad posn="6.7 -0.8 2" sizen="1.7 1.8" style="Icons64x64_1" substyle="Refresh" action="24"/>
<quad posn="8.5 -0.4 2" sizen="1.9 2.6" style="Icons64x64_1" substyle="ArrowGreen" action="25"/>
<quad posn="10.1 -0.4 2" sizen="1.9 2.6" style="Icons64x64_1" substyle="ArrowRed" action="26"/>
<quad posn="11.9 -0.4 2" sizen="1.8 2.4" style="Icons64x64_1" substyle="Buddy" action="27"/>
</frame></manialink>

View File

@ -0,0 +1,10 @@
<manialink id="3"><frame posn="-64.8 37.1 0">
<quad sizen="14.1 3.5" style="Bgs1" substyle="NavButton"/>
<quad posn="1.1 -0.8 2" sizen="1.7 1.8" style="Icons64x64_1" substyle="ClipRewind" action="21"/>
<quad posn="3.0 -0.8 2" sizen="1.7 1.8" style="Icons64x64_1" substyle="ClipPause" action="22"/>
<quad posn="4.9 -0.8 2" sizen="1.7 1.8" style="Icons64x64_1" substyle="ClipPlay" action="23"/>
<quad posn="6.7 -0.8 2" sizen="1.7 1.8" style="Icons64x64_1" substyle="Refresh" action="24"/>
<quad posn="8.5 -0.4 2" sizen="1.9 2.6" style="Icons64x64_1" substyle="ArrowGreen" action="25"/>
<quad posn="10.1 -0.4 2" sizen="1.9 2.6" style="Icons64x64_1" substyle="ArrowRed" action="26"/>
<quad posn="11.9 -0.4 2" sizen="1.8 2.4" style="Icons64x64_1" substyle="Buddy" action="27"/>
</frame></manialink>

View File

@ -0,0 +1,10 @@
<manialink id="3"><frame posn="-64.8 5.5 0">
<quad sizen="4.6 29.6" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="0.9 -1 2" sizen="3 3" style="Icons64x64_1" substyle="ClipRewind" action="21"/>
<quad posn="1.0 -5 2" sizen="3 3" style="Icons64x64_1" substyle="ClipPause" action="22"/>
<quad posn="1.0 -9 2" sizen="3 3" style="Icons64x64_1" substyle="ClipPlay" action="23"/>
<quad posn="1.0 -13 2" sizen="3 3.1" style="Icons64x64_1" substyle="Refresh" action="24"/>
<quad posn="0.8 -16 2" sizen="3.4 5" style="Icons64x64_1" substyle="ArrowGreen" action="25"/>
<quad posn="0.8 -20 2" sizen="3.4 5" style="Icons64x64_1" substyle="ArrowRed" action="26"/>
<quad posn="1.0 -25 2" sizen="3.2 4" style="Icons64x64_1" substyle="Buddy" action="27"/>
</frame></manialink>

View File

@ -0,0 +1,10 @@
<manialink id="3"><frame posn="60.1 5.5 0">
<quad sizen="4.6 29.6" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="0.6 -1 2" sizen="3 3" style="Icons64x64_1" substyle="ClipRewind" action="21"/>
<quad posn="0.7 -5 2" sizen="3 3" style="Icons64x64_1" substyle="ClipPause" action="22"/>
<quad posn="0.7 -9 2" sizen="3 3" style="Icons64x64_1" substyle="ClipPlay" action="23"/>
<quad posn="0.6 -13 2" sizen="3 3.1" style="Icons64x64_1" substyle="Refresh" action="24"/>
<quad posn="0.5 -16 2" sizen="3.4 5" style="Icons64x64_1" substyle="ArrowGreen" action="25"/>
<quad posn="0.5 -20 2" sizen="3.4 5" style="Icons64x64_1" substyle="ArrowRed" action="26"/>
<quad posn="0.6 -25 2" sizen="3.2 4" style="Icons64x64_1" substyle="Buddy" action="27"/>
</frame></manialink>

View File

@ -0,0 +1,10 @@
<manialink id="3"><frame posn="-12.1 44.2 0">
<quad sizen="16 3.5" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="0.8 -0.8 2" sizen="2 2" style="Icons64x64_1" substyle="ClipRewind" action="21"/>
<quad posn="2.9 -0.8 2" sizen="2 2" style="Icons64x64_1" substyle="ClipPause" action="22"/>
<quad posn="5.0 -0.8 2" sizen="2 2" style="Icons64x64_1" substyle="ClipPlay" action="23"/>
<quad posn="7.1 -0.8 2" sizen="2 2.1" style="Icons64x64_1" substyle="Refresh" action="24"/>
<quad posn="9.0 -0.2 2" sizen="2.4 3.3" style="Icons64x64_1" substyle="ArrowGreen" action="25"/>
<quad posn="10.8 -0.2 2" sizen="2.4 3.3" style="Icons64x64_1" substyle="ArrowRed" action="26"/>
<quad posn="13.0 -0.3 2" sizen="2.4 3" style="Icons64x64_1" substyle="Buddy" action="27"/>
</frame></manialink>

View File

@ -0,0 +1,10 @@
<manialink id="3"><frame posn="-12.1 44.2 0">
<quad sizen="23 4.2" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="1 -0.6 2" sizen="3 3" style="Icons64x64_1" substyle="ClipRewind" action="21"/>
<quad posn="4 -0.6 2" sizen="3 3" style="Icons64x64_1" substyle="ClipPause" action="22"/>
<quad posn="7 -0.6 2" sizen="3 3" style="Icons64x64_1" substyle="ClipPlay" action="23"/>
<quad posn="10 -0.6 2" sizen="3 3.1" style="Icons64x64_1" substyle="Refresh" action="24"/>
<quad posn="13 -0.1 2" sizen="3.5 4" style="Icons64x64_1" substyle="ArrowGreen" action="25"/>
<quad posn="16 -0.1 2" sizen="3.5 4" style="Icons64x64_1" substyle="ArrowRed" action="26"/>
<quad posn="19.2 -0.2 2" sizen="3 3.5" style="Icons64x64_1" substyle="Buddy" action="27"/>
</frame></manialink>

View File

@ -0,0 +1,13 @@
<manialink id="6"><frame posn="11.7 -45.6 0">
<quad sizen="40.8 2.5" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="1.5 -0.3 2" sizen="2 2" style="Icons128x128_1" substyle="Coppers"/>
<label posn="4.5 -0.6 2" textsize="1" textcolor="dddf" text="$f6fDonate:"/>
<format textsize="1" textcolor="ffff"/>
<label posn="11 -0.6 2" sizen="2.4 1.5" text="%COP1% C" action="30"/>
<label posn="14.5 -0.6 2" sizen="2.4 1.5" text="%COP2% C" action="31"/>
<label posn="18 -0.6 2" sizen="3 1.5" text="%COP3% C" action="32"/>
<label posn="22 -0.6 2" sizen="3 1.5" text="%COP4% C" action="33"/>
<label posn="26 -0.6 2" sizen="3 1.5" text="%COP5% C" action="34"/>
<label posn="30 -0.6 2" sizen="4 1.5" text="%COP6% C" action="35"/>
<label posn="35 -0.6 2" sizen="4 1.5" text="%COP7% C" action="36"/>
</frame></manialink>

View File

@ -0,0 +1,13 @@
<manialink id="6"><frame posn="11.7 -45.6 0">
<quad sizen="40.8 2.5" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="1.5 -0.3 2" sizen="2 2" style="Icons128x128_1" substyle="Coppers"/>
<label posn="4.4 -0.6 2" textsize="1" textcolor="dddf" text="$ccc$s$oDonate:$fff"/>
<format textsize="1" textcolor="ffff"/>
<label posn="11 -0.6 2" sizen="2.4 1.5" style="TextRaceChrono" scale="1.25" text="$s$abc%COP1%C" action="30"/>
<label posn="14.5 -0.6 2" sizen="2.4 1.5" style="TextRaceChrono" scale="1.25" text="$s$aaa%COP2%C" action="31"/>
<label posn="18 -0.6 2" sizen="3 1.5" style="TextRaceChrono" scale="1.25" text="$s$a98%COP3%C" action="32"/>
<label posn="22 -0.6 2" sizen="3 1.5" style="TextRaceChrono" scale="1.25" text="$s$a86%COP4%C" action="33"/>
<label posn="26 -0.6 2" sizen="3 1.5" style="TextRaceChrono" scale="1.25" text="$s$a74%COP5%C" action="34"/>
<label posn="30 -0.6 2" sizen="4 1.5" style="TextRaceChrono" scale="1.25" text="$s$a62%COP6%C" action="35"/>
<label posn="35 -0.6 2" sizen="4 1.5" style="TextRaceChrono" scale="1.25" text="$s$a50%COP7%C" action="36"/>
</frame></manialink>

View File

@ -0,0 +1,13 @@
<manialink id="6"><frame posn="-64.8 5.5 0">
<quad sizen="7.5 29.6" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="2 -0.5 2" sizen="4 4" style="Icons128x128_1" substyle="Coppers"/>
<label posn="1 -5 2" textsize="2" textcolor="dddf" text="$f6fDonate:"/>
<format textsize="2" textcolor="ffff"/>
<label posn="6.5 -8 2" sizen="3.8 2" halign="right" text="%COP1% C" action="30"/>
<label posn="6.5 -11 2" sizen="3.8 2" halign="right" text="%COP2% C" action="31"/>
<label posn="6.5 -14 2" sizen="4.8 2" halign="right" text="%COP3% C" action="32"/>
<label posn="6.5 -17 2" sizen="4.8 2" halign="right" text="%COP4% C" action="33"/>
<label posn="6.5 -20 2" sizen="4.8 2" halign="right" text="%COP5% C" action="34"/>
<label posn="6.5 -23 2" sizen="5.8 2" halign="right" text="%COP6% C" action="35"/>
<label posn="6.5 -26 2" sizen="5.8 2" halign="right" text="%COP7% C" action="36"/>
</frame></manialink>

View File

@ -0,0 +1,13 @@
<manialink id="6"><frame posn="-64.8 5.5 0">
<quad sizen="7.5 29.6" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="2 -0.5 2" sizen="4 4" style="Icons128x128_1" substyle="Coppers"/>
<label posn="1 -5 2" style="TextCardSmallScores2Rank" text="$f6fDonate:"/>
<format style="TextCardSmallScores2"/>
<label posn="6.5 -8 2" sizen="3.8 2" halign="right" text="%COP1% C" action="30"/>
<label posn="6.5 -11 2" sizen="3.8 2" halign="right" text="%COP2% C" action="31"/>
<label posn="6.5 -14 2" sizen="4.8 2" halign="right" text="%COP3% C" action="32"/>
<label posn="6.5 -17 2" sizen="4.8 2" halign="right" text="%COP4% C" action="33"/>
<label posn="6.5 -20 2" sizen="4.8 2" halign="right" text="%COP5% C" action="34"/>
<label posn="6.5 -23 2" sizen="5.8 2" halign="right" text="%COP6% C" action="35"/>
<label posn="6.5 -26 2" sizen="5.8 2" halign="right" text="%COP7% C" action="36"/>
</frame></manialink>

View File

@ -0,0 +1,13 @@
<!-- donate panel made by nouseforname -->
<manialink id="6"><frame posn="-64.8 -4.5 0">
<quad sizen="7.7 19.5" style="Bgs1InRace" substyle="NavButton"/>
<label posn="1 -0.8 2" style="TextCardSmallScores2Rank" text="$f6fDonate:"/>
<format style="TextCardSmallScores2"/>
<label posn="6.5 -3.5 2" sizen="3.8 2" halign="right" text="%COP1% C" action="30"/>
<label posn="6.5 -5.7 2" sizen="3.8 2" halign="right" text="%COP2% C" action="31"/>
<label posn="6.5 -7.9 2" sizen="4.8 2" halign="right" text="%COP3% C" action="32"/>
<label posn="6.5 -10.1 2" sizen="4.8 2" halign="right" text="%COP4% C" action="33"/>
<label posn="6.5 -12.3 2" sizen="4.8 2" halign="right" text="%COP5% C" action="34"/>
<label posn="6.5 -14.5 2" sizen="5.8 2" halign="right" text="%COP6% C" action="35"/>
<label posn="6.5 -16.7 2" sizen="5.8 2" halign="right" text="%COP7% C" action="36"/>
</frame></manialink>

View File

@ -0,0 +1,13 @@
<manialink id="6"><frame posn="57.2 5.5 0">
<quad sizen="7.5 29.6" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="1.6 -0.5 2" sizen="4 4" style="Icons128x128_1" substyle="Coppers"/>
<label posn="0.5 -5 2" textsize="2" textcolor="dddf" text="$f6fDonate:"/>
<format textsize="2" textcolor="ffff"/>
<label posn="6.3 -8 2" sizen="3.8 2" halign="right" text="%COP1% C" action="30"/>
<label posn="6.3 -11 2" sizen="3.8 2" halign="right" text="%COP2% C" action="31"/>
<label posn="6.3 -14 2" sizen="4.8 2" halign="right" text="%COP3% C" action="32"/>
<label posn="6.3 -17 2" sizen="4.8 2" halign="right" text="%COP4% C" action="33"/>
<label posn="6.3 -20 2" sizen="4.8 2" halign="right" text="%COP5% C" action="34"/>
<label posn="6.3 -23 2" sizen="5.8 2" halign="right" text="%COP6% C" action="35"/>
<label posn="6.3 -26 2" sizen="5.8 2" halign="right" text="%COP7% C" action="36"/>
</frame></manialink>

View File

@ -0,0 +1,13 @@
<manialink id="6"><frame posn="57.2 5.5 0">
<quad sizen="7.5 29.6" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="1.6 -0.5 2" sizen="4 4" style="Icons128x128_1" substyle="Coppers"/>
<label posn="0.5 -5 2" style="TextCardSmallScores2Rank" text="$f6fDonate:"/>
<format style="TextCardSmallScores2"/>
<label posn="6.3 -8 2" sizen="3.8 2" halign="right" text="%COP1% C" action="30"/>
<label posn="6.3 -11 2" sizen="3.8 2" halign="right" text="%COP2% C" action="31"/>
<label posn="6.3 -14 2" sizen="4.8 2" halign="right" text="%COP3% C" action="32"/>
<label posn="6.3 -17 2" sizen="4.8 2" halign="right" text="%COP4% C" action="33"/>
<label posn="6.3 -20 2" sizen="4.8 2" halign="right" text="%COP5% C" action="34"/>
<label posn="6.3 -23 2" sizen="5.8 2" halign="right" text="%COP6% C" action="35"/>
<label posn="6.3 -26 2" sizen="5.8 2" halign="right" text="%COP7% C" action="36"/>
</frame></manialink>

View File

@ -0,0 +1,13 @@
<!-- donate panel made by nouseforname -->
<manialink id="6"><frame posn="57.2 -4.5 0">
<quad sizen="7.7 19.5" style="Bgs1InRace" substyle="NavButton"/>
<label posn="0.5 -0.8 2" style="TextCardSmallScores2Rank" text="$f6fDonate:"/>
<format style="TextCardSmallScores2"/>
<label posn="6.3 -3.5 2" sizen="3.8 2" halign="right" text="%COP1% C" action="30"/>
<label posn="6.3 -5.7 2" sizen="3.8 2" halign="right" text="%COP2% C" action="31"/>
<label posn="6.3 -7.9 2" sizen="4.8 2" halign="right" text="%COP3% C" action="32"/>
<label posn="6.3 -10.1 2" sizen="4.8 2" halign="right" text="%COP4% C" action="33"/>
<label posn="6.3 -12.3 2" sizen="4.8 2" halign="right" text="%COP5% C" action="34"/>
<label posn="6.3 -14.5 2" sizen="5.8 2" halign="right" text="%COP6% C" action="35"/>
<label posn="6.3 -16.7 2" sizen="5.8 2" halign="right" text="%COP7% C" action="36"/>
</frame></manialink>

View File

@ -0,0 +1,13 @@
<manialink id="6"><frame posn="-64.2 48.2 0">
<quad sizen="39.5 2.5" style="Bgs1InRace" substyle="NavButton"/>
<quad posn="1 -0.2 2" sizen="2 2" style="Icons128x128_1" substyle="Coppers"/>
<label posn="4 -0.4 2" textsize="1" textcolor="dddf" text="$f6fDonate:"/>
<format textsize="1" textcolor="ffff"/>
<label posn="10 -0.4 2" sizen="2.4 1.5" text="%COP1% C" action="30"/>
<label posn="13.5 -0.4 2" sizen="2.4 1.5" text="%COP2% C" action="31"/>
<label posn="17 -0.4 2" sizen="3 1.5" text="%COP3% C" action="32"/>
<label posn="21 -0.4 2" sizen="3 1.5" text="%COP4% C" action="33"/>
<label posn="25 -0.4 2" sizen="3 1.5" text="%COP5% C" action="34"/>
<label posn="29 -0.4 2" sizen="4 1.5" text="%COP6% C" action="35"/>
<label posn="34 -0.4 2" sizen="4 1.5" text="%COP7% C" action="36"/>
</frame></manialink>

View File

@ -0,0 +1,13 @@
<manialink id="4"><frame posn="-64.8 23.3 0">
<quad sizen="14 11.5" style="Bgs1InRace" substyle="NavButton"/>
<format style="TextCardSmallScores2Rank"/>
<label posn="6.0 -1.0 2" sizen="4.5 2" halign="right" text="PB:"/>
<label posn="6.0 -3.5 2" sizen="4.5 2" halign="right" text="Local:"/>
<label posn="6.0 -6.0 2" sizen="4.5 2" halign="right" text="Dedi:"/>
<label posn="6.0 -8.5 2" sizen="4.5 2" halign="right" text="TMX:"/>
<format style="TextSubTitle2"/>
<label posn="6.5 -1.0 2" sizen="6.5 2" halign="left" text="%PB%" action="7"/>
<label posn="6.5 -3.5 2" sizen="6.5 2" halign="left" text="%LCL%" action="8"/>
<label posn="6.5 -6.0 2" sizen="6.5 2" halign="left" text="%DED%" action="9"/>
<label posn="6.5 -8.5 2" sizen="6.5 2" halign="left" text="%TMX%" action="10"/>
</frame></manialink>

View File

@ -0,0 +1,13 @@
<manialink id="4"><frame posn="-64.8 23.3 0">
<quad sizen="14 11.5" style="Bgs1InRace" substyle="NavButton"/>
<format style="TextCardSmallScores2Rank"/>
<label posn="6.0 -1.0 2" sizen="4.5 2" halign="right" text="PB:"/>
<label posn="6.0 -3.5 2" sizen="4.5 2" halign="right" text="Local:"/>
<label posn="6.0 -6.0 2" sizen="4.5 2" halign="right" text="Dedi:"/>
<label posn="6.0 -8.5 2" sizen="4.5 2" halign="right" text="TMX:"/>
<format style="TextButtonMedium"/>
<label posn="6.5 -1.0 2" sizen="6.5 2" halign="left" text="%PB%" action="7"/>
<label posn="6.5 -3.5 2" sizen="6.5 2" halign="left" text="%LCL%" action="8"/>
<label posn="6.5 -6.0 2" sizen="6.5 2" halign="left" text="%DED%" action="9"/>
<label posn="6.5 -8.5 2" sizen="6.5 2" halign="left" text="%TMX%" action="10"/>
</frame></manialink>

View File

@ -0,0 +1,13 @@
<manialink id="4"><frame posn="-64.8 23.3 0">
<quad sizen="14 11.5" style="Bgs1InRace" substyle="NavButton"/>
<format style="TextCardSmallScores2Rank"/>
<label posn="6.0 -1.0 2" sizen="4.5 2" halign="right" text="PB:"/>
<label posn="6.0 -3.5 2" sizen="4.5 2" halign="right" text="Local:"/>
<label posn="6.0 -6.0 2" sizen="4.5 2" halign="right" text="Dedi:"/>
<label posn="6.0 -8.5 2" sizen="4.5 2" halign="right" text="TMX:"/>
<format style="TextButtonSmall"/>
<label posn="6.5 -1.0 2" sizen="6.5 2" halign="left" text="%PB%" action="7"/>
<label posn="6.5 -3.5 2" sizen="6.5 2" halign="left" text="%LCL%" action="8"/>
<label posn="6.5 -6.0 2" sizen="6.5 2" halign="left" text="%DED%" action="9"/>
<label posn="6.5 -8.5 2" sizen="6.5 2" halign="left" text="%TMX%" action="10"/>
</frame></manialink>

View File

@ -0,0 +1,13 @@
<manialink id="4"><frame posn="-64.8 23.3 0">
<quad sizen="14 11.5" style="Bgs1InRace" substyle="NavButton"/>
<format style="TextCardSmallScores2Rank"/>
<label posn="6.0 -1.0 2" sizen="4.5 2" halign="right" text="PB:"/>
<label posn="6.0 -3.5 2" sizen="4.5 2" halign="right" text="Local:"/>
<label posn="6.0 -6.0 2" sizen="4.5 2" halign="right" text="Dedi:"/>
<label posn="6.0 -8.5 2" sizen="4.5 2" halign="right" text="TMX:"/>
<format style="TextRaceValueSmall"/>
<label posn="6.5 -1.0 2" sizen="6.5 2" halign="left" text="%PB%" action="7"/>
<label posn="6.5 -3.5 2" sizen="6.5 2" halign="left" text="%LCL%" action="8"/>
<label posn="6.5 -6.0 2" sizen="6.5 2" halign="left" text="%DED%" action="9"/>
<label posn="6.5 -8.5 2" sizen="6.5 2" halign="left" text="%TMX%" action="10"/>
</frame></manialink>

View File

@ -0,0 +1,13 @@
<manialink id="4"><frame posn="-64.8 23.3 0">
<quad sizen="14 11.5" style="Bgs1InRace" substyle="NavButton"/>
<format style="TextCardSmallScores2Rank"/>
<label posn="6.0 -1.0 2" sizen="4.5 2" halign="right" text="PB:"/>
<label posn="6.0 -3.5 2" sizen="4.5 2" halign="right" text="Local:"/>
<label posn="6.0 -6.0 2" sizen="4.5 2" halign="right" text="Dedi:"/>
<label posn="6.0 -8.5 2" sizen="4.5 2" halign="right" text="TMX:"/>
<format style="TextStaticSmall"/>
<label posn="6.5 -1.0 2" sizen="6.5 2" halign="left" text="%PB%" action="7"/>
<label posn="6.5 -3.5 2" sizen="6.5 2" halign="left" text="%LCL%" action="8"/>
<label posn="6.5 -6.0 2" sizen="6.5 2" halign="left" text="%DED%" action="9"/>
<label posn="6.5 -8.5 2" sizen="6.5 2" halign="left" text="%TMX%" action="10"/>
</frame></manialink>

View File

@ -0,0 +1,13 @@
<manialink id="4"><frame posn="-64.8 23.3 0">
<quad sizen="14 11.5" style="Bgs1InRace" substyle="NavButton"/>
<format style="TextCardSmallScores2Rank"/>
<label posn="6.0 -1.0 2" sizen="4.5 2" halign="right" text="PB:"/>
<label posn="6.0 -3.5 2" sizen="4.5 2" halign="right" text="Local:"/>
<label posn="6.0 -6.0 2" sizen="4.5 2" halign="right" text="Dedi:"/>
<label posn="6.0 -8.5 2" sizen="4.5 2" halign="right" text="TMX:"/>
<format style="TextTitle3"/>
<label posn="6.5 -1.0 2" sizen="6.5 2" halign="left" text="%PB%" action="7"/>
<label posn="6.5 -3.5 2" sizen="6.5 2" halign="left" text="%LCL%" action="8"/>
<label posn="6.5 -6.0 2" sizen="6.5 2" halign="left" text="%DED%" action="9"/>
<label posn="6.5 -8.5 2" sizen="6.5 2" halign="left" text="%TMX%" action="10"/>
</frame></manialink>

View File

@ -0,0 +1,13 @@
<manialink id="4"><frame posn="-64.8 23.3 0">
<quad sizen="14 11.5" style="Bgs1InRace" substyle="NavButton"/>
<format style="TextCardSmallScores2Rank"/>
<label posn="6.0 -1.0 2" sizen="4.5 2" halign="right" text="PB:"/>
<label posn="6.0 -3.5 2" sizen="4.5 2" halign="right" text="Local:"/>
<label posn="6.0 -6.0 2" sizen="4.5 2" halign="right" text="Dedi:"/>
<label posn="6.0 -8.5 2" sizen="4.5 2" halign="right" text="TMX:"/>
<format style="TextTips"/>
<label posn="6.5 -1.0 2" sizen="6.5 2" halign="left" text="%PB%" action="7"/>
<label posn="6.5 -3.5 2" sizen="6.5 2" halign="left" text="%LCL%" action="8"/>
<label posn="6.5 -6.0 2" sizen="6.5 2" halign="left" text="%DED%" action="9"/>
<label posn="6.5 -8.5 2" sizen="6.5 2" halign="left" text="%TMX%" action="10"/>
</frame></manialink>

View File

@ -0,0 +1,13 @@
<manialink id="4"><frame posn="-64.8 23.3 0">
<quad sizen="14 11.5" style="Bgs1InRace" substyle="NavButton"/>
<format style="TextCardSmallScores2Rank"/>
<label posn="6.0 -1.0 2" sizen="4.5 2" halign="right" text="PB:"/>
<label posn="6.0 -3.5 2" sizen="4.5 2" halign="right" text="Local:"/>
<label posn="6.0 -6.0 2" sizen="4.5 2" halign="right" text="Dedi:"/>
<label posn="6.0 -8.5 2" sizen="4.5 2" halign="right" text="TMX:"/>
<format style="TextRaceChat"/>
<label posn="6.5 -1.0 2" sizen="6.5 2" halign="left" text="%PB%" action="7"/>
<label posn="6.5 -3.5 2" sizen="6.5 2" halign="left" text="%LCL%" action="8"/>
<label posn="6.5 -6.0 2" sizen="6.5 2" halign="left" text="%DED%" action="9"/>
<label posn="6.5 -8.5 2" sizen="6.5 2" halign="left" text="%TMX%" action="10"/>
</frame></manialink>

View File

@ -0,0 +1,13 @@
<manialink id="4"><frame posn="-64.8 23.3 0">
<quad sizen="10.5 9" style="Bgs1InRace" substyle="NavButton"/>
<format textsize="1" textcolor="dddf"/>
<label posn="4.5 -1.5 2" sizen="3.5 2" halign="right" valign="center" text="PB:"/>
<label posn="4.5 -3.5 2" sizen="3.5 2" halign="right" valign="center" text="Local:"/>
<label posn="4.5 -5.5 2" sizen="3.5 2" halign="right" valign="center" text="Dedi:"/>
<label posn="4.5 -7.5 2" sizen="3.5 2" halign="right" valign="center" text="TMX:"/>
<format textsize="1" textcolor="ffff"/>
<label posn="5.0 -1.5 2" sizen="4.9 2" halign="left" valign="center" text="%PB%" action="7"/>
<label posn="5.0 -3.5 2" sizen="4.9 2" halign="left" valign="center" text="%LCL%" action="8"/>
<label posn="5.0 -5.5 2" sizen="4.9 2" halign="left" valign="center" text="%DED%" action="9"/>
<label posn="5.0 -7.5 2" sizen="4.9 2" halign="left" valign="center" text="%TMX%" action="10"/>
</frame></manialink>

View File

@ -0,0 +1,11 @@
<manialink id="4"><frame posn="-64.8 23.3 0">
<quad sizen="10.5 7.1" style="Bgs1InRace" substyle="NavButton"/>
<format textsize="1" textcolor="dddf"/>
<label posn="4.5 -1.5 2" sizen="3.5 2" halign="right" valign="center" text="PB:"/>
<label posn="4.5 -3.5 2" sizen="3.5 2" halign="right" valign="center" text="Local:"/>
<label posn="4.5 -5.5 2" sizen="3.5 2" halign="right" valign="center" text="TMX:"/>
<format textsize="1" textcolor="ffff"/>
<label posn="5.0 -1.5 2" sizen="4.9 2" halign="left" valign="center" text="%PB%" action="7"/>
<label posn="5.0 -3.5 2" sizen="4.9 2" halign="left" valign="center" text="%LCL%" action="8"/>
<label posn="5.0 -5.5 2" sizen="4.9 2" halign="left" valign="center" text="%TMX%" action="10"/>
</frame></manialink>

View File

@ -0,0 +1,13 @@
<manialink id="4"><frame posn="-64.8 23.3 0">
<quad sizen="14 11.5" style="Bgs1InRace" substyle="NavButton"/>
<format textsize="2" textcolor="dddf"/>
<label posn="6.0 -1.9 2" sizen="4.5 2" halign="right" valign="center" text="PB:"/>
<label posn="6.0 -4.4 2" sizen="4.5 2" halign="right" valign="center" text="Local:"/>
<label posn="6.0 -6.9 2" sizen="4.5 2" halign="right" valign="center" text="Dedi:"/>
<label posn="6.0 -9.4 2" sizen="4.5 2" halign="right" valign="center" text="TMX:"/>
<format textsize="2" textcolor="ffff"/>
<label posn="6.5 -1.9 2" sizen="6.5 2" halign="left" valign="center" text="%PB%" action="7"/>
<label posn="6.5 -4.4 2" sizen="6.5 2" halign="left" valign="center" text="%LCL%" action="8"/>
<label posn="6.5 -6.9 2" sizen="6.5 2" halign="left" valign="center" text="%DED%" action="9"/>
<label posn="6.5 -9.4 2" sizen="6.5 2" halign="left" valign="center" text="%TMX%" action="10"/>
</frame></manialink>

View File

@ -0,0 +1,11 @@
<manialink id="4"><frame posn="-64.8 23.3 0">
<quad sizen="14 9.0" style="Bgs1InRace" substyle="NavButton"/>
<format textsize="2" textcolor="dddf"/>
<label posn="6.0 -1.9 2" sizen="4.5 2" halign="right" valign="center" text="PB:"/>
<label posn="6.0 -4.4 2" sizen="4.5 2" halign="right" valign="center" text="Local:"/>
<label posn="6.0 -6.9 2" sizen="4.5 2" halign="right" valign="center" text="TMX:"/>
<format textsize="2" textcolor="ffff"/>
<label posn="6.5 -1.9 2" sizen="6.5 2" halign="left" valign="center" text="%PB%" action="7"/>
<label posn="6.5 -4.4 2" sizen="6.5 2" halign="left" valign="center" text="%LCL%" action="8"/>
<label posn="6.5 -6.9 2" sizen="6.5 2" halign="left" valign="center" text="%TMX%" action="10"/>
</frame></manialink>

View File

@ -0,0 +1,13 @@
<manialink id="4"><frame posn="-64.8 23.3 0">
<quad sizen="14 11.5" style="Bgs1InRace" substyle="NavButton"/>
<format style="TextCardSmallScores2Rank"/>
<label posn="6.0 -1.0 2" sizen="4.5 2" halign="right" text="PB:"/>
<label posn="6.0 -3.5 2" sizen="4.5 2" halign="right" text="Local:"/>
<label posn="6.0 -6.0 2" sizen="4.5 2" halign="right" text="Dedi:"/>
<label posn="6.0 -8.5 2" sizen="4.5 2" halign="right" text="TMX:"/>
<format style="TextCardSmallScores2"/>
<label posn="6.5 -1.0 2" sizen="6.5 2" halign="left" text="%PB%" action="7"/>
<label posn="6.5 -3.5 2" sizen="6.5 2" halign="left" text="%LCL%" action="8"/>
<label posn="6.5 -6.0 2" sizen="6.5 2" halign="left" text="%DED%" action="9"/>
<label posn="6.5 -8.5 2" sizen="6.5 2" halign="left" text="%TMX%" action="10"/>
</frame></manialink>

View File

@ -0,0 +1,12 @@
<manialink id="4"><frame posn="53.5 -32.7 0">
<format textsize="1" textcolor="dddf"/>
<label posn="4.5 -0.8 2" sizen="4.5 2" halign="right" valign="center" text="PB:"/>
<label posn="4.5 -2.6 2" sizen="4.5 2" halign="right" valign="center" text="Local:"/>
<label posn="4.5 -4.4 2" sizen="4.5 2" halign="right" valign="center" text="Dedi:"/>
<label posn="4.5 -6.2 2" sizen="4.5 2" halign="right" valign="center" text="TMX:"/>
<format textsize="1" textcolor="ffff"/>
<label posn="5.0 -0.8 2" sizen="6.5 2" halign="left" valign="center" text="%PB%" action="7"/>
<label posn="5.0 -2.6 2" sizen="6.5 2" halign="left" valign="center" text="%LCL%" action="8"/>
<label posn="5.0 -4.4 2" sizen="6.5 2" halign="left" valign="center" text="%DED%" action="9"/>
<label posn="5.0 -6.2 2" sizen="6.5 2" halign="left" valign="center" text="%TMX%" action="10"/>
</frame></manialink>

View File

@ -0,0 +1,10 @@
<manialink id="4"><frame posn="53.5 -32.7 0">
<format textsize="1" textcolor="dddf"/>
<label posn="4.5 -0.8 2" sizen="4.5 2" halign="right" valign="center" text="PB:"/>
<label posn="4.5 -2.6 2" sizen="4.5 2" halign="right" valign="center" text="Local:"/>
<label posn="4.5 -4.4 2" sizen="4.5 2" halign="right" valign="center" text="TMX:"/>
<format textsize="1" textcolor="ffff"/>
<label posn="5.0 -0.8 2" sizen="6.5 2" halign="left" valign="center" text="%PB%" action="7"/>
<label posn="5.0 -2.6 2" sizen="6.5 2" halign="left" valign="center" text="%LCL%" action="8"/>
<label posn="5.0 -4.4 2" sizen="6.5 2" halign="left" valign="center" text="%TMX%" action="10"/>
</frame></manialink>

View File

@ -0,0 +1,12 @@
<manialink id="4"><frame posn="53.5 -32.7 0">
<format textsize="1" textcolor="dddf"/>
<label posn="4.5 -0.8 2" sizen="4.5 2" halign="right" valign="center" text="$da2PB:"/>
<label posn="4.5 -2.6 2" sizen="4.5 2" halign="right" valign="center" text="$fffLocal:"/>
<label posn="4.5 -4.4 2" sizen="4.5 2" halign="right" valign="center" text="$dddDedi:"/>
<label posn="4.5 -6.2 2" sizen="4.5 2" halign="right" valign="center" text="$bbbTMX:"/>
<format textsize="1" textcolor="ffff"/>
<label posn="4.75 -0.75 2" sizen="6.5 2" halign="left" valign="center" style="TextRaceChrono" text="$da2%PB%" action="7"/>
<label posn="5.0 -2.7 2" sizen="6.5 2" halign="left" valign="center" style="TextRaceChrono" scale="0.8" text="$fff%LCL%" action="8"/>
<label posn="5.0 -4.5 2" sizen="6.5 2" halign="left" valign="center" style="TextRaceChrono" scale="0.8" text="$ddd%DED%" action="9"/>
<label posn="5.0 -6.3 2" sizen="6.5 2" halign="left" valign="center" style="TextRaceChrono" scale="0.8" text="$bbb%TMX%" action="10"/>
</frame></manialink>

View File

@ -0,0 +1,18 @@
<manialink id="9">
<frame posn="-63.9 -33.1 0">
<format textsize="1" textcolor="ddd"/>
<quad sizen="10 2.2" posn="2 0 0" style="Bgs1InRace" substyle="NavButton"/>
<label posn="7 -0.3" sizen="8 2" halign="center" textcolor="fff" text="Your Stats"/>
<quad sizen="14 11.2" posn="0 -1.9 0" style="Bgs1InRace" substyle="NavButton"/>
<label posn="6.8 -2.7" halign="right" text="Rank:"/>
<label posn="7.2 -2.7" sizen="6 2" textcolor="fff" text="%RANK%"/>
<label posn="6.8 -4.7" halign="right" text="Avg:"/>
<label posn="7.2 -4.7" textcolor="fff" text="%AVG%"/>
<label posn="6.8 -6.7" halign="right" text="Records:"/>
<label posn="7.2 -6.7" textcolor="fff" text="%RECS%"/>
<label posn="6.8 -8.7" halign="right" text="Wins:"/>
<label posn="7.2 -8.7" textcolor="fff" text="%WINS%"/>
<label posn="6.8 -10.7" halign="right" text="Played:"/>
<label posn="7.2 -10.7" textcolor="fff" text="%PLAY%"/>
</frame>
</manialink>

View File

@ -0,0 +1,20 @@
<manialink id="9">
<frame posn="-63.9 -33.1 0">
<format textsize="1" textcolor="ddd"/>
<quad sizen="10 2.2" posn="2 0 0" style="Bgs1InRace" substyle="NavButton"/>
<label posn="7 -0.3" sizen="8 2" halign="center" textcolor="ffc" text="Your Stats"/>
<quad sizen="14 13" posn="0 -1.9 0" style="Bgs1InRace" substyle="NavButton"/>
<label posn="6.8 -2.7" halign="right" text="Rank:"/>
<label posn="7.2 -2.7" sizen="6 2" textcolor="fff" text="%RANK%"/>
<label posn="6.8 -4.7" halign="right" text="Avg:"/>
<label posn="7.2 -4.7" textcolor="fff" text="%AVG%"/>
<label posn="6.8 -6.7" halign="right" text="Records:"/>
<label posn="7.2 -6.7" textcolor="fff" text="%RECS%"/>
<label posn="6.8 -8.7" halign="right" text="Wins:"/>
<label posn="7.2 -8.7" textcolor="fff" text="%WINS%"/>
<label posn="6.8 -10.7" halign="right" text="Played:"/>
<label posn="7.2 -10.7" textcolor="fff" text="%PLAY%"/>
<label posn="6.8 -12.7" halign="right" text="Donations:"/>
<label posn="7.2 -12.7" textcolor="fff" text="%DONS%"/>
</frame>
</manialink>

View File

@ -0,0 +1,7 @@
<manialink id="5"><frame posn="-56.8 -45.6 0">
<quad sizen="10 2.5" style="Bgs1InRace" substyle="BgButtonSmall" halign="center" action="18" actionkey="1"/>
<label sizen="5 1.5" posn="0 -0.3 2" style="TextButtonSmall" halign="center" text="%YES%"/>
</frame><frame posn="-46 -45.6 0">
<quad sizen="10 2.5" style="Bgs1InRace" substyle="BgButtonSmall" halign="center" action="19" actionkey="2"/>
<label sizen="5 1.5" posn="0 -0.3 2" style="TextButtonSmall" halign="center" text="%NO%"/>
</frame></manialink>

View File

@ -0,0 +1,7 @@
<manialink id="5"><frame posn="-5 -30 0">
<quad sizen="10 3.5" style="Bgs1InRace" substyle="BgButtonSmall" halign="center" action="18" actionkey="1"/>
<label sizen="5 2.5" posn="0 -0.8 2" style="TextButtonSmall" halign="center" text="%YES%"/>
</frame><frame posn="5.5 -30 0">
<quad sizen="10 3.5" style="Bgs1InRace" substyle="BgButtonSmall" halign="center" action="19" actionkey="2"/>
<label sizen="5 2.5" posn="0 -0.8 2" style="TextButtonSmall" halign="center" text="%NO%"/>
</frame></manialink>

View File

@ -0,0 +1,8 @@
<!-- vote panel made by nouseforname -->
<manialink id="5"><frame posn="-5 -30 0">
<quad sizen="10 3.5" style="Bgs1InRace" substyle="NavButton" halign="center" action="18" actionkey="1"/>
<label sizen="5 2.5" posn="0 -0.8 2" style="TextButtonSmall" halign="center" text="%YES%"/>
</frame><frame posn="5.5 -30 0">
<quad sizen="10 3.5" style="Bgs1InRace" substyle="NavButton" halign="center" action="19" actionkey="2"/>
<label sizen="5 2.5" posn="0 -0.8 2" style="TextButtonSmall" halign="center" text="%NO%"/>
</frame></manialink>

View File

@ -0,0 +1,7 @@
<manialink id="5"><frame posn="-45 37.1 0">
<quad sizen="10 3.5" style="Bgs1InRace" substyle="BgButtonSmall" halign="center" action="18" actionkey="1"/>
<label sizen="5 2.5" posn="0 -0.8 2" style="TextButtonSmall" halign="center" text="%YES%"/>
</frame><frame posn="-34.5 37.1 0">
<quad sizen="10 3.5" style="Bgs1InRace" substyle="BgButtonSmall" halign="center" action="19" actionkey="2"/>
<label sizen="5 2.5" posn="0 -0.8 2" style="TextButtonSmall" halign="center" text="%NO%"/>
</frame></manialink>

View File

@ -0,0 +1,7 @@
<manialink id="5"><frame posn="-5.8 40 0">
<quad sizen="10 3.5" style="Bgs1InRace" substyle="BgButtonSmall" halign="center" action="18" actionkey="1"/>
<label sizen="5 2.5" posn="0 -0.8 2" style="TextButtonSmall" halign="center" text="%YES%"/>
</frame><frame posn="4.7 40 0">
<quad sizen="10 3.5" style="Bgs1InRace" substyle="BgButtonSmall" halign="center" action="19" actionkey="2"/>
<label sizen="5 2.5" posn="0 -0.8 2" style="TextButtonSmall" halign="center" text="%NO%"/>
</frame></manialink>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,941 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
/**
* Chat plugin.
* Shows new/online Dedimania world records and their relations on the
* current track.
* Created by Xymph
*
* Dependencies: requires plugin.dedimania.php, plugin.checkpoints.php
* used by plugin.dedimania.php
*/
Aseco::addChatCommand('helpdedi', 'Displays info about the Dedimania records system');
Aseco::addChatCommand('dedihelp', 'Displays info about the Dedimania records system');
Aseco::addChatCommand('dedirecs', 'Displays all Dedimania records on current track');
if (!INHIBIT_RECCMDS) {
Aseco::addChatCommand('dedinew', 'Shows newly driven Dedimania records');
Aseco::addChatCommand('dedilive', 'Shows Dedimania records of online players');
Aseco::addChatCommand('dedipb', 'Shows your Dedimania personal best on current track');
Aseco::addChatCommand('dedifirst', 'Shows first Dedimania record on current track');
Aseco::addChatCommand('dedilast', 'Shows last Dedimania record on current track');
Aseco::addChatCommand('dedinext', 'Shows next better Dedimania record to beat');
Aseco::addChatCommand('dedidiff', 'Shows your difference to first Dedimania record');
Aseco::addChatCommand('dedirange', 'Shows difference first to last Dedimania record');
}
Aseco::addChatCommand('dedicps', 'Sets Dedimania record checkspoints tracking');
Aseco::addChatCommand('dedistats', 'Displays Dedimania track statistics');
Aseco::addChatCommand('dedicptms', 'Displays all Dedimania records\' checkpoint times');
Aseco::addChatCommand('dedisectms', 'Displays all Dedimania records\' sector times');
function chat_dedihelp($aseco, $command) { chat_helpdedi($aseco, $command); }
function chat_helpdedi($aseco, $command) {
// compile & display help message
if ($aseco->server->getGame() == 'TMN') {
$help = '{#dedimsg}Dedimania$g is an online World Records database for {#black}all$g' . LF;
$help .= 'TrackMania games. See its official site at:' . LF;
$help .= '{#black}http://www.dedimania.com/SITE/$g and the records database:' . LF;
$help .= '{#black}http://www.dedimania.com/tmstats/?do=stat$g .' . LF . LF;
$help .= 'Dedimania records are stored per game (TMN, TMU, etc)' . LF;
$help .= 'and mode (TimeAttack, Rounds, etc) and shared between' . LF;
$help .= 'all servers that operate with Dedimania support.' . LF . LF;
$help .= 'The available Dedimania commands are similar to local' . LF;
$help .= 'record commands:' . LF;
$help .= '{#black}/dedirecs$g, {#black}/dedinew$g, {#black}/dedilive$g, {#black}/dedipb$g, {#black}/dedicps$g, {#black}/dedistats$g,' . LF;
$help .= '{#black}/dedifirst$g, {#black}/dedilast$g, {#black}/dedinext$g, {#black}/dedidiff$g, {#black}/dedirange$g' . LF;
$help .= 'See the {#black}/helpall$g command for detailed descriptions.';
// display popup message
$aseco->client->query('SendDisplayServerMessageToLogin', $command['author']->login, $aseco->formatColors($help), 'OK', '', 0);
} elseif ($aseco->server->getGame() == 'TMF') {
$header = 'Dedimania information:';
$data = array();
$data[] = array('{#dedimsg}Dedimania$g is an online World Records database for {#black}all');
$data[] = array('TrackMania games. See its official site at:');
$data[] = array('{#black}$l[http://www.dedimania.com/SITE/]http://www.dedimania.com/SITE/$l$g and the records database:');
$data[] = array('{#black}$l[http://www.dedimania.com/tmstats/?do=stat]http://www.dedimania.com/tmstats/?do=stat$l$g .');
$data[] = array();
$data[] = array('Dedimania records are stored per game (TMN, TMU, etc)');
$data[] = array('and mode (TimeAttack, Rounds, etc) and shared between');
$data[] = array('all servers that operate with Dedimania support.');
$data[] = array();
$data[] = array('The available Dedimania commands are similar to local');
$data[] = array('record commands:');
$data[] = array('{#black}/dedirecs$g, {#black}/dedinew$g, {#black}/dedilive$g, {#black}/dedipb$g, {#black}/dedicps$g, {#black}/dedistats$g,');
$data[] = array('{#black}/dedifirst$g, {#black}/dedilast$g, {#black}/dedinext$g, {#black}/dedidiff$g, {#black}/dedirange$g');
$data[] = array();
$data[] = array('See the {#black}/helpall$g command for detailed descriptions.');
// display ManiaLink message
display_manialink($command['author']->login, $header, array('Icons64x64_1', 'TrackInfo', -0.01), $data, array(0.95), 'OK');
}
} // chat_helpdedi
function chat_dedirecs($aseco, $command) {
global $dedi_db;
$player = $command['author'];
$login = $player->login;
$dedi_recs = $dedi_db['Challenge']['Records'];
// split params into array
$arglist = explode(' ', strtolower(preg_replace('/ +/', ' ', $command['params'])));
// process optional relations commands
if ($arglist[0] == 'help') {
if ($aseco->server->getGame() == 'TMN') {
$help = '{#black}/dedirecs <option>$g shows Dedimania records and relations' . LF;
$help .= ' - {#black}help$g, displays this help information' . LF;
$help .= ' - {#black}pb$g, your personal best on current track' . LF;
$help .= ' - {#black}new$g, newly driven records' . LF;
$help .= ' - {#black}live$g, records of online players' . LF;
$help .= ' - {#black}first$g, first ranked record on current track' . LF;
$help .= ' - {#black}last$g, last ranked record on current track' . LF;
$help .= ' - {#black}next$g, next better ranked record to beat' . LF;
$help .= ' - {#black}diff$g, your difference to first ranked record' . LF;
$help .= ' - {#black}range$g, difference first to last ranked record' . LF;
$help .= LF . 'Without an option, the normal records list is displayed.';
// display popup message
$aseco->client->query('SendDisplayServerMessageToLogin', $login, $aseco->formatColors($help), 'OK', '', 0);
} elseif ($aseco->server->getGame() == 'TMF') {
$header = '{#black}/dedirecs <option>$g shows Dedimania records and relations:';
$help = array();
$help[] = array('...', '{#black}help',
'Displays this help information');
$help[] = array('...', '{#black}pb',
'Shows your personal best on current track');
$help[] = array('...', '{#black}new',
'Shows newly driven records');
$help[] = array('...', '{#black}live',
'Shows records of online players');
$help[] = array('...', '{#black}first',
'Shows first ranked record on current track');
$help[] = array('...', '{#black}last',
'Shows last ranked record on current track');
$help[] = array('...', '{#black}next',
'Shows next better ranked record to beat');
$help[] = array('...', '{#black}diff',
'Shows your difference to first ranked record');
$help[] = array('...', '{#black}range',
'Shows difference first to last ranked record');
$help[] = array();
$help[] = array('Without an option, the normal records list is displayed.');
// display ManiaLink message
display_manialink($login, $header, array('Icons64x64_1', 'TrackInfo', -0.01), $help, array(1.2, 0.05, 0.3, 0.85), 'OK');
}
return;
}
elseif ($arglist[0] == 'pb') {
chat_dedipb($aseco, $command);
return;
}
elseif ($arglist[0] == 'new') {
chat_dedinew($aseco, $command);
return;
}
elseif ($arglist[0] == 'live') {
chat_dedilive($aseco, $command);
return;
}
elseif ($arglist[0] == 'first') {
chat_dedifirst($aseco, $command);
return;
}
elseif ($arglist[0] == 'last') {
chat_dedilast($aseco, $command);
return;
}
elseif ($arglist[0] == 'next') {
chat_dedinext($aseco, $command);
return;
}
elseif ($arglist[0] == 'diff') {
chat_dedidiff($aseco, $command);
return;
}
elseif ($arglist[0] == 'range') {
chat_dedirange($aseco, $command);
return;
}
if (!$total = count($dedi_recs)) {
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No Dedimania records found!'), $login);
return;
}
$maxrank = max($dedi_db['ServerMaxRank'], $player->dedirank);
// display popup window for TMN
if ($aseco->server->getGame() == 'TMN') {
$head = 'Current TOP ' . $maxrank . ' Dedimania Records:' . LF;
$msg = '';
$lines = 0;
$player->msgs = array();
$player->msgs[0] = 1;
// create list of records
for ($i = 0; $i < $total; $i++) {
$cur_record = $dedi_recs[$i];
$nick = $cur_record['NickName'];
if (!$aseco->settings['lists_colornicks'])
$nick = stripColors($nick);
$msg .= str_pad($i+1, 2, '0', STR_PAD_LEFT) . '. {#black}'
. str_pad($nick, 20) . '$z - '
. ((isset($cur_record['NewBest']) && $cur_record['NewBest']) ? '{#black}': '')
. formatTime($cur_record['Best']) . LF;
if (++$lines > 9) {
$player->msgs[] = $aseco->formatColors($head . $msg);
$lines = 0;
$msg = '';
}
}
// add if last batch exists
if ($msg != '')
$player->msgs[] = $aseco->formatColors($head . $msg);
// display popup message
if (count($player->msgs) == 2) {
$aseco->client->query('SendDisplayServerMessageToLogin', $login, $player->msgs[1], 'OK', '', 0);
} else { // > 2
$aseco->client->query('SendDisplayServerMessageToLogin', $login, $player->msgs[1], 'Close', 'Next', 0);
}
// display ManiaLink window for TMF
} elseif ($aseco->server->getGame() == 'TMF') {
$head = 'Current TOP ' . $maxrank . ' Dedimania Records:';
$msg = array();
$lines = 0;
$player->msgs = array();
// reserve extra width for $w tags
$extra = ($aseco->settings['lists_colornicks'] ? 0.2 : 0);
if ($dedi_db['ShowRecLogins'])
$player->msgs[0] = array(1, $head, array(1.2+$extra, 0.1, 0.45+$extra, 0.4, 0.25), array('BgRaceScore2', 'Podium'));
else
$player->msgs[0] = array(1, $head, array(0.8+$extra, 0.1, 0.45+$extra, 0.25), array('BgRaceScore2', 'Podium'));
// create list of records
for ($i = 0; $i < $total; $i++) {
$cur_record = $dedi_recs[$i];
$nick = $cur_record['NickName'];
if (!$aseco->settings['lists_colornicks'])
$nick = stripColors($nick);
if ($dedi_db['ShowRecLogins']) {
$msg[] = array(str_pad($i+1, 2, '0', STR_PAD_LEFT) . '.',
'{#black}' . $nick,
'{#login}' . $cur_record['Login'],
((isset($cur_record['NewBest']) && $cur_record['NewBest']) ? '{#black}': '') .
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$cur_record['Best'] : formatTime($cur_record['Best'])));
} else {
$msg[] = array(str_pad($i+1, 2, '0', STR_PAD_LEFT) . '.',
'{#black}' . $nick,
((isset($cur_record['NewBest']) && $cur_record['NewBest']) ? '{#black}': '') .
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$cur_record['Best'] : formatTime($cur_record['Best'])));
}
if (++$lines > 14) {
$player->msgs[] = $msg;
$lines = 0;
$msg = array();
}
}
// add if last batch exists
if (!empty($msg))
$player->msgs[] = $msg;
// display ManiaLink message
display_manialink_multi($player);
// show chat message for TMO & TMS
} else {
$top = 4;
$msg = $aseco->formatColors("{#server}> Current TOP $top Dedimania Records:{#highlite}");
// create list of records
$total = ($total <= $top ? $total : $top);
for ($i = 0; $i < $total; $i++) {
$cur_record = $dedi_recs[$i];
$msg .= LF . ($i+1) . '. ' . str_pad(stripColors($cur_record['NickName']), 15)
. ' - ' . formatTime($cur_record['Best']);
}
// show chat message
$aseco->client->query('ChatSendServerMessageToLogin', $msg, $login);
}
} // chat_dedirecs
/*
* Universal function to generate list of Dedimania records for current track.
* Called by chat_dedinew, chat_dedilive, endRace & beginRace (plugin.dedimania.php).
* Show to a player if $login defined, otherwise show to all players.
* $mode = 0 (only new), 1 (top-8 & online players at start of track),
* 2 (top-6 & online during track), 3 (top-8 & new at end of track)
* In modes 1/2/3 the last Dedimania record is also shown
* top-8 is configurable via $dedi_db['ShowMinRecs']; top-6 is ShowMinRecs-2
*/
function show_dedirecs($aseco, $name, $uid, $dedi_recs, $login, $mode, $window) {
global $dedi_db, $dedi_debug;
$records = '$n'; // use narrow font
if ($dedi_debug > 2)
$aseco->console_text('show_dedirecs - dedi_recs' . CRLF . print_r($dedi_recs, true));
// check for records
if (!isset($dedi_recs) || ($total = count($dedi_recs)) == 0) {
$totalnew = -1;
} else {
// check whether to show range
if ($dedi_db['ShowRecsRange']) {
// get the first & last Dedimania records
$first = $dedi_recs[0];
$last = $dedi_recs[$total-1];
// compute difference between records
if ($aseco->server->gameinfo->mode != Gameinfo::STNT) {
$diff = $last['Best'] - $first['Best'];
$sec = floor($diff/1000);
$hun = ($diff - ($sec * 1000)) / 10;
} else { // Stunts
$diff = $first['Best'] - $last['Best'];
}
}
// get list of online players
$players = array();
foreach ($aseco->server->players->player_list as $pl) {
$players[] = $pl->login;
}
// collect new records and records by online players
$totalnew = 0;
// go through each record
for ($i = 0; $i < $total; $i++) {
$cur_record = $dedi_recs[$i];
// if the record is new then display it
if (isset($cur_record['NewBest']) && $cur_record['NewBest']) {
$totalnew++;
$record_msg = formatText($aseco->getChatMessage('RANKING_RECORD_NEW_ON'),
$i+1,
stripColors($cur_record['NickName']),
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$cur_record['Best'] : formatTime($cur_record['Best'])));
// always show new record
$records .= $record_msg;
} else {
// check if player is online
if (in_array($cur_record['Login'], $players) && $cur_record['Game'] ==
($aseco->server->getGame() == 'TMF' ? 'TMU' : $aseco->server->getGame())) {
$record_msg = formatText($aseco->getChatMessage('RANKING_RECORD_ON'),
$i+1,
stripColors($cur_record['NickName']),
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$cur_record['Best'] : formatTime($cur_record['Best'])));
// check if last Dedimania record
if ($mode != 0 && $i == $total-1) {
$records .= $record_msg;
}
// check if always show (start of/during track)
elseif ($mode == 1 || $mode == 2) {
$records .= $record_msg;
}
else {
// show record if < ShowMinRecs (end of track)
if ($mode == 3 && $i < $dedi_db['ShowMinRecs']) {
$records .= $record_msg;
}
}
} else {
$record_msg = formatText($aseco->getChatMessage('RANKING_RECORD'),
$i+1,
stripColors($cur_record['NickName']),
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$cur_record['Best'] : formatTime($cur_record['Best'])));
// check if last Dedimania record
if ($mode != 0 && $i == $total-1) {
$records .= $record_msg;
}
// show offline record if < ShowMinRecs-2 (during track)
elseif (($mode == 2 && $i < $dedi_db['ShowMinRecs']-2) ||
// show offline record if < ShowMinRecs (start/end of track)
(($mode == 1 || $mode == 3) && $i < $dedi_db['ShowMinRecs'])) {
$records .= $record_msg;
}
}
}
}
}
// define wording of the ranking message
switch ($mode) {
case 0:
$timing = 'during';
break;
case 1:
$timing = 'before';
break;
case 2:
$timing = 'during';
break;
case 3:
$timing = 'after';
break;
}
// hyperlink track name
$name = stripColors($name);
if ($aseco->server->getGame() == 'TMF')
$name = '$l[http://www.dedimania.com/tmstats/?do=stat&Show=RECORDS&RecOrder3=RANK-ASC&Uid=' . $uid . ']' . $name . '$l';
// define the ranking message
if ($totalnew > 0) {
$message = formatText($dedi_db['Messages']['RANKING_NEW'][0],
$name, $timing, $totalnew);
}
elseif ($totalnew == 0 && $records != '$n') {
// check whether to show range
if ($dedi_db['ShowRecsRange']) {
$message = formatText($dedi_db['Messages']['RANKING_RANGE'][0],
$name, $timing,
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$diff : sprintf("%d.%02d", $sec, $hun)));
} else {
$message = formatText($dedi_db['Messages']['RANKING'][0],
$name, $timing);
}
}
elseif ($totalnew == 0 && $records == '$n') {
$message = formatText($dedi_db['Messages']['RANKING_NONEW'][0],
$name, $timing);
}
else { // $totalnew == -1
$message = formatText($dedi_db['Messages']['RANKING_NONE'][0],
$name, $timing);
}
// append the records if any
if ($records != '$n') {
$records = substr($records, 0, strlen($records)-2); // strip trailing ", "
$message .= LF . $records;
}
// show to player or all
if ($login) {
// strip 1 leading '>' to indicate a player message instead of system-wide
$message = str_replace('{#server}>> ', '{#server}> ', $message);
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
} else {
if ($window == 2 && function_exists('send_window_message'))
send_window_message($aseco, $message, ($mode == 3));
else
$aseco->client->query('ChatSendServerMessage', $aseco->formatColors($message));
}
} // show_dedirecs
function chat_dedinew($aseco, $command) {
global $dedi_db;
// show only newly driven records
show_dedirecs($aseco, $aseco->server->challenge->name, $aseco->server->challenge->uid, $dedi_db['Challenge']['Records'], $command['author']->login, 0, 0);
} // chat_dedinew
function chat_dedilive($aseco, $command) {
global $dedi_db;
// show online & ShowMinRecs-2 records
show_dedirecs($aseco, $aseco->server->challenge->name, $aseco->server->challenge->uid, $dedi_db['Challenge']['Records'], $command['author']->login, 2, 0);
} // chat_dedilive
function chat_dedipb($aseco, $command) {
global $dedi_db;
$login = $command['author']->login;
$dedi_recs = $dedi_db['Challenge']['Records'];
$found = false;
// find Dedimania record
for ($i = 0; $i < count($dedi_recs); $i++) {
$rec = $dedi_recs[$i];
if ($rec['Login'] == $login && $rec['Game'] ==
($aseco->server->getGame() == 'TMF' ? 'TMU' : $aseco->server->getGame())) {
$score = $rec['Best'];
$rank = $i;
$found = true;
break;
}
}
if ($found) {
$message = formatText($dedi_db['Messages']['PB'][0],
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$score : formatTime($score)), $rank+1);
$message = $aseco->formatColors($message);
$aseco->client->query('ChatSendServerMessageToLogin', $message, $login);
} else {
$message = $dedi_db['Messages']['PB_NONE'][0];
$message = $aseco->formatColors($message);
$aseco->client->query('ChatSendServerMessageToLogin', $message, $login);
}
} // chat_dedipb
function chat_dedifirst($aseco, $command) {
global $dedi_db;
$dedi_recs = $dedi_db['Challenge']['Records'];
if (!empty($dedi_recs)) {
// get the first Dedimania record
$record = $dedi_recs[0];
// show chat message
$message = formatText($dedi_db['Messages']['FIRST_RECORD'][0])
. formatText($aseco->getChatMessage('RANKING_RECORD_NEW'),
1,
stripColors($record['NickName']),
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$record['Best'] : formatTime($record['Best'])));
$message = substr($message, 0, strlen($message)-2); // strip trailing ", "
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $command['author']->login);
} else {
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No Dedimania records found!'), $command['author']->login);
}
} // chat_dedifirst
function chat_dedilast($aseco, $command) {
global $dedi_db;
$dedi_recs = $dedi_db['Challenge']['Records'];
if ($total = count($dedi_recs)) {
// get the last Dedimania record
$record = $dedi_recs[$total-1];
// show chat message
$message = formatText($dedi_db['Messages']['LAST_RECORD'][0])
. formatText($aseco->getChatMessage('RANKING_RECORD_NEW'),
$total,
stripColors($record['NickName']),
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$record['Best'] : formatTime($record['Best'])));
$message = substr($message, 0, strlen($message)-2); // strip trailing ", "
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $command['author']->login);
} else {
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No Dedimania records found!'), $command['author']->login);
}
} // chat_dedilast
function chat_dedinext($aseco, $command) {
global $dedi_db;
$login = $command['author']->login;
$dedi_recs = $dedi_db['Challenge']['Records'];
if ($total = count($dedi_recs)) {
$found = false;
// find Dedimania record
for ($i = 0; $i < $total; $i++) {
$rec = $dedi_recs[$i];
if ($rec['Login'] == $login && $rec['Game'] ==
($aseco->server->getGame() == 'TMF' ? 'TMU' : $aseco->server->getGame())) {
$rank = $i;
$found = true;
break;
}
}
if ($found) {
// get current and next better Dedimania records
$nextrank = ($rank > 0 ? $rank-1 : 0);
$record = $dedi_recs[$rank];
$next = $dedi_recs[$nextrank];
// compute difference to next record
if ($aseco->server->gameinfo->mode != Gameinfo::STNT) {
$diff = $record['Best'] - $next['Best'];
$sec = floor($diff/1000);
$hun = ($diff - ($sec * 1000)) / 10;
} else { // Stunts mode
$diff = $next['Best'] - $record['Best'];
}
// show chat message
$message1 = formatText($aseco->getChatMessage('RANKING_RECORD_NEW'),
$rank+1,
stripColors($record['NickName']),
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$record['Best'] : formatTime($record['Best'])));
$message1 = substr($message1, 0, strlen($message1)-2); // strip trailing ", "
$message2 = formatText($aseco->getChatMessage('RANKING_RECORD_NEW'),
$nextrank+1,
stripColors($next['NickName']),
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$next['Best'] : formatTime($next['Best'])));
$message2 = substr($message2, 0, strlen($message2)-2); // strip trailing ", "
$message = formatText($dedi_db['Messages']['DIFF_RECORD'][0],
$message1, $message2,
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$diff : sprintf("%d.%02d", $sec, $hun)));
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
} else {
// look for unranked time instead
$order = ($aseco->server->gameinfo->mode == Gameinfo::STNT ? 'DESC' : 'ASC');
$query = 'SELECT score FROM rs_times
WHERE playerID=' . $command['author']->id . ' AND
challengeID=' . $aseco->server->challenge->id . '
ORDER BY score ' . $order . ' LIMIT 1';
$result = mysql_query($query);
if (mysql_num_rows($result) > 0) {
$unranked = mysql_fetch_object($result);
$found = true;
}
mysql_free_result($result);
if ($found) {
// get the last Dedimania record
$last = $dedi_recs[$total-1];
// compute difference to next record
if ($aseco->server->gameinfo->mode != Gameinfo::STNT) {
$sign = ($unranked->score < $last['Best'] ? '-' : '');
$diff = abs($unranked->score - $last['Best']);
$sec = floor($diff/1000);
$hun = ($diff - ($sec * 1000)) / 10;
} else { // Stunts mode
$diff = $last['Best'] - $unranked->score;
}
// show chat message
$message1 = formatText($aseco->getChatMessage('RANKING_RECORD_NEW'),
'PB',
stripColors($command['author']->nickname),
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$unranked->score : formatTime($unranked->score)));
$message1 = substr($message1, 0, strlen($message1)-2); // strip trailing ", "
$message2 = formatText($aseco->getChatMessage('RANKING_RECORD_NEW'),
$total,
stripColors($last['NickName']),
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$last['Best'] : formatTime($last['Best'])));
$message2 = substr($message2, 0, strlen($message2)-2); // strip trailing ", "
$message = formatText($dedi_db['Messages']['DIFF_RECORD'][0],
$message1, $message2,
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$diff : sprintf("%s%d.%02d", $sign, $sec, $hun)));
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
} else {
$message = '{#server}> {#error}You don\'t have Dedimania a record on this track yet... use {#highlite}$i/dedilast';
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
}
}
} else {
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No Dedimania records found!'), $login);
}
} // chat_dedinext
function chat_dedidiff($aseco, $command) {
global $dedi_db;
$login = $command['author']->login;
$dedi_recs = $dedi_db['Challenge']['Records'];
if ($total = count($dedi_recs)) {
$found = false;
// find Dedimania record
for ($i = 0; $i < $total; $i++) {
$rec = $dedi_recs[$i];
if ($rec['Login'] == $login && $rec['Game'] ==
($aseco->server->getGame() == 'TMF' ? 'TMU' : $aseco->server->getGame())) {
$rank = $i;
$found = true;
break;
}
}
if ($found) {
// get current and first Dedimania records
$record = $dedi_recs[$rank];
$first = $dedi_recs[0];
// compute difference to first record
if ($aseco->server->gameinfo->mode != Gameinfo::STNT) {
$diff = $record['Best'] - $first['Best'];
$sec = floor($diff/1000);
$hun = ($diff - ($sec * 1000)) / 10;
} else { // Stunts mode
$diff = $first['Best'] - $record['Best'];
}
// show chat message
$message1 = formatText($aseco->getChatMessage('RANKING_RECORD_NEW'),
$rank+1,
stripColors($record['NickName']),
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$record['Best'] : formatTime($record['Best'])));
$message1 = substr($message1, 0, strlen($message1)-2); // strip trailing ", "
$message2 = formatText($aseco->getChatMessage('RANKING_RECORD_NEW'),
1,
stripColors($first['NickName']),
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$first['Best'] : formatTime($first['Best'])));
$message2 = substr($message2, 0, strlen($message2)-2); // strip trailing ", "
$message = formatText($dedi_db['Messages']['DIFF_RECORD'][0],
$message1, $message2,
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$diff : sprintf("%d.%02d", $sec, $hun)));
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
} else {
$message = '{#server}> {#error}You don\'t have a Dedimania record on this track yet... use {#highlite}$i/dedilast';
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
}
} else {
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No Dedimania records found!'), $login);
}
} // chat_dedidiff
function chat_dedirange($aseco, $command) {
global $dedi_db;
$dedi_recs = $dedi_db['Challenge']['Records'];
if ($total = count($dedi_recs)) {
// get the first & last Dedimania records
$first = $dedi_recs[0];
$last = $dedi_recs[$total-1];
// compute difference between records
if ($aseco->server->gameinfo->mode != Gameinfo::STNT) {
$diff = $last['Best'] - $first['Best'];
$sec = floor($diff/1000);
$hun = ($diff - ($sec * 1000)) / 10;
} else { // Stunts mode
$diff = $first['Best'] - $last['Best'];
}
// show chat message
$message1 = formatText($aseco->getChatMessage('RANKING_RECORD_NEW'),
1,
stripColors($first['NickName']),
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$first['Best'] : formatTime($first['Best'])));
$message1 = substr($message1, 0, strlen($message1)-2); // strip trailing ", "
$message2 = formatText($aseco->getChatMessage('RANKING_RECORD_NEW'),
$total,
stripColors($last['NickName']),
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$last['Best'] : formatTime($last['Best'])));
$message2 = substr($message2, 0, strlen($message2)-2); // strip trailing ", "
$message = formatText($dedi_db['Messages']['DIFF_RECORD'][0],
$message1, $message2,
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$diff : sprintf("%d.%02d", $sec, $hun)));
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $command['author']->login);
} else {
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No Dedimania records found!'), $command['author']->login);
}
} // chat_dedirange
function chat_dedicps($aseco, $command) {
global $dedi_db, $checkpoints; // from plugin.checkpoints.php
$login = $command['author']->login;
if ($aseco->settings['display_checkpoints']) {
if (isset($checkpoints[$login]) && $checkpoints[$login]->loclrec != -1) {
// set Dedimania checkpoints tracking
$param = $command['params'];
if (strtolower($param) == 'off') {
$checkpoints[$login]->dedirec = -1;
$message = '{#server}> {#dedimsg}Dedimania checkpoints tracking: {#highlite}OFF';
}
elseif ($param == '') {
$checkpoints[$login]->dedirec = 0;
$message = '{#server}> {#dedimsg}Dedimania checkpoints tracking: {#highlite}ON {#dedimsg}(your own or the last record)';
}
elseif (is_numeric($param) && $param > 0 && $param <= max($dedi_db['ServerMaxRank'], $command['author']->dedirank)) {
$checkpoints[$login]->dedirec = intval($param);
$message = '{#server}> {#dedimsg}Dedimania checkpoints tracking record: {#highlite}' . $checkpoints[$login]->dedirec;
}
else {
$message = '{#server}> {#error}No such Dedimania record {#highlite}$i ' . $param;
}
} else {
$message = '{#server}> {#error}You must first enable checkpoints tracking with {#highlite}$i /cps';
}
} else {
$message = '{#server}> {#error}Dedimania checkpoints tracking permanently disabled by server';
}
// show chat message
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
} // chat_dedicps
function chat_dedistats($aseco, $command) {
global $dedi_db;
$login = $command['author']->login;
// compile & display stats message
if ($aseco->server->getGame() == 'TMN') {
$stats = '{#dedimsg}Dedimania$g Stats: {#black}' . stripColors($aseco->server->challenge->name) . LF . LF;
$stats .= '$gServer MaxRank: {#black}$n' . $dedi_db['ServerMaxRank'] . '$m' . LF;
$stats .= '$gYour MaxRank : {#black}$n' . $command['author']->dedirank . '$m' . LF . LF;
$stats .= '$gUID : {#black}$n' . $dedi_db['Challenge']['Uid'] . '$m' . LF;
$stats .= '$gTotal Races : {#black}' . $dedi_db['Challenge']['TotalRaces'] . LF;
$stats .= '$gTotal Players : {#black}' . $dedi_db['Challenge']['TotalPlayers'] . LF;
$stats .= '$gAvg. Players : {#black}' . ($dedi_db['Challenge']['TotalRaces'] > 0 ? round($dedi_db['Challenge']['TotalPlayers'] / $dedi_db['Challenge']['TotalRaces'], 2) : 0);
// display popup message
$aseco->client->query('SendDisplayServerMessageToLogin', $login, $aseco->formatColors($stats), 'OK', '', 0);
} elseif ($aseco->server->getGame() == 'TMF') {
$header = 'Dedimania Stats: {#black}' . stripColors($aseco->server->challenge->name);
$stats = array();
$stats[] = array('Server MaxRank', '{#black}' . $dedi_db['ServerMaxRank']);
$stats[] = array('Your MaxRank', '{#black}' . $command['author']->dedirank);
$stats[] = array();
$stats[] = array('UID', '{#black}' . $dedi_db['Challenge']['Uid']);
$stats[] = array('Total Races', '{#black}' . $dedi_db['Challenge']['TotalRaces']);
$stats[] = array('Total Players', '{#black}' . $dedi_db['Challenge']['TotalPlayers']);
$stats[] = array('Avg. Players', '{#black}' . ($dedi_db['Challenge']['TotalRaces'] > 0 ? round($dedi_db['Challenge']['TotalPlayers'] / $dedi_db['Challenge']['TotalRaces'], 2) : 0));
$stats[] = array();
$stats[] = array(' {#black}$l[http://dedimania.com/tmstats/?do=stat&RecOrder3=RANK-ASC&Uid=' . $dedi_db['Challenge']['Uid'] . '&Show=RECORDS]View all Dedimania records for this track$l');
// display ManiaLink message
display_manialink($login, $header, array('Icons64x64_1', 'Maximize', -0.01), $stats, array(1.0, 0.3, 0.7), 'OK');
}
} // chat_dedistats
function chat_dedicptms($aseco, $command) {
chat_dedisectms($aseco, $command, false);
} // chat_dedicptms
function chat_dedisectms($aseco, $command, $diff = true) {
global $dedi_db;
$player = $command['author'];
$login = $player->login;
$dedi_recs = $dedi_db['Challenge']['Records'];
if (!$total = count($dedi_recs)) {
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No Dedimania records found!'), $login);
return;
}
$maxrank = max($dedi_db['ServerMaxRank'], $player->dedirank);
$cpscnt = count($dedi_recs[0]['Checks']);
// display popup window for TMN
if ($aseco->server->getGame() == 'TMN') {
$head = 'Current TOP ' . $maxrank . ' Dedimania ' . ($diff ? 'Sector' : 'CP') . ' Times (' . $cpscnt . '):' . LF;
$cpsmax = 9;
$msg = '';
$lines = 0;
$player->msgs = array();
$player->msgs[0] = 1;
// create list of records
for ($i = 0; $i < $total; $i++) {
$cur_record = $dedi_recs[$i];
$msg .= str_pad($i+1, 2, '0', STR_PAD_LEFT) . '. '
. ((isset($cur_record['NewBest']) && $cur_record['NewBest']) ? '{#black}' : '')
. formatTime($cur_record['Best']);
// append up to $cpsmax sector/CP times
if (!empty($cur_record['Checks'])) {
$j = 1;
$pr = 0;
$msg .= '$n';
foreach ($cur_record['Checks'] as $cp) {
$msg .= ' ' . formatTime($cp - $pr);
if ($diff) $pr = $cp;
if (++$j > $cpsmax) {
if ($cpscnt > $cpsmax) $msg .= ' +';
break;
}
}
$msg .= '$m';
}
$msg .= LF;
if (++$lines > 9) {
$player->msgs[] = $aseco->formatColors($head . $msg);
$lines = 0;
$msg = '';
}
}
// add if last batch exists
if ($msg != '')
$player->msgs[] = $aseco->formatColors($head . $msg);
// display popup message
if (count($player->msgs) == 2) {
$aseco->client->query('SendDisplayServerMessageToLogin', $login, $player->msgs[1], 'OK', '', 0);
} else { // > 2
$aseco->client->query('SendDisplayServerMessageToLogin', $login, $player->msgs[1], 'Close', 'Next', 0);
}
// display ManiaLink window for TMF
} elseif ($aseco->server->getGame() == 'TMF') {
$head = 'Current TOP ' . $maxrank . ' Dedimania ' . ($diff ? 'Sector' : 'CP') . ' Times (' . $cpscnt . '):';
$cpsmax = 12;
// compute widths
$width = 0.1 + 0.18 + min($cpscnt, $cpsmax) * 0.1 + ($cpscnt > $cpsmax ? 0.06 : 0.0);
if ($width < 1.0) $width = 1.0;
$widths = array($width, 0.1, 0.18);
for ($i = 0; $i < min($cpscnt, $cpsmax); $i++)
$widths[] = 0.1; // cp
if ($cpscnt > $cpsmax)
$widths[] = 0.06;
$msg = array();
$lines = 0;
$player->msgs = array();
$player->msgs[0] = array(1, $head, $widths, array('BgRaceScore2', 'Podium'));
// create list of records
for ($i = 0; $i < $total; $i++) {
$cur_record = $dedi_recs[$i];
$line = array();
$line[] = str_pad($i+1, 2, '0', STR_PAD_LEFT) . '.';
$line[] = ((isset($cur_record['NewBest']) && $cur_record['NewBest']) ? '{#black}' : '')
. formatTime($cur_record['Best']);
// append up to $cpsmax sector/CP times
if (!empty($cur_record['Checks'])) {
$j = 1;
$pr = 0;
foreach ($cur_record['Checks'] as $cp) {
$line[] = '$n' . formatTime($cp - $pr);
if ($diff) $pr = $cp;
if (++$j > $cpsmax) {
if ($cpscnt > $cpsmax) $line[] = '+';
break;
}
}
}
$msg[] = $line;
if (++$lines > 14) {
$player->msgs[] = $msg;
$lines = 0;
$msg = array();
}
}
// add if last batch exists
if (!empty($msg))
$player->msgs[] = $msg;
// display ManiaLink message
display_manialink_multi($player);
// show chat message for TMO & TMS
} else {
$msg = $aseco->formatColors('{#server}> {#error}No Dedimania ' . ($diff ? 'sector' : 'CP') . ' times available');
$aseco->client->query('ChatSendServerMessageToLogin', $msg, $login);
}
} // chat_dedisectms
?>

View File

@ -0,0 +1,32 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
/**
* Chat plugin.
* Displays help for public chat commands.
* Updated by Xymph
*
* Dependencies: none
*/
Aseco::addChatCommand('help', 'Shows all available commands');
Aseco::addChatCommand('helpall', 'Displays help for available commands');
function chat_help($aseco, $command) {
// show normal chat commands on command line
showHelp($command['author'], $aseco->chat_commands, 'chat', false, false);
// add extra explanation?
if ($aseco->settings['help_explanation']) {
$message = $aseco->getChatMessage('HELP_EXPLANATION');
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $command['author']->login);
}
} // chat_help
function chat_helpall($aseco, $command) {
// display normal chat commands in popup with descriptions
showHelp($command['author'], $aseco->chat_commands, 'chat', false, true, 0.3);
} // chat_helpall
?>

View File

@ -0,0 +1,37 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
/**
* Chat plugin.
* Shows last online info.
* Created by Xymph
*
* Dependencies: none
*/
Aseco::addChatCommand('laston', 'Shows when a player was last online');
function chat_laston($aseco, $command) {
$player = $command['author'];
$target = $player;
// get player login or ID
if ($command['params'] != '')
if (!$target = $aseco->getPlayerParam($player, $command['params'], true))
return;
// obtain last online timestamp
$query = 'SELECT UpdatedAt FROM players
WHERE login=' . quotedString($target->login);
$result = mysql_query($query);
$laston = mysql_fetch_row($result);
mysql_free_result($result);
// show chat message (strip seconds off timestamp)
$message = '{#server}> Player {#highlite}' . $target->nickname .
'$z$s{#server} was last online on: {#highlite}' .
preg_replace('/:\d\d$/', '', $laston[0]);
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $player->login);
} // chat_laston
?>

View File

@ -0,0 +1,40 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
/**
* Chat plugin.
* Re-displays last closed multi-page window.
* Created by Xymph
*
* Dependencies: none
*/
Aseco::addChatCommand('lastwin', 'Re-opens the last closed multi-page window');
function chat_lastwin($aseco, $command) {
$player = $command['author'];
$login = $player->login;
if (!isset($player->msgs) || empty($player->msgs)) {
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No multi-page window available!'), $login);
return;
}
// redisplay popup window for TMN
if ($aseco->server->getGame() == 'TMN') {
$player->msgs[0] = 1; // reset page #
// display popup message
if (count($player->msgs) == 2) {
$aseco->client->query('SendDisplayServerMessageToLogin', $login, $player->msgs[1], 'OK', '', 0);
} else { // > 2
$aseco->client->query('SendDisplayServerMessageToLogin', $login, $player->msgs[1], 'Close', 'Next', 0);
}
// redisplay ManiaLink window for TMF
} elseif ($aseco->server->getGame() == 'TMF') {
// display ManiaLink message
display_manialink_multi($player);
}
} // chat_lastwin
?>

View File

@ -0,0 +1,31 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
/**
* Chat plugin.
* Builds a chat message starting with the player's nickname.
* Updated by Xymph
*
* Dependencies: none
*/
Aseco::addChatCommand('me', 'Can be used to express emotions');
function chat_me($aseco, $command) {
$player = $command['author'];
// check if on global mute list
if (in_array($player->login, $aseco->server->mutelist)) {
$message = formatText($aseco->getChatMessage('MUTED'), '/me');
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $player->login);
return;
}
// replace parameters
$message = formatText('$i{1}$z$s$i {#emotic}{2}',
$player->nickname, $command['params']);
// show chat message
$aseco->client->query('ChatSendServerMessage', $aseco->formatColors($message));
} // chat_me
?>

View File

@ -0,0 +1,141 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
/**
* Chat plugin.
* Displays main list of players.
* Updated by Xymph
*
* Dependencies: none
*/
Aseco::addChatCommand('players', 'Displays current list of nicks/logins');
// handles action id's "2001"-"2200" for /stats
Aseco::registerEvent('onPlayerManialinkPageAnswer', 'event_players');
function chat_players($aseco, $command) {
// use only first parameter
$command['params'] = explode(' ', $command['params'], 2);
$player = $command['author'];
$player->playerlist = array();
if ($aseco->server->getGame() == 'TMN') {
$head = 'Players On This Server:' . LF . 'Id {#nick}Nick $g/{#login} Login $g/{#black} $nNation$m'. LF;
$msg = '';
$pid = 1;
$lines = 0;
$player->msgs = array();
$player->msgs[0] = 1;
// create list of players, optionally by (sub)string
foreach ($aseco->server->players->player_list as $pl) {
if (strlen($command['params'][0]) == 0 ||
stripos(stripColors($pl->nickname), $command['params'][0]) !== false ||
stripos($pl->login, $command['params'][0]) !== false) {
$plarr = array();
$plarr['login'] = $pl->login;
$player->playerlist[] = $plarr;
$msg .= '$g' . str_pad($pid, 2, '0', STR_PAD_LEFT) . '. {#black}' .
str_ireplace('$w', '', $pl->nickname) . '$z / ' .
($aseco->isAnyAdmin($pl) ? '{#logina}' : '{#login}') . $pl->login .
'$g / {#black}$n' . $pl->nation . '$m' . LF;
$pid++;
if (++$lines > 9) {
$player->msgs[] = $aseco->formatColors($head . $msg);
$lines = 0;
$msg = '';
}
}
}
// add if last batch exists
if ($msg != '')
$player->msgs[] = $aseco->formatColors($head . $msg);
// 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);
} else { // == 1
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No player(s) found!'), $player->login);
}
} elseif ($aseco->server->getGame() == 'TMF') {
$head = 'Players On This Server:';
$msg = array();
$msg[] = array('Id', '{#nick}Nick $g/{#login} Login', '{#black}Nation');
$pid = 1;
$lines = 0;
$player->msgs = array();
$player->msgs[0] = array(1, $head, array(1.3, 0.1, 0.9, 0.3), array('Icons128x128_1', 'Buddies'));
// create list of players, optionally by (sub)string
foreach ($aseco->server->players->player_list as $pl) {
if (strlen($command['params'][0]) == 0 ||
stripos(stripColors($pl->nickname), $command['params'][0]) !== false ||
stripos($pl->login, $command['params'][0]) !== false) {
$plarr = array();
$plarr['login'] = $pl->login;
$player->playerlist[] = $plarr;
// format nickname & login
$ply = '{#black}' . $pl->nickname . '$z / ' .
($aseco->isAnyAdmin($pl) ? '{#logina}' : '{#login}') . $pl->login;
// add clickable button
if ($aseco->settings['clickable_lists'] && $pid <= 200)
$ply = array($ply, $pid+2000); // action id
$nat = $pl->nation;
if (strlen($nat) > 14)
$nat = mapCountry($nat);
$msg[] = array(str_pad($pid, 2, '0', STR_PAD_LEFT) . '.',
$ply, '{#black}' . $nat);
$pid++;
if (++$lines > 14) {
$player->msgs[] = $msg;
$lines = 0;
$msg = array();
$msg[] = array('Id', '{#nick}Nick $g/{#login} Login', '{#black}Nation');
}
}
}
// add if last batch exists
if (count($msg) > 1)
$player->msgs[] = $msg;
// display ManiaLink message
if (count($player->msgs) > 1) {
display_manialink_multi($player);
} else { // == 1
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No player(s) found!'), $player->login);
}
}
} // chat_players
// called @ onPlayerManialinkPageAnswer
// Handles ManiaLink player responses
// [0]=PlayerUid, [1]=Login, [2]=Answer
function event_players($aseco, $answer) {
// leave actions outside 2001 - 2200 to other handlers
if ($answer[2] >= 2001 && $answer[2] <= 2200) {
// get player
$player = $aseco->server->players->getPlayer($answer[1]);
$target = $player->playerlist[$answer[2]-2001]['login'];
// log clicked command
$aseco->console('player {1} clicked command "/stats {2}"',
$player->login, $target);
// close main window because /stats can take a while
mainwindow_off($aseco, $player->login);
// /stats selected player
$command = array();
$command['author'] = $player;
$command['params'] = $target;
chat_stats($aseco, $command);
}
} // event_players
?>

View File

@ -0,0 +1,217 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
/**
* Chat plugin.
* Displays more lists of players.
* Created by Xymph
*
* Dependencies: none
*/
Aseco::addChatCommand('ranks', 'Displays list of online ranks/nicks');
Aseco::addChatCommand('clans', 'Displays list of online clans/nicks');
Aseco::addChatCommand('topclans', 'Displays top 10 best ranked clans');
function chat_ranks($aseco, $command) {
global $rasp;
$player = $command['author'];
$ranks = array();
// sort players by rank, insuring rankless are last by sorting on INT_MAX
foreach ($aseco->server->players->player_list as $pl) {
$rank = $rasp->getRank($pl->login);
$ranks[$pl->login] = $rank != 'None' ?
(integer) preg_replace('/\/.*/', '', $rank) :
PHP_INT_MAX;
}
asort($ranks);
// compile the message
if ($aseco->server->getGame() == 'TMN') {
$head = 'Online Ranks ({#login}rank $g/{#nick} nick$g):' . LF;
$msg = '';
$lines = 0;
$player->msgs = array();
$player->msgs[0] = 1;
foreach ($ranks as $pl => $rk) {
$play = $aseco->server->players->getPlayer($pl);
$msg .= '$z{#login}' . ($rk != PHP_INT_MAX ? $rk : '{#grey}<none>') . '$g / {#black}' . $play->nickname . LF;
if (++$lines > 9) {
$player->msgs[] = $aseco->formatColors($head . $msg);
$lines = 0;
$msg = '';
}
}
// add if last batch exists
if ($msg != '')
$player->msgs[] = $aseco->formatColors($head . $msg);
// display popup message
if (count($player->msgs) == 2) {
$aseco->client->query('SendDisplayServerMessageToLogin', $player->login, $player->msgs[1], 'OK', '', 0);
} else { // > 2
$aseco->client->query('SendDisplayServerMessageToLogin', $player->login, $player->msgs[1], 'Close', 'Next', 0);
}
} elseif ($aseco->server->getGame() == 'TMF') {
$head = 'Online Ranks ({#login}rank $g/{#nick} nick$g):';
$msg = array();
$lines = 0;
$player->msgs = array();
$player->msgs[0] = array(1, $head, array(0.8, 0.15, 0.65), array('Icons128x128_1', 'Buddies'));
foreach ($ranks as $pl => $rk) {
$play = $aseco->server->players->getPlayer($pl);
$msg[] = array('{#login}' . ($rk != PHP_INT_MAX ? $rk : '{#grey}<none>'),
'{#black}' . $play->nickname);
if (++$lines > 14) {
$player->msgs[] = $msg;
$lines = 0;
$msg = array();
}
}
// add if last batch exists
if (!empty($msg))
$player->msgs[] = $msg;
// display ManiaLink message
display_manialink_multi($player);
}
} // chat_ranks
function chat_clans($aseco, $command) {
$player = $command['author'];
$clans = array();
// sort players by clan, insuring clanless are last by sorting on chr(255)
foreach ($aseco->server->players->player_list as $pl) {
$clans[$pl->login] = $pl->teamname ? $pl->teamname : chr(255);
}
asort($clans);
// compile the message
if ($aseco->server->getGame() == 'TMN') {
$head = 'Online Clans ({#login}clan $g/{#nick} nick$g):' . LF;
$msg = '';
$lines = 0;
$player->msgs = array();
$player->msgs[0] = 1;
foreach ($clans as $pl => $tm) {
$play = $aseco->server->players->getPlayer($pl);
$msg .= '$z{#login}' . ($tm != chr(255) ? $tm : '{#grey}<none>') . '$z / {#black}' . $play->nickname . LF;
if (++$lines > 9) {
$player->msgs[] = $aseco->formatColors($head . $msg);
$lines = 0;
$msg = '';
}
}
// add if last batch exists
if ($msg != '')
$player->msgs[] = $aseco->formatColors($head . $msg);
// display popup message
if (count($player->msgs) == 2) {
$aseco->client->query('SendDisplayServerMessageToLogin', $player->login, $player->msgs[1], 'OK', '', 0);
} else { // > 2
$aseco->client->query('SendDisplayServerMessageToLogin', $player->login, $player->msgs[1], 'Close', 'Next', 0);
}
} elseif ($aseco->server->getGame() == 'TMF') {
$head = 'Online Clans ({#login}clan $g/{#nick} nick$g):';
$msg = array();
$lines = 0;
$player->msgs = array();
$player->msgs[0] = array(1, $head, array(1.3, 0.65, 0.65), array('Icons128x128_1', 'Buddies'));
foreach ($clans as $pl => $tm) {
$play = $aseco->server->players->getPlayer($pl);
$msg[] = array('{#login}' . ($tm != chr(255) ? $tm : '{#grey}<none>'),
'{#black}' . $play->nickname);
if (++$lines > 14) {
$player->msgs[] = $msg;
$lines = 0;
$msg = array();
}
}
// add if last batch exists
if (!empty($msg))
$player->msgs[] = $msg;
// display ManiaLink message
display_manialink_multi($player);
}
} // chat_clans
function chat_topclans($aseco, $command) {
$player = $command['author'];
if ($aseco->server->getGame() == 'TMN') {
$msg = 'Current TOP 10 Clans $n(min. ' . $aseco->settings['topclans_minplayers'] . ' players)$m:';
$top = 10;
$bgn = '{#black}'; // clanname begin
} elseif ($aseco->server->getGame() == 'TMF') {
$header = 'Current TOP 10 Clans $n(min. ' . $aseco->settings['topclans_minplayers'] . ' players)$m:';
$top = 10;
$bgn = '{#black}'; // clanname begin
} else { // TMS/TMO
$msg = '{#server}> Current TOP 4 Clans $n(min. ' . $aseco->settings['topclans_minplayers'] . ' players)$m:{#highlite}';
$top = 4;
$bgn = '{#highlite}';
$end = '{#highlite}';
}
// find best ranked
$query = 'SELECT TeamName, count, teamrank FROM (
SELECT TeamName, COUNT(avg) AS count, SUM(avg)/COUNT(avg) AS teamrank
FROM players,rs_rank WHERE players.id=rs_rank.playerid
GROUP BY TeamName) as sub
WHERE sub.count>=' . $aseco->settings['topclans_minplayers'] . '
ORDER BY sub.teamrank LIMIT ' . $top;
$res = mysql_query($query);
if (mysql_num_rows($res) == 0) {
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No clan(s) found!'), $player->login);
mysql_free_result($res);
return false;
}
// compile the message with sorted clans
if ($aseco->server->getGame() == 'TMN') {
$i = 1;
while ($row = mysql_fetch_object($res)) {
$msg .= LF . $i . '. ' . $bgn . str_pad($row->TeamName . '$z $n(' . $row->count . ')$m', 35)
. ' - ' . sprintf("%4.1F", $row->teamrank/10000);
$i++;
}
// display popup message
$aseco->client->query('SendDisplayServerMessageToLogin', $player->login, $aseco->formatColors($msg), 'OK', '', 0);
} elseif ($aseco->server->getGame() == 'TMF') {
$i = 1;
while ($row = mysql_fetch_object($res)) {
$msg[] = array($i . '.',
$bgn . $row->TeamName . '$z $n(' . $row->count . ')$m',
sprintf("%4.1F", $row->teamrank/10000));
$i++;
}
// display ManiaLink message
display_manialink($player->login, $header, array('BgRaceScore2', 'Podium'), $msg, array(0.95, 0.1, 0.7, 0.15), 'OK');
} else { // TMS/TMO
$i = 1;
while ($row = mysql_fetch_object($res)) {
$msg .= LF . $i . '. ' . $bgn . str_pad(stripColors($row->TeamName)
. $end . ' $n(' . $row->count . ')$m', 25)
. ' - ' . sprintf("%4.1F", $row->teamrank/10000);
$i++;
}
// show chat message
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($msg), $player->login);
}
mysql_free_result($res);
} // chat_topclans
?>

View File

@ -0,0 +1,210 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
/**
* Chat plugin.
* Displays all records of the current track.
* Updated by Xymph
*
* Dependencies: none
*/
Aseco::addChatCommand('recs', 'Displays all records on current track');
function chat_recs($aseco, $command) {
$player = $command['author'];
$login = $player->login;
// split params into array
$arglist = explode(' ', strtolower(preg_replace('/ +/', ' ', $command['params'])));
// process optional relations commands
if ($arglist[0] == 'help') {
if ($aseco->server->getGame() == 'TMN') {
$help = '{#black}/recs <option>$g shows local records and relations' . LF;
$help .= ' - {#black}help$g, displays this help information' . LF;
$help .= ' - {#black}pb$g, your personal best on current track' . LF;
$help .= ' - {#black}new$g, newly driven records' . LF;
$help .= ' - {#black}live$g, records of online players' . LF;
$help .= ' - {#black}first$g, first ranked record on current track' . LF;
$help .= ' - {#black}last$g, last ranked record on current track' . LF;
$help .= ' - {#black}next$g, next better ranked record to beat' . LF;
$help .= ' - {#black}diff$g, your difference to first ranked record' . LF;
$help .= ' - {#black}range$g, difference first to last ranked record' . LF;
$help .= LF . 'Without an option, the normal records list is displayed.';
// display popup message
$aseco->client->query('SendDisplayServerMessageToLogin', $login, $aseco->formatColors($help), 'OK', '', 0);
} elseif ($aseco->server->getGame() == 'TMF') {
$header = '{#black}/recs <option>$g shows local records and relations:';
$help = array();
$help[] = array('...', '{#black}help',
'Displays this help information');
$help[] = array('...', '{#black}pb',
'Shows your personal best on current track');
$help[] = array('...', '{#black}new',
'Shows newly driven records');
$help[] = array('...', '{#black}live',
'Shows records of online players');
$help[] = array('...', '{#black}first',
'Shows first ranked record on current track');
$help[] = array('...', '{#black}last',
'Shows last ranked record on current track');
$help[] = array('...', '{#black}next',
'Shows next better ranked record to beat');
$help[] = array('...', '{#black}diff',
'Shows your difference to first ranked record');
$help[] = array('...', '{#black}range',
'Shows difference first to last ranked record');
$help[] = array();
$help[] = array('Without an option, the normal records list is displayed.');
// display ManiaLink message
display_manialink($login, $header, array('Icons64x64_1', 'TrackInfo', -0.01), $help, array(1.1, 0.05, 0.3, 0.75), 'OK');
}
return;
}
elseif ($arglist[0] == 'pb') {
chat_pb($aseco, $command);
return;
}
elseif ($arglist[0] == 'new') {
chat_newrecs($aseco, $command);
return;
}
elseif ($arglist[0] == 'live') {
chat_liverecs($aseco, $command);
return;
}
elseif ($arglist[0] == 'first') {
chat_firstrec($aseco, $command);
return;
}
elseif ($arglist[0] == 'last') {
chat_lastrec($aseco, $command);
return;
}
elseif ($arglist[0] == 'next') {
chat_nextrec($aseco, $command);
return;
}
elseif ($arglist[0] == 'diff') {
chat_diffrec($aseco, $command);
return;
}
elseif ($arglist[0] == 'range') {
chat_recrange($aseco, $command);
return;
}
// check for relay server
if ($aseco->server->isrelay) {
$message = formatText($aseco->getChatMessage('NOTONRELAY'));
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
return;
}
if (!$total = $aseco->server->records->count()) {
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No records found!'), $login);
return;
}
// display popup window for TMN
if ($aseco->server->getGame() == 'TMN') {
$head = 'Current TOP ' . $aseco->server->records->max . ' Local Records:' . LF;
$msg = '';
$lines = 0;
$player->msgs = array();
$player->msgs[0] = 1;
// create list of records
for ($i = 0; $i < $total; $i++) {
$cur_record = $aseco->server->records->getRecord($i);
$nick = $cur_record->player->nickname;
if (!$aseco->settings['lists_colornicks'])
$nick = stripColors($nick);
$msg .= str_pad($i+1, 2, '0', STR_PAD_LEFT) . '. {#black}'
. str_pad($nick, 20) . '$z - '
. ($cur_record->new ? '{#black}' : '')
. formatTime($cur_record->score) . LF;
if (++$lines > 9) {
$player->msgs[] = $aseco->formatColors($head . $msg);
$lines = 0;
$msg = '';
}
}
// add if last batch exists
if ($msg != '')
$player->msgs[] = $aseco->formatColors($head . $msg);
// display popup message
if (count($player->msgs) == 2) {
$aseco->client->query('SendDisplayServerMessageToLogin', $login, $player->msgs[1], 'OK', '', 0);
} else { // > 2
$aseco->client->query('SendDisplayServerMessageToLogin', $login, $player->msgs[1], 'Close', 'Next', 0);
}
// display ManiaLink window for TMF
} elseif ($aseco->server->getGame() == 'TMF') {
$head = 'Current TOP ' . $aseco->server->records->max . ' Local Records:';
$msg = array();
$lines = 0;
$player->msgs = array();
// reserve extra width for $w tags
$extra = ($aseco->settings['lists_colornicks'] ? 0.2 : 0);
if ($aseco->settings['show_rec_logins'])
$player->msgs[0] = array(1, $head, array(1.2+$extra, 0.1, 0.45+$extra, 0.4, 0.25), array('BgRaceScore2', 'Podium'));
else
$player->msgs[0] = array(1, $head, array(0.8+$extra, 0.1, 0.45+$extra, 0.25), array('BgRaceScore2', 'Podium'));
// create list of records
for ($i = 0; $i < $total; $i++) {
$cur_record = $aseco->server->records->getRecord($i);
$nick = $cur_record->player->nickname;
if (!$aseco->settings['lists_colornicks'])
$nick = stripColors($nick);
if ($aseco->settings['show_rec_logins']) {
$msg[] = array(str_pad($i+1, 2, '0', STR_PAD_LEFT) . '.',
'{#black}' . $nick,
'{#login}' . $cur_record->player->login,
($cur_record->new ? '{#black}' : '') .
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$cur_record->score : formatTime($cur_record->score)));
} else {
$msg[] = array(str_pad($i+1, 2, '0', STR_PAD_LEFT) . '.',
'{#black}' . $nick,
($cur_record->new ? '{#black}' : '') .
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$cur_record->score : formatTime($cur_record->score)));
}
if (++$lines > 14) {
$player->msgs[] = $msg;
$lines = 0;
$msg = array();
}
}
// add if last batch exists
if (!empty($msg))
$player->msgs[] = $msg;
// display ManiaLink message
display_manialink_multi($player);
// show chat message for TMO & TMS
} else {
$top = 4;
$msg = $aseco->formatColors("{#server}> Current TOP $top Local Records:{#highlite}");
// create list of records
$total = ($total <= $top ? $total : $top);
for ($i = 0; $i < $total; $i++) {
$cur_record = $aseco->server->records->getRecord($i);
$msg .= LF . ($i+1) . '. ' . str_pad(stripColors($cur_record->player->nickname), 15)
. ' - ' . formatTime($cur_record->score);
}
// show chat message
$aseco->client->query('ChatSendServerMessageToLogin', $msg, $login);
}
} // chat_recs
?>

View File

@ -0,0 +1,888 @@
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
/**
* Chat plugin.
* Shows new/online records of the current track, and displays related lists.
* Created by Xymph
*
* Dependencies: used by chat.stats.php, plugin.rasp_jukebox.php
*/
if (!INHIBIT_RECCMDS) {
Aseco::addChatCommand('newrecs', 'Shows newly driven records');
Aseco::addChatCommand('liverecs', 'Shows records of online players');
}
Aseco::addChatCommand('best', 'Displays your best records');
Aseco::addChatCommand('worst', 'Displays your worst records');
Aseco::addChatCommand('summary', 'Shows summary of all your records');
Aseco::addChatCommand('topsums', 'Displays top 100 of top-3 record holders');
Aseco::addChatCommand('toprecs', 'Displays top 100 ranked records holders');
/*
* Universal function to generate list of records for current track.
* Called by chat_newrecs, chat_liverecs, endRace & beginRace (aseco.php).
* Show to a player if $login defined, otherwise show to all players.
* $mode = 0 (only new), 1 (top-8 & online players at start of track),
* 2 (top-6 & online during track), 3 (top-8 & new at end of track)
* In modes 1/2/3 the last ranked record is also shown
* top-8 is configurable via 'show_min_recs'; top-6 is show_min_recs-2
*/
function show_trackrecs($aseco, $login, $mode, $window) {
$records = '$n'; // use narrow font
// check for records
if (($total = $aseco->server->records->count()) == 0) {
$totalnew = -1;
} else {
// check whether to show range
if ($aseco->settings['show_recs_range']) {
// get the first & last ranked records
$first = $aseco->server->records->getRecord(0);
$last = $aseco->server->records->getRecord($total-1);
// compute difference between records
if ($aseco->server->gameinfo->mode != Gameinfo::STNT) {
$diff = $last->score - $first->score;
$sec = floor($diff/1000);
$hun = ($diff - ($sec * 1000)) / 10;
} else { // Stunts
$diff = $first->score - $last->score;
}
}
// get list of online players
$players = array();
foreach ($aseco->server->players->player_list as $pl) {
$players[] = $pl->login;
}
// collect new records and records by online players
$totalnew = 0;
// go through each record
for ($i = 0; $i < $total; $i++) {
$cur_record = $aseco->server->records->getRecord($i);
// if the record is new then display it
if ($cur_record->new) {
$totalnew++;
$record_msg = formatText($aseco->getChatMessage('RANKING_RECORD_NEW_ON'),
$i+1,
stripColors($cur_record->player->nickname),
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$cur_record->score : formatTime($cur_record->score)));
// always show new record
$records .= $record_msg;
} else {
// check if player is online
if (in_array($cur_record->player->login, $players)) {
$record_msg = formatText($aseco->getChatMessage('RANKING_RECORD_ON'),
$i+1,
stripColors($cur_record->player->nickname),
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$cur_record->score : formatTime($cur_record->score)));
// check if last ranked record
if ($mode != 0 && $i == $total-1) {
$records .= $record_msg;
}
// check if always show (start of/during track)
elseif ($mode == 1 || $mode == 2) {
$records .= $record_msg;
}
else {
// show record if < show_min_recs (end of track)
if ($mode == 3 && $i < $aseco->settings['show_min_recs']) {
$records .= $record_msg;
}
}
} else {
$record_msg = formatText($aseco->getChatMessage('RANKING_RECORD'),
$i+1,
stripColors($cur_record->player->nickname),
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$cur_record->score : formatTime($cur_record->score)));
// check if last ranked record
if ($mode != 0 && $i == $total-1) {
$records .= $record_msg;
}
// show offline record if < show_min_recs-2 (during track)
elseif (($mode == 2 && $i < $aseco->settings['show_min_recs']-2) ||
// show offline record if < show_min_recs (start/end of track)
(($mode == 1 || $mode == 3) && $i < $aseco->settings['show_min_recs'])) {
$records .= $record_msg;
}
}
}
}
}
// define wording of the ranking message
switch ($mode) {
case 0:
$timing = 'during';
break;
case 1:
$timing = 'before';
break;
case 2:
$timing = 'during';
break;
case 3:
$timing = 'after';
break;
}
$name = stripColors($aseco->server->challenge->name);
if ($aseco->server->getGame() == 'TMF' &&
isset($aseco->server->challenge->tmx->name) && $aseco->server->challenge->tmx->name != '')
$name = '$l[http://' . $aseco->server->challenge->tmx->prefix .
'.tm-exchange.com/main.aspx?action=trackshow&id=' .
$aseco->server->challenge->tmx->id . ']' . $name . '$l';
// define the ranking message
if ($totalnew > 0) {
$message = formatText($aseco->getChatMessage('RANKING_NEW'),
$name, $timing, $totalnew);
}
elseif ($totalnew == 0 && $records != '$n') {
// check whether to show range
if ($aseco->settings['show_recs_range']) {
$message = formatText($aseco->getChatMessage('RANKING_RANGE'),
$name, $timing,
($aseco->server->gameinfo->mode == Gameinfo::STNT ?
$diff : sprintf("%d.%02d", $sec, $hun)));
} else {
$message = formatText($aseco->getChatMessage('RANKING'),
$name, $timing);
}
}
elseif ($totalnew == 0 && $records == '$n') {
$message = formatText($aseco->getChatMessage('RANKING_NONEW'),
$name, $timing);
}
else { // $totalnew == -1
$message = formatText($aseco->getChatMessage('RANKING_NONE'),
$name, $timing);
}
// append the records if any
if ($records != '$n') {
$records = substr($records, 0, strlen($records)-2); // strip trailing ", "
$message .= LF . $records;
}
// show to player or all
if ($login) {
// strip 1 leading '>' to indicate a player message instead of system-wide
$message = str_replace('{#server}>> ', '{#server}> ', $message);
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
} else {
if (($window & 4) == 4 && function_exists('send_window_message'))
send_window_message($aseco, $message, ($mode == 3));
else
$aseco->client->query('ChatSendServerMessage', $aseco->formatColors($message));
}
} // show_trackrecs
function chat_newrecs($aseco, $command) {
// check for relay server
if ($aseco->server->isrelay) {
$message = formatText($aseco->getChatMessage('NOTONRELAY'));
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $command['author']->login);
return;
}
// show only newly driven records
show_trackrecs($aseco, $command['author']->login, 0, 0);
} // chat_newrecs
function chat_liverecs($aseco, $command) {
// check for relay server
if ($aseco->server->isrelay) {
$message = formatText($aseco->getChatMessage('NOTONRELAY'));
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $command['author']->login);
return;
}
// show online & show_min_recs-2 records
show_trackrecs($aseco, $command['author']->login, 2, 0);
} // chat_liverecs
function chat_best($aseco, $command) {
// check for relay server
if ($aseco->server->isrelay) {
$message = formatText($aseco->getChatMessage('NOTONRELAY'));
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $command['author']->login);
return;
}
// display player records, best first
disp_recs($aseco, $command, true);
} // chat_best
function chat_worst($aseco, $command) {
// check for relay server
if ($aseco->server->isrelay) {
$message = formatText($aseco->getChatMessage('NOTONRELAY'));
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $command['author']->login);
return;
}
// display player records, worst first
disp_recs($aseco, $command, false);
} // chat_worst
/*
* Universal function to get list of all records for a player.
* Called by disp_recs (chat_best & chat_worst), chat_summary and
* chat_stats (in chat.stats.php).
*/
function get_recs($pid) {
global $aseco;
// get player's record for each track
$list = array();
$order = ($aseco->server->gameinfo->mode == Gameinfo::STNT ? 'DESC' : 'ASC');
$query = 'SELECT Uid,PlayerId FROM records r LEFT JOIN challenges c ON (r.ChallengeId=c.Id)
WHERE Uid IS NOT NULL ORDER BY ChallengeId ASC,Score ' . $order . ',Date ASC';
$result = mysql_query($query);
$last = '';
while ($row = mysql_fetch_object($result)) {
// check for new track & reset rank
if ($last != $row->Uid) {
$last = $row->Uid;
$pos = 1;
}
if (isset($list[$row->Uid]))
continue;
// store player's challenges & records
if ($row->PlayerId == $pid) {
$list[$row->Uid] = $pos;
continue;
}
$pos++;
}
mysql_free_result($result);
// return list
return $list;
} // get_recs
function disp_recs($aseco, $command, $order) {
global $jb_buffer;
$player = $command['author'];
$target = $player;
// check for optional login parameter if any admin
if ($command['params'] != '' && $aseco->allowAbility($player, 'chat_bestworst'))
if (!$target = $aseco->getPlayerParam($player, $command['params'], true))
return;
// check for records
if ($list = get_recs($target->id)) {
// sort for best or worst records
$order ? asort($list) : arsort($list);
// get new/cached list of tracks
$newlist = getChallengesCache($aseco); // from rasp.funcs.php
// create list of records
$player->tracklist = array();
if ($aseco->server->getGame() == 'TMN') {
$head = ($order ? 'Best' : 'Worst') . ' Records for ' . $target->nickname
. '$z:' . LF . 'Id Rec Name' . LF;
$recs = '';
$tid = 1;
$lines = 0;
$player->msgs = array();
$player->msgs[0] = 1;
foreach ($list as $uid => $pos) {
// does the uid exist in the current server track list?
if (array_key_exists($uid, $newlist)) {
$row = $newlist[$uid];
// store track in player object for jukeboxing
$trkarr = array();
$trkarr['name'] = $row['Name'];
$trkarr['environment'] = $row['Environnement'];
$trkarr['filename'] = $row['FileName'];
$trkarr['uid'] = $row['UId'];
$player->tracklist[] = $trkarr;
// format track name
$trackname = $row['Name'];
if (!$aseco->settings['lists_colortracks'])
$trackname = stripColors($trackname);
// grey out if in history
if (in_array($row['UId'], $jb_buffer))
$trackname = '{#grey}' . stripColors($trackname);
else
$trackname = '{#black}' . $trackname;
$recs .= '$z' . str_pad($tid, 3, '0', STR_PAD_LEFT) . '. '
. str_pad($pos, 2, '0', STR_PAD_LEFT) . '. '
. $trackname . LF;
$tid++;
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);
} else { // == 1
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No records found!'), $player->login);
}
} elseif ($aseco->server->getGame() == 'TMF') {
$envids = array('Stadium' => 11, 'Alpine' => 12, 'Bay' => 13, 'Coast' => 14, 'Island' => 15, 'Rally' => 16, 'Speed' => 17);
$head = ($order ? 'Best' : 'Worst') . ' Records for ' . str_ireplace('$w', '', $target->nickname) . '$z:';
$recs = array();
if ($aseco->server->packmask != 'Stadium')
$recs[] = array('Id', 'Rec', 'Name', 'Author', 'Env');
else
$recs[] = array('Id', 'Rec', 'Name', 'Author');
$tid = 1;
$lines = 0;
$player->msgs = array();
// no extra width for $w tags due to nickname width in header
if ($aseco->server->packmask != 'Stadium')
$player->msgs[0] = array(1, $head, array(1.59, 0.12, 0.1, 0.8, 0.4, 0.17), array('Icons128x128_1', 'NewTrack', 0.02));
else
$player->msgs[0] = array(1, $head, array(1.42, 0.12, 0.1, 0.8, 0.4), array('Icons128x128_1', 'NewTrack', 0.02));
foreach ($list as $uid => $pos) {
// does the uid exist in the current server track list?
if (array_key_exists($uid, $newlist)) {
$row = $newlist[$uid];
// store track in player object for jukeboxing
$trkarr = array();
$trkarr['name'] = $row['Name'];
$trkarr['author'] = $row['Author'];
$trkarr['environment'] = $row['Environnement'];
$trkarr['filename'] = $row['FileName'];
$trkarr['uid'] = $row['UId'];
$player->tracklist[] = $trkarr;
// format track name
$trackname = $row['Name'];
if (!$aseco->settings['lists_colortracks'])
$trackname = stripColors($trackname);
// grey out if in history
if (in_array($row['UId'], $jb_buffer))
$trackname = '{#grey}' . stripColors($trackname);
else {
$trackname = '{#black}' . $trackname;
// add clickable button
if ($aseco->settings['clickable_lists'] && $tid <= 1900)
$trackname = array($trackname, $tid+100); // action id
}
// format author name
$trackauthor = $row['Author'];
// add clickable button
if ($aseco->settings['clickable_lists'] && $tid <= 1900)
$trackauthor = array($trackauthor, -100-$tid); // action id
// format env name
$trackenv = $row['Environnement'];
// add clickable button
if ($aseco->settings['clickable_lists'])
$trackenv = array($trackenv, $envids[$row['Environnement']]); // action id
if ($aseco->server->packmask != 'Stadium')
$recs[] = array(str_pad($tid, 3, '0', STR_PAD_LEFT) . '.',
str_pad($pos, 2, '0', STR_PAD_LEFT) . '.',
$trackname, $trackauthor, $trackenv);
else
$recs[] = array(str_pad($tid, 3, '0', STR_PAD_LEFT) . '.',
str_pad($pos, 2, '0', STR_PAD_LEFT) . '.',
$trackname, $trackauthor);
$tid++;
if (++$lines > 14) {
$player->msgs[] = $recs;
$lines = 0;
$recs = array();
if ($aseco->server->packmask != 'Stadium')
$recs[] = array('Id', 'Rec', 'Name', 'Author', 'Env');
else
$recs[] = array('Id', 'Rec', 'Name', 'Author');
}
}
}
// add if last batch exists
if (count($recs) > 1)
$player->msgs[] = $recs;
if (count($player->msgs) > 1) {
// display ManiaLink message
display_manialink_multi($player);
} else {
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No records found!'), $player->login);
}
}
} else {
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No records found!'), $player->login);
}
} // disp_recs
function chat_summary($aseco, $command) {
global $maxrecs;
$player = $command['author'];
$target = $player;
// check for relay server
if ($aseco->server->isrelay) {
$message = formatText($aseco->getChatMessage('NOTONRELAY'));
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $player->login);
return;
}
// check for optional login parameter if any admin
if ($command['params'] != '' && $aseco->allowAbility($player, 'chat_summary'))
if (!$target = $aseco->getPlayerParam($player, $command['params'], true))
return;
// check for records
if ($list = get_recs($target->id)) {
// sort for best records
asort($list);
// get new/cached list of tracks
$newlist = getChallengesCache($aseco); // from rasp.funcs.php
// collect summary of first 3 records and count total
$show = 3;
$message = '';
$total = 0;
$cntrec = 0;
$currec = 0;
foreach ($list as $uid => $rec) {
// stop upon unranked record
if ($rec > $maxrecs) break;
// check if rec is for existing track
if (array_key_exists($uid, $newlist)) {
// count total ranked records
$total++;
// check for first 3 records
if ($show > 0) {
// check for same record
if ($rec == $currec) {
$cntrec++;
} else {
// collect next record sum
if ($currec > 0) {
$message .= formatText($aseco->getChatMessage('SUM_ENTRY'),
$cntrec, ($cntrec > 1 ? 's' : ''), $currec);
$show--;
}
// count first occurance of next record
$cntrec = 1;
$currec = $rec;
}
}
}
}
// if less than 3 records, add the last one found
if ($show > 0 && $currec > 0) {
$message .= formatText($aseco->getChatMessage('SUM_ENTRY'),
$cntrec, ($cntrec > 1 ? 's' : ''), $currec);
$show--;
}
if ($message) {
// define text version of number of top-3 records
switch (3-$show) {
case 1:
$show = 'one';
break;
case 2:
$show = 'two';
break;
case 3:
$show = 'three';
break;
}
// show chat message
$message = substr($message, 0, strlen($message)-2); // strip trailing ", "
$message = formatText($aseco->getChatMessage('SUMMARY'),
$target->nickname,
$total, ($total > 1 ? 's' : ''),
$show)
. $message;
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $player->login);
} else {
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No ranked records found!'), $player->login);
}
} else {
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No ranked records found!'), $player->login);
}
} // chat_summary
// define sorting function for descending top-3's
function top3_compare($a, $b) {
// compare #1 records
if ($a[0] < $b[0])
return 1;
elseif ($a[0] > $b[0])
return -1;
// compare #2 records
if ($a[1] < $b[1])
return 1;
elseif ($a[1] > $b[1])
return -1;
// compare #3 records
if ($a[2] < $b[2])
return 1;
elseif ($a[2] > $b[2])
return -1;
// all equal
return 0;
} // top3_compare
function chat_topsums($aseco, $command) {
$player = $command['author'];
// check for relay server
if ($aseco->server->isrelay) {
$message = formatText($aseco->getChatMessage('NOTONRELAY'));
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $player->login);
return;
}
if ($aseco->server->getGame() == 'TMN') {
$head = 'TOP 100 of Top-3 Record Holders:';
$top = 100;
$bgn = '{#black}'; // nickname begin
$end = '$z'; // ... & end colors
} elseif ($aseco->server->getGame() == 'TMF') {
$head = 'TOP 100 of Top-3 Record Holders:';
$top = 100;
$bgn = '{#black}'; // nickname begin
} else {
$head = '{#server}> TOP 4 of Top-3 Record Holders:{#highlite}';
$top = 4;
$bgn = '{#highlite}';
$end = '{#highlite}';
}
// get new/cached list of tracks
$newlist = getChallengesCache($aseco); // from rasp.funcs.php
// get current list of track IDs
$query = 'SELECT Id,Uid FROM challenges';
$result = mysql_query($query);
$tidlist = array();
while ($row = mysql_fetch_object($result)) {
if (array_key_exists($row->Uid, $newlist))
$tidlist[] = $row->Id;
}
// collect top-3 records
$recs = array();
$order = ($aseco->server->gameinfo->mode == Gameinfo::STNT ? 'DESC' : 'ASC');
foreach ($tidlist as $tid) {
// get top-3 ranked records on this track
$query = 'SELECT login FROM players,records
WHERE players.id=records.playerid AND
challengeid=' . $tid . '
ORDER BY score ' . $order . ',date ASC LIMIT 3';
$result = mysql_query($query);
// tally top-3 record totals by login
if ($row = mysql_fetch_array($result)) {
if (isset($recs[$row[0]])) {
$recs[$row[0]][0]++;
} else {
$recs[$row[0]] = array(1,0,0);
}
if ($row = mysql_fetch_array($result)) {
if (isset($recs[$row[0]])) {
$recs[$row[0]][1]++;
} else {
$recs[$row[0]] = array(0,1,0);
}
if ($row = mysql_fetch_array($result)) {
if (isset($recs[$row[0]])) {
$recs[$row[0]][2]++;
} else {
$recs[$row[0]] = array(0,0,1);
}
}
}
}
mysql_free_result($result);
}
if (empty($recs)) {
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No players with ranked records found!'), $player->login);
return;
}
// sort players by #1, #2 & #3 records
uasort($recs, 'top3_compare');
if ($aseco->server->getGame() == 'TMN') {
$records = '';
$i = 1;
$lines = 0;
$player->msgs = array();
$player->msgs[0] = 1;
foreach ($recs as $login => $top3) {
// obtain nickname for this login
$nick = $aseco->getPlayerNick($login);
if (!$aseco->settings['lists_colornicks'])
$nick = stripColors($nick);
$records .= LF . str_pad($i, 2, '0', STR_PAD_LEFT) . '. ' . $bgn
. str_pad($nick, 20) . $end . ' - '
. str_pad($top3[0], 3, ' ', STR_PAD_LEFT) . ' / '
. str_pad($top3[1], 3, ' ', STR_PAD_LEFT) . ' / '
. str_pad($top3[2], 3, ' ', STR_PAD_LEFT);
$i++;
if (++$lines > 9) {
$player->msgs[] = $aseco->formatColors($head . $records);
$lines = 0;
$records = '';
}
if ($i > $top) break;
}
// add if last batch exists
if ($records != '')
$player->msgs[] = $aseco->formatColors($head . $records);
// display popup message
if (count($player->msgs) == 2) {
$aseco->client->query('SendDisplayServerMessageToLogin', $player->login, $player->msgs[1], 'OK', '', 0);
} else { // > 2
$aseco->client->query('SendDisplayServerMessageToLogin', $player->login, $player->msgs[1], 'Close', 'Next', 0);
}
} elseif ($aseco->server->getGame() == 'TMF') {
$records = 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.85+$extra, 0.1, 0.45+$extra, 0.3), array('BgRaceScore2', 'LadderRank'));
foreach ($recs as $login => $top3) {
// obtain nickname for this login
$nick = $aseco->getPlayerNick($login);
if (!$aseco->settings['lists_colornicks'])
$nick = stripColors($nick);
$records[] = array(str_pad($i, 2, '0', STR_PAD_LEFT) . '.',
$bgn . $nick,
str_pad($top3[0], 3, ' ', STR_PAD_LEFT) . ' / '
. str_pad($top3[1], 3, ' ', STR_PAD_LEFT) . ' / '
. str_pad($top3[2], 3, ' ', STR_PAD_LEFT));
$i++;
if (++$lines > 14) {
$player->msgs[] = $records;
$lines = 0;
$records = array();
}
if ($i > $top) break;
}
// add if last batch exists
if (!empty($records))
$player->msgs[] = $records;
// display ManiaLink message
display_manialink_multi($player);
} else { // TMS/TMO
$records = $head;
$i = 1;
foreach ($recs as $login => $top3) {
// obtain nickname for this login
$nick = stripColors($aseco->getPlayerNick($login));
$records .= LF . $i . '. ' . $bgn
. str_pad($nick, 15) . $end . ' - '
. str_pad($top3[0], 3, ' ', STR_PAD_LEFT) . ' / '
. str_pad($top3[1], 3, ' ', STR_PAD_LEFT) . ' / '
. str_pad($top3[2], 3, ' ', STR_PAD_LEFT);
$i++;
if ($i > $top) break;
}
// show chat message
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($records), $player->login);
}
} // chat_topsums
function chat_toprecs($aseco, $command) {
global $maxrecs;
$player = $command['author'];
// check for relay server
if ($aseco->server->isrelay) {
$message = formatText($aseco->getChatMessage('NOTONRELAY'));
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $player->login);
return;
}
if ($aseco->server->getGame() == 'TMN') {
$head = 'TOP 100 Ranked Record Holders:';
$top = 100;
$bgn = '{#black}'; // nickname begin
$end = '$z'; // ... & end colors
} elseif ($aseco->server->getGame() == 'TMF') {
$head = 'TOP 100 Ranked Record Holders:';
$top = 100;
$bgn = '{#black}'; // nickname begin
} else {
$head = '{#server}> TOP 4 Ranked Record Holders:{#highlite}';
$top = 4;
$bgn = '{#highlite}';
$end = '{#highlite}';
}
// get new/cached list of tracks
$newlist = getChallengesCache($aseco); // from rasp.funcs.php
// get current list of track IDs
$query = 'SELECT Id,Uid FROM challenges';
$result = mysql_query($query);
$tidlist = array();
while ($row = mysql_fetch_object($result)) {
if (array_key_exists($row->Uid, $newlist))
$tidlist[] = $row->Id;
}
// collect record totals
$recs = array();
$order = ($aseco->server->gameinfo->mode == Gameinfo::STNT ? 'DESC' : 'ASC');
foreach ($tidlist as $tid) {
// get ranked records on this track
$query = 'SELECT login FROM players,records
WHERE players.id=records.playerid AND
challengeid=' . $tid . '
ORDER BY score ' . $order . ',date ASC LIMIT ' . $maxrecs;
$result = mysql_query($query);
// update record totals by login
while ($row = mysql_fetch_array($result)) {
if (isset($recs[$row[0]]))
$recs[$row[0]]++;
else
$recs[$row[0]] = 1;
}
mysql_free_result($result);
}
if (empty($recs)) {
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors('{#server}> {#error}No players with ranked records found!'), $player->login);
return;
}
// sort for most records
arsort($recs);
if ($aseco->server->getGame() == 'TMN') {
$records = '';
$i = 1;
$lines = 0;
$player->msgs = array();
$player->msgs[0] = 1;
foreach ($recs as $login => $rec) {
// obtain nickname for this login
$nick = $aseco->getPlayerNick($login);
if (!$aseco->settings['lists_colornicks'])
$nick = stripColors($nick);
$records .= LF . str_pad($i, 2, '0', STR_PAD_LEFT) . '. ' . $bgn
. str_pad($nick, 20) . $end . ' - ' . $rec;
$i++;
if (++$lines > 9) {
$player->msgs[] = $aseco->formatColors($head . $records);
$lines = 0;
$records = '';
}
if ($i > $top) break;
}
// add if last batch exists
if ($records != '')
$player->msgs[] = $aseco->formatColors($head . $records);
// display popup message
if (count($player->msgs) == 2) {
$aseco->client->query('SendDisplayServerMessageToLogin', $player->login, $player->msgs[1], 'OK', '', 0);
} else { // > 2
$aseco->client->query('SendDisplayServerMessageToLogin', $player->login, $player->msgs[1], 'Close', 'Next', 0);
}
} elseif ($aseco->server->getGame() == 'TMF') {
$records = 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'));
foreach ($recs as $login => $rec) {
// obtain nickname for this login
$nick = $aseco->getPlayerNick($login);
if (!$aseco->settings['lists_colornicks'])
$nick = stripColors($nick);
$records[] = array(str_pad($i, 2, '0', STR_PAD_LEFT) . '.',
$bgn . $nick,
$rec);
$i++;
if (++$lines > 14) {
$player->msgs[] = $records;
$lines = 0;
$records = array();
}
if ($i > $top) break;
}
// add if last batch exists
if (!empty($records))
$player->msgs[] = $records;
// display ManiaLink message
display_manialink_multi($player);
} else { // TMS/TMO
$records = $head;
$i = 1;
foreach ($recs as $login => $rec) {
// obtain nickname for this login
$nick = stripColors($aseco->getPlayerNick($login));
$records .= LF . $i . '. ' . $bgn
. str_pad($nick, 15) . $end . ' - ' . $rec;
$i++;
if ($i > $top) break;
}
// show chat message
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($records), $player->login);
}
} // chat_toprecs
?>

Some files were not shown because too many files have changed in this diff Show More