Compare commits

..
44 Commits
Author SHA1 Message Date
scriptos 5a321db094 Release: Docker Image Version 1.2.1 gepusht 2026-03-22 23:41:40 +01:00
scriptos 344029c551 feat: TeamSpeak3-Plugin mit eigenem Gateway reaktivieren 2026-03-22 23:35:11 +01:00
scriptos bf3b7858ee Merge pull request 'Release: Docker Image Version 1.2.0 gepusht' (#11) from release-1.1.2 into master
Reviewed-on: #11
2026-03-22 20:30:55 +00:00
scriptos ebd20d4653 Merge branch 'master' into release-1.1.2 2026-03-22 20:30:41 +00:00
scriptos fcea5b4d41 Release: Docker Image Version 1.2.0 gepusht 2026-03-22 21:29:37 +01:00
scriptos 6f4426e080 Merge pull request 'release-1.2.0' (#10) from release-1.1.2 into master
Reviewed-on: #10
2026-03-22 20:28:48 +00:00
scriptos ed5b3d22fb fix: PHP-Warnungen im RemoteCP Mods-Plugin behoben (foreach auf leere Umgebungen) 2026-03-22 21:27:03 +01:00
scriptos 3fb1dac5ba feat: Forced Mods (Skins) per Umgebungsvariable beim Containerstart setzen 2026-03-22 20:58:46 +01:00
scriptos 61deb93273 feat: Automatische MatchSettings-Erkennung & AdminServ-Bugfixes 2026-03-22 17:05:25 +01:00
scriptos fdcc82e935 Workspace wird nicht mehr mit committed 2026-03-22 15:24:31 +01:00
scriptos 75ae777f1e feat: Auto-Import von AdminServ ServerOptions beim Containerstart 2026-03-22 15:23:44 +01:00
scriptos b7a909392e Merge pull request 'release-1.1.1' (#7) from release-1.1.1 into master
Reviewed-on: #7
2026-03-22 11:26:40 +00:00
scriptos ec6418e9ab Release: Docker Image Version 1.1.1 gepusht 2026-03-22 12:25:58 +01:00
scriptos f013ccd61e fix: XMLRPC-Port aus externem Port-Mapping entfernen, Doku anpassen 2026-03-22 12:24:08 +01:00
scriptos 0717088eb4 fix: RemoteCP settings.xml-Erstellung entfernt (verursacht Folgefehler bei fehlenden Defaults) 2026-03-22 12:06:16 +01:00
scriptos 5ccba54e84 Merge pull request 'release-1.1.0' (#6) from release-1.0.1 into master
Reviewed-on: #6
2026-03-22 00:56:53 +00:00
scriptos 9b37dfd2bc Release: Docker Image Version 1.1.0 gepusht 2026-03-22 01:55:16 +01:00
scriptos 503fadf1d6 docs: Projektstruktur aktualisiert und in Doku-Uebersicht verschoben 2026-03-22 01:47:47 +01:00
scriptos 3354ebd541 docs: Fertiges Docker Image aus Container-Registry bewerben 2026-03-22 01:44:09 +01:00
scriptos 7482071f2c README: Hinweis auf Repository-Spiegelung von Gitea nach GitHub ergänzt 2026-03-22 01:36:56 +01:00
scriptos 69940ee56b Kurzbeschreibung des Projekts zur README hinzugefügt 2026-03-22 01:32:18 +01:00
scriptos c081e43375 fix(mariadb): XAseco-DB-Variablen explizit im Compose ergänzt 2026-03-22 01:29:10 +01:00
scriptos 35e485b065 docs: Danksagung an Thomas (retronerd.at) in README ergänzt 2026-03-22 01:24:09 +01:00
scriptos b672f84f55 fix: RemoteCP-Benutzerregistrierung standardmäßig deaktivieren 2026-03-22 01:21:07 +01:00
scriptos 76e739f12a refactor(license): Redundante Namensnennung in Lizenzbedingungen entfernt 2026-03-22 01:15:37 +01:00
scriptos 5891429c83 fix: RemoteCP CustomPoints PHP-Warnungen behoben (undefined constants)
Bare-constant-Zugriffe (pt_custom, pt_points, ...) in CustomPoints/index.php
durch defined()-Prüfungen ersetzt, um PHP 7.2+ Warnungen zu vermeiden.
Zusätzlich werden im Produktivmodus (PHP_DISPLAY_ERRORS=false) Warnungen
und Notices in der PHP error_reporting unterdrückt.

- Gepatchte index.php als assets/config/remotecp/plugins/CustomPoints/index.php
- Dockerfile: COPY der gepatchten Datei statt fragiler sed-Patches
- RunTrackmaniaServer.sh: Auto-Patch für bestehende Volumes beim Container-Start
- error_reporting im Produktivmodus um ~E_WARNING & ~E_NOTICE ergänzt

Closes #4
2026-03-22 01:09:37 +01:00
scriptos ffdc11a02b feat: Warmup deaktivieren & als ENV-Variable steuerbar machen
- Default von allwarmupduration in custom_game_settings.txt auf 0 gesetzt
- Neue Umgebungsvariable ALLWARMUPDURATION (Default: 0)
- Startup-Script wendet ENV auf MatchSettings an (inkl. FORCE_CONFIG_UPDATE)
- Dokumentation ergänzt (umgebungsvariablen.md)

Closes #2
2026-03-22 00:29:12 +01:00
scriptos e05de1fbe2 feat: SERVER_LADDER_LIMIT_MAX als ENV-Variable hinzugefügt (Closes #3) 2026-03-22 00:22:15 +01:00
scriptos 495a5a2663 fix: Berechtigungen bei Volume-Kopie korrigiert (cp -a statt cp -r)
Closes #5
2026-03-22 00:09:35 +01:00
scriptos 64e76803e5 Docker-Image in Compose eingefügt. 2026-03-20 23:19:12 +01:00
scriptos 6292025389 Merge pull request 'release-1.0.0' (#1) from release-1.0.0 into master
Reviewed-on: #1
2026-03-20 22:12:00 +00:00
scriptos bef71bd924 xaseco implementiert + Doku erweitert + Lizenz erweitert. 2026-03-20 23:11:15 +01:00
scriptos 5d626a7c9d Kosmetische Änderungen. 2026-03-20 19:21:09 +01:00
scriptos 65568e9f59 RemoteCP hinzugefügt. 2026-03-20 18:51:49 +01:00
scriptos c1c8ce5b3d Einige Anpassungen und Verbesserungen 2026-03-19 22:04:43 +01:00
scriptos 67b0fafeea PHP debug. 2026-03-18 22:36:34 +01:00
scriptos 3774e46924 Internet Mode neu konfiguriert. Neues BaseImage gewählt. 2026-03-18 21:54:28 +01:00
scriptos 7c3c228e47 Docker Image bauen beschrieben. 2026-03-18 19:30:48 +01:00
scriptos 8d4f858b31 Dokumentation aktualisiert. 2026-03-18 18:32:06 +01:00
scriptos 86e53d4233 Commit Konvention entfernt. 2026-03-18 18:31:13 +01:00
scriptos 705fe4f0bc Docker Netzwerk hinzugefügt. 2026-03-18 13:12:02 +01:00
scriptos 28b8ba3ac3 Internet-Dedicated hinzugefügt + Docs erstellt. 2026-03-18 13:06:46 +01:00
scriptos 8dad2637f8 Readme übersetzt und LICENSE hinzugefügt. 2026-03-18 12:56:05 +01:00
scriptos f57da6009d Files verschoben und Docker-Compose erstellt. 2026-03-18 12:49:22 +01:00
32 changed files with 3571 additions and 84 deletions

No files matched your search

+18
View File
@@ -0,0 +1,18 @@
# Git
.git
.gitignore
.gitattributes
# IDE
.vscode
# Docs
README.md
docs
# Umgebungsvariablen (Secrets!)
.env
.env.example
# Persistente Daten
data
+111
View File
@@ -0,0 +1,111 @@
# ============================================================
# tmserver-docker – Umgebungsvariablen
# ============================================================
# Diese Datei wird von docker-compose.yml eingelesen.
# Kopiere diese Vorlage nach .env und passe die Werte an:
# cp .env.example .env
#
# ⚠ SICHERHEITSHINWEIS:
# Die unten stehenden Passwörter sind vorgenerierte Beispielwerte
# und öffentlich einsehbar! Ändere ALLE Passwörter, bevor du
# den Server produktiv einsetzt.
# ============================================================
# --- Authentifizierung ---
# User: SuperAdmin, Admin, User
SERVER_SA_PASSWORD="3HbQc2EVarykEPX"
SERVER_ADM_PASSWORD="YhNDuChmzoaHP72"
SERVER_USER_PASSWORD="8e9J8AKwS6YDXmT"
# --- Masterserver-Account ---
# Wenn du deinen Server im Internet-Modus betreiben möchtest, musst du hier die Zugangsdaten für deinen Masterserver-Account eingeben.
# Diese Informationen erhältst du, wenn du deinen Server auf der Trackmania-Webseite registrierst.
SERVER_LOGIN=
SERVER_LOGIN_PASSWORD=
SERVER_VALIDATION_KEY=
# --- Server-Optionen ---
# Bitte ändere den Server-Namen und die Beschreibung, um deinen Server von anderen zu unterscheiden.
# Du kannst auch die Anzahl der Spieler und Zuschauer anpassen, sowie ein Passwort setzen, um den Zugang zu beschränken.
SERVER_NAME='Trackmania Server'
SERVER_DESC="Powered by tmserver-docker"
SERVER_HIDE=0
SERVER_MAX_PLAYERS=40
SERVER_PASSWORD=
SERVER_MAX_SPECTATORS=40
SERVER_SPEC_PASSWORD=
SERVER_LADDER_MODE=forced
SERVER_LADDER_LIMIT_MAX=60000
# --- Netzwerk ---
# Bitte ändere die Ports, wenn sie in deinem Netzwerk bereits verwendet werden.
SERVER_PORT=2350
SERVER_P2P_PORT=3450
SERVER_XMLRPC_PORT=5000
SERVER_UPLOAD_RATE=512
SERVER_DOWNLOAD_RATE=8192
# --- Server-Modus und Config-Steuerung ---
# Der Server-Modus kann auf "internet" oder "lan" gesetzt werden. Im Internet-Modus wird eine Verbindung zum Masterserver hergestellt, um den Server öffentlich sichtbar zu machen.
# Im LAN-Modus wird keine Verbindung zum Masterserver hergestellt, und der Server ist nur im lokalen Netzwerk sichtbar.
SERVER_MODE=internet
# Wenn FORCE_CONFIG_UPDATE auf true gesetzt ist, wird die Server-Konfiguration bei jedem Start des Containers überschrieben.
# Dies kann nützlich sein, wenn du Änderungen an der .env-Datei vornimmst und sicherstellen möchtest, dass diese Änderungen angewendet werden.
# In einer Produktionsumgebung sollte dieser Wert jedoch auf false belassen werden, um zu verhindern, dass die Konfiguration versehentlich überschrieben wird.
FORCE_CONFIG_UPDATE=false
# --- Forced Mods (Skins) ---
# Beim Containerstart kann automatisch ein Mod (Skin) pro Umgebung forciert werden.
# Der Wert ist die vollständige URL zu einer Mod-ZIP-Datei, die Spieler beim Betreten des Servers herunterladen.
# Verfügbare Skins findest du unter: https://assets.techniverse.net/tm/skins/
# Beispiel: FORCE_MOD_STADIUM=https://assets.techniverse.net/tm/skins/Portal.zip
# Leer lassen = kein Mod für diese Umgebung.
FORCE_MOD_STADIUM=
FORCE_MOD_ISLAND=
FORCE_MOD_BAY=
FORCE_MOD_COAST=
FORCE_MOD_SPEED=
FORCE_MOD_ALPINE=
FORCE_MOD_RALLY=
# --- MatchSettings ---
# Steuert, welche MatchSettings-Datei beim Serverstart geladen wird.
# "auto" = die neueste .txt-Datei in data/gamedata/Tracks/MatchSettings/ wird automatisch erkannt.
# Alternativ kann ein expliziter Dateiname angegeben werden (z.B. "turnier_settings.txt").
MATCHSETTINGS_FILE=auto
# --- Debugging ---
# Setze diesen Wert auf true, um PHP-Fehlermeldungen anzuzeigen. Dies kann bei der Fehlersuche hilfreich sein, sollte aber in einer Produktionsumgebung auf false belassen werden.
PHP_DISPLAY_ERRORS=false
# --- RemoteCP-Datenbank ---
# --- MariaDB ---
# Bitte ändere das Root-Passwort für MariaDB, um die Sicherheit deiner Datenbank zu gewährleisten.
MARIADB_ROOT_PASSWORD="RAPjY2FLtLoLDYU"
# --- RemoteCP-Datenbankzugang ---
# Diese Zugangsdaten werden von RemoteCP verwendet, um sich mit der Datenbank zu verbinden. Ändere diese Werte, um die Sicherheit deiner Datenbank zu gewährleisten.
REMOTECP_DB_HOST=mariadb
REMOTECP_DB_NAME=remotecp
REMOTECP_DB_USER=remotecp
REMOTECP_DB_PASSWORD="6MP2mBdF9TBtdmN"
# --- XAseco ---
# XAseco ist ein Server-Controller, der Rekorde, Karma, Jukebox und vieles mehr verwaltet.
# Setze XASECO_ENABLED auf false, um XAseco zu deaktivieren.
XASECO_ENABLED=true
# Dein Spieler-Login (wird als MasterAdmin und Admin in XAseco eingetragen)
XASECO_MASTERADMIN_LOGIN=
# XAseco-Datenbankzugang
# Bitte ändere die Zugangsdaten für die XAseco-Datenbank, um die Sicherheit deiner Datenbank zu gewährleisten.
XASECO_DB_HOST=mariadb
XASECO_DB_NAME=xaseco
XASECO_DB_USER=xaseco
XASECO_DB_PASSWORD="4KpL8mWnR3xYvBq"
# Dedimania-Nation (3-Zeichen IOC-Code, z.B. DEU, AUT, CHE)
# Server-Login und -Passwort werden automatisch aus SERVER_LOGIN / SERVER_LOGIN_PASSWORD übernommen.
XASECO_DEDIMANIA_NATION=DEU
+1
View File
@@ -0,0 +1 @@
*.sh text eol=lf
+7
View File
@@ -0,0 +1,7 @@
# Umgebungsvariablen (enthaelt Passwoerter!)
.env
# Persistente Server-Daten
data/
.ki-workspace/
+194 -19
View File
@@ -1,29 +1,204 @@
FROM debian:stretch-slim
FROM debian:bullseye-slim
RUN mkdir /opt/tmserver
WORKDIR /opt/tmserver
COPY TrackmaniaServer_2011-02-21.zip /opt/tmserver
RUN apt-get update && apt-get install -y unzip
RUN unzip /opt/tmserver/TrackmaniaServer_2011-02-21.zip -d /opt/tmserver
COPY custom_game_settings.txt /opt/tmserver/GameData/Tracks/MatchSettings/
COPY RunTrackmaniaServer.sh /opt/tmserver/
# Alle benoetigten Pakete in einem Layer installieren und Cache aufraeumen
RUN apt-get update && apt-get install -y --no-install-recommends \
unzip \
apache2 \
php \
php-zip \
php-xml \
php-mbstring \
php-mysql \
php-curl \
default-mysql-client \
&& rm -rf /var/lib/apt/lists/*
RUN apt-get -y install apache2 php php-zip php-xml
COPY AdminServ_v2.1.1.zip /var/www/html
RUN unzip /var/www/html/AdminServ_v2.1.1.zip -d /var/www/html
RUN chmod -R 777 /var/www/html/
RUN rm -f /var/www/html/index.html
# Apache mod_rewrite aktivieren und AllowOverride fuer .htaccess (RemoteCP-Sicherheit)
RUN a2enmod rewrite \
&& sed -i '/<Directory \/var\/www\/>/,/<\/Directory>/ s/AllowOverride None/AllowOverride All/' /etc/apache2/apache2.conf
ARG SERVER_NAME='Trackmania Server'
ARG SERVER_DESC='This is a Trackmania Server'
ARG SERVER_SA_PASSWORD='SuperAdmin'
ARG SERVER_ADM_PASSWORD='Admin'
ENV SERVER_NAME $SERVER_NAME
ENV SERVER_DESC $SERVER_DESC
ENV SERVER_SA_PASSWORD $SERVER_SA_PASSWORD
ENV SERVER_ADM_PASSWORD $SERVER_ADM_PASSWORD
COPY assets/bin/TrackmaniaServer_2011-02-21.zip /opt/tmserver
RUN unzip /opt/tmserver/TrackmaniaServer_2011-02-21.zip -d /opt/tmserver \
&& rm -f /opt/tmserver/TrackmaniaServer_2011-02-21.zip
# Custom-Konfiguration ueber die Standard-Config aus dem ZIP kopieren
COPY assets/config/dedicated_cfg.txt /opt/tmserver/GameData/Config/dedicated_cfg.txt
COPY assets/config/custom_game_settings.txt /opt/tmserver/GameData/Tracks/MatchSettings/
# Config-Verzeichnis rekursiv beschreibbar machen (Server benoetigt Schreibzugriff)
RUN chmod -R 777 /opt/tmserver/GameData/Config/
# Tracks-Verzeichnis fuer AdminServ beschreibbar machen (Maps-Upload/Download)
RUN chown -R www-data:www-data /opt/tmserver/GameData/Tracks/ \
&& chmod -R 755 /opt/tmserver/GameData/Tracks/
# AdminServ-Verzeichnisse im Config-Ordner anlegen
RUN mkdir -p /opt/tmserver/GameData/Config/AdminServ/ServerOptions \
&& chown -R www-data:www-data /opt/tmserver/GameData/Config/AdminServ
# Gesamtes GameData als Default-Template sichern (wird beim ersten Start ins Volume kopiert)
RUN cp -a /opt/tmserver/GameData /opt/tmserver/default-gamedata
COPY assets/bin/RunTrackmaniaServer.sh /opt/tmserver/
RUN sed -i 's/\r$//' /opt/tmserver/RunTrackmaniaServer.sh \
&& chmod +x /opt/tmserver/RunTrackmaniaServer.sh
COPY assets/bin/AdminServ_v2.1.1.zip /var/www/html
RUN unzip /var/www/html/AdminServ_v2.1.1.zip -d /var/www/html \
&& rm -f /var/www/html/AdminServ_v2.1.1.zip \
&& rm -f /var/www/html/index.html \
&& mkdir -p /var/www/html/logs \
&& chmod -R 777 /var/www/html/logs \
&& chmod 666 /var/www/html/config/adminlevel.cfg.php \
&& chmod 666 /var/www/html/config/servers.cfg.php \
&& chmod 666 /var/www/html/config/adminserv.cfg.php \
&& chown -R www-data:www-data /var/www/html/
# RemoteCP installieren (als Subpath /remotecp/)
COPY assets/bin/remoteCP_v4.0.3.5.zip /var/www/html
RUN unzip /var/www/html/remoteCP_v4.0.3.5.zip -d /var/www/html \
&& mv /var/www/html/remoteCP_4.0.3.5-1 /var/www/html/remotecp \
&& rm -f /var/www/html/remoteCP_v4.0.3.5.zip \
&& mkdir -p /var/www/html/remotecp/cache \
&& chmod -R 777 /var/www/html/remotecp/cache \
&& chmod -R 666 /var/www/html/remotecp/xml/*.xml \
&& chmod -R 777 /var/www/html/remotecp/xml \
&& chmod -R 777 /var/www/html/remotecp/xml/settings \
&& chown -R www-data:www-data /var/www/html/remotecp/
# Fix PHP-Warnungen in RemoteCP CustomPoints-Plugin (undefined constants)
# RemoteCP nutzt bare constants (pt_custom, pt_points, ...), die in PHP 7.2+
# Warnungen ausloesen. Das gepatchte Plugin nutzt stattdessen defined()-Pruefungen.
COPY assets/config/remotecp/plugins/CustomPoints/index.php /var/www/html/remotecp/plugins/CustomPoints/index.php
RUN chown www-data:www-data /var/www/html/remotecp/plugins/CustomPoints/index.php
# Fix PHP-Warnungen in RemoteCP Mods-Plugin (foreach auf leere Umgebungen)
# Leere Umgebungen (Island, Bay, …) fuehrten zu "Invalid argument supplied
# for foreach()", weil $this->mods['Env'] nicht initialisiert war.
# Zusaetzlich bare-constant-Warnungen (pt_*) mit defined()-Pruefungen entschaerft.
COPY assets/config/remotecp/plugins/Mods/index.php /var/www/html/remotecp/plugins/Mods/index.php
RUN chown www-data:www-data /var/www/html/remotecp/plugins/Mods/index.php
# RemoteCP Mods-Plugin: Vorkonfigurierte Skin-Liste (techniverse.net)
COPY assets/config/remotecp/plugins/Mods/settings.xml /var/www/html/remotecp/plugins/Mods/settings.xml
RUN chown www-data:www-data /var/www/html/remotecp/plugins/Mods/settings.xml
# Fix AdminServ MatchSettings-Bugs fuer TmForever:
# 1) get_matchset_mapimport.php: Falscher Pfad-Praefix (MatchSettings/ statt
# des tatsaechlichen Map-Ordners) beim Erstellen von MatchSettings.
# 2) maps-creatematchset.php: GetModeScriptInfo-Aufruf (existiert nur in
# ManiaPlanet/TM2) wird fuer TmForever uebersprungen.
COPY assets/config/adminserv/get_matchset_mapimport.php /var/www/html/resources/ajax/get_matchset_mapimport.php
COPY assets/config/adminserv/maps-creatematchset.php /var/www/html/resources/process/maps-creatematchset.php
RUN chown www-data:www-data /var/www/html/resources/ajax/get_matchset_mapimport.php \
&& chown www-data:www-data /var/www/html/resources/process/maps-creatematchset.php
# AdminServ- und RemoteCP-Dateien als Default-Template sichern (wird beim ersten Start ins Volume kopiert)
RUN cp -a /var/www/html /opt/tmserver/default-controlpanel
# XAseco installieren
COPY assets/bin/xaseco_v1.16.zip /opt/tmserver/
RUN unzip /opt/tmserver/xaseco_v1.16.zip -d /opt/tmserver/ \
&& rm -f /opt/tmserver/xaseco_v1.16.zip \
# newinstall-Dateien in den Hauptordner verschieben
&& cp /opt/tmserver/xaseco/newinstall/access.xml /opt/tmserver/xaseco/ \
&& cp /opt/tmserver/xaseco/newinstall/adminops.xml /opt/tmserver/xaseco/ \
&& cp /opt/tmserver/xaseco/newinstall/Aseco.sh /opt/tmserver/xaseco/ \
&& cp /opt/tmserver/xaseco/newinstall/AsecoF.sh /opt/tmserver/xaseco/ \
&& cp /opt/tmserver/xaseco/newinstall/autotime.xml /opt/tmserver/xaseco/ \
&& cp /opt/tmserver/xaseco/newinstall/bannedips.xml /opt/tmserver/xaseco/ \
&& cp /opt/tmserver/xaseco/newinstall/config.xml /opt/tmserver/xaseco/ \
&& cp /opt/tmserver/xaseco/newinstall/dedimania.xml /opt/tmserver/xaseco/ \
&& cp /opt/tmserver/xaseco/newinstall/html.tpl /opt/tmserver/xaseco/ \
&& cp /opt/tmserver/xaseco/newinstall/localdatabase.xml /opt/tmserver/xaseco/ \
&& cp /opt/tmserver/xaseco/newinstall/matchsave.xml /opt/tmserver/xaseco/ \
&& cp /opt/tmserver/xaseco/newinstall/musicserver.xml /opt/tmserver/xaseco/ \
&& cp /opt/tmserver/xaseco/newinstall/plugins.xml /opt/tmserver/xaseco/ \
&& cp /opt/tmserver/xaseco/newinstall/rasp.xml /opt/tmserver/xaseco/ \
&& cp /opt/tmserver/xaseco/newinstall/text.tpl /opt/tmserver/xaseco/ \
# Die drei PHP-Dateien nach includes/
&& cp /opt/tmserver/xaseco/newinstall/jfreu.config.php /opt/tmserver/xaseco/includes/ \
&& cp /opt/tmserver/xaseco/newinstall/rasp.settings.php /opt/tmserver/xaseco/includes/ \
&& cp /opt/tmserver/xaseco/newinstall/votes.config.php /opt/tmserver/xaseco/includes/ \
# Start-Scripte ausfuehrbar machen
&& chmod +x /opt/tmserver/xaseco/Aseco.sh \
&& chmod +x /opt/tmserver/xaseco/AsecoF.sh \
# newinstall-Ordner aufraeumen
&& rm -rf /opt/tmserver/xaseco/newinstall
# Nicht mehr verfuegbares freezone-Plugin entfernen
RUN sed -i '/<plugin>plugin\.freezone\.php<\/plugin>/d' /opt/tmserver/xaseco/plugins.xml
# TeamSpeak3-Plugin: Eigenes Gateway einbinden (Original-Gateway nicht mehr verfuegbar)
# Die teamspeak3.xml wird direkt in den XAseco-Ordner kopiert, damit das Plugin
# beim Start automatisch den konfigurierten TS3-Server anzeigt.
COPY assets/config/xaseco/teamspeak3.xml /opt/tmserver/xaseco/teamspeak3.xml
# XAseco als Default-Template sichern (wird beim ersten Start ins Volume kopiert)
RUN cp -a /opt/tmserver/xaseco /opt/tmserver/default-xaseco
# PHP-Debug-Konfiguration: wird zur Laufzeit vom Startup-Script gesetzt
# (kein Rebuild noetig – nur Container neustarten)
# --- Umgebungsvariablen ---
# Sensible Werte (Passwoerter, Keys) werden NICHT im Image hinterlegt,
# sondern muessen zur Laufzeit uebergeben werden (z.B. via .env-Datei).
# Server-Optionen (nicht-sensible Standardwerte)
ENV SERVER_NAME="Trackmania Server"
ENV SERVER_DESC="Powered by tmserver-docker"
ENV SERVER_HIDE=0
ENV SERVER_MAX_PLAYERS=32
ENV SERVER_MAX_SPECTATORS=32
ENV SERVER_LADDER_MODE=forced
# Netzwerk
ENV SERVER_PORT=2350
ENV SERVER_P2P_PORT=3450
ENV SERVER_XMLRPC_PORT=5000
ENV SERVER_UPLOAD_RATE=512
ENV SERVER_DOWNLOAD_RATE=8192
# Server-Modus und Config-Steuerung
ENV SERVER_MODE=internet
ENV FORCE_CONFIG_UPDATE=false
# Spieleinstellungen (MatchSettings)
ENV ALLWARMUPDURATION=0
# Forced Mods (Skins) - URL zu ZIP-Dateien, die beim Start forciert werden
ENV FORCE_MOD_STADIUM=""
ENV FORCE_MOD_ISLAND=""
ENV FORCE_MOD_BAY=""
ENV FORCE_MOD_COAST=""
ENV FORCE_MOD_SPEED=""
ENV FORCE_MOD_ALPINE=""
ENV FORCE_MOD_RALLY=""
# RemoteCP
ENV REMOTECP_DB_HOST=mariadb
ENV REMOTECP_DB_NAME=remotecp
ENV REMOTECP_DB_USER=remotecp
# XAseco
ENV XASECO_ENABLED=true
ENV XASECO_MASTERADMIN_LOGIN=""
ENV XASECO_DB_HOST=mariadb
ENV XASECO_DB_NAME=xaseco
ENV XASECO_DB_USER=xaseco
ENV XASECO_DEDIMANIA_NATION=DEU
# Debugging
ENV PHP_DISPLAY_ERRORS=false
# Volumes fuer persistente Daten
VOLUME /opt/tmserver/GameData
VOLUME /var/www/html
VOLUME /opt/tmserver/xaseco
EXPOSE 5000/tcp
EXPOSE 2350/tcp
+86
View File
@@ -0,0 +1,86 @@
MIT-Lizenz
Copyright © 2019 lduriez (Original: https://github.com/lduriez/tmserver-docker)
Copyright © 2026 Patrick Asmus
---
Lizenzinhaber
Name: Patrick Asmus (scriptos)
Email: support@techniverse.net
Website: https://www.patrick-asmus.de
Blog: https://www.cleveradmin.de
---
Hiermit wird jeder Person, die eine Kopie dieser Software und der zugehörigen
Dokumentationsdateien (die „Software") erhält, kostenlos die Erlaubnis erteilt,
uneingeschränkt mit der Software zu handeln, einschließlich und ohne
Einschränkung der Rechte, die Software zu verwenden, zu kopieren, zu
verändern, zusammenzuführen, zu veröffentlichen, zu verbreiten,
unterzulizenzieren und/oder Kopien der Software zu verkaufen, und Personen,
denen die Software zur Verfügung gestellt wird, dies unter den folgenden
Bedingungen zu gestatten:
Der obige Urheberrechtshinweis einschließlich der Angaben zum Lizenzinhaber
sowie dieser Genehmigungshinweis müssen in allen Kopien oder wesentlichen
Teilen der Software enthalten sein.
DIE SOFTWARE WIRD „WIE BESEHEN" OHNE JEGLICHE AUSDRÜCKLICHE ODER
STILLSCHWEIGENDE GEWÄHRLEISTUNG ZUR VERFÜGUNG GESTELLT, EINSCHLIESSLICH,
ABER NICHT BESCHRÄNKT AUF DIE GEWÄHRLEISTUNG DER MARKTGÄNGIGKEIT, DER
EIGNUNG FÜR EINEN BESTIMMTEN ZWECK UND DER NICHTVERLETZUNG VON RECHTEN
DRITTER. IN KEINEM FALL HAFTEN DIE AUTOREN ODER URHEBERRECHTSINHABER FÜR
JEGLICHE ANSPRÜCHE, SCHÄDEN ODER SONSTIGE HAFTUNG, OB AUS VERTRAG,
UNERLAUBTER HANDLUNG ODER ANDERWEITIG, DIE SICH AUS ODER IN VERBINDUNG MIT
DER SOFTWARE ODER DER NUTZUNG ODER ANDEREN GESCHÄFTEN MIT DER SOFTWARE
ERGEBEN.
===============================================================================
Hinweise zu enthaltenen Drittanbieter-Tools
===============================================================================
Die oben genannte MIT-Lizenz gilt ausschließlich für den Code, die
Konfiguration und die Container-Arbeit dieses Repositories (tmserver-docker)
von Patrick Asmus. Die folgenden Drittanbieter-Tools werden in diesem Projekt
eingebunden und unterliegen jeweils ihren eigenen Lizenzbedingungen. Alle
Rechte an diesen Tools verbleiben bei den jeweiligen Entwicklern bzw.
Rechteinhabern.
-------------------------------------------------------------------------------
1. AdminServ
-------------------------------------------------------------------------------
Beschreibung: Web-Interface zur Verwaltung von TrackMania Forever und
ManiaPlanet Dedicated Servern.
Ursprünglicher Entwickler: Kev717
Weiterentwicklung: Chris92de
Repository: https://github.com/Chris92de/AdminServ
Lizenz: Keine Lizenzdatei im Repository vorhanden.
Alle Rechte verbleiben bei den jeweiligen Entwicklern
(Kev717, Chris92de und weitere Mitwirkende).
-------------------------------------------------------------------------------
2. remoteCP
-------------------------------------------------------------------------------
Beschreibung: Remote-Control-Panel für TrackMania Server.
Entwickler: hal.sascha
Lizenz: GNU Lesser General Public License (LGPL)
https://www.gnu.org/licenses/lgpl-3.0.html
Hinweis: Alle Rechte verbleiben beim Entwickler hal.sascha.
-------------------------------------------------------------------------------
3. XASECO
-------------------------------------------------------------------------------
Beschreibung: Server-Controller für TrackMania (Nations/Forever).
Basiert auf ASECO von Florian Schnell und RASP von
Eyez (Iain Surgey) & Assembler Maniac (Brad Mettee).
Entwickler: Xymph (Frans P. de Vries)
Copyright: © 2007-2026 Frans P. de Vries
Website: https://www.xaseco.org
Kontakt: tm@gamers.org
Lizenz: Keine explizite Open-Source-Lizenz im Projekt angegeben.
Alle Rechte verbleiben bei Frans P. de Vries (Xymph)
und den ursprünglichen Autoren von ASECO und RASP.
===============================================================================
+58 -47
View File
@@ -1,77 +1,88 @@
# tmserver-docker
Trackmania Nation Forever Docker Server
Ein vollständiges Docker-Setup für einen **TrackMania Nations Forever**-Server – inklusive Web-Verwaltung und Server-Controller:
You can find it on Docker Hub [lduriez/tmserver](https://hub.docker.com/r/lduriez/tmserver)
- **TrackMania Dedicated Server** – der eigentliche Spielserver für Internet- oder LAN-Betrieb
- **[XAseco](docs/xaseco.md)** – Server-Controller, der lokale Rekorde, Dedimania-Weltrekorde, Karma/Votes und eine Track-Jukebox direkt im Spielchat verwaltet
- **[AdminServ](docs/adminserv.md)** – Web-Oberfläche zur komfortablen Verwaltung und Konfiguration des Servers
- **[RemoteCP](docs/remotecp.md)** – alternative Web-Verwaltungsoberfläche mit eigenem Login- und Benutzersystem
- **[Mods / Skins](docs/remotecp.md#mods--skins)** – über 50 vorkonfigurierte Stadium-Skins (gehostet auf [assets.techniverse.net](https://assets.techniverse.net/tm/skins/)), automatisch beim Start forcierbar
Only lan dedicated is enable (internet dedicated will be added in future version)
Alle Komponenten laufen in einem einzigen Container und werden über Umgebungsvariablen konfiguriert.
Server management webui provided base on [AdminServ](https://github.com/Chris92de/AdminServ)
> **Hinweis:** Dieses Projekt ist ein Fork von [lduriez/tmserver-docker](https://github.com/lduriez/tmserver-docker?tab=readme-ov-file).
## Running
Der Server unterstützt sowohl den **Internet-Dedicated-Modus** (Standard) als auch den **LAN-Dedicated-Modus**.
On a docker environment simply do the following
## Schnellstart
### 1. Umgebungsvariablen einrichten
```bash
docker run -d -p 2350:2350 -p 3450:3450 -p 80:80 --name tm-server lduriez/tmserver
cp .env.example .env
```
You can add environment variable to modify default values : [#Environment variables](#environment-variables)
Passe die Werte in der `.env`-Datei an deine Umgebung an (Passwörter, Masterserver-Account, etc.).
### Configure Server Management Webui
> **⚠ Sicherheitshinweis:** Die `.env.example` enthält **vorgenerierte Beispiel-Passwörter**. Diese dienen nur als Platzhalter und sind öffentlich einsehbar! **Ändere unbedingt alle Passwörter**, bevor du den Server produktiv einsetzt.
Visit `http:<host-server-of-the-conntainer>` and start configuration by setting the password of your choice.
This password will be the AdminServ password for configurations (add TM server)
### 2. Server starten
Then add TM server information (you can let all by default). Be sure that `Address` is set to `localhost` in order to manage our embed server.
Das fertige Docker Image kann direkt verwendet werden – kein eigener Build nötig:
Once you saved, you can go to Servers list by pushing "Servers" button and then go to management server list with button "Back".
```bash
docker compose up -d
```
You should see the server you added, in the to banner you ca go to the management environment.
Select the server you want to manage, the admin level you want to go in, and tape the password of this admion level.
Das Image wird automatisch aus der Container-Registry geladen:
By default `SuperAdmin` password is `SuperAdmin`, `Admin` password is `Admin`, `User` password is `User`. (You can modify the admin level in configuration settings `http:<host-server-of-the-conntainer>/config`)
```
git.techniverse.net/scriptos/trackmania-server:latest
```
Congratulation you can now manage your TM server.
> **Tipp:** Alle verfügbaren Tags findest du in der [Container-Registry](https://git.techniverse.net/scriptos/-/packages/container/trackmania-server/). Wenn du das Image lieber selbst bauen möchtest, findest du die Anleitung unter [Schnellstart – Selbst bauen](docs/schnellstart.md#docker-image-selbst-bauen).
Enjoy your game.
### 3. Verwaltungsoberflächen öffnen
## Exposed ports
- **AdminServ:** `http://<host-ip>/`
- **RemoteCP:** `http://<host-ip>/remotecp/`
* 2350/tcp game server port
* 2350/udp game server port
* 3450/tcp p2p game server port
* 80/tcp server management webui port
> **Hinweis:** Für den Internet-Modus müssen `SERVER_LOGIN` und `SERVER_VALIDATION_KEY` in der `.env`-Datei gesetzt sein. Einen Server-Account kannst du auf [players.trackmaniaforever.com](https://players.trackmaniaforever.com) erstellen. Für den LAN-Modus setze `SERVER_MODE=lan`.
## Environment variables
## Dokumentation
* SERVER_NAME name of your server (default is 'Trackmania Server')
* SERVER_DESC description of your server (default is 'This is a Trackmania Server')
* SERVER_SA_PASSWORD superadmin management password (default is 'SuperAdmin')
* SERVER_ADM_PASSWORD admin management password (default is 'Admin')
Die vollständige Dokumentation befindet sich im Ordner [`docs/`](docs/README.md):
## Commit convention
- [Schnellstart](docs/schnellstart.md) – Erste Schritte und minimale Konfiguration
- [Konfiguration](docs/konfiguration.md) – Persistente Serverkonfiguration (dedicated_cfg.txt)
- [Umgebungsvariablen](docs/umgebungsvariablen.md) – Alle verfügbaren Umgebungsvariablen
- [Server-Modi](docs/server-modi.md) – LAN- und Internet-Dedicated-Modus
- [AdminServ](docs/adminserv.md) – Einrichtung der Server-Verwaltungsoberfläche
- [RemoteCP](docs/remotecp.md) – Alternative Server-Verwaltungsoberfläche
- [XAseco](docs/xaseco.md) – Server-Controller für Rekorde, Karma und Jukebox
- [Ports](docs/ports.md) – Freigegebene Ports und deren Verwendung
Format : `<type>(<portée>): <sujet>`
## Danksagung
### type
Danke an **Thomas** ([retronerd.at](https://retronerd.at)), dass er mir sein Wissen zur Verfügung gestellt hat und dass er hier im Projekt mitgewirkt hat.
* `build` : changements qui affectent le système de build ou des dépendances externes (npm, make…)
* `ci` : changements concernant les fichiers et scripts d’intégration ou de configuration (Travis, Ansible, BrowserStack…)
* `feat` : ajout d’une nouvelle fonctionnalité
* `fix` : correction d’un bug
* `perf` : amélioration des performances
* `refactor` : modification qui n’apporte ni nouvelle fonctionnalité ni d’amélioration de performances
* `style` : changement qui n’apporte aucune altération fonctionnelle ou sémantique (indentation, mise en forme, ajout d’espace, renommante d’une variable…)
* `docs` : rédaction ou mise à jour de documentation
* `test` : ajout ou modification de tests
* `revert` : annuler un précédent commit, forme `revert sujet du commit annulé hash du commit annulé`
## Spiegelung
### sujet
Dieses Repository wird von **Gitea** auf **GitHub** gespiegelt. Das Master-Repository befindet sich auf Gitea:
* `add`
* `change`
* `update`
* `remove`
- **Gitea (Master):** [git.techniverse.net/scriptos/tmserver-docker](https://git.techniverse.net/scriptos/tmserver-docker.git)
- **GitHub (Spiegel):** [github.com/pscriptos/tmserver-docker](https://github.com/pscriptos/tmserver-docker.git)
[source](https://buzut.net/git-bien-nommer-ses-commits/)
---
📝 **Blog:** [www.cleveradmin.de](https://www.cleveradmin.de)
🌐 **Webseite:** [www.patrick-asmus.de](https://www.patrick-asmus.de)
📧 **E-Mail:** [support@techniverse.net](mailto:support@techniverse.net)
<p align="center">
<img src="https://assets.techniverse.net/f1/git/graphics/gray0-catonline.svg" alt="">
</p>
<p align="center">
<img src="https://assets.techniverse.net/f1/logos/small/license.png" alt="License" width="15" height="15"> <a href="./LICENSE">License</a> | <img src="https://assets.techniverse.net/f1/logos/small/matrix2.svg" alt="Matrix" width="15" height="15"> <a href="https://matrix.to/#/#community:techniverse.net">Matrix</a> | <img src="https://assets.techniverse.net/f1/logos/small/mastodon2.svg" alt="Mastodon" width="15" height="15"> <a href="https://social.techniverse.net/@donnerwolke">Mastodon</a>
</p>
-17
View File
@@ -1,17 +0,0 @@
#!/bin/sh
echo "Starting apache server"
service apache2 start
echo "Setting ENV/ARG variables"
sed -i "s/<password>SuperAdmin/<password>${SERVER_SA_PASSWORD}/" /opt/tmserver/GameData/Config/dedicated_cfg.txt
sed -i "s/<password>Admin/<password>${SERVER_ADM_PASSWORD}/" /opt/tmserver/GameData/Config/dedicated_cfg.txt
sed -i "s/<name></<name>${SERVER_NAME}</" /opt/tmserver/GameData/Config/dedicated_cfg.txt
sed -i "s/<comment></<comment>${SERVER_DESC}</" /opt/tmserver/GameData/Config/dedicated_cfg.txt
sed -i "s/<xmlrpc_allowremote>False/<xmlrpc_allowremote>True/" /opt/tmserver/GameData/Config/dedicated_cfg.txt
echo "Server config dedicated_cfg.txt is"
cat /opt/tmserver/GameData/Config/dedicated_cfg.txt
echo "Launching Server"
exec ./TrackmaniaServer /dedicated_cfg=dedicated_cfg.txt /game_settings=MatchSettings/custom_game_settings.txt /nodaemon /lan
View File
File renamed without changes.
+971
View File
@@ -0,0 +1,971 @@
#!/bin/sh
# ============================================================
# PHP-Debug-Modus konfigurieren (per Umgebungsvariable)
# ============================================================
PHP_DISPLAY_ERRORS="${PHP_DISPLAY_ERRORS:-false}"
PHP_INI_DIR=$(find /etc/php -type d -name "conf.d" -path "*/apache2/*" | head -1)
if [ "$PHP_DISPLAY_ERRORS" = "true" ]; then
echo "==> PHP-Debug-Modus AKTIVIERT (PHP_DISPLAY_ERRORS=true)"
cat > "$PHP_INI_DIR/99-adminserv-debug.ini" <<EOF
display_errors = On
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
log_errors = On
error_log = /var/log/php_errors.log
EOF
else
echo "==> PHP-Debug-Modus deaktiviert"
cat > "$PHP_INI_DIR/99-adminserv-debug.ini" <<EOF
display_errors = Off
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_WARNING & ~E_NOTICE
log_errors = On
error_log = /var/log/php_errors.log
EOF
fi
# ============================================================
# AdminServ: First-Run-Logik
# ============================================================
# Beim ersten Start (leeres Volume) werden die AdminServ-Dateien
# aus dem Default-Template ins Volume kopiert.
# Bei weiteren Starts bleiben vorhandene Daten (Passwort,
# Server-Eintraege, etc.) erhalten.
# ============================================================
ADMINSERV_DIR="/var/www/html"
DEFAULT_CONTROLPANEL="/opt/tmserver/default-controlpanel"
if [ ! -f "$ADMINSERV_DIR/index.php" ]; then
echo "==> Erster Start erkannt: Kopiere AdminServ-Dateien ins Volume..."
cp -a "$DEFAULT_CONTROLPANEL"/* "$ADMINSERV_DIR/"
chmod -R 777 "$ADMINSERV_DIR/logs/"
chmod 666 "$ADMINSERV_DIR/config/adminlevel.cfg.php"
chmod 666 "$ADMINSERV_DIR/config/servers.cfg.php"
chmod 666 "$ADMINSERV_DIR/config/adminserv.cfg.php"
chown -R www-data:www-data "$ADMINSERV_DIR/"
# AdminServ-Server-Eintrag automatisch konfigurieren
XMLRPC_PORT="${SERVER_XMLRPC_PORT:-5000}"
# Servernamen fuer PHP-Single-Quotes escapen
SAFE_NAME=$(printf '%s' "${SERVER_NAME:-Trackmania Server}" | sed "s/'/\\\\'/g")
# ds_pw: Passwort fuer DisplayServ (Serverstatusanzeige auf der Login-Seite)
DS_PW=$(printf '%s' "${SERVER_USER_PASSWORD:-User}" | sed "s/'/\\\\'/g")
cat > "$ADMINSERV_DIR/config/servers.cfg.php" <<EOPHP
<?php
class ServerConfig {
public static \$SERVERS = array(
'${SAFE_NAME}' => array(
'address' => '127.0.0.1',
'port' => ${XMLRPC_PORT},
'mapsbasepath' => '',
'matchsettings' => 'MatchSettings/',
'adminlevel' => array('SuperAdmin' => 'all', 'Admin' => 'all', 'User' => 'all'),
'ds_pw' => '${DS_PW}'
),
);
}
?>
EOPHP
chmod 666 "$ADMINSERV_DIR/config/servers.cfg.php"
chown www-data:www-data "$ADMINSERV_DIR/config/servers.cfg.php"
echo " AdminServ-Server-Eintrag automatisch konfiguriert (Port: ${XMLRPC_PORT})."
echo " AdminServ-Dateien erfolgreich kopiert."
# ============================================================
# RemoteCP: Automatische Konfiguration
# ============================================================
REMOTECP_DIR="$ADMINSERV_DIR/remotecp"
if [ -d "$REMOTECP_DIR" ]; then
echo "==> Konfiguriere RemoteCP..."
# DB-Konfiguration aus Umgebungsvariablen
REMOTECP_DB_HOST="${REMOTECP_DB_HOST:-}"
REMOTECP_DB_NAME="${REMOTECP_DB_NAME:-remotecp}"
REMOTECP_DB_USER="${REMOTECP_DB_USER:-remotecp}"
REMOTECP_DB_PASSWORD="${REMOTECP_DB_PASSWORD:-}"
if [ -n "$REMOTECP_DB_HOST" ] && [ -n "$REMOTECP_DB_PASSWORD" ]; then
DB_ENABLED="true"
DB_DSN="mysql:dbname=${REMOTECP_DB_NAME};host=${REMOTECP_DB_HOST}"
else
DB_ENABLED="false"
DB_DSN="mysql:dbname=remotecp;host=localhost"
echo " HINWEIS: Keine DB-Zugangsdaten gesetzt (REMOTECP_DB_HOST/REMOTECP_DB_PASSWORD)."
echo " RemoteCP wird ohne Datenbank konfiguriert. Manuelle Einrichtung moeglich."
fi
# servers.xml: Serververbindung und Datenbank automatisch konfigurieren
SA_PW=$(printf '%s' "${SERVER_SA_PASSWORD:-SuperAdmin}" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
SAFE_RCP_NAME=$(printf '%s' "${SERVER_NAME:-Trackmania Server}" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
SAFE_DB_DSN=$(printf '%s' "$DB_DSN" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
SAFE_DB_USER=$(printf '%s' "$REMOTECP_DB_USER" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
SAFE_DB_PW=$(printf '%s' "$REMOTECP_DB_PASSWORD" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
cat > "$REMOTECP_DIR/xml/servers.xml" <<EORCPSERV
<?xml version="1.0" encoding="utf-8"?>
<servers>
<server>
<id>1</id>
<login></login>
<name>${SAFE_RCP_NAME}</name>
<settingset></settingset>
<filepath></filepath>
<connection>
<host>127.0.0.1</host>
<port>${XMLRPC_PORT}</port>
<password>${SA_PW}</password>
<communitycode>000000</communitycode>
</connection>
<ftp enabled='false'>
<host>localhost</host>
<port></port>
<username>username</username>
<password>password</password>
<path>/GameData/Tracks/</path>
</ftp>
<sql enabled='${DB_ENABLED}'>
<dsn>${SAFE_DB_DSN}</dsn>
<username>${SAFE_DB_USER}</username>
<password>${SAFE_DB_PW}</password>
</sql>
<lists>
<guestlist>guestlist.txt</guestlist>
<blacklist>blacklist.txt</blacklist>
</lists>
</server>
</servers>
EORCPSERV
# admins.xml: Admin-Zugang aus SuperAdmin-Passwort konfigurieren
RCP_PW="${SERVER_SA_PASSWORD:-SuperAdmin}"
RCP_PW_MD5=$(printf '%s' "$RCP_PW" | md5sum | cut -d' ' -f1)
cat > "$REMOTECP_DIR/xml/admins.xml" <<EORCPADM
<?xml version="1.0"?>
<admins>
<admin>
<id>L1</id>
<active>true</active>
<servers>
<server id='1' group='1' />
</servers>
<username>rcplive</username>
<password>5b8e508f6f4a95bc581a37243d88f07e</password>
<nocode>false</nocode>
<tmaccount>false</tmaccount>
<language>en</language>
<style>default</style>
</admin>
<admin>
<id>G1</id>
<active>true</active>
<servers>
<server id='1' group='G1' />
</servers>
<username>Guest</username>
<password>adb831a7fdd83dd1e2a309ce7591dff8</password>
<nocode>false</nocode>
<tmaccount>false</tmaccount>
<language>en</language>
<style>default</style>
</admin>
<admin>
<id>1</id>
<active>true</active>
<servers>
<server id='1' group='1' />
</servers>
<username>SuperAdmin</username>
<password>${RCP_PW_MD5}</password>
<nocode>false</nocode>
<tmaccount>false</tmaccount>
<language>de</language>
<style>default</style>
</admin>
</admins>
EORCPADM
# ============================================================
# RemoteCP: Datenbank-Initialisierung
# ============================================================
if [ "$DB_ENABLED" = "true" ]; then
echo " Warte auf MariaDB (${REMOTECP_DB_HOST})..."
DB_READY=false
for i in $(seq 1 30); do
if mysql -h "$REMOTECP_DB_HOST" -u "$REMOTECP_DB_USER" -p"$REMOTECP_DB_PASSWORD" "$REMOTECP_DB_NAME" -e "SELECT 1" > /dev/null 2>&1; then
echo " MariaDB erreichbar."
DB_READY=true
break
fi
echo " Versuch $i/30 - MariaDB noch nicht bereit, warte 3s..."
sleep 3
done
if [ "$DB_READY" = "true" ]; then
echo " Importiere RemoteCP-Datenbankschema..."
for sqlfile in "$REMOTECP_DIR"/plugins/*/mysql_*.sql "$REMOTECP_DIR"/live/*/mysql_*.sql; do
if [ -f "$sqlfile" ]; then
echo " -> $(basename "$sqlfile")"
mysql -h "$REMOTECP_DB_HOST" -u "$REMOTECP_DB_USER" -p"$REMOTECP_DB_PASSWORD" "$REMOTECP_DB_NAME" < "$sqlfile"
fi
done
# Installer-Markierung setzen (ueberspringt den Web-Installer)
echo "installed" > "$REMOTECP_DIR/cache/installed"
chown www-data:www-data "$REMOTECP_DIR/cache/installed"
echo " RemoteCP-Datenbank erfolgreich initialisiert."
else
echo " WARNUNG: MariaDB nicht erreichbar nach 90s!"
echo " RemoteCP-Datenbank muss manuell eingerichtet werden."
echo " Installer: http://<host-ip>/remotecp/index.php?page=install"
fi
fi
# Berechtigungen fuer RemoteCP setzen
chmod -R 777 "$REMOTECP_DIR/cache"
chmod -R 777 "$REMOTECP_DIR/xml"
chown -R www-data:www-data "$REMOTECP_DIR/"
echo " RemoteCP-Konfiguration abgeschlossen (Port: ${XMLRPC_PORT}, User: SuperAdmin)."
fi
else
echo "==> Vorhandene AdminServ-Daten gefunden. Keine Aenderungen."
fi
# ============================================================
# XAseco: First-Run-Logik
# ============================================================
# Beim ersten Start (leeres Volume) werden die XAseco-Dateien
# aus dem Default-Template ins Volume kopiert und die
# Konfiguration aus den Umgebungsvariablen angewendet.
# ============================================================
XASECO_DIR="/opt/tmserver/xaseco"
DEFAULT_XASECO="/opt/tmserver/default-xaseco"
XASECO_ENABLED="${XASECO_ENABLED:-true}"
if [ "$XASECO_ENABLED" = "true" ]; then
if [ ! -f "$XASECO_DIR/aseco.php" ]; then
echo "==> Erster Start erkannt: Kopiere XAseco-Dateien ins Volume..."
cp -a "$DEFAULT_XASECO"/* "$XASECO_DIR/"
XMLRPC_PORT="${SERVER_XMLRPC_PORT:-5000}"
XASECO_ADMIN="${XASECO_MASTERADMIN_LOGIN:-}"
SA_PW_XASECO=$(printf '%s' "${SERVER_SA_PASSWORD:-SuperAdmin}" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
# --- config.xml: MasterAdmin und TMServer-Verbindung konfigurieren ---
if [ -n "$XASECO_ADMIN" ]; then
SAFE_ADMIN=$(printf '%s' "$XASECO_ADMIN" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
# MasterAdmin in die masteradmins-Liste einfuegen (nach <masteradmins>-Tag)
sed -i "/<masteradmins>/a\\
<tmlogin>${SAFE_ADMIN}</tmlogin> <ipaddress></ipaddress>" "$XASECO_DIR/config.xml"
echo " config.xml: MasterAdmin '${XASECO_ADMIN}' gesetzt."
else
echo " HINWEIS: XASECO_MASTERADMIN_LOGIN nicht gesetzt."
echo " Bitte manuell in xaseco/config.xml eintragen!"
fi
sed -i "s|<password>YOUR_SUPERADMIN_PASSWORD</password>|<password>${SA_PW_XASECO}</password>|" "$XASECO_DIR/config.xml"
sed -i "s|<port>5000</port>|<port>${XMLRPC_PORT}</port>|" "$XASECO_DIR/config.xml"
echo " config.xml: TMServer-Verbindung konfiguriert (Port: ${XMLRPC_PORT})."
# --- adminops.xml: Admin-Login eintragen ---
if [ -n "$XASECO_ADMIN" ]; then
SAFE_ADMIN=$(printf '%s' "$XASECO_ADMIN" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
sed -i "s|<!-- format:\n.*<tmlogin>YOUR_ADMIN_LOGIN</tmlogin>.*\n.*-->||" "$XASECO_DIR/adminops.xml"
# Admin in die admins-Liste einfuegen
sed -i "/<admins>/a\\
<tmlogin>${SAFE_ADMIN}</tmlogin> <ipaddress></ipaddress>" "$XASECO_DIR/adminops.xml"
echo " adminops.xml: Admin '${XASECO_ADMIN}' eingetragen."
fi
# --- localdatabase.xml: MySQL-Verbindung konfigurieren ---
XASECO_DB_HOST="${XASECO_DB_HOST:-mariadb}"
XASECO_DB_NAME="${XASECO_DB_NAME:-xaseco}"
XASECO_DB_USER="${XASECO_DB_USER:-xaseco}"
XASECO_DB_PASSWORD="${XASECO_DB_PASSWORD:-}"
if [ -n "$XASECO_DB_PASSWORD" ]; then
SAFE_DB_HOST=$(printf '%s' "$XASECO_DB_HOST" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
SAFE_DB_USER=$(printf '%s' "$XASECO_DB_USER" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
SAFE_DB_PW=$(printf '%s' "$XASECO_DB_PASSWORD" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
SAFE_DB_NAME=$(printf '%s' "$XASECO_DB_NAME" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
sed -i "s|<mysql_server>localhost</mysql_server>|<mysql_server>${SAFE_DB_HOST}</mysql_server>|" "$XASECO_DIR/localdatabase.xml"
sed -i "s|<mysql_login>YOUR_MYSQL_LOGIN</mysql_login>|<mysql_login>${SAFE_DB_USER}</mysql_login>|" "$XASECO_DIR/localdatabase.xml"
sed -i "s|<mysql_password>YOUR_MYSQL_PASSWORD</mysql_password>|<mysql_password>${SAFE_DB_PW}</mysql_password>|" "$XASECO_DIR/localdatabase.xml"
sed -i "s|<mysql_database>aseco</mysql_database>|<mysql_database>${SAFE_DB_NAME}</mysql_database>|" "$XASECO_DIR/localdatabase.xml"
echo " localdatabase.xml: MySQL-Verbindung konfiguriert (Host: ${XASECO_DB_HOST}, DB: ${XASECO_DB_NAME})."
else
echo " WARNUNG: XASECO_DB_PASSWORD nicht gesetzt!"
echo " XAseco-Datenbank muss manuell in xaseco/localdatabase.xml konfiguriert werden."
fi
# --- dedimania.xml: Server-Account konfigurieren ---
DEDI_LOGIN="${SERVER_LOGIN:-}"
DEDI_PASSWORD="${SERVER_LOGIN_PASSWORD:-}"
DEDI_NATION="${XASECO_DEDIMANIA_NATION:-DEU}"
if [ -n "$DEDI_LOGIN" ] && [ -n "$DEDI_PASSWORD" ]; then
SAFE_DEDI_LOGIN=$(printf '%s' "$DEDI_LOGIN" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
SAFE_DEDI_PW=$(printf '%s' "$DEDI_PASSWORD" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
SAFE_DEDI_NATION=$(printf '%s' "$DEDI_NATION" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
sed -i "s|<login>YOUR_SERVER_LOGIN</login>|<login>${SAFE_DEDI_LOGIN}</login>|" "$XASECO_DIR/dedimania.xml"
sed -i "s|<password>YOUR_SERVER_PASSWORD</password>|<password>${SAFE_DEDI_PW}</password>|" "$XASECO_DIR/dedimania.xml"
sed -i "s|<nation>YOUR_SERVER_NATION</nation>|<nation>${SAFE_DEDI_NATION}</nation>|" "$XASECO_DIR/dedimania.xml"
echo " dedimania.xml: Server-Account konfiguriert (Nation: ${DEDI_NATION})."
else
echo " HINWEIS: SERVER_LOGIN/SERVER_LOGIN_PASSWORD nicht gesetzt."
echo " Dedimania wird ohne Account konfiguriert."
fi
# --- XAseco-Datenbank: Schema importieren ---
if [ -n "$XASECO_DB_PASSWORD" ]; then
echo " Warte auf MariaDB (${XASECO_DB_HOST}) fuer XAseco-DB..."
DB_READY=false
for i in $(seq 1 30); do
if mysql -h "$XASECO_DB_HOST" -u "$XASECO_DB_USER" -p"$XASECO_DB_PASSWORD" "$XASECO_DB_NAME" -e "SELECT 1" > /dev/null 2>&1; then
echo " MariaDB erreichbar."
DB_READY=true
break
fi
echo " Versuch $i/30 - MariaDB noch nicht bereit, warte 3s..."
sleep 3
done
if [ "$DB_READY" = "true" ]; then
echo " Importiere XAseco-Datenbankschema..."
for sqlfile in "$XASECO_DIR"/localdb/*.sql; do
if [ -f "$sqlfile" ]; then
echo " -> $(basename "$sqlfile")"
mysql -h "$XASECO_DB_HOST" -u "$XASECO_DB_USER" -p"$XASECO_DB_PASSWORD" "$XASECO_DB_NAME" < "$sqlfile"
fi
done
echo " XAseco-Datenbank erfolgreich initialisiert."
else
echo " WARNUNG: MariaDB nicht erreichbar nach 90s!"
echo " XAseco-Datenbankschema muss manuell importiert werden."
fi
fi
echo " XAseco-Konfiguration abgeschlossen."
else
echo "==> Vorhandene XAseco-Daten gefunden. Keine Aenderungen."
fi
fi
# ============================================================
# XAseco: TeamSpeak3-Plugin Gateway aktualisieren (fuer bestehende Volumes)
# ============================================================
# Das Original-TS3-Gateway ist nicht mehr verfuegbar. Die eigene
# teamspeak3.xml mit dem Ersatz-Gateway wird in das Volume kopiert,
# falls sie fehlt oder noch das alte (nicht mehr erreichbare) Gateway
# referenziert. Gleichzeitig wird das Plugin reaktiviert, falls es
# in einer frueheren Version auskommentiert wurde.
# ============================================================
XASECO_DIR_TS3="/opt/tmserver/xaseco"
TS3_XML="$XASECO_DIR_TS3/teamspeak3.xml"
TS3_DEFAULT="/opt/tmserver/default-xaseco/teamspeak3.xml"
TS3_PLUGINS_XML="$XASECO_DIR_TS3/plugins.xml"
# teamspeak3.xml aktualisieren: Kopieren wenn fehlend oder veraltet
if [ -f "$TS3_DEFAULT" ]; then
if [ ! -f "$TS3_XML" ]; then
echo "==> TeamSpeak3-Gateway: teamspeak3.xml fehlt, kopiere aus Template..."
cp "$TS3_DEFAULT" "$TS3_XML"
echo " teamspeak3.xml erfolgreich kopiert."
elif ! diff -q "$TS3_DEFAULT" "$TS3_XML" > /dev/null 2>&1; then
echo "==> TeamSpeak3-Gateway: teamspeak3.xml wird aktualisiert..."
cp "$TS3_DEFAULT" "$TS3_XML"
echo " teamspeak3.xml erfolgreich aktualisiert."
fi
fi
# TS3-Plugin reaktivieren, falls es auskommentiert ist
if [ -f "$TS3_PLUGINS_XML" ] && grep -q '<!-- <plugin>plugin\.teamspeak3\.php</plugin> -->' "$TS3_PLUGINS_XML"; then
echo "==> TeamSpeak3-Plugin: Reaktiviere auskommentiertes Plugin..."
sed -i 's|<!-- <plugin>plugin\.teamspeak3\.php</plugin> -->|<plugin>plugin.teamspeak3.php</plugin>|' "$TS3_PLUGINS_XML"
echo " TeamSpeak3-Plugin erfolgreich aktiviert."
fi
# ============================================================
# RemoteCP: PHP-Warnungen in Plugins fixen (fuer bestehende Volumes)
# ============================================================
# RemoteCP nutzt bare constants (pt_custom, pt_points, ...), die in
# PHP 7.2+ Warnungen ausloesen. Die gepatchte Datei aus dem Image
# wird in das Volume kopiert, falls die alte Version noch vorhanden ist.
# ============================================================
CUSTOMPOINTS_FILE="/var/www/html/remotecp/plugins/CustomPoints/index.php"
CUSTOMPOINTS_DEFAULT="/opt/tmserver/default-controlpanel/remotecp/plugins/CustomPoints/index.php"
if [ -f "$CUSTOMPOINTS_FILE" ] && ! grep -q 'defined.*pt_custom' "$CUSTOMPOINTS_FILE"; then
echo "==> Patche CustomPoints-Plugin (PHP-Warnungen beheben)..."
cp "$CUSTOMPOINTS_DEFAULT" "$CUSTOMPOINTS_FILE"
chown www-data:www-data "$CUSTOMPOINTS_FILE"
echo " CustomPoints-Plugin erfolgreich gepatcht."
fi
# ============================================================
# AdminServ: MatchSettings-Bugfixes fuer bestehende Volumes
# ============================================================
# 1) get_matchset_mapimport.php: Berechnet den relativen Pfad aus dem
# absoluten Dropdown-Pfad statt den URL-Parameter 'd' zu verwenden.
# Ohne Fix wird z.B. "MatchSettings/" statt "Challenges/Downloaded/"
# als Praefix in die MatchSettings-Datei geschrieben.
# 2) maps-creatematchset.php: Ueberspringt GetModeScriptInfo fuer
# TmForever (Methode existiert nur in ManiaPlanet/TM2, Fehler -506).
# ============================================================
ADMINSERV_MAPIMPORT="/var/www/html/resources/ajax/get_matchset_mapimport.php"
ADMINSERV_MAPIMPORT_DEFAULT="/opt/tmserver/default-controlpanel/resources/ajax/get_matchset_mapimport.php"
if [ -f "$ADMINSERV_MAPIMPORT" ] && ! grep -q 'relativePath' "$ADMINSERV_MAPIMPORT"; then
echo "==> Patche AdminServ: MatchSettings Map-Import (Pfad-Fix)..."
cp "$ADMINSERV_MAPIMPORT_DEFAULT" "$ADMINSERV_MAPIMPORT"
chown www-data:www-data "$ADMINSERV_MAPIMPORT"
echo " get_matchset_mapimport.php erfolgreich gepatcht."
fi
ADMINSERV_CREATEMATCHSET="/var/www/html/resources/process/maps-creatematchset.php"
ADMINSERV_CREATEMATCHSET_DEFAULT="/opt/tmserver/default-controlpanel/resources/process/maps-creatematchset.php"
if [ -f "$ADMINSERV_CREATEMATCHSET" ] && grep -q "query('GetModeScriptInfo')" "$ADMINSERV_CREATEMATCHSET" && ! grep -q "SERVER_VERSION_NAME != 'TmForever'" "$ADMINSERV_CREATEMATCHSET"; then
echo "==> Patche AdminServ: GetModeScriptInfo-Fix fuer TmForever..."
cp "$ADMINSERV_CREATEMATCHSET_DEFAULT" "$ADMINSERV_CREATEMATCHSET"
chown www-data:www-data "$ADMINSERV_CREATEMATCHSET"
echo " maps-creatematchset.php erfolgreich gepatcht."
fi
# ============================================================
# RemoteCP: Mods-Plugin settings.xml aktualisieren (fuer bestehende Volumes)
# ============================================================
# Die vorkonfigurierte Skin-Liste aus dem Image wird in das Volume
# kopiert, falls die alte Standard-settings.xml noch vorhanden ist
# (erkennbar am Beispiel-Eintrag "blacksunonline.com").
# ============================================================
MODS_SETTINGS_FILE="/var/www/html/remotecp/plugins/Mods/settings.xml"
MODS_SETTINGS_DEFAULT="/opt/tmserver/default-controlpanel/remotecp/plugins/Mods/settings.xml"
if [ -f "$MODS_SETTINGS_FILE" ] && grep -q 'blacksunonline.com' "$MODS_SETTINGS_FILE"; then
echo "==> Aktualisiere RemoteCP Mods-Plugin (Skin-Liste von techniverse.net)..."
cp "$MODS_SETTINGS_DEFAULT" "$MODS_SETTINGS_FILE"
chown www-data:www-data "$MODS_SETTINGS_FILE"
echo " Mods/settings.xml erfolgreich aktualisiert."
fi
echo "Starting apache server"
service apache2 start
CONFIG="/opt/tmserver/GameData/Config/dedicated_cfg.txt"
GAME_SETTINGS="/opt/tmserver/GameData/Tracks/MatchSettings/custom_game_settings.txt"
GAMEDATA_DIR="/opt/tmserver/GameData"
DEFAULT_GAMEDATA="/opt/tmserver/default-gamedata"
# ============================================================
# Persistente GameData: First-Run-Logik
# ============================================================
# Beim ersten Start (leeres Volume) wird das gesamte GameData-
# Verzeichnis aus dem Default-Template ins Volume kopiert und
# die Umgebungsvariablen auf die Config angewendet.
# Bei weiteren Starts wird die vorhandene Konfiguration beibehalten,
# damit manuelle Aenderungen nicht ueberschrieben werden.
# Mit FORCE_CONFIG_UPDATE=true kann ein erneutes Anwenden erzwungen werden.
# ============================================================
FORCE_CONFIG_UPDATE="${FORCE_CONFIG_UPDATE:-false}"
if [ ! -f "$CONFIG" ]; then
echo "==> Erster Start erkannt: Kopiere Default-GameData ins Volume..."
cp -a "$DEFAULT_GAMEDATA"/* "$GAMEDATA_DIR/"
chmod -R 777 "$GAMEDATA_DIR/Config/"
mkdir -p "$GAMEDATA_DIR/Config/AdminServ/ServerOptions"
chown -R www-data:www-data "$GAMEDATA_DIR/Config/AdminServ"
# Tracks-Verzeichnis fuer AdminServ beschreibbar machen (Maps-Upload/Download)
chown -R www-data:www-data "$GAMEDATA_DIR/Tracks/"
chmod -R 755 "$GAMEDATA_DIR/Tracks/"
APPLY_ENV=true
elif [ "$FORCE_CONFIG_UPDATE" = "true" ]; then
echo "==> FORCE_CONFIG_UPDATE ist aktiv: Umgebungsvariablen werden erneut angewendet..."
echo " ACHTUNG: Manuelle Aenderungen an den betroffenen Feldern werden ueberschrieben!"
# Template neu kopieren, damit alle Platzhalter vorhanden sind
cp "$DEFAULT_GAMEDATA/Config/dedicated_cfg.txt" "$CONFIG"
cp "$DEFAULT_GAMEDATA/Tracks/MatchSettings/custom_game_settings.txt" "$GAME_SETTINGS"
APPLY_ENV=true
else
echo "==> Vorhandene Konfiguration gefunden. Umgebungsvariablen werden NICHT angewendet."
echo " Zum erneuten Anwenden: FORCE_CONFIG_UPDATE=true setzen."
APPLY_ENV=false
fi
# ============================================================
# Platzhalter in dedicated_cfg.txt durch Umgebungsvariablen ersetzen
# ============================================================
if [ "$APPLY_ENV" = "true" ]; then
echo "Ersetze Platzhalter in dedicated_cfg.txt mit Umgebungsvariablen..."
# Authentifizierung
sed -i "s|%%SERVER_SA_PASSWORD%%|${SERVER_SA_PASSWORD}|g" "$CONFIG"
sed -i "s|%%SERVER_ADM_PASSWORD%%|${SERVER_ADM_PASSWORD}|g" "$CONFIG"
sed -i "s|%%SERVER_USER_PASSWORD%%|${SERVER_USER_PASSWORD}|g" "$CONFIG"
# Masterserver-Account
sed -i "s|%%SERVER_LOGIN%%|${SERVER_LOGIN}|g" "$CONFIG"
sed -i "s|%%SERVER_LOGIN_PASSWORD%%|${SERVER_LOGIN_PASSWORD}|g" "$CONFIG"
sed -i "s|%%SERVER_VALIDATION_KEY%%|${SERVER_VALIDATION_KEY}|g" "$CONFIG"
# Server-Optionen
sed -i "s|%%SERVER_NAME%%|${SERVER_NAME}|g" "$CONFIG"
sed -i "s|%%SERVER_DESC%%|${SERVER_DESC}|g" "$CONFIG"
sed -i "s|%%SERVER_HIDE%%|${SERVER_HIDE}|g" "$CONFIG"
sed -i "s|%%SERVER_MAX_PLAYERS%%|${SERVER_MAX_PLAYERS}|g" "$CONFIG"
sed -i "s|%%SERVER_PASSWORD%%|${SERVER_PASSWORD}|g" "$CONFIG"
sed -i "s|%%SERVER_MAX_SPECTATORS%%|${SERVER_MAX_SPECTATORS}|g" "$CONFIG"
sed -i "s|%%SERVER_SPEC_PASSWORD%%|${SERVER_SPEC_PASSWORD}|g" "$CONFIG"
sed -i "s|%%SERVER_LADDER_MODE%%|${SERVER_LADDER_MODE}|g" "$CONFIG"
sed -i "s|%%SERVER_LADDER_LIMIT_MAX%%|${SERVER_LADDER_LIMIT_MAX:-60000}|g" "$CONFIG"
# Netzwerk
sed -i "s|%%SERVER_PORT%%|${SERVER_PORT}|g" "$CONFIG"
sed -i "s|%%SERVER_P2P_PORT%%|${SERVER_P2P_PORT}|g" "$CONFIG"
sed -i "s|%%SERVER_XMLRPC_PORT%%|${SERVER_XMLRPC_PORT}|g" "$CONFIG"
sed -i "s|%%SERVER_UPLOAD_RATE%%|${SERVER_UPLOAD_RATE}|g" "$CONFIG"
sed -i "s|%%SERVER_DOWNLOAD_RATE%%|${SERVER_DOWNLOAD_RATE}|g" "$CONFIG"
# Spieleinstellungen (MatchSettings)
sed -i "s|<allwarmupduration>[^<]*</allwarmupduration>|<allwarmupduration>${ALLWARMUPDURATION:-0}</allwarmupduration>|" "$GAME_SETTINGS"
echo "Platzhalter erfolgreich ersetzt."
fi
# ============================================================
# AdminServ ServerOptions: Exportierte Einstellungen anwenden
# ============================================================
# Falls ein AdminServ-Export in GameData/Config/AdminServ/ServerOptions/
# vorhanden ist, werden die darin enthaltenen Werte (z.B. Servername,
# Beschreibung, Spielerzahl) in die dedicated_cfg.txt uebernommen.
# So bleiben Aenderungen, die ueber AdminServ vorgenommen und exportiert
# wurden, auch nach einem Container-Neustart erhalten.
# ============================================================
ADMINSERV_OPTIONS_DIR="$GAMEDATA_DIR/Config/AdminServ/ServerOptions"
if [ -d "$ADMINSERV_OPTIONS_DIR" ]; then
LATEST_EXPORT=$(ls -t "$ADMINSERV_OPTIONS_DIR"/*.txt "$ADMINSERV_OPTIONS_DIR"/*.xml 2>/dev/null | head -1)
if [ -n "$LATEST_EXPORT" ] && [ -f "$LATEST_EXPORT" ]; then
echo "==> AdminServ ServerOptions-Export gefunden: $(basename "$LATEST_EXPORT")"
echo " Uebernehme exportierte Einstellungen in dedicated_cfg.txt..."
php -r '
$xmlFile = $argv[1];
$cfgFile = $argv[2];
// AdminServ-Export parsen
$dom = new DOMDocument();
if (!@$dom->load($xmlFile)) {
echo " WARNUNG: AdminServ-Export konnte nicht gelesen werden.\n";
exit(0);
}
$root = $dom->documentElement;
$exportValues = [];
foreach ($root->childNodes as $node) {
if ($node->nodeType === XML_ELEMENT_NODE) {
$exportValues[$node->nodeName] = $node->nodeValue;
}
}
// Mapping: AdminServ-XML-Feld => dedicated_cfg.txt-Feld
$mapping = [
"Name" => "name",
"Comment" => "comment",
"HideServer" => "hide_server",
"NextMaxPlayers" => "max_players",
"Password" => "password",
"PasswordForSpectator" => "password_spectator",
"NextMaxSpectators" => "max_spectators",
"NextLadderMode" => "ladder_mode",
"NextCallVoteTimeOut" => "callvote_timeout",
"CallVoteRatio" => "callvote_ratio",
"AllowChallengeDownload" => "allow_challenge_download",
"AutoSaveReplays" => "autosave_replays",
"IsP2PUpload" => "enable_p2p_upload",
"IsP2PDownload" => "enable_p2p_download",
];
// Bool-Felder: 1/0 => True/False (dedicated_cfg.txt-Format)
$boolFields = [
"allow_challenge_download", "autosave_replays",
"enable_p2p_upload", "enable_p2p_download",
];
// Ladder-Modus: 0 => inactive, 1 => forced
$ladderMap = ["0" => "inactive", "1" => "forced"];
// Zu ersetzende Werte aufbauen
$replacements = [];
foreach ($mapping as $xmlField => $cfgField) {
if (isset($exportValues[$xmlField])) {
$value = $exportValues[$xmlField];
if (in_array($cfgField, $boolFields)) {
$value = ($value == "1" || strtolower($value) === "true") ? "True" : "False";
}
if ($cfgField === "ladder_mode" && isset($ladderMap[$value])) {
$value = $ladderMap[$value];
}
$replacements[$cfgField] = $value;
}
}
if (empty($replacements)) {
echo " Keine anwendbaren Einstellungen im Export gefunden.\n";
exit(0);
}
// dedicated_cfg.txt zeilenweise verarbeiten
// Nur Tags innerhalb von <server_options> werden ersetzt,
// damit <name> und <password> in <authorization_levels> unangetastet bleiben.
$lines = file($cfgFile);
$inServerOptions = false;
$updated = 0;
foreach ($lines as $i => $line) {
if (strpos($line, "<server_options>") !== false) {
$inServerOptions = true;
}
if (strpos($line, "</server_options>") !== false) {
$inServerOptions = false;
}
if ($inServerOptions) {
foreach ($replacements as $field => $value) {
$pattern = "/(<" . preg_quote($field, "/") . ">)[^<]*(<\/" . preg_quote($field, "/") . ">)/";
if (preg_match($pattern, $line)) {
$safeValue = htmlspecialchars($value, ENT_XML1 | ENT_QUOTES, "UTF-8");
// $ und \ im Replacement escapen, damit preg_replace
// sie nicht als Backreferences interpretiert (wichtig
// fuer TM-Farbcodes wie $03F, $z, $s etc.)
$escapedValue = str_replace(["\\", "$"], ["\\\\", "\\$"], $safeValue);
$lines[$i] = preg_replace($pattern, "\${1}" . $escapedValue . "\${2}", $line, 1);
echo " " . $field . " => " . $value . "\n";
$updated++;
unset($replacements[$field]);
break;
}
}
}
}
if ($updated > 0) {
file_put_contents($cfgFile, implode("", $lines));
echo " " . $updated . " Einstellung(en) aus AdminServ-Export uebernommen.\n";
}
' "$LATEST_EXPORT" "$CONFIG"
fi
else
echo "==> Kein AdminServ ServerOptions-Verzeichnis gefunden. Ueberspringe Import."
fi
# ============================================================
# MatchSettings: Neueste Datei automatisch ermitteln
# ============================================================
# Ueber die Umgebungsvariable MATCHSETTINGS_FILE kann gesteuert werden,
# welche MatchSettings-Datei beim Serverstart geladen wird:
# - "auto" (Standard): Die neueste .txt-Datei im MatchSettings-Ordner
# wird automatisch anhand des Aenderungsdatums ermittelt.
# - "<dateiname.txt>": Eine bestimmte Datei wird direkt verwendet.
# Fallback: custom_game_settings.txt (Standard-MatchSettings aus dem Image).
# ============================================================
MATCHSETTINGS_DIR="$GAMEDATA_DIR/Tracks/MatchSettings"
MATCHSETTINGS_FILE_ENV="${MATCHSETTINGS_FILE:-auto}"
if [ "$MATCHSETTINGS_FILE_ENV" = "auto" ]; then
echo "==> MatchSettings: Automatische Erkennung (MATCHSETTINGS_FILE=auto)..."
# Neueste .txt-Datei im MatchSettings-Ordner anhand des Aenderungsdatums ermitteln
NEWEST_MS=$(ls -t "$MATCHSETTINGS_DIR"/*.txt 2>/dev/null | head -1)
if [ -n "$NEWEST_MS" ] && [ -f "$NEWEST_MS" ]; then
MS_FILENAME=$(basename "$NEWEST_MS")
GAME_SETTINGS_PATH="MatchSettings/${MS_FILENAME}"
echo " Neueste MatchSettings gefunden: ${MS_FILENAME}"
echo " Aenderungsdatum: $(stat -c '%y' "$NEWEST_MS" 2>/dev/null || ls -la "$NEWEST_MS" | awk '{print $6, $7, $8}')"
else
GAME_SETTINGS_PATH="MatchSettings/custom_game_settings.txt"
echo " Keine .txt-Dateien in ${MATCHSETTINGS_DIR} gefunden."
echo " Fallback: ${GAME_SETTINGS_PATH}"
fi
else
# Explizit angegebene Datei verwenden
if [ -f "$MATCHSETTINGS_DIR/$MATCHSETTINGS_FILE_ENV" ]; then
GAME_SETTINGS_PATH="MatchSettings/${MATCHSETTINGS_FILE_ENV}"
echo "==> MatchSettings: Verwende explizit gesetzte Datei: ${MATCHSETTINGS_FILE_ENV}"
else
echo "==> WARNUNG: Angegebene MatchSettings-Datei nicht gefunden: ${MATCHSETTINGS_FILE_ENV}"
echo " Vorhandene Dateien in ${MATCHSETTINGS_DIR}:"
ls -la "$MATCHSETTINGS_DIR"/*.txt 2>/dev/null || echo " (keine .txt-Dateien vorhanden)"
GAME_SETTINGS_PATH="MatchSettings/custom_game_settings.txt"
echo " Fallback: ${GAME_SETTINGS_PATH}"
fi
fi
echo " Aktive MatchSettings: ${GAME_SETTINGS_PATH}"
# Bestimme Server-Modus (Standard: internet)
SERVER_MODE="${SERVER_MODE:-internet}"
if [ "$SERVER_MODE" = "internet" ]; then
echo "Configuring Internet-Dedicated mode"
if [ -z "$SERVER_LOGIN" ] || [ -z "$SERVER_VALIDATION_KEY" ]; then
echo "ERROR: SERVER_LOGIN and SERVER_VALIDATION_KEY are required for Internet-Dedicated mode."
echo "Set SERVER_MODE=lan to start in LAN mode, or provide the required variables."
exit 1
fi
LAUNCH_MODE="/internet"
else
echo "Configuring LAN-Dedicated mode"
LAUNCH_MODE="/lan"
fi
echo "Server config dedicated_cfg.txt is"
cat "$CONFIG"
echo "Launching Server in ${SERVER_MODE} mode (MatchSettings: ${GAME_SETTINGS_PATH})"
./TrackmaniaServer /dedicated_cfg=dedicated_cfg.txt /game_settings=${GAME_SETTINGS_PATH} /nodaemon ${LAUNCH_MODE} &
TM_PID=$!
echo "TrackmaniaServer gestartet (PID: ${TM_PID})"
# ============================================================
# Warte auf XMLRPC-Verfuegbarkeit
# ============================================================
# Sowohl XAseco als auch Forced Mods benoetigen eine aktive
# XMLRPC-Verbindung zum TrackmaniaServer.
# ============================================================
echo "==> Warte auf TrackmaniaServer XMLRPC..."
XMLRPC_PORT="${SERVER_XMLRPC_PORT:-5000}"
XMLRPC_READY=false
for i in $(seq 1 30); do
if php -r "@fsockopen('127.0.0.1', ${XMLRPC_PORT}, \$e, \$m, 2) ? exit(0) : exit(1);" 2>/dev/null; then
XMLRPC_READY=true
break
fi
sleep 2
done
if [ "$XMLRPC_READY" = "true" ]; then
echo " XMLRPC-Port ${XMLRPC_PORT} erreichbar."
else
echo " WARNUNG: XMLRPC-Port ${XMLRPC_PORT} nicht erreichbar nach 60s!"
fi
# ============================================================
# Forced Mods (Skins) per XMLRPC setzen
# ============================================================
# Wenn FORCE_MOD_*-Variablen gesetzt sind, werden die
# entsprechenden Mods per SetForcedMods-XMLRPC-Aufruf beim
# Serverstart automatisch aktiviert. Dies funktioniert bei
# jedem Containerstart und ist unabhaengig von FORCE_CONFIG_UPDATE.
# ============================================================
# Pruefen, ob mindestens ein Mod gesetzt ist (Variable fuer spaeter)
HAS_MODS=false
if [ "$XMLRPC_READY" = "true" ]; then
for ENV_NAME in FORCE_MOD_STADIUM FORCE_MOD_ISLAND FORCE_MOD_BAY FORCE_MOD_COAST FORCE_MOD_SPEED FORCE_MOD_ALPINE FORCE_MOD_RALLY; do
eval "MOD_VAL=\${$ENV_NAME:-}"
if [ -n "$MOD_VAL" ]; then
HAS_MODS=true
break
fi
done
fi
# ============================================================
# XAseco starten (nach TrackmaniaServer, VOR Forced Mods)
# ============================================================
# XAseco muss zuerst starten und sich initialisieren, damit
# es die Forced Mods nicht ueberschreibt oder zuruecksetzt.
# ============================================================
if [ "$XMLRPC_READY" = "true" ]; then
if [ "${XASECO_ENABLED:-true}" = "true" ] && [ -f "/opt/tmserver/xaseco/aseco.php" ]; then
echo "==> Starte XAseco..."
cd /opt/tmserver/xaseco
php aseco.php TMN </dev/null >>aseco.log 2>&1 &
XASECO_PID=$!
echo " XAseco gestartet (PID: ${XASECO_PID})"
cd /opt/tmserver
elif [ "${XASECO_ENABLED:-true}" != "true" ]; then
echo "==> XAseco ist deaktiviert (XASECO_ENABLED=${XASECO_ENABLED})."
fi
else
echo " WARNUNG: XMLRPC nicht erreichbar - XAseco und Forced Mods wurden NICHT gestartet."
fi
# ============================================================
# Forced Mods (Skins) per XMLRPC setzen
# ============================================================
# Wird NACH XAseco-Start ausgefuehrt, damit XAseco die Mods
# nicht bei seiner Initialisierung zuruecksetzt.
# ============================================================
if [ "$XMLRPC_READY" = "true" ] && [ "$HAS_MODS" = "true" ]; then
# Warten, damit XAseco und der TM-Server sich vollstaendig initialisieren
echo "==> Forced Mods: Warte 10 Sekunden auf vollstaendige Server-Initialisierung..."
sleep 10
echo "==> Forced Mods: Setze Mods per XMLRPC..."
# JSON-Array der Mods aufbauen
MODS_JSON="["
MODS_FIRST=true
for PAIR in "FORCE_MOD_STADIUM:Stadium" "FORCE_MOD_ISLAND:Island" "FORCE_MOD_BAY:Bay" "FORCE_MOD_COAST:Coast" "FORCE_MOD_SPEED:Speed" "FORCE_MOD_ALPINE:Alpine" "FORCE_MOD_RALLY:Rally"; do
VAR_NAME="${PAIR%%:*}"
ENV_NAME="${PAIR##*:}"
eval "MOD_URL=\${$VAR_NAME:-}"
if [ -n "$MOD_URL" ]; then
if [ "$MODS_FIRST" = "true" ]; then
MODS_FIRST=false
else
MODS_JSON="${MODS_JSON},"
fi
# URL fuer JSON escapen (Backslash und Anfuehrungszeichen)
SAFE_URL=$(printf '%s' "$MOD_URL" | sed 's/\\/\\\\/g; s/"/\\"/g')
MODS_JSON="${MODS_JSON}{\"Env\":\"${ENV_NAME}\",\"Url\":\"${SAFE_URL}\"}"
echo " ${ENV_NAME} => ${MOD_URL}"
fi
done
MODS_JSON="${MODS_JSON}]"
# GBXRemote2-Protokoll: Authenticate + SetForcedMods
SA_PW_MODS="${SERVER_SA_PASSWORD:-SuperAdmin}"
php -r '
$port = (int)$argv[1];
$password = $argv[2];
$modsJson = $argv[3];
$mods = json_decode($modsJson, true);
if (empty($mods)) { echo " Keine Mods zu setzen.\n"; exit(0); }
// GBXRemote2: Verbindung herstellen
$fp = @fsockopen("127.0.0.1", $port, $errno, $errstr, 5);
if (!$fp) { echo " FEHLER: Verbindung zu XMLRPC fehlgeschlagen ($errno: $errstr).\n"; exit(1); }
stream_set_timeout($fp, 10);
// Handshake lesen (4 Bytes Laenge + Protokollstring)
$data = fread($fp, 4);
if (strlen($data) < 4) { echo " FEHLER: Handshake fehlgeschlagen.\n"; fclose($fp); exit(1); }
$info = unpack("Vsize", $data);
$handshake = fread($fp, $info["size"]);
if (strpos($handshake, "GBXRemote") === false) {
echo " FEHLER: Kein GBXRemote-Protokoll.\n"; fclose($fp); exit(1);
}
echo " Protokoll: $handshake\n";
$reqhandle = 0x80000001;
// XML-RPC-Wert kodieren
function encodeVal($v) {
if (is_bool($v)) return "<value><boolean>" . ($v ? "1" : "0") . "</boolean></value>";
if (is_int($v)) return "<value><int>" . $v . "</int></value>";
if (is_string($v)) return "<value><string>" . htmlspecialchars($v, ENT_XML1) . "</string></value>";
if (is_array($v)) {
if (array_keys($v) !== range(0, count($v) - 1)) {
$x = "<value><struct>";
foreach ($v as $k => $val) $x .= "<member><name>" . $k . "</name>" . encodeVal($val) . "</member>";
return $x . "</struct></value>";
} else {
$x = "<value><array><data>";
foreach ($v as $val) $x .= encodeVal($val);
return $x . "</data></array></value>";
}
}
return "<value><string>" . htmlspecialchars((string)$v, ENT_XML1) . "</string></value>";
}
// Ein einzelnes Paket vom Server lesen (Header + Body)
function readPacket($fp) {
$header = "";
while (strlen($header) < 8) {
$chunk = @fread($fp, 8 - strlen($header));
if ($chunk === false || strlen($chunk) === 0) return false;
$header .= $chunk;
}
$info = unpack("Vsize/Vhandle", $header);
$size = $info["size"];
$handle = $info["handle"];
if ($size > 4194304 || $size == 0) return false;
$body = "";
$remaining = $size;
while ($remaining > 0) {
$chunk = @fread($fp, min($remaining, 8192));
if ($chunk === false || strlen($chunk) === 0) break;
$body .= $chunk;
$remaining -= strlen($chunk);
}
return ["handle" => $handle, "body" => $body];
}
// XMLRPC-Request senden und Antwort lesen
// Callbacks (handle < 0x80000000) werden uebersprungen
function gbxQuery($fp, &$reqhandle, $method, $params) {
$xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
. "<methodCall><methodName>" . $method . "</methodName><params>";
foreach ($params as $p) $xml .= "<param>" . encodeVal($p) . "</param>";
$xml .= "</params></methodCall>";
$myHandle = $reqhandle++;
$packet = pack("VV", strlen($xml), $myHandle) . $xml;
$written = @fwrite($fp, $packet);
if ($written === false || $written === 0) {
echo " FEHLER: Konnte Request nicht senden ($method).\n";
return false;
}
// Auf Antwort warten, Callbacks ueberspringen
for ($attempt = 0; $attempt < 30; $attempt++) {
$pkt = readPacket($fp);
if ($pkt === false) {
echo " FEHLER: Keine Antwort fuer $method.\n";
return false;
}
// Callback? (Handle < 0x80000000) -> ueberspringen
if ($pkt["handle"] < 0x80000000) {
continue;
}
// Response gefunden
return $pkt["body"];
}
echo " FEHLER: Zu viele Callbacks, keine Antwort fuer $method.\n";
return false;
}
// 1. Authenticate
echo " Authentifiziere als SuperAdmin...\n";
$resp = gbxQuery($fp, $reqhandle, "Authenticate", ["SuperAdmin", $password]);
if ($resp === false || strpos($resp, "<boolean>1</boolean>") === false) {
echo " FEHLER: Authentifizierung fehlgeschlagen.\n";
if ($resp) echo " Antwort: " . substr(trim($resp), 0, 500) . "\n";
fclose($fp); exit(1);
}
echo " Authentifizierung erfolgreich.\n";
// 2. EnableCallbacks deaktivieren (weniger Rauschen)
gbxQuery($fp, $reqhandle, "EnableCallbacks", [false]);
// 3. SetForcedMods(override=true, mods=[{Env, Url}, ...])
// Debug: Zeige das XML das wir senden
$setXml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
. "<methodCall><methodName>SetForcedMods</methodName><params>"
. "<param>" . encodeVal(true) . "</param>"
. "<param>" . encodeVal($mods) . "</param>"
. "</params></methodCall>";
echo " Debug SetForcedMods-XML:\n " . $setXml . "\n";
echo " Sende SetForcedMods (" . count($mods) . " Mod(s))...\n";
$resp = gbxQuery($fp, $reqhandle, "SetForcedMods", [true, $mods]);
echo " SetForcedMods-Antwort: " . trim($resp) . "\n";
if ($resp !== false && strpos($resp, "<boolean>1</boolean>") !== false) {
echo " SetForcedMods: OK\n";
} else {
echo " FEHLER: SetForcedMods fehlgeschlagen.\n";
}
// 4. GetForcedMods zur Verifikation (vollstaendige Antwort)
echo " Verifiziere mit GetForcedMods...\n";
$resp = gbxQuery($fp, $reqhandle, "GetForcedMods", []);
echo " GetForcedMods-Antwort:\n " . trim($resp) . "\n";
fclose($fp);
' "$XMLRPC_PORT" "$SA_PW_MODS" "$MODS_JSON"
elif [ "$XMLRPC_READY" = "true" ]; then
echo "==> Forced Mods: Keine FORCE_MOD_*-Variablen gesetzt. Ueberspringe."
fi
# Auf TrackmaniaServer warten (Hauptprozess)
wait $TM_PID
File renamed without changes.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,76 @@
<?php
// INCLUDES
session_start();
if( !isset($_SESSION['adminserv']['sid']) ){ exit; }
$configPath = '../../'.$_SESSION['adminserv']['path'].'config/';
require_once $configPath.'adminlevel.cfg.php';
require_once $configPath.'adminserv.cfg.php';
require_once $configPath.'extension.cfg.php';
require_once $configPath.'servers.cfg.php';
require_once '../core/adminserv.php';
AdminServConfig::$PATH_RESOURCES = '../';
AdminServ::getClass();
AdminServUI::lang();
// ISSET
if( isset($_GET['path']) ){ $path = addslashes($_GET['path']); }else{ $path = null; }
if( isset($_GET['d']) ){ $directory = addslashes($_GET['d']); }else{ $directory = null; }
if( isset($_GET['op']) ){ $operation = addslashes($_GET['op']); }else{ $operation = null; }
if( isset($_GET['select']) ){ $selection = $_GET['select']; }else{ $selection = null; }
// DATA
$out = null;
if( AdminServ::initialize() && $path != null ){
// Maps
if($path == 'currentServerSelection'){
$mapsImport = AdminServ::getMapList();
}
else{
// FIX: Den relativen Pfad aus dem absoluten Pfad (Dropdown-Auswahl)
// berechnen, statt den URL-Parameter 'd' (= MatchSettings-Speicherordner)
// zu verwenden. Sonst wird z.B. "MatchSettings/" statt "Challenges/Downloaded/"
// als Pfad-Praefix in die MatchSettings-Datei geschrieben.
$mapsDirectoryPath = AdminServ::getMapsDirectoryPath();
$relativePath = str_replace($mapsDirectoryPath, '', Str::toSlash($path));
// Sicherstellen, dass der Pfad mit / endet (wenn nicht leer)
if($relativePath && substr($relativePath, -1) !== '/'){
$relativePath .= '/';
}
$currentDir = Folder::read($path, AdminServConfig::$MATCHSET_HIDDEN_FOLDERS, AdminServConfig::$MATCHSET_EXTENSION, intval(AdminServConfig::RECENT_STATUS_PERIOD * 3600) );
$mapsImport = AdminServ::getLocalMapList($currentDir, $relativePath);
}
// Faire une sélection
if($operation == 'setSelection'){
// On supprime les maps non sélectionnées
if( $selection != null && count($selection) > 0 ){
foreach($mapsImport['lst'] as $id => $values){
if( !in_array($id, $selection) ){
unset($mapsImport['lst'][$id]);
}
}
}
else{
foreach($mapsImport['lst'] as $id => $values){
unset($mapsImport['lst'][$id]);
}
}
}
// Enregistrement de la sélection du MatchSettings
if($operation != 'getSelection'){
AdminServ::saveMatchSettingSelection($mapsImport);
}
$client->Terminate();
}
// OUT
if($operation == 'getSelection'){
echo json_encode($mapsImport);
}
else{
echo json_encode($_SESSION['adminserv']['matchset_maps_selected']);
}
?>
@@ -0,0 +1,160 @@
<?php
// ENREGISTREMENT
if( isset($_POST['savematchsetting']) && isset($_SESSION['adminserv']['matchset_maps_selected']) ){
// Filename
$matchSettingName = Str::replaceChars($_POST['matchSettingName']);
$filename = $data['mapsDirectoryPath'].$args['directory'].$matchSettingName;
if(File::getExtension($matchSettingName) != 'txt'){
$filename .= '.txt';
}
$struct = array();
// Gameinfos
$gameinfos = AdminServ::getGameInfosStructFromPOST();
$struct['gameinfos'] = array(
'game_mode' => $gameinfos['GameMode'],
'chat_time' => $gameinfos['ChatTime'],
'finishtimeout' => $gameinfos['FinishTimeout'],
'allwarmupduration' => $gameinfos['AllWarmUpDuration'],
'disablerespawn' => $gameinfos['DisableRespawn'],
'forceshowallopponents' => $gameinfos['ForceShowAllOpponents'],
'rounds_pointslimit' => $gameinfos['RoundsPointsLimit'],
'rounds_custom_points' => $gameinfos['RoundCustomPoints'],
'rounds_usenewrules' => $gameinfos['RoundsUseNewRules'],
'rounds_forcedlaps' => $gameinfos['RoundsForcedLaps'],
'rounds_pointslimitnewrules' => $gameinfos['RoundsPointsLimitNewRules'],
'team_pointslimit' => $gameinfos['TeamPointsLimit'],
'team_maxpoints' => $gameinfos['TeamMaxPoints'],
'team_usenewrules' => $gameinfos['TeamUseNewRules'],
'team_pointslimitnewrules' => $gameinfos['TeamPointsLimitNewRules'],
'timeattack_limit' => $gameinfos['TimeAttackLimit'],
'timeattack_synchstartperiod' => $gameinfos['TimeAttackSynchStartPeriod'],
'laps_nblaps' => $gameinfos['LapsNbLaps'],
'laps_timelimit' => $gameinfos['LapsTimeLimit'],
'cup_pointslimit' => $gameinfos['CupPointsLimit'],
'cup_roundsperchallenge' => $gameinfos['CupRoundsPerMap'],
'cup_nbwinners' => $gameinfos['CupNbWinners'],
'cup_warmupduration' => $gameinfos['CupWarmUpDuration']
);
if(SERVER_VERSION_NAME != 'TmForever'){
$struct['gameinfos']['script_name'] = $gameinfos['ScriptName'];
}
// HotSeat
$struct['hotseat'] = array(
'game_mode' => intval($_POST['hotSeatGameMode']),
'time_limit' => TimeDate::secToMillisec( intval($_POST['hotSeatTimeLimit']) ),
'rounds_count' => intval($_POST['hotSeatCountRound'])
);
// Filter
$struct['filter'] = array(
'is_lan' => array_key_exists('filterIsLan', $_POST),
'is_internet' => array_key_exists('filterIsInternet', $_POST),
'is_solo' => array_key_exists('filterIsSolo', $_POST),
'is_hotseat' => array_key_exists('filterIsHotSeat', $_POST),
'sort_index' => intval($_POST['filterSortIndex']),
'random_map_order' => array_key_exists('filterRandomMaps', $_POST),
'force_default_gamemode' => intval($_POST['filterDefaultGameMode']),
);
// ScriptSettings (nur fuer ManiaPlanet/TM2, nicht fuer TmForever)
// TmForever kennt die Methode 'GetModeScriptInfo' nicht (Fehler -506).
if(SERVER_VERSION_NAME != 'TmForever'){
if( !$client->query('GetModeScriptInfo') ){
AdminServ::error();
}
else{
$scriptsettings = $client->getResponse();
if( !empty($scriptsettings['ParamDescs']) ){
foreach($scriptsettings['ParamDescs'] as $param){
$struct['scriptsettings'][] = array(
'name' => $param['Name'],
'type' => $param['Type'],
'value' => $param['Default']
);
}
}
}
}
// Maps
$struct['startindex'] = 1;
$maps = $_SESSION['adminserv']['matchset_maps_selected']['lst'];
if( isset($maps) && is_array($maps) && !empty($maps) ){
$mapsField = (SERVER_VERSION_NAME == 'TmForever') ? 'challenge' : 'map';
foreach($maps as $id => $values){
$struct[$mapsField][$values['UId']] = $values['FileName'];
}
}
// Enregistrement
if( ($result = AdminServ::saveMatchSettings($filename, $struct)) !== true ){
AdminServ::error(Utils::t('Unable to save the MatchSettings').' : '.$matchSettingName.' ('.$result.')');
}
else{
$action = Utils::t('The MatchSettings "!matchSettingName" was successfully created in the folder', array('!matchSettingName' => $matchSettingName)).' : '.$data['mapsDirectoryPath'].$args['directory'];
AdminServ::info($action);
AdminServLogs::add('action', $action);
Utils::redirection(false, '?p='.USER_PAGE .$hasDirectory);
}
}
else{
if( !isset($_GET['f']) ){
unset($_SESSION['adminserv']['matchset_maps_selected']);
}
}
// LECTURE
$data['directoryList'] = Folder::getArborescence($data['mapsDirectoryPath'], AdminServConfig::$MAPS_HIDDEN_FOLDERS, substr_count($data['mapsDirectoryPath'], '/'));
$data['matchSettings'] = array();
// Édition
if( isset($_GET['f']) && $_GET['f'] != null ){
$data['pageTitle'] = Utils::t('Edit');
$data['matchSettings']['name'] = $_GET['f'];
$matchSettingsData = AdminServ::getMatchSettingsData($data['mapsDirectoryPath'].$args['directory'].$data['matchSettings']['name']);
$data['gameInfos'] = array(
'curr' => null,
'next' => $matchSettingsData['gameinfos']
);
unset($matchSettingsData['gameinfos']);
$data['matchSettings'] += $matchSettingsData;
if( isset($data['matchSettings']['maps']) ){
$maps = AdminServ::getMapListFromMatchSetting($data['matchSettings']['maps']);
$data['matchSettings']['nbm'] = $maps['nbm']['count'];
$_SESSION['adminserv']['matchset_maps_selected'] = $maps;
}
else{
$data['matchSettings']['nbm'] = 0;
}
}
else{
$data['pageTitle'] = Utils::t('Create');
$data['matchSettings']['name'] = 'match_settings';
$gameInfos = AdminServ::getGameInfos();
$data['gameInfos'] = array(
'curr' => null,
'next' => $gameInfos['next']
);
$data['matchSettings']['hotseat'] = array(
'GameMode' => 1,
'TimeLimit' => 300000,
'RoundsCount' => 5
);
$data['matchSettings']['filter'] = array(
'IsLan' => 1,
'IsInternet' => 1,
'IsSolo' => 0,
'IsHotseat' => 1,
'SortIndex' => 1000,
'RandomMapOrder' => 0,
'ForceDefaultGameMode' => 1
);
$data['matchSettings']['StartIndex'] = 0;
$data['matchSettings']['nbm'] = 0;
}
?>
@@ -4,7 +4,7 @@
<game_mode>1</game_mode>
<chat_time>10000</chat_time>
<finishtimeout>1</finishtimeout>
<allwarmupduration>1</allwarmupduration>
<allwarmupduration>0</allwarmupduration>
<disablerespawn>0</disablerespawn>
<forceshowallopponents>0</forceshowallopponents>
<rounds_pointslimit>30</rounds_pointslimit>
+92
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>%%SERVER_USER_PASSWORD%%</password>
</level>
</authorization_levels>
<masterserver_account>
<login>%%SERVER_LOGIN%%</login>
<password>%%SERVER_LOGIN_PASSWORD%%</password>
<validation_key>%%SERVER_VALIDATION_KEY%%</validation_key>
</masterserver_account>
<server_options>
<name>%%SERVER_NAME%%</name>
<comment>%%SERVER_DESC%%</comment>
<hide_server>%%SERVER_HIDE%%</hide_server> <!-- value is 0 (always shown), 1 (always hidden), 2 (hidden from nations) -->
<max_players>%%SERVER_MAX_PLAYERS%%</max_players>
<password>%%SERVER_PASSWORD%%</password>
<max_spectators>%%SERVER_MAX_SPECTATORS%%</max_spectators>
<password_spectator>%%SERVER_SPEC_PASSWORD%%</password_spectator>
<ladder_mode>%%SERVER_LADDER_MODE%%</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>%%SERVER_LADDER_LIMIT_MAX%%</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>False</allow_challenge_download>
<autosave_replays>True</autosave_replays>
<autosave_validation_replays>True</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>%%SERVER_UPLOAD_RATE%%</connection_uploadrate> <!-- Kbps (kilo bits per second) -->
<connection_downloadrate>%%SERVER_DOWNLOAD_RATE%%</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>%%SERVER_XMLRPC_PORT%%</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>
@@ -0,0 +1,135 @@
<?php
/**
* remoteCP 4
* ütf-8 release
*
* @package remoteCP
* @author hal.sascha
* @copyright (c) 2006-2009
* @version 4.0.3.5
*
* Patched by tmserver-docker:
* - Bare-constant-Zugriffe (pt_custom, pt_points, ...) durch
* defined()-Pruefungen ersetzt, um PHP 7.2+ Warnungen zu vermeiden.
*/
class CustomPoints extends rcp_plugin
{
public $display = 'side';
public $title = 'Points';
public $author = 'hal.ko.sascha';
public $version = '4.0.3.5';
public $nservstatus = array(2,3,4,5);
public $vpermissions = array('editgamesettings');
public $apermissions = array(
'setPoints' => 'editgamesettings',
'setPointsPreset' => 'editgamesettings'
);
public $presets;
public function onLoad()
{
$this->presets = Core::getObject('session')->loadXML(
Core::getSetting('pluginpath') . $this->id . '/presets.xml'
);
}
public function onOutput()
{
$CustomPoints = array();
if (Core::getObject('gbx')->query('GetRoundCustomPoints')) {
$CustomPoints = Core::getObject('gbx')->getResponse();
}
if (!is_array($CustomPoints)) {
$CustomPoints = array();
}
if (!Core::getObject('session')->checkPerm('editgamesettings')) {
return;
}
echo "<form action='ajax.php' method='post' id='custompoints' name='custompoints' class='postcmd' rel='{$this->display}area'>";
echo "<fieldset>";
echo "<div class='legend'>" . (defined('pt_custom') ? pt_custom : 'Custom Points') . "</div>";
echo "<div class='f-row'>
<label for='points'>" . (defined('pt_points') ? pt_points : 'Points') . "</label>
<div class='f-field'>
<input type='text' name='points' id='points' value='" . implode(',', $CustomPoints) . "' />
<div class='small'>" . (defined('pt_commasep') ? pt_commasep : 'Comma separated') . "</div>
</div>";
echo "</div>";
echo "</fieldset>";
echo "<input type='hidden' name='plugin' value='{$this->id}' />";
echo "<input type='hidden' name='action' value='setPoints' />";
echo "<button type='submit' title='" . (defined('ct_submit') ? ct_submit : 'Submit') . "' class='wide'>" . (defined('ct_submit') ? ct_submit : 'Submit') . "</button>";
echo "</form>";
echo "<form action='ajax.php' method='post' id='custompointspreset' name='custompointspreset' class='postcmd' rel='{$this->display}area'>";
echo "<fieldset>";
echo "<div class='legend'>" . (defined('pt_presets') ? pt_presets : 'Presets') . "</div>";
if ($this->presets && is_object($this->presets)) {
foreach ($this->presets->children() as $preset) {
$name = isset($preset['name']) ? $preset['name'] : 'Preset';
$points = isset($preset['points']) ? (string)$preset['points'] : '';
echo "<div class='f-row'>
<label>{$name}</label>
<div class='f-field'>";
echo "<input style='width:25px;' type='radio' name='preset' value='{$points}' /> ";
if (strlen($points) > 25) {
echo substr($points, 0, 25) . "...";
} else {
echo $points;
}
echo "</div>";
echo "</div>";
}
}
echo "</fieldset>";
echo "<input type='hidden' name='plugin' value='{$this->id}' />";
echo "<input type='hidden' name='action' value='setPointsPreset' />";
echo "<button type='submit' title='" . (defined('ct_submit') ? ct_submit : 'Submit') . "' class='wide'>" . (defined('ct_submit') ? ct_submit : 'Submit') . "</button>";
echo "</form>";
}
public function setPoints()
{
if (!array_key_exists('points', $_REQUEST)) return;
$str = preg_replace("/[^0-9,]/", "", $_REQUEST['points']);
$array = $this->makeIntArray(explode(',', $str));
Core::getObject('actions')->add('SetRoundCustomPoints', $array, true);
}
public function setPointsPreset()
{
if (!array_key_exists('preset', $_REQUEST)) return;
$str = preg_replace("/[^0-9,]/", "", $_REQUEST['preset']);
$array = $this->makeIntArray(explode(',', $str));
Core::getObject('actions')->add('SetRoundCustomPoints', $array, true);
}
private function makeIntArray($array)
{
foreach ($array as $key => $value) {
$array[$key] = (int)$value;
}
return $array;
}
}
@@ -0,0 +1,320 @@
<?php
/**
* remoteCP 4
* ütf-8 release
*
* @package remoteCP
* @author hal.sascha
* @copyright (c) 2006-2009
* @version 4.0.2.6
*
* Patch: foreach()-Warnungen behoben – leere Umgebungen (Island, Bay, …)
* fuehrten zu "Invalid argument supplied for foreach()", weil
* $this->mods['Env'] nicht initialisiert war.
* Zusaetzlich bare-constant-Warnungen (pt_*) mit defined()-Pruefungen entschaerft.
*/
class Mods extends rcp_plugin
{
public $display = 'side';
public $title = 'Mods';
public $author = 'hal.ko.sascha';
public $version = '4.0.3.5';
public $nservstatus = array(2,3,4,5);
public $vpermissions = array('editserversettings');
public $apermissions = array(
'setMods' => 'editserversettings',
'setMusic' => 'editserversettings'
);
private $mods = array();
private $music = array();
/**
* Alle bekannten Umgebungs-Keys mit leeren Arrays vorbelegen,
* damit foreach() auch bei fehlenden Eintraegen nicht warnt.
*/
private function initModDefaults()
{
$envs = array('Stadium', 'Island', 'Bay', 'Coast', 'Speed', 'Alpine', 'Rally');
foreach($envs as $env) {
if(!isset($this->mods[$env])) {
$this->mods[$env] = array();
}
}
}
public function onLoadSettings($settings)
{
// Set defaults
$this->mods = array();
$this->music = array();
// Alle Umgebungen vorinitialisieren
$this->initModDefaults();
// Read mods settings
if(!$settings->mods) return;
foreach($settings->mods->children() AS $env)
{
if(!$env) continue;
$tmp = (string) $env->getName();
if(!isset($this->mods[$tmp])) {
$this->mods[$tmp] = array();
}
foreach($env->children() AS $item)
{
$this->mods[$tmp][] = array(
'url' => (string) $item,
'name' => (string) $item['name']
);
}
}
// Read music settings
if(!$settings->music) return;
foreach($settings->music->children() AS $song)
{
$this->music[] = array(
'url' => (string) $song,
'name' => (string) $song['name']
);
}
}
public function onOutput() {
if(Core::getObject('gbx')->query('GetForcedMods')) {
$ForcedMods = Core::getObject('gbx')->getResponse();
if(!empty($ForcedMods)) {
echo "<fieldset>";
echo "<div class='legend'>".(defined('pt_forcedmods') ? pt_forcedmods : 'Forced Mods')."</div>";
if(is_array($ForcedMods['Mods'])) {
foreach($ForcedMods['Mods'] as $mod)
{
echo "<div class='f-row'>
<label>{$mod['Env']}</label>
<div class='f-field'>". $this->getModName($mod['Env'], $mod['Url']) ."</div>";
echo "</div>";
}
}
echo "</fieldset>";
}
}
echo "<form action='ajax.php' method='post' id='forcemods' name='forcemods' class='postcmd' rel='{$this->display}area'>";
echo "<fieldset>";
echo "<div class='legend'>".(defined('pt_forcemods') ? pt_forcemods : 'Force Mods')."</div>";
// --- Stadium ---
echo " <div class='f-row'>
<label for='points'>".(defined('pt_stadium') ? pt_stadium : 'Stadium')."</label>
<div class='f-field'>
<select name='modstadium'>";
echo "<option value='0'>".(defined('pt_none') ? pt_none : 'None')."</option>";
if(!empty($this->mods['Stadium'])) {
foreach($this->mods['Stadium'] AS $mod)
{
echo "<option value='{$mod['url']}'>{$mod['name']}</option>";
}
}
echo " </select>
</div>
</div>";
// --- Island ---
echo " <div class='f-row'>
<label for='points'>".(defined('pt_island') ? pt_island : 'Island')."</label>
<div class='f-field'>
<select name='modisland'>";
echo "<option value='0'>".(defined('pt_none') ? pt_none : 'None')."</option>";
if(!empty($this->mods['Island'])) {
foreach($this->mods['Island'] AS $mod)
{
echo "<option value='{$mod['url']}'>{$mod['name']}</option>";
}
}
echo " </select>
</div>
</div>";
// --- Bay ---
echo "<div class='f-row'>
<label for='points'>".(defined('pt_bay') ? pt_bay : 'Bay')."</label>
<div class='f-field'>
<select name='modbay'>";
echo "<option value='0'>".(defined('pt_none') ? pt_none : 'None')."</option>";
if(!empty($this->mods['Bay'])) {
foreach($this->mods['Bay'] AS $mod)
{
echo "<option value='{$mod['url']}'>{$mod['name']}</option>";
}
}
echo " </select>
</div>
</div>";
// --- Coast ---
echo " <div class='f-row'>
<label for='points'>".(defined('pt_coast') ? pt_coast : 'Coast')."</label>
<div class='f-field'>
<select name='modcoast'>";
echo "<option value='0'>".(defined('pt_none') ? pt_none : 'None')."</option>";
if(!empty($this->mods['Coast'])) {
foreach($this->mods['Coast'] AS $mod)
{
echo "<option value='{$mod['url']}'>{$mod['name']}</option>";
}
}
echo " </select>
</div>
</div>";
// --- Speed ---
echo " <div class='f-row'>
<label for='points'>".(defined('pt_speed') ? pt_speed : 'Speed')."</label>
<div class='f-field'>
<select name='modspeed'>";
echo "<option value='0'>".(defined('pt_none') ? pt_none : 'None')."</option>";
if(!empty($this->mods['Speed'])) {
foreach($this->mods['Speed'] AS $mod)
{
echo "<option value='{$mod['url']}'>{$mod['name']}</option>";
}
}
echo " </select>
</div>
</div>";
// --- Alpine ---
echo " <div class='f-row'>
<label for='points'>".(defined('pt_alpine') ? pt_alpine : 'Alpine')."</label>
<div class='f-field'>
<select name='modalpine'>";
echo "<option value='0'>".(defined('pt_none') ? pt_none : 'None')."</option>";
if(!empty($this->mods['Alpine'])) {
foreach($this->mods['Alpine'] AS $mod)
{
echo "<option value='{$mod['url']}'>{$mod['name']}</option>";
}
}
echo " </select>
</div>
</div>";
// --- Rally ---
echo " <div class='f-row'>
<label for='points'>".(defined('pt_rally') ? pt_rally : 'Rally')."</label>
<div class='f-field'>
<select name='modrally'>";
echo "<option value='0'>".(defined('pt_none') ? pt_none : 'None')."</option>";
if(!empty($this->mods['Rally'])) {
foreach($this->mods['Rally'] AS $mod)
{
echo "<option value='{$mod['url']}'>{$mod['name']}</option>";
}
}
echo " </select>
</div>
</div>";
echo " <div class='f-row'>
<label for='DisableAllMods'>".(defined('pt_disableall') ? pt_disableall : 'Disable All')."</label>
<div class='f-field'><input type='checkbox' class='checkbox' name='DisableAllMods' /></div>
</div>";
echo "</fieldset>";
echo "<input type='hidden' name='plugin' value='{$this->id}' />";
echo "<input type='hidden' name='action' value='setMods' />";
echo "<button type='submit' title='".(defined('ct_submit') ? ct_submit : 'Submit')."' class='wide'>".(defined('ct_submit') ? ct_submit : 'Submit')."</button>";
echo "</form>";
if(Core::getObject('gbx')->query('GetForcedMusic')) {
$ForcedMusic = Core::getObject('gbx')->getResponse();
if(!empty($ForcedMusic)) {
echo "<fieldset>";
echo "<div class='legend'>".(defined('pt_forcedmusic') ? pt_forcedmusic : 'Forced Music')."</div>";
echo "<div class='f-row'>
<label>{$ForcedMusic['File']}</label>
<div class='f-field'>{$ForcedMusic['Url']}</div>";
echo "</div>";
echo "</fieldset>";
}
}
echo "<form action='ajax.php' method='post' id='forcemusic' name='forcemusic' class='postcmd' rel='{$this->display}area'>";
echo "<fieldset>";
echo "<div class='legend'>".(defined('pt_forcemusic') ? pt_forcemusic : 'Force Music')."</div>";
echo "<div class='f-row'>
<label for='song'>".(defined('pt_song') ? pt_song : 'Song')."</label>
<div class='f-field'>
<select name='song'>";
if(!empty($this->music)) {
foreach($this->music AS $song)
{
echo "<option value='{$song['url']}'>{$song['name']}</option>";
}
}
echo " </select>
</div>";
echo " <div class='f-row'>
<label for='DisableAllMusic'>".(defined('pt_disableall') ? pt_disableall : 'Disable All')."</label>
<div class='f-field'><input type='checkbox' class='checkbox' name='DisableAllMusic' /></div>
</div>";
echo "</fieldset>";
echo "<input type='hidden' name='plugin' value='{$this->id}' />";
echo "<input type='hidden' name='action' value='setMusic' />";
echo "<button type='submit' title='".(defined('ct_submit') ? ct_submit : 'Submit')."' class='wide'>".(defined('ct_submit') ? ct_submit : 'Submit')."</button>";
echo "</form>";
}
public function setMods()
{
$array = array();
$override = true;
if(!array_key_exists('DisableAllMods', $_REQUEST)) {
if(!empty($_REQUEST['modstadium']))
$array[] = array('Env' => 'Stadium', 'Url' => $_REQUEST['modstadium']);
if(!empty($_REQUEST['modisland']))
$array[] = array('Env' => 'Island' , 'Url' => $_REQUEST['modisland']);
if(!empty($_REQUEST['modbay']))
$array[] = array('Env' => 'Bay' , 'Url' => $_REQUEST['modbay']);
if(!empty($_REQUEST['modcoast']))
$array[] = array('Env' => 'Coast' , 'Url' => $_REQUEST['modcoast']);
if(!empty($_REQUEST['modspeed']))
$array[] = array('Env' => 'Speed' , 'Url' => $_REQUEST['modspeed']);
if(!empty($_REQUEST['modalpine']))
$array[] = array('Env' => 'Alpine' , 'Url' => $_REQUEST['modalpine']);
if(!empty($_REQUEST['modrally']))
$array[] = array('Env' => 'Rally' , 'Url' => $_REQUEST['modrally']);
} else {
$override = false;
}
Core::getObject('actions')->add('SetForcedMods', $override, $array);
}
public function setMusic()
{
$url = '';
$override = true;
if(!array_key_exists('DisableAllMusic', $_REQUEST)) {
$url = $_REQUEST['song'];
} else {
$override = false;
}
Core::getObject('actions')->add('SetForcedMusic', $override, $url);
}
private function getModName($env, $url)
{
if(!isset($this->mods[$env]) || !is_array($this->mods[$env])) {
return '';
}
foreach($this->mods[$env] AS $value)
{
if($url == $value['url']) {
return $value['name'];
}
}
return '';
}
}
@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<settings>
<mods>
<!-- Mods fuer die Stadium-Umgebung -->
<!-- Alle Mods werden von https://assets.techniverse.net/tm/skins/ bereitgestellt -->
<Stadium>
<item name='A Romanorum Superbia'>https://assets.techniverse.net/tm/skins/ARomanorumSuperbia.zip</item>
<item name='Bluemod'>https://assets.techniverse.net/tm/skins/Bluemod.zip</item>
<item name='Candy Box II'>https://assets.techniverse.net/tm/skins/CANDY_BOX_II.zip</item>
<item name='Egyptian Stadium'>https://assets.techniverse.net/tm/skins/Egyptian_Stadium.zip</item>
<item name='FC Mod'>https://assets.techniverse.net/tm/skins/FC-mod.zip</item>
<item name='Forest Mod'>https://assets.techniverse.net/tm/skins/FoResT_MoD.zip</item>
<item name='Imperial Palace'>https://assets.techniverse.net/tm/skins/ImperialPalace.zip</item>
<item name='Inca 3'>https://assets.techniverse.net/tm/skins/Inca3.zip</item>
<item name='Jurassic Park'>https://assets.techniverse.net/tm/skins/JurassicPark.zip</item>
<item name='LMDS Matrix'>https://assets.techniverse.net/tm/skins/LMDSMatrix.zip</item>
<item name='Lego Mod'>https://assets.techniverse.net/tm/skins/LegoMod.zip</item>
<item name='Lego Mod v2'>https://assets.techniverse.net/tm/skins/LegoModv2.zip</item>
<item name='Lego City'>https://assets.techniverse.net/tm/skins/Lego_City.zip</item>
<item name='Mario Mod'>https://assets.techniverse.net/tm/skins/MarioMod.zip</item>
<item name='Moon Base'>https://assets.techniverse.net/tm/skins/MoonBase.zip</item>
<item name='Portal'>https://assets.techniverse.net/tm/skins/Portal.zip</item>
<item name='Quantum Leap'>https://assets.techniverse.net/tm/skins/QuantumLeap.zip</item>
<item name='RDV Urban 01'>https://assets.techniverse.net/tm/skins/RDV-Urban01.zip</item>
<item name='Rainbow Road'>https://assets.techniverse.net/tm/skins/Rainbow%20Road.zip</item>
<item name='Star Wars Mod'>https://assets.techniverse.net/tm/skins/StarWarsMod.zip</item>
<item name='The Pirate Bay'>https://assets.techniverse.net/tm/skins/ThePirateBay.zip</item>
<item name='Toxic'>https://assets.techniverse.net/tm/skins/Toxic.zip</item>
<item name='Transparence V1'>https://assets.techniverse.net/tm/skins/TransparenceV1.zip</item>
<item name='Western Fortress'>https://assets.techniverse.net/tm/skins/WesternFortress.zip</item>
<item name='Wood Mod'>https://assets.techniverse.net/tm/skins/Wood%20Mod.zip</item>
<item name='Wooden Domnann'>https://assets.techniverse.net/tm/skins/Wooden%20Domnann.zip</item>
<item name='Xmas'>https://assets.techniverse.net/tm/skins/Xmas.zip</item>
<item name='N64 Rainbow Road'>https://assets.techniverse.net/tm/skins/_N64_%20Rainbow%20Road.zip</item>
<item name='Blue Light'>https://assets.techniverse.net/tm/skins/bluelight.zip</item>
<item name='Blue Water'>https://assets.techniverse.net/tm/skins/bluewater.zip</item>
<item name='Construct'>https://assets.techniverse.net/tm/skins/construct.zip</item>
<item name='Dark Mirror'>https://assets.techniverse.net/tm/skins/darkmirror.zip</item>
<item name='Formel 1'>https://assets.techniverse.net/tm/skins/formel1.zip</item>
<item name='Future'>https://assets.techniverse.net/tm/skins/future.zip</item>
<item name='Hypercube'>https://assets.techniverse.net/tm/skins/hypercube.zip</item>
<item name='Icebreaker'>https://assets.techniverse.net/tm/skins/icebraker.zip</item>
<item name='Just Black'>https://assets.techniverse.net/tm/skins/justblack.zip</item>
<item name='Lego II'>https://assets.techniverse.net/tm/skins/lego_II.zip</item>
<item name='Mars'>https://assets.techniverse.net/tm/skins/mars.zip</item>
<item name='Modernizer'>https://assets.techniverse.net/tm/skins/modernizer.zip</item>
<item name='Neon Glow'>https://assets.techniverse.net/tm/skins/neonglow.zip</item>
<item name='Pioneer'>https://assets.techniverse.net/tm/skins/pioneer.zip</item>
<item name='Push'>https://assets.techniverse.net/tm/skins/push.zip</item>
<item name='Puzzle'>https://assets.techniverse.net/tm/skins/puzzle.zip</item>
<item name='Robot Mod 2'>https://assets.techniverse.net/tm/skins/robotmod2.zip</item>
<item name='Rubik'>https://assets.techniverse.net/tm/skins/rubik.zip</item>
<item name='Smarties'>https://assets.techniverse.net/tm/skins/smarties.zip</item>
<item name='Sonic'>https://assets.techniverse.net/tm/skins/sonic.zip</item>
<item name='Stadium 2010'>https://assets.techniverse.net/tm/skins/stadium2010.zip</item>
<item name='Stadium Storm'>https://assets.techniverse.net/tm/skins/stadium_storm_mod.zip</item>
<item name='Tomb'>https://assets.techniverse.net/tm/skins/tomb.zip</item>
<item name='Tron Blue'>https://assets.techniverse.net/tm/skins/tronblue.zip</item>
<item name='Tron Green'>https://assets.techniverse.net/tm/skins/trongreen.zip</item>
<item name='Tron Red'>https://assets.techniverse.net/tm/skins/tronred.zip</item>
<item name='Tron Yellow'>https://assets.techniverse.net/tm/skins/tronyellow.zip</item>
<item name='Wipeout'>https://assets.techniverse.net/tm/skins/wipeout.zip</item>
</Stadium>
<!-- Mods fuer die Island-Umgebung -->
<Island>
</Island>
<!-- Mods fuer die Bay-Umgebung -->
<Bay>
</Bay>
<!-- Mods fuer die Coast-Umgebung -->
<Coast>
</Coast>
<!-- Mods fuer die Speed-Umgebung -->
<Speed>
</Speed>
<!-- Mods fuer die Alpine-Umgebung -->
<Alpine>
</Alpine>
<!-- Mods fuer die Rally-Umgebung -->
<Rally>
</Rally>
</mods>
<music>
<!-- Musik-URLs hier eintragen -->
<!-- <item name='Songname'>https://example.com/song.ogg</item> -->
</music>
</settings>
+23
View File
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<settings>
<!-- Server Configuration, can be address or ip -->
<server>ts3.techniverse.net</server>
<serverid>1</serverid>
<serverport>9987</serverport>
<queryport>10011</queryport>
<!-- Channel & Update Settings -->
<defaultchannel></defaultchannel>
<subchannel></subchannel>
<channelpassword></channelpassword>
<update_interval>30</update_interval>
<!-- Helpers and images -->
<helperURL>http://assets.techniverse.net/tm/ts3gateway/gateway3.html</helperURL>
<logoURL>http://assets.techniverse.net/tm/ts3gateway/ts3logo.jpg</logoURL>
<!-- Widget position -->
<posx>-64</posx>
<posy>45</posy>
</settings>
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
# ============================================================
# MariaDB Init-Script: XAseco-Datenbank erstellen
# ============================================================
# Wird automatisch beim ersten Start des MariaDB-Containers
# ausgefuehrt (via /docker-entrypoint-initdb.d/).
# Erstellt Datenbank und Benutzer fuer XAseco, sofern die
# entsprechenden Umgebungsvariablen gesetzt sind.
# ============================================================
XASECO_DB_NAME="${XASECO_DB_NAME:-xaseco}"
XASECO_DB_USER="${XASECO_DB_USER:-xaseco}"
XASECO_DB_PASSWORD="${XASECO_DB_PASSWORD:-}"
if [ -z "$XASECO_DB_PASSWORD" ]; then
echo "HINWEIS: XASECO_DB_PASSWORD nicht gesetzt – ueberspringe XAseco-DB-Erstellung."
exit 0
fi
echo "Erstelle XAseco-Datenbank '${XASECO_DB_NAME}' und Benutzer '${XASECO_DB_USER}'..."
mariadb -u root -p"${MYSQL_ROOT_PASSWORD}" <<-EOSQL
CREATE DATABASE IF NOT EXISTS \`${XASECO_DB_NAME}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS '${XASECO_DB_USER}'@'%' IDENTIFIED BY '${XASECO_DB_PASSWORD}';
GRANT ALL PRIVILEGES ON \`${XASECO_DB_NAME}\`.* TO '${XASECO_DB_USER}'@'%';
FLUSH PRIVILEGES;
EOSQL
echo "XAseco-Datenbank erfolgreich erstellt."
+63
View File
@@ -0,0 +1,63 @@
services:
tmserver:
image: git.techniverse.net/scriptos/trackmania-server:1.2.1
build:
context: .
container_name: tmserver
restart: unless-stopped
depends_on:
mariadb:
condition: service_healthy
ports:
- "${SERVER_PORT:-2350}:${SERVER_PORT:-2350}/tcp"
- "${SERVER_PORT:-2350}:${SERVER_PORT:-2350}/udp"
- "${SERVER_P2P_PORT:-3450}:${SERVER_P2P_PORT:-3450}/tcp"
- "80:80/tcp"
env_file:
- .env
volumes:
- ./data/gamedata:/opt/tmserver/GameData
- ./data/controlpanel:/var/www/html
- ./data/xaseco:/opt/tmserver/xaseco
networks:
tmserver_net:
ipv4_address: 172.20.60.10
mariadb:
image: mariadb:11.4
container_name: tmserver-mariadb
restart: unless-stopped
env_file:
- .env
environment:
MYSQL_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
# RemoteCP-Datenbank (via MariaDB-Init)
MYSQL_DATABASE: ${REMOTECP_DB_NAME:-remotecp}
MYSQL_USER: ${REMOTECP_DB_USER:-remotecp}
MYSQL_PASSWORD: ${REMOTECP_DB_PASSWORD}
# XAseco-Datenbank (via Init-Script init-xaseco-db.sh)
XASECO_DB_NAME: ${XASECO_DB_NAME:-xaseco}
XASECO_DB_USER: ${XASECO_DB_USER:-xaseco}
XASECO_DB_PASSWORD: ${XASECO_DB_PASSWORD}
volumes:
- ./data/mariadb:/var/lib/mysql
- ./assets/db/init-xaseco-db.sh:/docker-entrypoint-initdb.d/20-init-xaseco-db.sh:ro
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
networks:
tmserver_net:
ipv4_address: 172.20.60.11
networks:
tmserver_net:
name: tmserver.dockernetwork.local
driver: bridge
ipam:
config:
- subnet: 172.20.60.0/25
gateway: 172.20.60.1
ip_range: 172.20.60.64/26
+53
View File
@@ -0,0 +1,53 @@
# Dokumentation
Übersicht aller verfügbaren Dokumentationen für den Trackmania Nations Forever Docker Server.
> **Wichtig:** Vor dem ersten Start muss eine `.env`-Datei aus der Vorlage erstellt werden: `cp .env.example .env`
> **⚠ Sicherheitshinweis:** Die `.env.example` enthält **vorgenerierte Beispiel-Passwörter**. Diese dienen nur als Platzhalter und sind öffentlich einsehbar! **Ändere unbedingt alle Passwörter** in der `.env`-Datei, bevor du den Server produktiv einsetzt.
| Dokument | Beschreibung |
|----------|-------------|
| [Schnellstart](schnellstart.md) | Erste Schritte und minimale Konfiguration |
| [Konfiguration](konfiguration.md) | Persistente Serverkonfiguration (dedicated_cfg.txt) |
| [Umgebungsvariablen](umgebungsvariablen.md) | Alle verfügbaren Umgebungsvariablen |
| [Server-Modi](server-modi.md) | LAN- und Internet-Dedicated-Modus |
| [AdminServ](adminserv.md) | Einrichtung der Server-Verwaltungsoberfläche |
| [RemoteCP](remotecp.md) | Alternative Server-Verwaltungsoberfläche (inkl. Mods/Skins) |
| [XAseco](xaseco.md) | Server-Controller für Rekorde, Karma und Jukebox |
| [Ports](ports.md) | Freigegebene Ports und deren Verwendung |
## Projektstruktur
```
├── assets/
│ ├── bin/ # Binaries und Startscript
│ │ ├── AdminServ_v2.1.1.zip # AdminServ Web-UI
│ │ ├── remoteCP_v4.0.3.5.zip # RemoteCP Web-UI
│ │ ├── xaseco_v1.16.zip # XAseco Server-Controller
│ │ ├── RunTrackmaniaServer.sh # Container-Startscript
│ │ └── TrackmaniaServer_*.zip # Trackmania Server Binary
│ ├── config/
│ │ ├── custom_game_settings.txt # MatchSettings (Spielmodus, Map-Rotation)
│ │ ├── dedicated_cfg.txt # Server-Config-Template (mit Platzhaltern)
│ │ └── remotecp/
│ │ └── plugins/
│ │ ├── CustomPoints/
│ │ │ └── index.php # CustomPoints-Plugin fuer RemoteCP
│ │ └── Mods/
│ │ └── settings.xml # Skin-Bibliothek (techniverse.net)
│ └── db/
│ └── init-xaseco-db.sh # MariaDB Init-Script fuer XAseco-DB
├── docs/ # Dokumentation
├── docker-compose.yml # Docker Compose Konfiguration
├── Dockerfile # Docker Build-Definition
├── .env.example # Vorlage fuer Umgebungsvariablen
├── .env # Lokale Umgebungsvariablen (nicht im Git!)
├── LICENSE # Lizenz
├── README.md # Projektbeschreibung
└── data/ # Persistente Daten (zur Laufzeit)
├── gamedata/ # TM-Server-Daten
├── controlpanel/ # AdminServ + RemoteCP
├── xaseco/ # XAseco-Konfiguration und Logs
└── mariadb/ # MariaDB-Datenbankdateien
```
+100
View File
@@ -0,0 +1,100 @@
# AdminServ – Server-Verwaltungsoberfläche
Die Server-Verwaltungsoberfläche basiert auf [AdminServ](https://github.com/Chris92de/AdminServ) und ist über Port 80 erreichbar.
## Einrichtung
1. `http://<host-server-des-containers>` im Browser aufrufen
2. Ein Passwort festlegen – dieses wird als AdminServ-Passwort verwendet
3. TM-Server-Informationen eintragen (Standardwerte können beibehalten werden)
4. `Address` auf `localhost` setzen, um den eingebetteten Server zu verwalten
5. Speichern
## Verbindung zum Server
1. Über den Button „Servers" zur Serverliste navigieren
2. Den gewünschten Server auswählen
3. Admin-Stufe wählen und zugehöriges Passwort eingeben
## Standard-Passwörter
| Stufe | Standard-Passwort |
|-------|-------------------|
| SuperAdmin | `SuperAdmin` |
| Admin | `Admin` |
| User | `User` |
Die Admin-Stufen können unter `http://<host-server-des-containers>/config` geändert werden.
> **Hinweis:** Es wird empfohlen, die Standard-Passwörter über die `.env`-Datei (`SERVER_SA_PASSWORD`, `SERVER_ADM_PASSWORD`) zu ändern. Siehe [Umgebungsvariablen](umgebungsvariablen.md).
## Persistente Speicherung
Alle AdminServ-Daten (Passwort, Server-Einträge, Konfiguration, Logs) werden über einen Bind-Mount (`./data/controlpanel`) persistent auf dem Host gespeichert. Beim ersten Start werden die Dateien automatisch aus dem Image ins Volume kopiert.
| Host-Pfad | Container-Pfad | Beschreibung |
|-----------|----------------|-------------|
| `./data/controlpanel` | `/var/www/html` | AdminServ- und RemoteCP-Installation |
> **Hinweis:** Im selben Volume befindet sich auch [RemoteCP](remotecp.md) unter `./data/controlpanel/remotecp/`.
## Fehlerbehebung
### Login funktioniert nicht nach dem Anlegen eines Servers
Falls der Login nicht funktioniert oder ein Authentifizierungsfehler angezeigt wird, können die PHP-Logs Aufschluss geben:
1. In der `.env`-Datei `PHP_DISPLAY_ERRORS=true` setzen
2. Container neu starten: `docker compose up -d`
3. AdminServ im Browser öffnen und den Fehler reproduzieren
4. Die PHP-Fehlermeldung wird direkt auf der Seite angezeigt
Alternativ können die PHP-Logs eingesehen werden:
```bash
docker exec tmserver cat /var/log/php_errors.log
```
### AdminServ komplett zurücksetzen
Falls AdminServ in einen inkonsistenten Zustand geraten ist:
```bash
# AdminServ-Daten auf dem Host löschen
rm -rf ./data/controlpanel/*
# Container neu starten – AdminServ wird frisch initialisiert
docker compose up -d
```
## Gepatchte AdminServ-Bugs (TmForever)
AdminServ (v2.1.1) enthält zwei Bugs, die speziell im Zusammenspiel mit TmForever auftreten. Diese werden beim Container-Start automatisch gepatcht – auch bei bestehenden Volumes.
### Falscher Pfad in MatchSettings-Dateien
Beim Erstellen einer neuen MatchSettings-Datei über AdminServ wurden die Track-Pfade falsch geschrieben. Statt des tatsächlichen Ordners (z.B. `Challenges/Downloaded/`) wurde immer der Speicherort der MatchSettings (`MatchSettings/`) als Pfad-Präfix verwendet:
```xml
<!-- Fehlerhaft (Original) -->
<file>MatchSettings/speed vs. fullspeed.Challenge.Gbx</file>
<!-- Korrekt (nach Patch) -->
<file>Challenges/Downloaded/speed vs. fullspeed.Challenge.Gbx</file>
```
**Ursache:** Die AJAX-Funktion `get_matchset_mapimport.php` hat den URL-Parameter `d` (= MatchSettings-Speicherordner) als relativen Pfad für die Map-Dateinamen verwendet, anstatt den tatsächlichen Ordner aus der Dropdown-Auswahl zu berechnen.
**Betroffene Datei:** `resources/ajax/get_matchset_mapimport.php`
### GetModeScriptInfo-Fehler (-506)
Beim Speichern einer MatchSettings-Datei erschien die Fehlermeldung:
```
[-506] Method 'GetModeScriptInfo' not defined
```
**Ursache:** `GetModeScriptInfo` ist eine XML-RPC-Methode, die nur in ManiaPlanet/TM2 existiert. AdminServ hat sie ohne Versionsprüfung aufgerufen. An anderen Stellen im Code wurde korrekt mit `SERVER_VERSION_NAME != 'TmForever'` unterschieden – nur hier fehlte die Prüfung.
**Betroffene Datei:** `resources/process/maps-creatematchset.php`
+208
View File
@@ -0,0 +1,208 @@
# Konfiguration (dedicated_cfg.txt)
Die Server-Konfiguration wird in der Datei `dedicated_cfg.txt` im Verzeichnis `GameData/Config/` gespeichert. Diese Datei enthält alle wichtigen Parameter für den Serverbetrieb.
Die Template-Konfiguration befindet sich im Repository unter `assets/config/dedicated_cfg.txt`. Sie enthält `%%PLATZHALTER%%` anstelle von echten Werten – keine Passwörter oder Zugangsdaten im Repo! Die Platzhalter werden beim ersten Container-Start durch die [Umgebungsvariablen](umgebungsvariablen.md) ersetzt.
## Persistente Speicherung
Das gesamte **GameData-Verzeichnis** wird über ein Bind-Mount (`./data/gamedata`) persistent auf dem Host gespeichert. Das bedeutet:
- Alle Daten (Konfiguration, Tracks, Skins, Scores, Cache, etc.) bleiben erhalten
- Manuelle Änderungen gehen nicht verloren, auch wenn der Container neu erstellt wird
- Dateien können direkt auf dem Host bearbeitet werden
### Volume-Pfade
| Host-Pfad | Container-Pfad | Beschreibung |
|-----------|----------------|-------------|
| `./data/gamedata` | `/opt/tmserver/GameData` | Gesamtes GameData-Verzeichnis |
| `./data/controlpanel` | `/var/www/html` | AdminServ- und RemoteCP-Daten |
| `./data/mariadb` | `/var/lib/mysql` | MariaDB-Datenbankdateien |
### Enthaltene Unterordner
| Ordner | Beschreibung |
|--------|-------------|
| `Config/` | Server-Konfiguration (`dedicated_cfg.txt`, Blacklist, Guestlist) |
| `Tracks/` | Strecken und MatchSettings |
| `Skins/` | Spieler- und Fahrzeug-Skins |
| `Scores/` | Gespeicherte Punktestände |
| `Cache/` | Server-Cache |
| `Profiles/` | Spielerprofile |
| `Replays/` | Gespeicherte Replays |
## Reihenfolge beim Docker-Build
1. ZIP-Archiv wird entpackt → Standard-`dedicated_cfg.txt` aus dem ZIP landet in `GameData/Config/`
2. Template aus `assets/config/dedicated_cfg.txt` (mit `%%PLATZHALTERN%%`) wird darüber kopiert
3. Gesamtes `GameData/`-Verzeichnis wird als Default-Template gesichert
## First-Run-Verhalten
Beim **ersten Start** des Containers (leeres Volume) passiert Folgendes:
1. Das gesamte Default-GameData-Template wird aus dem Image ins Volume kopiert
2. Alle `%%PLATZHALTER%%` in der `dedicated_cfg.txt` werden durch die [Umgebungsvariablen](umgebungsvariablen.md) ersetzt
3. Der Server startet mit der so erzeugten Konfiguration
Bei **weiteren Starts** wird die vorhandene Konfiguration **nicht überschrieben**. Umgebungsvariablen haben dann keine Wirkung mehr auf die `dedicated_cfg.txt`.
## Konfiguration manuell bearbeiten
Nach dem ersten Start kann die Konfiguration direkt auf dem Host bearbeitet werden:
```bash
# Konfiguration direkt auf dem Host bearbeiten
nano ./data/gamedata/Config/dedicated_cfg.txt
# Alternativ: im Container anzeigen
docker exec tmserver cat /opt/tmserver/GameData/Config/dedicated_cfg.txt
# Server neustarten, damit die Änderungen wirksam werden
docker restart tmserver
```
> **Hinweis:** Da `GameData/` als Bind-Mount eingebunden ist, sind Dateien direkt unter `./data/gamedata/` auf dem Host verfügbar – ohne `docker cp` oder `docker exec`.
## Konfiguration zurücksetzen (FORCE_CONFIG_UPDATE)
Falls die Umgebungsvariablen erneut auf die Konfiguration angewendet werden sollen (z.B. nach einer Passwortänderung), kann die Variable `FORCE_CONFIG_UPDATE` verwendet werden.
In der `.env`-Datei:
```bash
FORCE_CONFIG_UPDATE=true
```
Oder per `docker run`:
```bash
docker run -d \
--env-file .env \
-e FORCE_CONFIG_UPDATE=true \
-p 2350:2350/tcp \
-p 2350:2350/udp \
-p 3450:3450/tcp \
-p 80:80/tcp \
-v ./data/gamedata:/opt/tmserver/GameData \
-v ./data/controlpanel:/var/www/html \
-v ./data/xaseco:/opt/tmserver/xaseco \
--name tmserver git.techniverse.net/scriptos/trackmania-server:latest
```
> **Achtung:** Bei `FORCE_CONFIG_UPDATE=true` wird die `dedicated_cfg.txt` komplett aus dem Template neu erzeugt und alle Platzhalter mit den aktuellen Umgebungsvariablen ersetzt. **Manuelle Änderungen an der Config gehen dabei verloren!** Andere Dateien im GameData-Volume (Tracks, Skins, Scores, etc.) bleiben erhalten. Nach dem Update sollte `FORCE_CONFIG_UPDATE` wieder auf `false` gesetzt werden.
## Wichtige Dateien im Config-Ordner
Der Ordner `GameData/Config/` enthält:
| Datei | Beschreibung |
|-------|-------------|
| `dedicated_cfg.txt` | Haupt-Konfigurationsdatei des Servers |
| `blacklist.txt` | Liste gesperrter Spieler |
| `guestlist.txt` | Liste erlaubter Spieler |
| `Default.SystemConfig.Gbx` | System-Konfiguration |
| `AdminServ/ServerOptions/` | Von AdminServ exportierte Server-Einstellungen |
## AdminServ ServerOptions-Import
Wenn über AdminServ Änderungen an den Server-Optionen vorgenommen und als Export gespeichert werden (z.B. Servername, Beschreibung, Spielerzahl), werden diese beim nächsten Container-Start **automatisch** in die `dedicated_cfg.txt` übernommen.
**Ablauf:**
1. In AdminServ unter „Server Options" die gewünschten Einstellungen ändern und „Export" klicken
2. Die exportierte Datei wird in `GameData/Config/AdminServ/ServerOptions/` gespeichert
3. Beim nächsten Start des Containers wird die **neueste** Export-Datei erkannt
4. Die darin enthaltenen Werte werden in die `dedicated_cfg.txt` geschrieben
**Unterstützte Felder:**
| AdminServ-Feld | dedicated_cfg.txt-Feld |
|----------------|----------------------|
| `Name` | `<name>` |
| `Comment` | `<comment>` |
| `HideServer` | `<hide_server>` |
| `NextMaxPlayers` | `<max_players>` |
| `Password` | `<password>` |
| `PasswordForSpectator` | `<password_spectator>` |
| `NextMaxSpectators` | `<max_spectators>` |
| `NextLadderMode` | `<ladder_mode>` |
| `NextCallVoteTimeOut` | `<callvote_timeout>` |
| `CallVoteRatio` | `<callvote_ratio>` |
| `AllowChallengeDownload` | `<allow_challenge_download>` |
| `AutoSaveReplays` | `<autosave_replays>` |
| `IsP2PUpload` | `<enable_p2p_upload>` |
| `IsP2PDownload` | `<enable_p2p_download>` |
> **Hinweis:** Die AdminServ-Exports haben **Vorrang** vor den Werten aus den Umgebungsvariablen. Beim ersten Start werden zunächst die Umgebungsvariablen angewendet, danach die AdminServ-Exports (falls vorhanden). Bei weiteren Starts werden nur die AdminServ-Exports angewendet.
## Wichtige Parameter in der dedicated_cfg.txt
Alle diese Parameter können über [Umgebungsvariablen](umgebungsvariablen.md) gesetzt werden:
| Parameter | Umgebungsvariable | Standard |
|-----------|------------------|----------|
| `<name>` (SuperAdmin-PW) | `SERVER_SA_PASSWORD` | `SuperAdmin` |
| `<name>` (Admin-PW) | `SERVER_ADM_PASSWORD` | `Admin` |
| `<name>` (User-PW) | `SERVER_USER_PASSWORD` | `User` |
| `<login>` | `SERVER_LOGIN` | *(leer)* |
| `<validation_key>` | `SERVER_VALIDATION_KEY` | *(leer)* |
| `<name>` (Servername) | `SERVER_NAME` | `Trackmania Server` |
| `<comment>` | `SERVER_DESC` | `Powered by tmserver-docker` |
| `<hide_server>` | `SERVER_HIDE` | `0` |
| `<max_players>` | `SERVER_MAX_PLAYERS` | `32` |
| `<password>` | `SERVER_PASSWORD` | *(leer)* |
| `<max_spectators>` | `SERVER_MAX_SPECTATORS` | `32` |
| `<ladder_mode>` | `SERVER_LADDER_MODE` | `forced` |
| `<server_port>` | `SERVER_PORT` | `2350` |
| `<server_p2p_port>` | `SERVER_P2P_PORT` | `3450` |
| `<xmlrpc_port>` | `SERVER_XMLRPC_PORT` | `5000` |
| `<connection_uploadrate>` | `SERVER_UPLOAD_RATE` | `512` |
| `<connection_downloadrate>` | `SERVER_DOWNLOAD_RATE` | `8192` |
## MatchSettings (Spieleinstellungen)
Die MatchSettings-Dateien liegen im Verzeichnis `data/gamedata/Tracks/MatchSettings/` und definieren Spielmodus, Regeln und die aktive Track-Liste des Servers. Sie werden als `.txt`-Dateien im XML-Format gespeichert.
### Automatische Erkennung (Standard)
Standardmäßig wird beim Serverstart automatisch die **neueste** `.txt`-Datei im MatchSettings-Ordner anhand des Änderungsdatums geladen. So werden z.B. über AdminServ erstellte oder bearbeitete MatchSettings beim nächsten Neustart automatisch aktiv.
```bash
# In der .env-Datei (Standardwert):
MATCHSETTINGS_FILE=auto
```
**Ablauf bei jedem Containerstart:**
1. Der Ordner `data/gamedata/Tracks/MatchSettings/` wird nach `.txt`-Dateien durchsucht
2. Die Datei mit dem neuesten Änderungsdatum wird ermittelt
3. Diese Datei wird als `/game_settings`-Parameter an den TM-Server übergeben
4. Dateiname und Änderungsdatum werden in der Konsole ausgegeben
### Bestimmte Datei verwenden
Alternativ kann eine bestimmte MatchSettings-Datei direkt angegeben werden:
```bash
# In der .env-Datei:
MATCHSETTINGS_FILE=turnier_settings.txt
```
Der Dateiname bezieht sich immer auf den Ordner `data/gamedata/Tracks/MatchSettings/`.
### Fallback
Falls die angegebene oder automatisch ermittelte Datei nicht existiert, wird auf die mitgelieferte Standard-Datei `custom_game_settings.txt` zurückgefallen.
### Neue MatchSettings über AdminServ erstellen
1. In AdminServ mit SuperAdmin einloggen
2. Unter „Maps" → „MatchSettings" → „Create" eine neue Datei anlegen
3. Tracks aus den gewünschten Ordnern importieren (z.B. `Challenges/Downloaded/`)
4. Spielmodus und Regeln konfigurieren
5. Speichern – die Datei wird in `MatchSettings/` abgelegt
6. Container neustarten (`docker compose restart`) – die neue Datei wird automatisch als neueste erkannt und geladen
> **Hinweis:** Die aktive MatchSettings-Datei wird beim Serverstart in der Konsole ausgegeben. Mit `docker logs tmserver` kann überprüft werden, welche Datei geladen wurde.
+38
View File
@@ -0,0 +1,38 @@
# Freigegebene Ports
| Port | Protokoll | Beschreibung |
|------|-----------|-------------|
| 2350 | TCP | Gameserver-Port |
| 2350 | UDP | Gameserver-Port |
| 3450 | TCP | P2P-Gameserver-Port |
| 5000 | TCP | XML-RPC-Port (nur containerintern, nicht nach außen freigegeben) |
| 80 | TCP | Server-Verwaltungsoberflächen (AdminServ + RemoteCP) |
## Minimale Port-Freigabe
Für den reinen Spielbetrieb ohne Verwaltungsoberfläche reichen die Ports 2350 und 3450:
```bash
docker run -d \
--env-file .env \
-p 2350:2350/tcp \
-p 2350:2350/udp \
-p 3450:3450/tcp \
-v ./data/gamedata:/opt/tmserver/GameData \
-v ./data/xaseco:/opt/tmserver/xaseco \
--name tmserver git.techniverse.net/scriptos/trackmania-server:latest
```
> **Hinweis:** Port 5000 (XML-RPC) wird containerintern von AdminServ, RemoteCP und XAseco verwendet und ist standardmäßig **nicht** nach außen freigegeben.
>
> Falls du den XML-RPC-Port extern benötigst (z. B. für ein externes Tool außerhalb des Containers), kannst du ihn nachträglich in der `docker-compose.yml` unter `ports:` ergänzen:
>
> ```yaml
> - "${SERVER_XMLRPC_PORT:-5000}:${SERVER_XMLRPC_PORT:-5000}/tcp"
> ```
>
> Bzw. bei `docker run`:
>
> ```bash
> -p 5000:5000/tcp
> ```
+170
View File
@@ -0,0 +1,170 @@
# RemoteCP – Alternative Server-Verwaltungsoberfläche
RemoteCP ist eine weitere Web-Verwaltungsoberfläche für Trackmania-Server, die parallel zu AdminServ installiert ist. Es handelt sich um ein älteres Tool mit eigenem Login-System und eigener Benutzerverwaltung.
## Zugriff
RemoteCP ist als Subpath unter der gleichen Adresse wie AdminServ erreichbar:
- **AdminServ:** `http://<host-ip>/`
- **RemoteCP:** `http://<host-ip>/remotecp/`
## Standard-Zugangsdaten
RemoteCP verwendet die SuperAdmin-Zugangsdaten des Trackmania-Servers:
| Benutzer | Passwort | Beschreibung |
|----------|----------|-------------|
| `SuperAdmin` | Wert aus `SERVER_SA_PASSWORD` | Administrator (volle Rechte) |
| `Guest` | `Guest` | Gast-Zugang (nur Lesen) |
Der Admin-Login für RemoteCP ist identisch mit dem SuperAdmin-Passwort des TM-Servers (`SERVER_SA_PASSWORD`). Es werden keine separaten Zugangsdaten benötigt.
## Umgebungsvariablen
| Variable | Beschreibung | Standard |
|----------|-------------|----------|
| `REMOTECP_DB_HOST` | Hostname des Datenbankservers | `mariadb` |
| `REMOTECP_DB_NAME` | Name der RemoteCP-Datenbank | `remotecp` |
| `REMOTECP_DB_USER` | Datenbank-Benutzername | `remotecp` |
| `REMOTECP_DB_PASSWORD` | Datenbank-Passwort | *(muss gesetzt werden)* |
## Datenbank (MariaDB)
RemoteCP benötigt eine MariaDB-Datenbank für erweiterte Funktionen (Spielerstatistiken, Records, Chat-Nachrichten, etc.). Ein MariaDB-Container wird automatisch als Teil des Docker-Setups bereitgestellt.
### Automatische Einrichtung
Wenn die Datenbank-Variablen in der `.env`-Datei gesetzt sind (`REMOTECP_DB_HOST`, `REMOTECP_DB_PASSWORD`), wird beim ersten Start:
1. Die **MariaDB** erstellt automatisch die Datenbank und den Benutzer (über `MYSQL_DATABASE`, `MYSQL_USER`, `MYSQL_PASSWORD`)
2. Das **Startup-Script** wartet, bis MariaDB erreichbar ist
3. Die **Datenbank-Tabellen** werden automatisch aus den SQL-Schema-Dateien importiert
4. Die **Installer-Markierung** (`cache/installed`) wird gesetzt, damit der Web-Installer übersprungen wird
Es ist kein manuelles Setup über den Web-Installer nötig.
### Manuelle Einrichtung
Falls die automatische Einrichtung nicht gewünscht ist oder fehlschlägt (z.B. MariaDB nicht erreichbar), kann RemoteCP auch manuell eingerichtet werden:
1. Die Datenbank-Variablen (`REMOTECP_DB_HOST`, `REMOTECP_DB_PASSWORD`) **müssen trotzdem in der `.env`-Datei gesetzt sein**, damit die MariaDB die Datenbank und den Benutzer beim ersten Start erstellt
2. RemoteCP-Installer im Browser aufrufen: `http://<host-ip>/remotecp/index.php?page=install`
3. „Install with remoteCP-Database" wählen
4. Die Datenbank-Zugangsdaten eingeben:
- **Database DSN:** `mysql:dbname=remotecp;host=mariadb` (bzw. die Werte aus der `.env`)
- **Database Username:** Wert aus `REMOTECP_DB_USER`
- **Database Password:** Wert aus `REMOTECP_DB_PASSWORD`
5. Server-Daten eingeben (XMLRPC-Port, SuperAdmin-Passwort)
6. Installation abschließen
### Persistente Daten
Die MariaDB-Daten werden über ein eigenes Bind-Mount persistent gespeichert:
| Host-Pfad | Container-Pfad | Beschreibung |
|-----------|----------------|-------------|
| `./data/mariadb` | `/var/lib/mysql` | MariaDB-Datenbankdateien |
> **Hinweis:** Der MariaDB-Container kann auch für weitere Tools genutzt werden. Zusätzliche Datenbanken und Benutzer können über den Root-Zugang (`MARIADB_ROOT_PASSWORD`) erstellt werden.
## Automatische Konfiguration
Beim ersten Start wird RemoteCP automatisch konfiguriert:
- Die **Serververbindung** wird mit dem XML-RPC-Port und dem SuperAdmin-Passwort (`SERVER_SA_PASSWORD`) aus der `.env`-Datei eingerichtet
- Der **Admin-Zugang** verwendet den Benutzernamen `SuperAdmin` mit dem Passwort aus `SERVER_SA_PASSWORD`
- Das Passwort wird als MD5-Hash in der Konfiguration gespeichert
- Die **Datenbankverbindung** wird automatisch in `servers.xml` eingetragen (wenn DB-Variablen gesetzt sind)
## Persistente Speicherung
RemoteCP wird im gleichen Volume wie AdminServ gespeichert:
| Host-Pfad | Container-Pfad | Beschreibung |
|-----------|----------------|-------------|
| `./data/controlpanel/remotecp` | `/var/www/html/remotecp` | RemoteCP-Installation |
Die Konfigurationsdateien befinden sich unter `./data/controlpanel/remotecp/xml/`:
| Datei | Beschreibung |
|-------|-------------|
| `servers.xml` | Server-Verbindungsdaten (Host, Port, Passwort, Datenbank) |
| `admins.xml` | Benutzer und Zugangsdaten |
| `groups.xml` | Berechtigungsgruppen |
## Mods / Skins
RemoteCP enthält ein **Mods-Plugin**, mit dem Texturpakete (Skins) pro Spielumgebung auf dem Server forciert werden können. Spieler laden den jeweiligen Mod automatisch beim Betreten des Servers herunter.
### Wie funktionieren Mods?
In TrackMania Forever sind Mods ZIP-Archive mit alternativen Texturen für eine Spielumgebung (Stadium, Island, Bay, etc.). Der Ablauf:
1. Der Mod liegt als `.zip`-Datei auf einem **Webserver**, der für die Spieler erreichbar ist
2. Der Serveradmin forciert den Mod über den XML-RPC-Befehl `SetForcedMods` (pro Umgebung eine URL)
3. Wenn ein Spieler dem Server beitritt, lädt sein Client den Mod automatisch von der angegebenen URL herunter
4. Mods sind **flüchtig** – nach einem Serverneustart müssen sie erneut gesetzt werden (das Startup-Script übernimmt das automatisch, siehe unten)
### Vorkonfigurierte Skin-Bibliothek
Das Image wird mit einer vorkonfigurierten Skin-Bibliothek ausgeliefert, die über **50 Skins** für die Stadium-Umgebung enthält. Alle Skins werden von [assets.techniverse.net](https://assets.techniverse.net/tm/skins/) bereitgestellt und sind im RemoteCP-Mods-Plugin als Dropdown-Auswahl verfügbar.
Die Konfiguration befindet sich unter:
| Host-Pfad | Container-Pfad | Beschreibung |
|-----------|----------------|-------------|
| `./data/controlpanel/remotecp/plugins/Mods/settings.xml` | `/var/www/html/remotecp/plugins/Mods/settings.xml` | Mod-Katalog (URLs pro Umgebung) |
> **Hinweis:** Bei bestehenden Installationen (Volumes) wird die alte Standard-`settings.xml` (mit den Original-Beispiel-URLs von blacksunonline.com) beim nächsten Containerstart automatisch durch die neue Version mit den techniverse.net-Skins ersetzt.
### Mods über RemoteCP verwalten
1. RemoteCP öffnen: `http://<host-ip>/remotecp/`
2. Mit SuperAdmin-Zugangsdaten einloggen
3. Im Seitenmenü das **Mods**-Plugin aufrufen
4. Pro Umgebung (Stadium, Island, etc.) einen Skin aus dem Dropdown auswählen
5. "Submit" klicken – der Mod wird sofort auf dem Server aktiviert
### Mods automatisch beim Start forcieren
Über die Umgebungsvariablen `FORCE_MOD_*` kann ein Mod pro Umgebung automatisch bei **jedem** Containerstart gesetzt werden – unabhängig von `FORCE_CONFIG_UPDATE`. Siehe [Umgebungsvariablen – Forced Mods](umgebungsvariablen.md#forced-mods-skins) für Details.
**Beispiel** (in der `.env`-Datei):
```bash
FORCE_MOD_STADIUM=https://assets.techniverse.net/tm/skins/Portal.zip
```
### Eigene Skins hinzufügen
Um eigene Skins in das RemoteCP-Dropdown aufzunehmen, bearbeite die Datei `data/controlpanel/remotecp/plugins/Mods/settings.xml` und füge innerhalb der gewünschten Umgebung einen neuen Eintrag hinzu:
```xml
<Stadium>
<item name='Mein Skin'>https://example.com/mods/mein_skin.zip</item>
</Stadium>
```
> **Wichtig:** Die ZIP-Datei muss von den Spielern über HTTP/HTTPS erreichbar sein.
## Sicherheit
RemoteCP liefert eine `.htaccess`-Datei mit, die den direkten Zugriff auf XML-Konfigurationsdateien über den Browser verhindert. Apache `mod_rewrite` und `AllowOverride` sind im Image aktiviert, damit dieser Schutz funktioniert.
## Fehlerbehebung
Falls RemoteCP nicht erreichbar ist oder Fehler anzeigt:
1. Prüfe, ob beide Container laufen: `docker ps`
2. MariaDB-Verbindung testen: `docker exec tmserver mysql -h mariadb -u remotecp -p remotecp -e "SELECT 1"`
3. PHP-Debug-Modus aktivieren: `PHP_DISPLAY_ERRORS=true` in der `.env`-Datei setzen
4. Container neu starten: `docker compose restart`
5. PHP-Logs prüfen: `docker exec tmserver cat /var/log/php_errors.log`
6. MariaDB-Logs prüfen: `docker logs tmserver-mariadb`
### Bekannte Hinweise
- RemoteCP ist ein älteres Tool (Version 4.0.3.5) und wurde für PHP 5.x entwickelt, läuft aber mit PHP 7.4
- Die Live-Funktionen (`remoteCP[Live]`) benötigen eine laufende Serververbindung
- **Sicherheitshinweis:** Die Registrierung neuer Benutzer ist standardmäßig aktiviert. Aus Sicherheitsgründen sollte diese deaktiviert werden, damit sich keine unbefugten Nutzer einen Zugang anlegen können. Dazu in der Datei `data/controlpanel/remotecp/xml/settings/settings.xml` den Wert `<register>false</register>` setzen
+114
View File
@@ -0,0 +1,114 @@
# Schnellstart
## Voraussetzungen
- Docker und Docker Compose müssen installiert sein
## 1. Umgebungsvariablen einrichten
Kopiere die Vorlage und passe die Werte an:
```bash
cp .env.example .env
```
Bearbeite die `.env`-Datei und setze mindestens die gewünschten Passwörter. Für den Internet-Modus müssen zusätzlich `SERVER_LOGIN` und `SERVER_VALIDATION_KEY` gesetzt werden.
> **⚠ Sicherheitshinweis:** Die `.env.example` enthält **vorgenerierte Beispiel-Passwörter**. Diese dienen nur als Platzhalter und sind öffentlich einsehbar! **Ändere unbedingt alle Passwörter**, bevor du den Server produktiv einsetzt.
> **Wichtig:** Die `.env`-Datei enthält sensible Daten (Passwörter, Keys) und wird über die `.gitignore` vom Einchecken ausgeschlossen.
## 2. Server starten
### Fertiges Docker Image verwenden (empfohlen)
Es steht ein fertiges Docker Image in der Container-Registry bereit – kein eigener Build nötig:
```
git.techniverse.net/scriptos/trackmania-server:latest
```
> **Tipp:** Alle verfügbaren Tags findest du in der [Container-Registry](https://git.techniverse.net/scriptos/-/packages/container/trackmania-server/).
#### Mit Docker Compose
```bash
docker compose up -d
```
Die Konfiguration erfolgt über die `.env`-Datei, die automatisch eingelesen wird. Das Image wird automatisch aus der Registry geladen.
### Docker Image selbst bauen
Alternativ kannst du das Image auch selbst bauen:
```bash
docker build -t tmserver:latest -t tmserver:1.1.1 .
```
Damit wird das Image mit zwei Tags erstellt: `tmserver:latest` und `tmserver:1.1.1`.
Anschließend den Server starten:
```bash
docker compose up -d --build
```
### Internet-Modus (docker run)
Für den Internet-Modus wird ein Server-Account benötigt. Dieser kann auf der [Trackmania Players-Seite](https://players.trackmaniaforever.com) erstellt werden.
```bash
docker run -d \
--env-file .env \
-p 2350:2350/tcp \
-p 2350:2350/udp \
-p 3450:3450/tcp \
-p 80:80/tcp \
-v ./data/gamedata:/opt/tmserver/GameData \
-v ./data/controlpanel:/var/www/html \
-v ./data/xaseco:/opt/tmserver/xaseco \
--name tmserver git.techniverse.net/scriptos/trackmania-server:latest
```
### LAN-Modus (docker run)
Setze in der `.env`-Datei `SERVER_MODE=lan` oder übergib es direkt:
```bash
docker run -d \
--env-file .env \
-e SERVER_MODE=lan \
-p 2350:2350/tcp \
-p 2350:2350/udp \
-p 3450:3450/tcp \
-p 80:80/tcp \
-v ./data/gamedata:/opt/tmserver/GameData \
-v ./data/controlpanel:/var/www/html \
-v ./data/xaseco:/opt/tmserver/xaseco \
--name tmserver git.techniverse.net/scriptos/trackmania-server:latest
```
## 3. Verwaltungsoberflächen öffnen
| Tool | URL | Beschreibung |
|------|-----|-------------|
| AdminServ | `http://<host-ip>/` | Server-Verwaltungsoberfläche |
| RemoteCP | `http://<host-ip>/remotecp/` | Alternative Verwaltungsoberfläche |
Weitere Details unter [AdminServ](adminserv.md) und [RemoteCP](remotecp.md).
## Persistente Konfiguration
Alle Server- und AdminServ-Daten werden über Bind-Mounts persistent auf dem Host gespeichert:
| Host-Pfad | Container-Pfad | Beschreibung |
|-----------|----------------|-------------|
| `./data/gamedata` | `/opt/tmserver/GameData` | TM-Server-Daten (Config, Tracks, Skins, etc.) |
| `./data/controlpanel` | `/var/www/html` | AdminServ- und RemoteCP-Daten |
| `./data/xaseco` | `/opt/tmserver/xaseco` | XAseco-Konfiguration und Logs |
| `./data/mariadb` | `/var/lib/mysql` | MariaDB-Datenbankdateien |
Beim ersten Start werden die Verzeichnisse automatisch aus dem Image erzeugt und die Umgebungsvariablen aus der `.env`-Datei angewendet. Bei weiteren Starts bleibt alles erhalten.
Weitere Details unter [Konfiguration](konfiguration.md).
+60
View File
@@ -0,0 +1,60 @@
# Server-Modi
Der Trackmania-Server kann in zwei Modi betrieben werden.
## Internet-Dedicated (Standard)
Im Internet-Modus ist der Server über das Trackmania-Masterserver-Netzwerk erreichbar und in der öffentlichen Serverliste sichtbar.
### Voraussetzungen
- Ein Server-Account auf der [Trackmania Players-Seite](https://players.trackmaniaforever.com)
- Die Umgebungsvariablen `SERVER_LOGIN` und `SERVER_VALIDATION_KEY` müssen gesetzt sein
### Konfiguration
In der `.env`-Datei:
```bash
SERVER_MODE=internet
SERVER_LOGIN=dein_login
SERVER_LOGIN_PASSWORD=dein_passwort
SERVER_VALIDATION_KEY=dein_key
```
Wenn `SERVER_LOGIN` oder `SERVER_VALIDATION_KEY` nicht gesetzt sind, bricht der Server mit einer Fehlermeldung ab.
### Server-Account erstellen
1. [players.trackmaniaforever.com](https://players.trackmaniaforever.com) aufrufen
2. Einloggen oder einen neuen Account erstellen
3. Unter „Dedicated Server" einen neuen Server-Account anlegen
4. Login und Validation Key notieren und als Umgebungsvariablen setzen
## LAN-Dedicated
Im LAN-Modus ist der Server nur im lokalen Netzwerk erreichbar. Es werden keine Masterserver-Zugangsdaten benötigt.
### Konfiguration
In der `.env`-Datei:
```bash
SERVER_MODE=lan
```
Oder per Docker-Run:
```bash
docker run -d \
--env-file .env \
-e SERVER_MODE=lan \
-p 2350:2350/tcp \
-p 2350:2350/udp \
-p 3450:3450/tcp \
-p 80:80/tcp \
-v ./data/gamedata:/opt/tmserver/GameData \
-v ./data/controlpanel:/var/www/html \
-v ./data/xaseco:/opt/tmserver/xaseco \
--name tmserver git.techniverse.net/scriptos/trackmania-server:latest
```
+221
View File
@@ -0,0 +1,221 @@
# Umgebungsvariablen
Alle Umgebungsvariablen werden über die `.env`-Datei konfiguriert, die von `docker-compose.yml` automatisch eingelesen wird. Alternativ können sie beim `docker run` über `--env-file .env` oder einzeln mit `-e` gesetzt werden.
Die Variablen werden beim **ersten Start** (leeres GameData-Volume) in die `dedicated_cfg.txt` eingetragen. Bei weiteren Starts bleiben manuelle Änderungen erhalten. Siehe [Konfiguration](konfiguration.md) für Details.
> **Sicherheitshinweis:** Sensible Werte (Passwörter, Keys) sind **nicht** im Docker-Image hinterlegt. Sie werden ausschließlich zur Laufzeit über die `.env`-Datei übergeben. Die `.env`-Datei ist über `.gitignore` und `.dockerignore` geschützt und wird weder ins Git noch in den Docker-Build-Context aufgenommen.
## Einrichtung
```bash
# Vorlage kopieren
cp .env.example .env
# Werte anpassen
nano .env
```
> **⚠ Sicherheitshinweis:** Die `.env.example` enthält **vorgenerierte Beispiel-Passwörter**. Diese dienen nur als Platzhalter und sind öffentlich einsehbar! **Ändere unbedingt alle Passwörter**, bevor du den Server produktiv einsetzt. Betroffen sind: `SERVER_SA_PASSWORD`, `SERVER_ADM_PASSWORD`, `SERVER_USER_PASSWORD`, `MARIADB_ROOT_PASSWORD`, `REMOTECP_DB_PASSWORD` und `XASECO_DB_PASSWORD`.
## Authentifizierung
| Variable | Beschreibung | Standard |
|----------|-------------|----------|
| `SERVER_SA_PASSWORD` | SuperAdmin-Passwort | `SuperAdmin` |
| `SERVER_ADM_PASSWORD` | Admin-Passwort | `Admin` |
| `SERVER_USER_PASSWORD` | User-Passwort | `User` |
## Masterserver-Account
| Variable | Beschreibung | Standard |
|----------|-------------|----------|
| `SERVER_LOGIN` | Masterserver-Login (nur Internet-Modus) | *(leer)* |
| `SERVER_LOGIN_PASSWORD` | Masterserver-Passwort | *(leer)* |
| `SERVER_VALIDATION_KEY` | Masterserver-Validierungsschlüssel | *(leer)* |
> **Wichtig:** Im Internet-Modus müssen `SERVER_LOGIN` und `SERVER_VALIDATION_KEY` gesetzt sein, andernfalls startet der Server nicht.
## Server-Optionen
| Variable | Beschreibung | Standard |
|----------|-------------|----------|
| `SERVER_NAME` | Servername (im Spiel sichtbar) | `Trackmania Server` |
| `SERVER_DESC` | Serverbeschreibung | `Powered by tmserver-docker` |
| `SERVER_HIDE` | Server verstecken (`0` = sichtbar, `1` = versteckt, `2` = vor Nations versteckt) | `0` |
| `SERVER_MAX_PLAYERS` | Maximale Spieleranzahl | `32` |
| `SERVER_PASSWORD` | Passwort zum Beitreten (leer = offen) | *(leer)* |
| `SERVER_MAX_SPECTATORS` | Maximale Zuschaueranzahl | `32` |
| `SERVER_SPEC_PASSWORD` | Zuschauer-Passwort (leer = offen) | *(leer)* |
| `SERVER_LADDER_MODE` | Ladder-Modus (`inactive` oder `forced`) | `forced` |
| `SERVER_LADDER_LIMIT_MAX` | Oberes Ladder-Serverlimit (Punktegrenze) | `60000` |
## Netzwerk
| Variable | Beschreibung | Standard |
|----------|-------------|----------|
| `SERVER_PORT` | Gameserver-Port | `2350` |
| `SERVER_P2P_PORT` | P2P-Port | `3450` |
| `SERVER_XMLRPC_PORT` | XML-RPC-Port (intern für AdminServ) | `5000` |
| `SERVER_UPLOAD_RATE` | Upload-Rate in Kbps | `512` |
| `SERVER_DOWNLOAD_RATE` | Download-Rate in Kbps | `8192` |
## Server-Modus & Steuerung
| Variable | Beschreibung | Standard |
|----------|-------------|----------|
| `SERVER_MODE` | Server-Modus (`internet` oder `lan`) | `internet` |
| `FORCE_CONFIG_UPDATE` | Erzwingt erneutes Anwenden aller Umgebungsvariablen auf die Config | `false` |
## Spieleinstellungen (MatchSettings)
| Variable | Beschreibung | Standard |
|----------|-------------|----------|
| `MATCHSETTINGS_FILE` | MatchSettings-Datei beim Serverstart: `auto` = neueste `.txt`-Datei im Ordner wird automatisch geladen, oder ein expliziter Dateiname (z.B. `meine_settings.txt`) | `auto` |
| `ALLWARMUPDURATION` | Warmup-Dauer für alle Runden (`0` = deaktiviert, `1` = eine Runde Warmup) | `0` |
### Automatische MatchSettings-Erkennung
Standardmäßig (`MATCHSETTINGS_FILE=auto`) wird beim Serverstart automatisch die **neueste** `.txt`-Datei im Verzeichnis `data/gamedata/Tracks/MatchSettings/` anhand des Änderungsdatums ermittelt und geladen. So werden z.B. über AdminServ exportierte MatchSettings beim nächsten Neustart automatisch aktiv.
**Beispiele:**
```bash
# Automatisch die neueste Datei laden (Standard)
MATCHSETTINGS_FILE=auto
# Eine bestimmte Datei verwenden
MATCHSETTINGS_FILE=turnier_settings.txt
```
> **Hinweis:** Falls die angegebene oder automatisch ermittelte Datei nicht existiert, wird auf `custom_game_settings.txt` zurückgefallen. Die aktiv geladene Datei wird beim Serverstart in der Konsole ausgegeben.
## RemoteCP
RemoteCP verwendet die SuperAdmin-Zugangsdaten (`SERVER_SA_PASSWORD`) des TM-Servers für den Web-Login. Es werden keine separaten Login-Variablen benötigt.
| Variable | Beschreibung | Standard |
|----------|-------------|----------|
| `REMOTECP_DB_HOST` | Hostname des Datenbankservers | `mariadb` |
| `REMOTECP_DB_NAME` | Name der RemoteCP-Datenbank | `remotecp` |
| `REMOTECP_DB_USER` | Datenbank-Benutzername | `remotecp` |
| `REMOTECP_DB_PASSWORD` | Datenbank-Passwort | *(muss gesetzt werden)* |
> **Hinweis:** Diese Werte werden nur beim ersten Start (leeres Volume) angewendet. Weitere Details unter [RemoteCP](remotecp.md).
## Forced Mods (Skins)
Mods sind Texturpakete (Skins), die das Aussehen einer Spielumgebung komplett verändern. Über `FORCE_MOD_*`-Variablen kann beim Containerstart automatisch ein Mod pro Umgebung forciert werden. Spieler laden den Mod dann automatisch beim Betreten des Servers herunter.
| Variable | Beschreibung | Standard |
|----------|-------------|----------|
| `FORCE_MOD_STADIUM` | Mod-URL für die Stadium-Umgebung | *(leer)* |
| `FORCE_MOD_ISLAND` | Mod-URL für die Island-Umgebung | *(leer)* |
| `FORCE_MOD_BAY` | Mod-URL für die Bay-Umgebung | *(leer)* |
| `FORCE_MOD_COAST` | Mod-URL für die Coast-Umgebung | *(leer)* |
| `FORCE_MOD_SPEED` | Mod-URL für die Speed-Umgebung | *(leer)* |
| `FORCE_MOD_ALPINE` | Mod-URL für die Alpine-Umgebung | *(leer)* |
| `FORCE_MOD_RALLY` | Mod-URL für die Rally-Umgebung | *(leer)* |
> **Hinweis:** Die Mods werden per XML-RPC (`SetForcedMods`) bei **jedem** Containerstart gesetzt – unabhängig von `FORCE_CONFIG_UPDATE`. Die URL muss auf eine gültige Mod-ZIP-Datei zeigen, die für die Spieler erreichbar ist.
### Verfügbare Skins
Eine Auswahl vorkonfigurierter Skins steht unter `https://assets.techniverse.net/tm/skins/` bereit und ist auch im RemoteCP-Mods-Plugin als Dropdown auswählbar.
**Beispiel:**
```bash
# Portal-Mod für Stadium forcieren
FORCE_MOD_STADIUM=https://assets.techniverse.net/tm/skins/Portal.zip
# Mehrere Umgebungen gleichzeitig
FORCE_MOD_STADIUM=https://assets.techniverse.net/tm/skins/Xmas.zip
FORCE_MOD_ISLAND=https://example.com/mods/island_mod.zip
```
### Mods über RemoteCP verwalten
Zusätzlich zur automatischen Konfiguration per Umgebungsvariable können Mods auch zur Laufzeit über das RemoteCP-Web-Interface (`http://<host-ip>/remotecp/`) im Mods-Plugin per Dropdown ausgewählt und aktiviert werden.
## MariaDB
| Variable | Beschreibung | Standard |
|----------|-------------|----------|
| `MARIADB_ROOT_PASSWORD` | Root-Passwort für den MariaDB-Server | *(muss gesetzt werden)* |
> **Wichtig:** `MARIADB_ROOT_PASSWORD` muss gesetzt sein, damit der MariaDB-Container startet. Die Datenbank und der Benutzer für RemoteCP werden automatisch aus `REMOTECP_DB_NAME`, `REMOTECP_DB_USER` und `REMOTECP_DB_PASSWORD` erstellt.
> **Hinweis:** Der MariaDB-Container kann mehrere Datenbanken beherbergen. Zusätzliche Datenbanken und Benutzer können über den Root-Zugang erstellt werden.
## XAseco
XAseco ist ein Server-Controller für Rekorde, Karma, Jukebox und mehr. Siehe [XAseco](xaseco.md) für Details.
| Variable | Beschreibung | Standard |
|----------|-------------|----------|
| `XASECO_ENABLED` | XAseco aktivieren/deaktivieren | `true` |
| `XASECO_MASTERADMIN_LOGIN` | Dein Spieler-Login (MasterAdmin) | *(muss gesetzt werden)* |
| `XASECO_DB_HOST` | Hostname des Datenbankservers | `mariadb` |
| `XASECO_DB_NAME` | Name der XAseco-Datenbank | `xaseco` |
| `XASECO_DB_USER` | Datenbank-Benutzername | `xaseco` |
| `XASECO_DB_PASSWORD` | Datenbank-Passwort | *(muss gesetzt werden)* |
| `XASECO_DEDIMANIA_NATION` | Dedimania-Nation (IOC-Code) | `DEU` |
> **Hinweis:** Die Server-Zugangsdaten (`SERVER_SA_PASSWORD`, `SERVER_XMLRPC_PORT`) und Dedimania-Daten (`SERVER_LOGIN`, `SERVER_LOGIN_PASSWORD`) werden automatisch aus der bestehenden Konfiguration übernommen.
## Debugging
| Variable | Beschreibung | Standard |
|----------|-------------|----------|
| `PHP_DISPLAY_ERRORS` | Aktiviert den PHP-Debug-Modus: Fehlermeldungen im Browser + vollständige Warnungen im Log (nur zur Fehlersuche!) | `false` |
> **Hinweis:** Der Debug-Modus erfordert **keinen** Rebuild des Images. Es genügt, die Variable in der `.env`-Datei zu ändern und den Container neu zu starten (`docker compose restart`). Im Produktivbetrieb sollte `PHP_DISPLAY_ERRORS` immer auf `false` stehen.
>
> Bei `false` werden nur schwerwiegende Fehler geloggt (keine Warnungen/Notices). Bei `true` werden zusätzlich alle Warnungen und Hinweise angezeigt und geloggt – nützlich zur Fehlersuche bei Problemen mit RemoteCP oder AdminServ.
> **Hinweis:** Bei `FORCE_CONFIG_UPDATE=true` wird die `dedicated_cfg.txt` aus dem Template neu erzeugt und alle Platzhalter mit den aktuellen Umgebungsvariablen ersetzt. Manuelle Änderungen gehen dabei verloren! Nach dem Update sollte `FORCE_CONFIG_UPDATE` wieder auf `false` gesetzt werden.
## Beispiel
### Docker Compose (empfohlen)
Passe die Werte in der `.env`-Datei an und starte mit:
```bash
docker compose up -d
```
> **Tipp:** Das fertige Docker Image wird automatisch aus der [Container-Registry](https://git.techniverse.net/scriptos/-/packages/container/trackmania-server/) geladen. Wenn du das Image selbst bauen möchtest, verwende stattdessen `docker compose up -d --build`.
### docker run
```bash
docker run -d \
--env-file .env \
-p 2350:2350/tcp \
-p 2350:2350/udp \
-p 3450:3450/tcp \
-p 80:80/tcp \
-v ./data/gamedata:/opt/tmserver/GameData \
-v ./data/controlpanel:/var/www/html \
-v ./data/xaseco:/opt/tmserver/xaseco \
--name tmserver git.techniverse.net/scriptos/trackmania-server:latest
```
Einzelne Werte können zusätzlich überschrieben werden:
```bash
docker run -d \
--env-file .env \
-e SERVER_NAME="Mein Server" \
-e SERVER_MAX_PLAYERS=64 \
-p 2350:2350/tcp \
-p 2350:2350/udp \
-p 3450:3450/tcp \
-p 80:80/tcp \
-v ./data/gamedata:/opt/tmserver/GameData \
-v ./data/controlpanel:/var/www/html \
-v ./data/xaseco:/opt/tmserver/xaseco \
--name tmserver git.techniverse.net/scriptos/trackmania-server:latest
```
+169
View File
@@ -0,0 +1,169 @@
# XAseco
XAseco ist ein Server-Controller für TrackMania Nations Forever. Er verwaltet lokale Rekorde, Dedimania-Weltrekorde, Karma/Votes, eine Track-Jukebox und vieles mehr direkt im Spielchat.
Im Container wird die modifizierte Version **XAseco 1.16** verwendet, die für PHP 7.x angepasst wurde (Patches von Bueddel, Xymph, undef.de und reaby).
## Funktionsweise
XAseco verbindet sich über XML-RPC mit dem TrackMania-Server und reagiert auf Spielereignisse (neue Rekorde, Spieler-Connects, Chat-Befehle usw.). Die Daten werden in einer eigenen MySQL-Datenbank gespeichert.
## TeamSpeak 3 Integration
XAseco enthält ein Plugin (`plugin.teamspeak3.php`), das im Spiel ein Widget mit den aktuell verbundenen TeamSpeak-3-Nutzern anzeigt. Das Plugin ist **standardmäßig aktiviert** und verwendet ein eigenes Gateway, das im Container mitgeliefert wird (das Original-Gateway des Plugin-Entwicklers ist nicht mehr verfügbar).
### TS3-Server konfigurieren
Die Konfiguration erfolgt über die Datei `data/xaseco/teamspeak3.xml`. Dort kann der eigene TeamSpeak-3-Server eingetragen werden:
```xml
<?xml version="1.0" encoding="utf-8"?>
<settings>
<!-- Server Configuration, can be address or ip -->
<server>ts3.techniverse.net</server>
<serverid>1</serverid>
<serverport>9987</serverport>
<queryport>10011</queryport>
<!-- Channel & Update Settings -->
<defaultchannel></defaultchannel>
<subchannel></subchannel>
<channelpassword></channelpassword>
<update_interval>30</update_interval>
<!-- Helpers and images -->
<helperURL>http://assets.techniverse.net/tm/ts3gateway/gateway3.html</helperURL>
<logoURL>http://assets.techniverse.net/tm/ts3gateway/ts3logo.jpg</logoURL>
<!-- Widget position -->
<posx>-64</posx>
<posy>45</posy>
</settings>
```
| Feld | Beschreibung |
|------|-------------|
| `server` | Hostname oder IP des TS3-Servers |
| `serverid` | Virtuelle-Server-ID (meist `1`) |
| `serverport` | Voice-Port des TS3-Servers (Standard: `9987`) |
| `queryport` | ServerQuery-Port (Standard: `10011`) |
| `defaultchannel` | Standard-Channel (leer = Server-Default) |
| `subchannel` | Sub-Channel (optional) |
| `channelpassword` | Channel-Passwort (optional) |
| `update_interval` | Aktualisierungsintervall in Sekunden |
| `helperURL` | URL zur Gateway-HTML-Seite (nicht ändern) |
| `logoURL` | URL zum TS3-Logo im Widget (nicht ändern) |
| `posx` / `posy` | Widget-Position im Spiel |
> **Hinweis:** Standardmäßig ist der TS3-Server `ts3.techniverse.net` vorkonfiguriert. Zum Anpassen einfach nach dem ersten Start die Datei `data/xaseco/teamspeak3.xml` bearbeiten. Die Felder `helperURL` und `logoURL` verweisen auf das mitgelieferte Gateway und sollten nicht geändert werden.
## Konfiguration
Die Konfiguration erfolgt ausschließlich über Umgebungsvariablen in der `.env`-Datei. Beim **ersten Start** (leeres XAseco-Volume) werden die Werte automatisch in die XML-Konfigurationsdateien eingetragen.
### Erforderliche Variablen
| Variable | Beschreibung | Standard |
|----------|-------------|----------|
| `XASECO_MASTERADMIN_LOGIN` | Dein Spieler-Login (MasterAdmin mit allen Rechten) | *(muss gesetzt werden)* |
| `XASECO_DB_PASSWORD` | Passwort für die XAseco-Datenbank | *(muss gesetzt werden)* |
### Optionale Variablen
| Variable | Beschreibung | Standard |
|----------|-------------|----------|
| `XASECO_ENABLED` | XAseco aktivieren/deaktivieren | `true` |
| `XASECO_DB_HOST` | Hostname des Datenbankservers | `mariadb` |
| `XASECO_DB_NAME` | Name der XAseco-Datenbank | `xaseco` |
| `XASECO_DB_USER` | Datenbank-Benutzername | `xaseco` |
| `XASECO_DEDIMANIA_NATION` | Dedimania-Nation ([IOC-Code](https://en.wikipedia.org/wiki/List_of_IOC_country_codes), z.B. `DEU`, `AUT`, `CHE`) | `DEU` |
### Automatisch übernommene Variablen
Diese Werte werden aus der bestehenden Server-Konfiguration übernommen und müssen **nicht** separat gesetzt werden:
| Quelle | Verwendung in XAseco |
|--------|---------------------|
| `SERVER_SA_PASSWORD` | SuperAdmin-Passwort für die XML-RPC-Verbindung |
| `SERVER_XMLRPC_PORT` | XML-RPC-Port für die Verbindung zum TM-Server |
| `SERVER_LOGIN` | Dedimania Server-Login |
| `SERVER_LOGIN_PASSWORD` | Dedimania Server-Passwort |
## Datenbank
XAseco verwendet eine **eigene Datenbank** im selben MariaDB-Container. Die Datenbank und der Benutzer werden beim ersten Start des MariaDB-Containers automatisch erstellt (über ein Init-Script).
Das Datenbankschema (drei Tabellen-Dateien) wird beim ersten Start des tmserver-Containers automatisch importiert.
### Manueller Zugriff
Falls nötig, kann die XAseco-Datenbank über den MariaDB-Root-Zugang administriert werden:
```bash
docker exec -it tmserver-mariadb mysql -u root -p
```
### Nachträgliche Installation (bestehende MariaDB)
Das Init-Script (`assets/db/init-xaseco-db.sh`) wird von MariaDB nur beim **allerersten Start** (leeres `data/mariadb/`-Volume) ausgeführt. Wenn der MariaDB-Container bereits Daten enthält (z.B. weil er zuvor ohne XAseco lief), muss die Datenbank **einmalig manuell** erstellt werden:
```bash
docker exec -it tmserver-mariadb mysql -u root -p -e "
CREATE DATABASE IF NOT EXISTS xaseco CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS 'xaseco'@'%' IDENTIFIED BY 'DEIN_XASECO_DB_PASSWORT';
GRANT ALL PRIVILEGES ON xaseco.* TO 'xaseco'@'%';
FLUSH PRIVILEGES;"
```
Ersetze `DEIN_XASECO_DB_PASSWORT` durch den Wert von `XASECO_DB_PASSWORD` aus der `.env`-Datei. Das Tabellen-Schema wird anschließend beim nächsten Container-Start automatisch importiert, sofern das XAseco-Volume noch leer ist.
## Admin-System
XAseco kennt drei Berechtigungsstufen:
| Stufe | Beschreibung |
|-------|-------------|
| **MasterAdmin** | Volle Kontrolle (wird über `XASECO_MASTERADMIN_LOGIN` gesetzt) |
| **Admin** | Erweiterte Befehle (ebenfalls über `XASECO_MASTERADMIN_LOGIN` gesetzt) |
| **Operator** | Eingeschränkte Moderationsbefehle |
Weitere Admins/Operatoren können nach dem ersten Start manuell in `data/xaseco/adminops.xml` eingetragen werden.
## Persistente Daten
Alle XAseco-Daten werden im Volume `./data/xaseco/` gespeichert:
- Konfigurationsdateien (`config.xml`, `localdatabase.xml`, `dedimania.xml`, ...)
- Log-Datei (`aseco.log`)
- Admin-/Operator-Listen (`adminops.xml`)
## Deaktivieren
XAseco kann über die `.env`-Datei deaktiviert werden:
```env
XASECO_ENABLED=false
```
Der TrackMania-Server läuft dann ohne Server-Controller.
## Logs
Die XAseco-Logdatei befindet sich unter:
```
./data/xaseco/aseco.log
```
Bei Problemen ist dies die erste Anlaufstelle für die Fehlersuche.
## Wichtige Chat-Befehle
| Befehl | Beschreibung |
|--------|-------------|
| `/helpadmin` | Admin-Befehle anzeigen |
| `/recs` | Lokale Rekorde anzeigen |
| `/dedirecs` | Dedimania-Rekorde anzeigen |
| `/jukebox` | Track-Jukebox öffnen |
| `/stats` | Spieler-Statistiken anzeigen |
| `/admin help` | Alle Admin-Befehle |