From 61deb9327382ef26ab1fb71705007822031e3098 Mon Sep 17 00:00:00 2001 From: "Patrick Asmus (scriptos)" Date: Sun, 22 Mar 2026 17:05:25 +0100 Subject: [PATCH] feat: Automatische MatchSettings-Erkennung & AdminServ-Bugfixes --- .env.example | 6 + Dockerfile | 10 ++ assets/bin/RunTrackmaniaServer.sh | 76 ++++++++- .../adminserv/get_matchset_mapimport.php | 76 +++++++++ .../config/adminserv/maps-creatematchset.php | 160 ++++++++++++++++++ docs/adminserv.md | 32 ++++ docs/konfiguration.md | 46 +++++ docs/umgebungsvariablen.md | 17 ++ 8 files changed, 421 insertions(+), 2 deletions(-) create mode 100644 assets/config/adminserv/get_matchset_mapimport.php create mode 100644 assets/config/adminserv/maps-creatematchset.php diff --git a/.env.example b/.env.example index f216c68..66cd619 100644 --- a/.env.example +++ b/.env.example @@ -55,6 +55,12 @@ SERVER_MODE=internet # In einer Produktionsumgebung sollte dieser Wert jedoch auf false belassen werden, um zu verhindern, dass die Konfiguration versehentlich überschrieben wird. FORCE_CONFIG_UPDATE=false +# --- 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 diff --git a/Dockerfile b/Dockerfile index 055b6b6..ec1486a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -76,6 +76,16 @@ RUN unzip /var/www/html/remoteCP_v4.0.3.5.zip -d /var/www/html \ 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 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 diff --git a/assets/bin/RunTrackmaniaServer.sh b/assets/bin/RunTrackmaniaServer.sh index 42872da..a3e7afe 100644 --- a/assets/bin/RunTrackmaniaServer.sh +++ b/assets/bin/RunTrackmaniaServer.sh @@ -369,6 +369,34 @@ if [ -f "$CUSTOMPOINTS_FILE" ] && ! grep -q 'defined.*pt_custom' "$CUSTOMPOINTS_ 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 + echo "Starting apache server" service apache2 start @@ -576,6 +604,50 @@ 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. +# - "": 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}" @@ -595,8 +667,8 @@ fi echo "Server config dedicated_cfg.txt is" cat "$CONFIG" -echo "Launching Server in ${SERVER_MODE} mode" -./TrackmaniaServer /dedicated_cfg=dedicated_cfg.txt /game_settings=MatchSettings/custom_game_settings.txt /nodaemon ${LAUNCH_MODE} & +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})" diff --git a/assets/config/adminserv/get_matchset_mapimport.php b/assets/config/adminserv/get_matchset_mapimport.php new file mode 100644 index 0000000..fceeb8c --- /dev/null +++ b/assets/config/adminserv/get_matchset_mapimport.php @@ -0,0 +1,76 @@ + 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']); + } +?> diff --git a/assets/config/adminserv/maps-creatematchset.php b/assets/config/adminserv/maps-creatematchset.php new file mode 100644 index 0000000..3d809ac --- /dev/null +++ b/assets/config/adminserv/maps-creatematchset.php @@ -0,0 +1,160 @@ + $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; + } +?> diff --git a/docs/adminserv.md b/docs/adminserv.md index 9dc11d9..ed5a726 100644 --- a/docs/adminserv.md +++ b/docs/adminserv.md @@ -66,3 +66,35 @@ 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 + +MatchSettings/speed vs. fullspeed.Challenge.Gbx + + +Challenges/Downloaded/speed vs. fullspeed.Challenge.Gbx +``` + +**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` diff --git a/docs/konfiguration.md b/docs/konfiguration.md index f9ae1ee..26dd20a 100644 --- a/docs/konfiguration.md +++ b/docs/konfiguration.md @@ -160,3 +160,49 @@ Alle diese Parameter können über [Umgebungsvariablen](umgebungsvariablen.md) g | `` | `SERVER_XMLRPC_PORT` | `5000` | | `` | `SERVER_UPLOAD_RATE` | `512` | | `` | `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. diff --git a/docs/umgebungsvariablen.md b/docs/umgebungsvariablen.md index 705139c..c1d7068 100644 --- a/docs/umgebungsvariablen.md +++ b/docs/umgebungsvariablen.md @@ -71,8 +71,25 @@ nano .env | 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.