# MSCO Dedicated Server Dedizierter Server für **MSCO – My Summer Car Online**. Er hält einen gemeinsamen Spielstand, speichert alles, was die Spieler verändern, und gibt es an später Beitretende weiter. .NET 10, läuft unter Linux (Docker oder systemd) und Windows. - Mod: [MSCO](https://git.mbm.wtf/MrMaho/MSCO) - Keymaster & Serverliste: [MSCO-Keymaster](https://git.mbm.wtf/MrMaho/MSCO-Keymaster) – https://msco.mbm.wtf - Launcher für Spieler: [MSCO-Launcher](https://git.mbm.wtf/MrMaho/MSCO-Launcher) – https://msco.mbm.wtf/download ![Web-Panel: Übersicht mit Start/Stop, Spielern, Laufzeit, Welt, Backups und letzter Aktivität](docs/images/panel-dashboard.png) ## Voraussetzungen - Ein **Lizenzschlüssel** von https://msco.mbm.wtf/keys (Login mit Steam). Der Server prüft ihn beim Start und meldet sich danach regelmäßig beim Master für die Serverliste. - Ein My-Summer-Car-Spielstand als Startwelt. - Port **7777/TCP** im Router/Firewall freigegeben (sonst wird der Server nicht gelistet). - Server und Mod müssen in den ersten zwei Stellen der Version passen (z.B. Server 2.8 ↔ Mod 2.8.x), sonst bekommen Spieler eine Warnung. Kleine Mod-Updates (dritte Stelle) brauchen kein Server-Update. ## Mit Docker (empfohlen) ```bash git clone https://git.mbm.wtf/MrMaho/MSCO-Dedicated-Server.git cd MSCO-Dedicated-Server docker compose up -d --build # erster Start legt data/server.json an und beendet sich ``` 1. In `data/server.json` mindestens `LicenseKey` eintragen. 2. Die Spielstand-Dateien nach `data/save/` kopieren (siehe unten). 3. `docker compose up -d` | Aktion | Befehl | |---|---| | Konsole | `docker attach msco-server` – verlassen mit **Strg+P, dann Strg+Q** (Strg+C beendet den Server) | | Einzelner Befehl | `docker exec msco-server msco `, z.B. `msco players` | | Logs | `docker logs -f msco-server` | | Stoppen | `docker compose stop` – speichert vorher den Weltzustand und meldet den Server ab | | Update | `git pull && docker compose up -d --build` | Anderer Port: in `data/server.json` **und** in `docker-compose.yml` ändern. ### Linux: „Permission denied“ in `data/` Der Container läuft als Benutzer-ID **1000** (der erste Linux-Benutzer), damit dir die Dateien in `data/` gehören. Hat dein Benutzer eine andere ID (`id -u` / `id -g`), trag sie in eine `.env` neben der `docker-compose.yml` ein: ```bash echo "MSCO_UID=$(id -u)" > .env echo "MSCO_GID=$(id -g)" >> .env ``` Wurden Dateien schon von einer älteren Version als root angelegt (typisch: „Permission denied“ beim Hochladen per SFTP oder bei `mkdir` in `data/`), einmal zurückholen: ```bash docker compose down sudo chown -R $(id -u):$(id -g) data docker compose up -d --build ``` ## Ohne Docker **Linux (systemd):** .NET 10 Runtime installieren (`sudo apt install dotnet-runtime-10.0`), dann `dotnet publish -c Release -o /opt/msco-server` und [`deploy/msco-server.service`](deploy/msco-server.service) einrichten (Anleitung oben in der Datei). Daten liegen in `/var/lib/msco-server`. Für eine Konsole den Server stattdessen in `screen`/`tmux` starten. **Windows:** `dotnet publish -c Release -o publish`, dann `publish\MSCO.Server.exe` starten. Startparameter: - `--data ` – Datenordner (Standard: Programmordner; Docker: `/data`) - `--import-save [pfad]` – Spielstand übernehmen (ohne Pfad: der eigene Spielstand dieses PCs, auch Proton) - `--cmd ` – Befehl an einen laufenden Server im selben Datenordner schicken ## Spielstand In `data/save/` gehören diese Dateien aus dem My-Summer-Car-Spielstand: `defaultES2File.txt`, `items.txt`, `meshsave.txt`, `graveyard.txt` (und `notepad.txt`, falls vorhanden). - Windows: `%USERPROFILE%\AppData\LocalLow\Amistech\My Summer Car` - Linux (Proton): `~/.steam/steam/steamapps/compatdata/516750/pfx/drive_c/users/steamuser/AppData/LocalLow/Amistech/My Summer Car` ### Spielstand hochladen (SFTP, z.B. WinSCP / FileZilla) Die Dateien einfach nach `data/save/` im Server-Ordner hochladen, z.B. `/home//MSCO-Dedicated-Server/data/save/`. Das geht auch, während der Server läuft. Kommt dabei **„Permission denied“**, gehört der Ordner noch root (siehe Abschnitt „Linux: Permission denied“ oben) – einmal `sudo chown -R $(id -u):$(id -g) data` ausführen. Meldet der **Server** nach dem Hochladen `Cannot read the save game … Permission denied`, gehören die neuen Dateien einem anderen Benutzer (z.B. mit einem anderen SFTP-Konto oder als root hochgeladen) oder deine Benutzer-ID ist nicht 1000. Der Server nennt im Log seine Benutzer-ID und den passenden `chown`-Befehl. ### Neuen Spielstand übernehmen Nach dem Hochladen eines **anderen** Spielstands die alten Änderungen verwerfen und die Dateien neu einlesen – sonst passt der gespeicherte Weltzustand nicht zum neuen Spielstand: ```bash docker exec msco-server msco reset-world docker exec msco-server msco reload-save ``` Ohne Docker: die beiden Befehle in der Server-Konsole eingeben, oder den Server stoppen, `data/world.json` löschen und neu starten. ## Einstellungen (`data/server.json`) | Einstellung | Bedeutung | |---|---| | `LicenseKey` | Schlüssel von der Keymaster-Seite (Pflicht) | | `Name`, `Description` | Anzeige im Server-Browser | | `MaxPlayers`, `Port` | Spieler-Limit (max. 3 sinnvoll), Port | | `Public` | `false` = nicht in der Liste, nur Direct Connect | | `RequireSteam` | `true` = nur Spieler mit Steam; `false` = Non-Steam-Server (Spieler werden über den Namen erkannt) | | `PublicAddress` | öffentliche IP/Domain für die Liste (nur hinter NAT/Proxy nötig) | | `RemoteManagement` | `true` = der Besitzer kann den Server im MSCO Launcher verwalten (Befehle über den Keymaster) | | `AutosaveSeconds` | wie oft der Weltzustand gespeichert wird | | `SaveSyncMinutes` | wie oft (Minuten) sich der Server von einem Spieler im Spiel den **aktuellen Spielstand** hochladen lässt (Standard 10, zusätzlich bei jedem Beitritt; nur wenn sich etwas geändert hat; 0 = nur beim Beitritt) | | `SteamWebApiKey` | Steam-Anmeldetickets der Spieler bei Steam prüfen – **nötig für Admins** (kostenlos: https://steamcommunity.com/dev/apikey) | | `BannedSteamIds` | gesperrte Spieler | | `Admins` | SteamID64s, die im Spiel mit `/admin` kicken/bannen dürfen (nur mit `SteamWebApiKey`, siehe Sicherheit) | Fehlende Einstellungen ergänzt der Server beim Start automatisch. Umgebungsvariablen (`MSCO_LICENSE_KEY`, `MSCO_NAME`, `MSCO_PORT`, `MSCO_MAX_PLAYERS`, `MSCO_PUBLIC`, `MSCO_REQUIRE_STEAM`, `MSCO_PUBLIC_ADDRESS`, `MSCO_STEAM_WEB_API_KEY`) überschreiben `server.json`. ## Web-Panel Der Server bringt ein Web-Panel mit – **auf demselben Port wie das Spiel** (Standard `7777`). Einfach im Browser `http://:7777/` öffnen; der Server erkennt selbst, ob ein Browser oder das Spiel verbindet. Es muss also kein weiterer Port freigegeben werden. | Spieler | Live-Konsole | |---|---| | ![Spielerliste mit Kick, Ban und Admin-Rechten](docs/images/panel-players.png) | ![Live-Konsole mit Chat, Beitritten und Befehlseingabe](docs/images/panel-console.png) | | **Welt & Backups** | **Einstellungen** | | ![Welt zurücksetzen, Backups, Reset-Punkt und Spielstand-Upload](docs/images/panel-world.png) | ![Einstellungen: Name, Spielerzahl, Sichtbarkeit, Speichern, Ports und Schlüssel](docs/images/panel-settings.png) | **Erstes Anmelden:** Beim Start ohne Panel-Konto steht im Log ein Einrichtungs-Code: ```bash docker logs msco-server | grep -A2 "WEB PANEL SETUP" ``` Mit diesem Code legst du im Browser das **Owner**-Konto an (erneut anzeigen: `msco panel`). | Bereich | Was geht | |---|---| | Dashboard | Spieler, Laufzeit, Weltzustand, Backups – **Start / Stop / Restart** des Gameservers, Grund wenn er nicht läuft | | Players | Liste mit ID, Name, SteamID, IP · Kick, Ban, Admin geben/nehmen · Nachricht an alle | | Console | Live-Log (Chat, Beitritte, Admin-Aktionen) und Befehle eingeben (mit Verlauf ↑/↓) | | Admins | Ingame-Admins per SteamID hinzufügen/entfernen (braucht einen [Steam Web API Key](https://steamcommunity.com/dev/apikey)) | | Bans | Gebannte ansehen, entbannen, SteamID auch offline bannen | | World & Backups | Welt zurücksetzen, Backup, Reset-Punkt, Backups wiederherstellen, **Spielstand als ZIP, RAR oder 7z hochladen**, als ZIP herunterladen, **neues Spiel starten** | | Settings | `server.json` bearbeiten (Name, Beschreibung, **Tags**, Spielerzahl, Sichtbarkeit, Ports, Panel, Schlüssel …) | | Update | neue Server-Versionen prüfen und mit Fortschrittsanzeige installieren (oranger Punkt in der Seitenleiste, wenn eine da ist) | | Panel Users | weitere Benutzer anlegen: **Moderator** (Spieler/Chat), **Admin** (alles am Server), **Owner** (+ Benutzer) | - **Das Panel ist unabhängig vom Gameserver:** Es läuft auch, wenn der Server gestoppt ist, abgestürzt ist oder gar nicht starten kann (z.B. kein Lizenzschlüssel, kein Spielstand). Das Dashboard zeigt dann den Grund – Schlüssel unter **Settings** eintragen bzw. Spielstand als ZIP unter **World & Backups** hochladen und **Start** drücken. - **Stop** fährt den Gameserver herunter (Spieler bekommen den Grund, niemand kann beitreten), **Start** startet ihn wieder, **Restart** lädt Einstellungen, Lizenz und Spielstand neu. Nach einem Absturz startet er automatisch neu. - Erster Start ganz ohne Einrichtung: `docker compose up -d`, Panel öffnen, Einrichtungs-Code aus dem Log eingeben, Lizenzschlüssel und Spielstand im Panel hinterlegen, Start. - Oberfläche auf **Deutsch oder Englisch** (Umschalter unten in der Seitenleiste). - **Port oder Panel-Einstellungen geändert?** In den Einstellungen unten **„Panel neu starten“** – übernimmt sie sofort, man bleibt angemeldet und wird auf die neue Adresse weitergeleitet. Ist *Web-Panel aktiviert* abgehakt, wird das Panel dabei wirklich ausgeschaltet – wieder an mit `panel on` in der Server-Konsole. - **Tags**: eigene Tags für die Serverliste (z.B. `deutsch, roleplay`, max. 6). `official` und `featured` vergeben nur die MSCO-Admins im Keymaster. - **Anmeldungen überstehen Neustarts** (auch Updates): Sitzungen liegen gehasht in `data/panel_sessions.json`. - **Keymaster nicht erreichbar oder lehnt den Schlüssel ab?** Ein laufender Server läuft bis zum nächsten Neustart weiter und warnt im Log/Panel. Ein Neustart klappt noch bis 72 Stunden nach der letzten Bestätigung (gespeichert in `data/license.json`); danach startet der Server von selbst, sobald der Master den Schlüssel wieder bestätigt. - **Neues Spiel** (Welt & Backups → „Neues Spiel starten“, Konsole `new-game`): statt eines hochgeladenen Spielstands legt der erste Spieler, der beitritt, eine frische Welt an; der Server übernimmt danach dessen Spielstand. Der bisherige Spielstand landet in den Backups, „Welt zurücksetzen“ geht dann wieder auf ein neues Spiel zurück. - **Updates im Panel** (Seite „Update“): Der Server prüft alle 6 Stunden, ob auf git.mbm.wtf eine neuere Version liegt (Hinweis im Log und in der Seitenleiste). „Update installieren“ lädt den Quellcode, baut ihn mit dem .NET SDK und startet neu – mit Fortschrittsanzeige (Download, Bauen, Installieren, Neustart), die auch den Neustart des Panels übersteht; man bleibt angemeldet und Spieler verbinden sich automatisch neu. Das Update liegt in `data/app/current` und wird beim Start statt der eingebauten Version geladen; startet es dreimal nicht, läuft wieder die eingebaute. Ein neueres Docker-Image (`git pull && docker compose up -d --build`) ersetzt ein älteres Panel-Update automatisch. Voraussetzung: .NET SDK (im Docker-Image ab 2.9 enthalten) und ein Dienst, der nach dem Beenden neu startet (Docker `restart: unless-stopped`, systemd) – unter Windows startet sich der Server selbst neu. - Passwörter werden nur als PBKDF2-Hash in `data/panel_users.json` gespeichert, Login mit Sperre nach Fehlversuchen, Sitzungs-Cookie (HttpOnly, SameSite=Strict), Schutz gegen CSRF. ### Hinter einem Reverse-Proxy Den Proxy einfach auf `http://:7777` zeigen lassen und in `server.json` setzen: ```json "PanelTrustProxy": true, "PanelBasePath": "" ``` `PanelTrustProxy` übernimmt die echte Besucher-IP und HTTPS (`X-Forwarded-For` / `-Proto`), `PanelBasePath` (z.B. `"/panel"`) bzw. `X-Forwarded-Prefix` für einen Unterpfad. Lieber einen eigenen Port? `"PanelPort": 40120` (und im `docker-compose.yml` freigeben); mit `"PanelBindAddress": "127.0.0.1"` ist das Panel dann nur über den lokalen Proxy erreichbar. Ganz aus: `"PanelEnabled": false`. > **Eigener Panel-Port – nicht aussperren:** Der Port gilt erst nach einem Neustart des ganzen Programms > (`docker compose restart`; der Neustart-Knopf im Panel startet nur den Gameserver neu). Danach ist das Panel **nur > noch** unter dem neuen Port erreichbar, in Docker nur, wenn er in `docker-compose.yml` unter `ports` steht > (`- "40120:40120/tcp"`). Schon ausgesperrt? `"PanelPort": 0` in `data/server.json` setzen und > `docker compose restart` – dann ist das Panel wieder auf dem Spiel-Port. ## Verwalten aus dem MSCO Launcher Im Launcher unter **Meine Server** verknüpft der Besitzer seinen Server einmal mit dem Lizenzschlüssel. Danach sieht er dort Status und Spieler und kann Befehle schicken: `say`, `kick`, `ban`, `save`, `backup`, `restart`, `mods reload`, `status`, `players`. Der Server holt sie alle 5 Sekunden beim Keymaster ab und führt nur Befehle aus dieser Liste aus (im Log mit „Launcher (Konto)“). Abschalten: im Web-Panel unter *Einstellungen* „Verwalten aus dem MSCO Launcher“ bzw. `"RemoteManagement": false` in `server.json`. ## Mods MSCO-Mods (.dll, siehe [MODDING.md](https://git.mbm.wtf/MrMaho/MSCO/src/branch/main/MODDING.md)) in `data/mods` – oder im Web-Panel unter **Mods** hochladen. Jeder Spieler bekommt beim Beitreten die Liste; fehlende Mods lädt sein Spiel nach seiner Zustimmung direkt vom Server (SHA-256 geprüft, höchstens 30 Mods à 20 MB). In der Serverliste bekommt der Server automatisch den Tag **MODDED** und zeigt seine Mods an. Nach Änderungen im Ordner: `mods reload` (Panel: automatisch). Im Panel unter **Mods → MSCO-Modliste** lassen sich Mods aus der offiziellen Liste ([msco.mbm.wtf/mods](https://msco.mbm.wtf/mods), von den MSCO-Admins geprüft) durchsuchen und mit einem Klick zum Server hinzufügen oder aktualisieren – der Server lädt sie nur vom offiziellen Keymaster und prüft die SHA-256. > Mods laufen auf den PCs der Spieler – nur Mods anbieten, denen du vertraust. ## Konsolenbefehle | Befehl | Beschreibung | |---|---| | `status`, `players` | Übersicht, Spielerliste mit IDs | | `kick [grund]`, `ban [grund]` | Spieler entfernen / sperren | | `bans`, `unban ` | Gesperrte anzeigen / Sperre aufheben | | `say ` | Chatnachricht an alle | | `admins`, `op `, `deop <...>` | Admins anzeigen / geben / nehmen | | `save` | Weltzustand jetzt speichern | | `reload-save` | Spielstand-Dateien neu einlesen – verbundene Spieler verbinden sich automatisch neu | | `backups`, `restore-save ` | gesicherte Spielstände anzeigen / einen davon zurückholen (z.B. wenn der aktuelle kaputt ist) – alle verbinden sich neu | | `backup` | aktuellen Spielstand jetzt sichern | | `reset-world` | ganze Welt zurück auf den Reset-Punkt (den Spielstand, wie er dem Server gegeben wurde: Start, `--import-save`, `reload-save`) – alle verbinden neu | | `set-reset-point` | aktuellen Spielstand als neuen Reset-Punkt nehmen | | `stop` / `start` | Gameserver stoppen (das Web-Panel läuft weiter) / wieder starten | | `restart` | Server neu starten (Einstellungen, Lizenz, Spielstand neu laden) | | `exit` | ganzes Programm beenden (auch das Panel) – mit Docker startet es danach wieder, dauerhaft aus: `docker compose stop` | | `new-game` | Spielstand sichern und ein frisches neues Spiel starten (der erste Spieler legt die Welt an) – alle verbinden neu | | `panel [on\|off]` | Adresse des Web-Panels und ggf. den Einrichtungs-Code anzeigen / Panel an- oder ausschalten | | `mods`, `mods reload` | Server-Mods anzeigen / `data/mods` neu einlesen | Im Spiel öffnen Admins mit `/admin` (Chat, Taste T) oder `admin` (F7-Konsole) das Admin-Menü: Kick/Ban, **Admin geben/nehmen**, GOTO/BRING (zu einem Spieler / Spieler zu sich teleportieren), Cheats (Geld, Bedürfnisse auffüllen, Satsuma zu sich holen), Items spawnen und Welt/Backups. Cheats werden allen im Chat angezeigt, Teleports stehen im Server-Log. Der Chat landet auch im Server-Log. ## Was der Server speichert **Aktueller Spielstand statt Nachspielen:** Tritt jemand bei (und alle `SaveSyncMinutes`), bittet der Server einen Spieler, der schon im Spiel ist, zu speichern und den Spielstand hochzuladen – genau wie das Spiel selbst speichert (Schrauben, angebaute Teile, alles). Neue Spieler bekommen diesen Stand; alte Aktionen werden dann nicht mehr nachgespielt. Der vorherige Spielstand liegt jeweils in `data/save_backups/` (die letzten 5). - `save/` – der Ausgangs-Spielstand, den jeder neue Spieler bekommt. - `world.json` – alles, was seitdem passiert ist: Positionen bewegter Objekte, Schraub-/Montage-Aktionen, Geld, Türen/Tore, Fahrzeugzustand (Tank, Öl, Verschleiß, Teile, Lack …), Schalter (Licht, Zündung, Motor …), Jobs und Events, gekaufte/verbrauchte Items sowie die letzte Position und die Bedürfnisse (Hunger, Durst …) jedes Spielers. - Den Spielstand holt sich der Server nur, wenn sich seitdem etwas geändert hat (Speichern lässt das Spiel kurz hängen). Beim Beitreten: Spielstand laden → Objekte erfassen → Weltzustand anwenden → an der letzten eigenen Position weiterspielen. ## Betrieb in Produktion **Checkliste** - [ ] `LicenseKey` gesetzt, Server startet ohne Fehler (`docker logs msco-server`) - [ ] Nur Port **7777/TCP** nach außen offen; der Server braucht sonst nur ausgehend HTTPS zu `msco.mbm.wtf` - [ ] Log meldet keine Warnung „NOT reachable from the internet“ (sonst erscheint der Server nicht in der Liste) - [ ] Steam-Server (`RequireSteam: true`): `SteamWebApiKey` eintragen, damit Anmeldetickets wirklich bei Steam geprüft werden - [ ] Mindestens eine SteamID in `Admins` - [ ] Backups von `data/` eingerichtet (siehe unten) **Neustarts:** Docker startet den Server nach einem Absturz neu (`restart: on-failure`). Bei Konfigurationsfehlern (kein/ungültiger Schlüssel, kein Spielstand) beendet er sich mit Code 1 und wird nach 3 Versuchen nicht mehr gestartet – dann ins Log schauen. `docker compose stop` / `systemctl stop` speichern vorher den Weltzustand. **Logs:** werden von Docker auf 3 × 10 MB begrenzt. Chat, Beitritte, Kicks/Bans und Admin-Aktionen stehen darin. **Backups:** Alles Wichtige liegt in `data/` (`server.json`, `save/`, `world.json`). Den Server dafür nicht anhalten – der Weltzustand wird atomar geschrieben. Beispiel (täglich per cron): ```bash tar czf /backup/msco-$(date +%F).tar.gz -C /pfad/zu/MSCO-Dedicated-Server data ``` **Updates:** am einfachsten im Web-Panel unter **Update**. Server und Mod müssen in den ersten zwei Stellen der Version passen; die Mod bei den Spielern aktualisiert der Launcher. Ohne Panel: ```bash git pull docker compose up -d --build ``` Spieler mit einer anderen Mod-Version bekommen beim Verbinden eine Warnung. **Weltzustand zurücksetzen:** `msco reset-world` (oder RESET WORLD im Admin-Menü) setzt die ganze Welt auf den Reset-Punkt zurück – den Spielstand, wie er dem Server gegeben wurde (liegt in `data/save_original/`). Einen **neuen** Spielstand übernehmen: Dateien nach `data/save/` legen und `msco reload-save` ausführen – der wird dann auch der neue Reset-Punkt. ## Sicherheit - Die Adresse des Masters (`https://msco.mbm.wtf`) ist fest eingebaut, damit niemand die Schlüsselprüfung mit einem eigenen Master umgehen kann. Sie lässt sich weder in `server.json` noch per Umgebungsvariable ändern. - `data/` enthält Lizenzschlüssel und API-Keys – nicht veröffentlichen (steht in `.gitignore`). - Der Container läuft nicht als root. Spielerdaten (Nachrichten, Objektzustände) werden in der Größe begrenzt, unbekannte oder zu große Nachrichten verworfen. - **Admins nur mit `SteamWebApiKey`:** Ohne Key prüft der Server die Steam-Tickets nicht – jeder könnte dann eine fremde SteamID (z.B. die eines Admins) angeben. Deshalb gibt es ohne Key keine Admin-Rechte (das Log warnt beim Start), und eine unbestätigte SteamID kann keinen verbundenen Spieler verdrängen. - Gebannte Spieler werden über ihre (bei Steam geprüfte) SteamID erkannt. Auf Non-Steam-Servern gibt es keine verlässliche Identität – dort sind Bans nur über den Namen möglich. ## Lizenz GNU AGPL-3.0 – siehe [LICENSE](LICENSE). Made by **MBM** – [mbm.wtf](https://mbm.wtf)