Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
1aa6c3f
Fix container failing to start after docker daemon restart
felacek Jun 15, 2025
9ae0ca8
docker run script: added proper error handling, support for relaunchi…
felacek Jun 18, 2025
9e5b621
moved version and repo information to package.json, improved update s…
felacek Jun 19, 2025
d616dd3
db auto-reconnect and timeout for failing
felacek Jun 21, 2025
f3a7b6a
added option to disable update utility
felacek Jun 21, 2025
8ade2cc
default values for version and homepage, logs display milliseconds fo…
felacek Jun 24, 2025
711557f
initialise best checkpoints to prevent crashes
felacek Jun 24, 2025
16e2ff1
change default tmx fetcher site to tmuf if non-nations packmask
felacek Jun 27, 2025
5fd8b62
tmxfetcher docs
felacek Jun 27, 2025
ee6d505
speed up /best to O(n) average case, O(log n) worst case, make long-t…
felacek Jul 4, 2025
1e9ae23
pretty sure I fixed the checkpoint crashing controller during startup…
felacek Jul 4, 2025
68df794
added env variable to disable re-installing npm packages on container…
felacek Jul 4, 2025
e1a8d42
fix cp counter width in rounds mode
wsrvn Jul 8, 2025
e4a4edb
Fix Dockerfile build
AroPix Aug 18, 2025
74938b6
Add authorTimeMultiplier and toggle
standort Aug 20, 2025
788f104
Remove duplicate ServerStateChanged event listener
standort Aug 20, 2025
7f2be8c
Merge pull request #282 from AroPix/bugfix/dockerfile
wsrvn Aug 20, 2025
b135e37
Refactored for styling consistency
standort Aug 20, 2025
b058f9d
Added chat message with the new time + multiplier
standort Aug 20, 2025
dcd9e19
Changed the chatmessage to be toggable
standort Aug 20, 2025
c438aeb
Move function call to correct spot
AroPix Aug 20, 2025
63b49f2
Call nextsong update on EndMap and Startup only
AroPix Aug 20, 2025
839aeef
Merge pull request #284 from AroPix/bugfix/music
lythx Aug 20, 2025
239eb65
Add favourite button in code
AroPix Aug 20, 2025
af778b2
Add favourite button
AroPix Aug 20, 2025
62da46b
Add greet command
AroPix Aug 20, 2025
95c5d2f
Remove coloring before exclamation mark in default config
AroPix Aug 20, 2025
b844236
Add config option for CP Counter to not display times
AroPix Aug 22, 2025
45327fd
Merge pull request #289 from AroPix/enhancement/cpcounterwithouttimes
lythx Aug 23, 2025
711a02e
Merge branch 'dev' into feature/favouritesbutton
lythx Aug 23, 2025
ba912a0
Merge pull request #285 from AroPix/feature/favouritesbutton
lythx Aug 23, 2025
d457dea
Merge branch 'dev' into feature/greet
lythx Aug 23, 2025
4c1a1fb
Merge pull request #286 from AroPix/feature/greet
lythx Aug 23, 2025
bd2d9a5
Add config option for the callVoteRatio
AroPix Aug 23, 2025
1aa434f
Implementing per player buttons
AroPix Aug 22, 2025
db856bf
Add buttons showing medal of current record
AroPix Aug 22, 2025
1d4c633
Remove unneeded and actually add button class (oops)
AroPix Aug 22, 2025
634e1a0
Trigger update on PlayerDataUpdated
AroPix Aug 22, 2025
a84af0e
Render MedalButton per-player conditionally, add improvements and com…
lythx Aug 23, 2025
8be6267
fix bronze medal text color
lythx Aug 23, 2025
3025d72
Merge pull request #292 from AroPix/feature/perplayerbuttons
lythx Aug 23, 2025
a9c7638
Merge pull request #290 from AroPix/feature/callvoteratio
lythx Aug 23, 2025
9540914
reformat all code and introduce .editorconfig file
felacek Sep 21, 2025
67a8fad
handle null regions and don't force ip
felacek Sep 21, 2025
4692e02
bump version
felacek Sep 21, 2025
6eae424
fix null player paths
felacek Sep 21, 2025
d656a74
add dynamic time limit plugin
lythx Oct 2, 2025
5f72e55
strip trackmania formatting in maplist search queries
lythx Oct 2, 2025
705d692
add caching for matchString()
lythx Oct 2, 2025
1241066
improve search util
lythx Oct 2, 2025
4a40625
revert timerwidget changes
lythx Oct 2, 2025
b144819
dynamic time limit implementation with dynamic timer
lythx Oct 3, 2025
48caa99
change GameService setTimeLimit function to work only for the next ro…
lythx Oct 3, 2025
06c00a0
implement DynamicTimeLimit for static timer
lythx Oct 3, 2025
3a97946
implement beginamap message for DynamicTimeLimit plugin
lythx Oct 3, 2025
2d577d0
add enable/disable commands for DynamicTimeLimit plugin
lythx Oct 3, 2025
5e755af
Merge pull request #283 from AuthBypass/feature/author-timer-multiplier
lythx Oct 3, 2025
cb3c3c2
make dynamic time limit plugin inactive by default
lythx Oct 3, 2025
c05a1dd
Fix topsums
AroPix Oct 25, 2025
4be1974
Fix sorting function
AroPix Oct 25, 2025
318ce3e
Fix stripping regex
AroPix Nov 24, 2025
8d1eeef
Create Database if it doesn't exist
AroPix Nov 24, 2025
4b9bcdb
Add once method to Events
AroPix Nov 27, 2025
26dbaef
Merge pull request #302 from AroPix/feature/once
wsrvn Nov 27, 2025
9e10855
Merge pull request #298 from AroPix/hotfix/topsums
wsrvn Nov 27, 2025
c1f4e75
Wait for BeginMap after 5 tries
AroPix Nov 27, 2025
f949adb
Add Option to set manialinks for buttons
AroPix Dec 28, 2025
9f4e342
Merge pull request #304 from AroPix/feature/manialink-buttons
wsrvn Jan 8, 2026
ffb00c1
first attempt at fixing infinite loops with manual map loading
felacek Jan 29, 2026
9ff09b6
skip if server desyncs from queue (corrupt files), fix rewriting MS o…
felacek Jan 29, 2026
924d48f
docs and remove unnecessary client call
felacek Jan 29, 2026
a78a433
Merge pull request #307 from lythx/fix-looping-tracks
felacek Feb 1, 2026
9a4646d
Merge pull request #301 from AroPix/hotfix/stripping
lythx Feb 11, 2026
9a710e2
Merge pull request #300 from AroPix/feature/create-database
lythx Feb 11, 2026
b5b61cb
fix status flag parsing
lythx Feb 19, 2026
ba72b31
remove professional debugging suite
lythx Feb 19, 2026
beb3090
implement custom environment support
lythx Feb 19, 2026
1cdce22
fix comment
lythx Feb 19, 2026
99e163f
Merge pull request #303 from AroPix/hotfix/RoundsService
lythx Feb 19, 2026
f8e0ced
fix crash on Quinninup
lythx Feb 19, 2026
9c1646c
Send amount instead of just tax with getCoppersAfterTax
AroPix Feb 19, 2026
90600a4
Merge pull request #311 from AroPix/fix/betting
lythx Feb 19, 2026
ce5d430
Default to OTH for countryToCode
AroPix Feb 23, 2026
bfed0d3
Merge pull request #312 from AroPix/fix/Quinninup
lythx Feb 23, 2026
e2f75fd
Merge pull request #309 from lythx/support-custom-environments
lythx Feb 23, 2026
31ec861
Add option to delete map from the filesystem for manual map loading
AroPix Mar 3, 2026
f3588dc
Merge pull request #314 from AroPix/feature/map-deletion
lythx Mar 4, 2026
2bb3826
Revert "Add option to delete map from the filesystem for manual map l…
felacek Mar 4, 2026
2351f11
Merge pull request #316 from lythx/revert-314-feature/map-deletion
felacek Mar 4, 2026
973d0bb
add unitedladder support for player data
wsrvn Aug 11, 2026
7b8962c
zoneid
wsrvn Aug 11, 2026
3996d7e
FavouriteButton: use addfavorite manialink (doesn't leave server)
AroPix Aug 11, 2026
ce5aa22
Merge pull request #319 from AroPix/fix/favourite-button
wsrvn Aug 11, 2026
5c7c9df
fix vote message formatting
wsrvn Aug 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
372 changes: 372 additions & 0 deletions .editorconfig

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ RUN mv GameData/Config/dedicated_cfg.txt dedicated_cfg.txt.bk && \
mkdir -p Tracksbk/MatchSettings && \
mkdir -p Tracksbk/Campaigns/Nations && \
mv GameData/Tracks/MatchSettings/Nations/NationsBlue.txt Tracksbk/MatchSettings/MatchSettings.txt && \
mv GameData/Tracks/Campaigns/Nations/Blue Tracksbk/Campaigns/Nations/
mv GameData/Tracks/Campaigns/Nations/Blue Tracksbk/Campaigns/Nations/ && \
rm -rf GameData/Tracks
# expose volumes
VOLUME /app/server/GameData/Config
VOLUME /app/server/GameData/Tracks
Expand Down
1 change: 1 addition & 0 deletions Plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ import './plugins/info_messages/InfoMessages.js'
import './plugins/skip_endscreen/SkipEndscreen.js'
import './plugins/ultimania/Ultimania.js'
import './plugins/live_checkpoints/LiveCheckpoints.js'
import './plugins/dynamic_time_limit/DynamicTimeLimit.js'
32 changes: 25 additions & 7 deletions Update.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,18 @@ async function hashFile(path) {

async function generateHashes() {
console.log('Generating hashes for current files...')
const entries = (await fs.readdir('./src', { recursive: true, withFileTypes: true }))
.concat(await fs.readdir('./plugins', { recursive: true, withFileTypes: true }))
.concat(await fs.readdir('./config', { recursive: true, withFileTypes: true }))
const entries = (await fs.readdir('./src', {
recursive: true,
withFileTypes: true
}))
.concat(await fs.readdir('./plugins', {
recursive: true,
withFileTypes: true
}))
.concat(await fs.readdir('./config', {
recursive: true,
withFileTypes: true
}))

const res = new Map(
await Promise.all(entries.filter(a => a.isFile() && (a.name.endsWith('.js') || a.name.endsWith('.ts')))
Expand Down Expand Up @@ -118,6 +127,7 @@ async function doUpdate(fromPath, newHashes, oldHashes = null) {
errorOccurred = true
}
}

// copy new hashes so you only have to update once
try {
await fs.copyFile(process.argv[2], '.hashes.json')
Expand All @@ -130,27 +140,35 @@ async function doUpdate(fromPath, newHashes, oldHashes = null) {
console.log('Errors occurred during update, exiting...')
process.exit(1)
}
let log = 'Update successful!'

let log = `Update log to version ${JSON.parse(
await fs.readFile(path.join(fromPath, 'package.json'), { encoding: 'utf-8' }))['version']}\n\n`
if (conflicts.length > 0) {
console.log('!!! Update conflicts, make sure to fix them by comparing your files to files ending in .new !!!')
console.log('If you do not fix these conflicts, the controller might fail to start or crash.')
console.log('Affected files: ')
log = 'Update did not succeed because of conflicts. Please merge the following files manually:\n'
log += 'Update did not succeed because of conflicts. Please merge the following files manually:\n'
conflicts.forEach(name => {
console.log(name)
log += name + '\n'
})
console.log('_____________________________________')
} else {
log += 'Update successful!'
}

try {
await fs.writeFile('update.log', log, { encoding: 'utf-8' })
} catch(e) {
console.log('Failed to write update log.')
console.log(e)
}
if (conflicts.length > 0) { process.exit(2) }

if (conflicts.length > 0) {
process.exit(2)
}
if (updatePerformed) {
console.log('Update successful.')
console.log('Update successful!')
} else {
console.log('Update not necessary.')
}
Expand Down
30 changes: 17 additions & 13 deletions config/Config.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default {
kick: 1,
forceSpectator: 1,
addMap: 1,
removeMap: 1,
removeMap: 1
},
/**
* Enable this for Trakman to take over handling maps. The controller will read maps from a folder
Expand All @@ -51,16 +51,16 @@ export default {
/** Path to the `GameData/Tracks/` directory of the server. MUST end with a slash '/'!
* Default value: "../GameData/Tracks/" assuming the Trakman directory is on the same level as the server.
*/
mapsDirectoryPrefix: "../GameData/Tracks/",
mapsDirectoryPrefix: '../GameData/Tracks/',
/** Relative path to all the maps. Read recursively from `GameData/Tracks/`.
* MUST end with a slash '/' when a directory is specified (i.e. if not left empty)!
* Default value: ""
*/
mapsDirectory: "",
mapsDirectory: '',
/** Ignore non-stadium maps */
stadiumOnly: undefined,
/** Amount of maps to load into the server. Default value: 5 */
preloadMaps: 5,
preloadMaps: 5
},
/**
* When adding a large amount of maps, it is better to add them in smaller chunks. This is the size
Expand All @@ -77,12 +77,14 @@ export default {
defaultTimeAttackTimeLimit: 300000,
/** Minimal time value to which the dynamic timer can be set (in milliseconds) */
dynamicTimerSubtractionLimit: 30000,
/** CallVotes are skip/restart votes that can be done through the ingame UI (ESC>Advanced Options), setting it to -1 disables CallVotes entirely */
callVoteRatio: -1,
/** Relative path (/GameData/Config/) to the blacklist file */
blacklistFile: "blacklist.txt",
blacklistFile: 'blacklist.txt',
/** Relative path (/GameData/Config/) to the guestlist file */
guestlistFile: "guestlist.txt",
/** Relative path (/GameData/Tracks/) to the matchsettings file */
matchSettingsFile: "MatchSettings/MatchSettings.txt",
guestlistFile: 'guestlist.txt',
/** Relative path (/GameData/Tracks/) to the match settings file, ignored if manualMapLoading is enabled */
matchSettingsFile: 'MatchSettings/MatchSettings.txt',
/** Default message sent as the reason for administrative actions if nothing was specified by the admin */
defaultReasonMessage: 'No reason specified',
/** Things that will be interpreted as true for the boolean command parameter */
Expand All @@ -92,10 +94,12 @@ export default {
/** Object with options for ufuzzy, see https://github.com/leeoniya/uFuzzy#options */
searchOptions: {
intraMode: 1,
alpha: "a-zа-яё"
alpha: 'a-zа-яё'
},
/** Current controller version */
version: "1.6.1",
/** Controller repository link */
repo: "github.com/lythx/trakman",
/** When using `matchString()` util with `stripTrackmaniaFormatting = true`, values are cached to optimize performance */
matchStringCacheSize: 10000,
/** If haystack size exceeds this threshold `matchString()` will skip expensive haystack processing to optimize performance */
matchStringReduxModeThreshold: 5000,
/** Custom map environments. If a map has an environment that is not in this list, it will be marked as "Stadium" */
customEnvironments: ["Highlands", "NewSnowCar"]
}
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ services:
DISCORD_EMBED_TITLE_URL: ""
DISCORD_EMBED_TITLE_ICON_URL: ""
DISCORD_TAGGED_USERS: ""
# Setup
UPDATE_UTILITY_DISABLED: "NO"
ports: # Don't forget to change the ports
# if you already have another server running
# **The ports MUST match** because of the way the server works
Expand Down
71 changes: 51 additions & 20 deletions docker_run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# create and copy dedicated config
if find /app/server/GameData/Config -mindepth 1 -maxdepth 1 | read; then
echo 'Server config exists, skipping initial setup.'
rm dedicated_cfg.txt.bk
rm dedicated_cfg.txt.bk 2>/dev/null
else
echo 'Setting up server...'
# ugly xml replacement
Expand All @@ -17,35 +17,63 @@ else
xml ed -L -u "/dedicated/masterserver_account/password" -v "$SERVER_ACC_PASSWORD" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/masterserver_account/validation_key" -v "$SERVER_ACC_KEY" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/server_options/name" -v "$SERVER_NAME" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/server_options/comment" -v "$SERVER_COMMENT" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/server_options/hide_server" -v "${SERVER_HIDE_SERVER=0}" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/server_options/max_players" -v "${SERVER_MAX_PLAYERS=32}" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/server_options/password" -v "$SERVER_PASSWORD" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/server_options/max_spectators" -v "${SERVER_MAX_SPECTATORS=32}" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/server_options/password_spectator" -v "$SERVER_PASSWORD_SPECTATOR" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/server_options/ladder_mode" -v "${SERVER_LADDER_MODE=1}" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/server_options/ladder_serverlimit_min" -v "${SERVER_LADDER_SERVERLIMIT_MIN=0}" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/server_options/ladder_serverlimit_max" -v "${SERVER_LADDER_SERVERLIMIT_MAX=50000}" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/server_options/enable_p2p_upload" -v "${SERVER_ENABLE_P2P_UPLOAD=True}" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/server_options/enable_p2p_download" -v "${SERVER_ENABLE_P2P_DOWNLOAD=True}" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/system_config/connection_uploadrate" -v "${SERVER_CONNECTION_UPLOADRATE=512}" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/system_config/connection_downloadrate" -v "${SERVER_CONNECTION_DOWNLOADRATE=8192}" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/system_config/server_port" -v "$SERVER_NET_PORT" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/system_config/server_p2p_port" -v "$SERVER_P2P_PORT" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/system_config/xmlrpc_port" -v "$SERVER_PORT" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/system_config/packmask" -v "$SERVER_PACKMASK" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/system_config/xmlrpc_allowremote" -v "${REMOTE_XMLRPC=False}" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/system_config/packmask" -v "${SERVER_PACKMASK=nations}" dedicated_cfg.txt.bk
xml ed -L -u "/dedicated/system_config/disable_coherence_checks" -v "${SERVER_DISABLE_COHERENCE_CHECKS=laps}" dedicated_cfg.txt.bk
mv /app/server/dedicated_cfg.txt.bk /app/server/GameData/Config/dedicated_cfg.txt
fi
# copy over default tracks
if find /app/server/GameData/Tracks -mindepth 1 -maxdepth 1 | read; then
echo 'Tracks exist, skipping initial setup.'
rm -r Tracksbk
rm -r Tracksbk 2>/dev/null
else
echo 'Setting up tracks...'
mv /app/server/Tracksbk/* /app/server/GameData/Tracks/
fi
# update and copy over Trakman directory
if find /app/server/trakman -mindepth 1 -maxdepth 1 | read; then
echo 'Trakman exists. Attempting update...'
cd trakman || exit
cp ../trakmanbk/Update.js .
node Update.js /app/server/trakmanbk/.hashes.json
if [ $? -gt 0 ]; then
chown server:server update.log
echo 'Update not fully successful, please stop the container.'
sleep 1m # wait a minute for user to read the message, or to realise something's wrong
exit
if [ "$UPDATE_UTILITY_DISABLED" = "YES" ]; then
echo 'Trakman exists, skipping initial setup.'
else
echo 'Trakman exists. Attempting update...'
if ! cd trakman 2>/dev/null
then
echo 'Trakman actually does not exist. This makes no sense. Aborting and trying again.'
exit
fi
if ! cp ../trakmanbk/Update.js . 2>/dev/null
then
# this runs if trakmanbk doesn't exist so we have nothing to update from
echo "Update not available."
else
if ! node Update.js /app/server/trakmanbk/.hashes.json
then
# this runs if trakmanbk exists and the update script fails
chown server:server update.log
echo 'Update not fully successful, please stop the container.'
sleep 1m # wait a minute for the user to read the message, or to realise something's wrong
exit 1
fi
fi
cd ..
fi
cd ..
rm -r trakmanbk
rm -r trakmanbk 2>/dev/null
else
echo 'Setting up Trakman...'
mv /app/server/trakmanbk/* /app/server/trakman/
Expand All @@ -63,6 +91,7 @@ touch trakman/logs/trace.log
mkdir -p .pm2/logs
touch .pm2/logs/Trakman-error.log
touch .pm2/logs/Trakman-out.log
touch .pm2/logs/docker.log
mkdir -p trakman/temp
touch trakman/temp/rank_coherence.txt
mkdir -p trakman/plugins/server_links/temp
Expand All @@ -72,16 +101,18 @@ chown -R server:server /app/server
echo "#!/bin/sh
(while true; do
/app/server/TrackmaniaServer /game_settings=MatchSettings/MatchSettings.txt /dedicated_cfg=dedicated_cfg.txt /nodaemon
echo 'Server exited with code ' $?
echo [\$(date +'%d %b %Y %T.%3N')] Server exited with code \$? | tee -a /app/server/.pm2/logs/docker.log
echo 'Restarting...'
done) &
npm i --prefix /app/server/trakman
npm run build --prefix /app/server/trakman
done) &" > run.sh
if [ "$INSTALL_ON_STARTUP" != "NO" ]; then
echo "npm i --prefix /app/server/trakman" >> run.sh
fi
echo "npm run build --prefix /app/server/trakman
chmod -R a+w /app/server
cd trakman
trap 'echo Terminating; npx pm2 stop 0; npx pm2 kill; exit' SIGTERM SIGINT
npx pm2 start ./built/src/Main.js --name Trakman
wait $!" > run.sh
npm run daemon
wait \$!" >> run.sh
chown server:server run.sh
chmod 766 run.sh
exec su-exec server ./run.sh
Loading
Loading