Initial commit: Minecraft Java & Bedrock crossplay server setup

This commit is contained in:
2026-09-13 22:06:00 +02:00
commit 9f50df534c
8 changed files with 781 additions and 0 deletions
+37
View File
@@ -0,0 +1,37 @@
# Stellschrauben fuer den Crossplay-Server.
# Nach jeder Aenderung: docker compose up -d
# Minecraft-/Paper-Version. LATEST = jeweils neueste stabile Version.
# Fest pinnen (z.B. 26.2) macht Updates planbar - empfohlen im Dauerbetrieb.
MC_VERSION=LATEST
# Arbeitsspeicher fuer die JVM (Xms=Xmx). Faustregel: 4-8G fuer eine
# Handvoll Spieler, nie mehr als ~50-60% des Host-RAM.
MC_MEMORY=4G
# Ports auf dem Host. Container-intern bleibt es immer 25565/19132.
JAVA_PORT=25565
BEDROCK_PORT=19132
# server.properties
MC_MOTD=Crossplay Server - Java & Bedrock
MC_DIFFICULTY=normal
MC_MAX_PLAYERS=20
MC_VIEW_DISTANCE=10
MC_SIMULATION_DISTANCE=8
# Operatoren: Java-Namen kommagetrennt. Bedrock-Spieler kommen mit ihrem
# Floodgate-Namen inkl. Praefix, also z.B. .MeinGamertag
MC_OPS=PlayerName
# Whitelist. Erst auf true stellen, wenn unten der richtige Name drin steht -
# sonst sperrst du dich vom eigenen Server aus.
# MC_WHITELIST nimmt nur Java-Profilnamen (kommagetrennt). Bedrock-Spieler
# kommen per ␍con-cli "fwhitelist add <Gamertag>" dazu.
MC_WHITELIST_ENABLED=true
MC_WHITELIST=PlayerName
# Passwort fuer RCON (nur containerintern erreichbar, kein Host-Port).
RCON_PASSWORD=change_this_to_a_secure_password
TZ=Europe/Berlin
+5
View File
@@ -0,0 +1,5 @@
.env
*.log
.DS_Store
Thumbs.db
+265
View File
@@ -0,0 +1,265 @@
# Deployment: Oracle Cloud (Ampere A1) auf minecraft.djnizz.at
Anleitung fuer den Umzug des lokal getesteten Stacks auf die bestehende
OCI-Instanz. Alles per Hand, kein Automatismus.
## Ausgangslage
| | |
|---|---|
| Ziel-Host | `130.61.230.183` |
| DNS | `minecraft.djnizz.at` → `130.61.230.183` (A-Record bei helloly, **existiert bereits**) |
| Shape | Ampere A1, ARM64 |
| SSH-Key | `C:\Users\nicol\Documents\oracle free private key.ppk` |
| Paket | `deploy/minecraft-crossplay.tar.gz` |
Geprueft: DNS loest korrekt auf, die Ports 25565/TCP und 19132/UDP sind von
aussen aktuell noch dicht — erwartbar, es laeuft ja noch nichts.
**ARM ist kein Problem.** `itzg/minecraft-server` ist multiarch und bringt
`linux/arm64` mit; Paper, Geyser und Floodgate sind reines Java.
**Caddy wird nicht gebraucht.** Minecraft spricht ein eigenes Protokoll ueber
rohes TCP, Bedrock zusaetzlich UDP — beides kann ein HTTP-Reverse-Proxy nicht
sinnvoll weiterreichen. Die Container-Ports werden direkt veroeffentlicht.
80/443 bleiben unberuehrt, es gibt keine Kollision.
## 1. SSH-Key nutzbar machen
Die `.ppk` ist PuTTY-Format, `scp`/`ssh` von Windows koennen damit nichts
anfangen. Entweder mit `pscp`/WinSCP arbeiten oder einmalig konvertieren:
```powershell
puttygen "$HOME\Documents\oracle free private key.ppk" -O private-openssh -o "$HOME\.ssh\oracle_mc.key"
```
Danach Rechte einschraenken, sonst verweigert OpenSSH den Key:
```powershell
icacls "$HOME\.ssh\oracle_mc.key" /inheritance:r /grant:r "$($env:USERNAME):(R)"
```
Der Login-User haengt am Image: Ubuntu → `ubuntu`, Oracle Linux → `opc`.
## 2. Paket hochladen und entpacken
```powershell
scp -i "$HOME\.ssh\oracle_mc.key" "deploy\minecraft-crossplay.tar.gz" ubuntu@130.61.230.183:~/
```
```powershell
ssh -i "$HOME\.ssh\oracle_mc.key" ubuntu@130.61.230.183
```
Auf dem Server:
```bash
mkdir -p ~/docker && tar xzf ~/minecraft-crossplay.tar.gz -C ~/docker/ && cd ~/docker/minecraft-crossplay && ls -la
```
Docker liegt durch den Caddy-Stack schon vor. Kurz gegenpruefen:
```bash
docker version --format '{{.Server.Version}}' && docker compose version
```
## 3. Ports in OCI oeffnen (der eigentliche Knackpunkt)
Das ist in der Praxis die Ursache fuer die meisten „Server nicht erreichbar"-
Faelle. In der OCI-Konsole:
**Networking → Virtual Cloud Networks → <deine VCN> → Security Lists →
Default Security List → Add Ingress Rules**
Zwei Regeln, beide *stateful* (Haken bei „Stateless" NICHT setzen):
| Source CIDR | IP Protocol | Destination Port Range |
|---|---|---|
| `0.0.0.0/0` | TCP | `25565` |
| `0.0.0.0/0` | UDP | `19132` |
Haengt an der VNIC deiner Instanz eine **Network Security Group**, muessen die
beiden Regeln dort hinein statt in die Security List — sonst greifen sie nicht.
Die UDP-Regel wird gerne vergessen. Ohne sie funktioniert Java einwandfrei und
Bedrock gar nicht.
## 4. Firewall auf der Instanz
Von Docker veroeffentlichte Ports gehen per DNAT an der `INPUT`-Kette vorbei,
deshalb laeuft das meist ohne Zutun. Erst testen (Schritt 6), und nur wenn es
scheitert, hier nachziehen.
Welche Firewall aktiv ist:
```bash
systemctl is-active firewalld; sudo iptables -S INPUT | head
```
**Oracle Linux (firewalld):**
```bash
sudo firewall-cmd --permanent --add-port=25565/tcp --add-port=19132/udp && sudo firewall-cmd --reload
```
**Ubuntu (iptables + netfilter-persistent):**
```bash
sudo iptables -I INPUT 5 -p tcp --dport 25565 -j ACCEPT && sudo iptables -I INPUT 6 -p udp --dport 19132 -j ACCEPT && sudo netfilter-persistent save
```
## 5. Konfiguration anpassen
In `~/docker/minecraft-crossplay/.env`:
* `MC_MEMORY` — im Paket auf `8G` gesetzt. Die Instanz hat 24 GB, davon gehen
Caddy und der uebrige Kram ab; 8 GB sind fuer eine private Runde reichlich.
* `MC_OPS` — dein Java-Name, sonst hast du auf dem Server keine Rechte.
* `MC_MOTD` — steht auf `djnizz.at - Java & Bedrock`.
* `RCON_PASSWORD` — im Paket bereits neu erzeugt, nicht identisch mit lokal.
## 6. Starten und pruefen
```bash
cd ~/docker/minecraft-crossplay && docker compose up -d && docker compose logs -f
```
Fertig, sobald `Started Geyser on UDP port 19132` und `Done (…)! For help, type
"help"` erscheinen. Erster Start dauert einige Minuten (Paper, Plugins, Welt).
Danach **von deinem Windows-Rechner aus** — das ist der ehrliche Test, weil er
den kompletten Weg inklusive Security List prueft:
```bash
python "scripts/check-crossplay.py" minecraft.djnizz.at
```
Beide Abschnitte muessen `OK` melden. Kommt bei Java ein Timeout, fehlt die
TCP-Regel; kommt er nur bei Bedrock, fehlt die UDP-Regel.
## 7. Verbinden
* **Java Edition:** `minecraft.djnizz.at` (Port 25565 ist Standard, muss nicht
mitgegeben werden)
* **Bedrock Edition:** Adresse `minecraft.djnizz.at`, Port `19132`
* **Konsolen** (PS/Xbox/Switch) koennen keine freie Adresse eintragen — dort
fuehrt der Weg ueber BedrockConnect oder eine DNS-Umleitung im Heimnetz.
### Zum SRV-Record
Ein SRV-Record (`_minecraft._tcp.minecraft.djnizz.at`) lohnt nur, wenn Java auf
einem anderen Port als 25565 laufen soll — dann koennen Java-Spieler die
Portangabe weglassen. **Bedrock wertet keine SRV-Records aus**, dort muss der
Port immer 19132 sein oder manuell eingetippt werden. Bei der jetzigen
Konfiguration brauchst du also keinen.
## 8. Betrieb
**Wichtig — reservierte IP:** Ist die Public IP der Instanz *ephemeral*, aendert
sie sich beim Stoppen/Starten der VM und der A-Record bei helloly zeigt ins
Leere. In der OCI-Konsole unter *Instance → Attached VNICs → IPv4 Addresses* auf
**Reserved** umstellen, falls noch nicht geschehen.
Autostart nach Reboot deckt `restart: unless-stopped` ab, solange der
Docker-Dienst selbst aktiviert ist:
```bash
sudo systemctl is-enabled docker
```
Konsole und Backup laufen wie lokal (siehe [README.md](README.md)):
```bash
docker compose exec minecraft rcon-cli
```
```bash
docker compose exec minecraft rcon-cli save-all && docker run --rm -v mc-crossplay_mc-data:/data -v "$PWD":/backup alpine tar czf /backup/mc-backup-$(date +%F).tar.gz -C /data .
```
Ein Cronjob dafuer ist auf einem oeffentlich erreichbaren Server keine schlechte
Idee.
## 9. Oeffentlicher Server: Whitelist
Sobald die Ports offen sind, findet dich jeder Scanner. Die Whitelist ist im
Stack bereits verkabelt und wird ueber die `.env` gesteuert:
```
MC_WHITELIST_ENABLED=true
MC_WHITELIST=Nizz_05
```
`MC_WHITELIST` nimmt **nur Java-Profilnamen** - itzg loest sie beim Start ueber
PlayerDB/Mojang in UUIDs auf. Ein Xbox-Gamertag scheitert an diesem Lookup und
landet still nicht in der Liste. Trag hier also nie einen Bedrock-Namen ein.
### Bedrock-Spieler auf die Whitelist
Erster Versuch ueber den Gamertag - Floodgate strippt einen fuehrenden Punkt
selbst:
```bash
docker compose exec minecraft rcon-cli "fwhitelist add Nizz05"
```
Das schlaegt oft fehl, weil Floodgate den Gamertag ueber die oeffentliche
GeyserMC-API in eine XUID aufloest und die nur Accounts kennt, die dort schon
mal abgefragt wurden. **Achtung:** ueber RCON kommt in beiden Faellen *keine*
Ausgabe zurueck - Floodgate schickt seine Antworten ueber ein eigenes
Message-System, das den RCON-Aufrufer nicht erreicht. Leerer Output heisst also
nicht „hat geklappt". Maszgeblich ist immer:
```bash
docker compose exec minecraft rcon-cli "whitelist list"
```
Der zuverlaessige Weg fuehrt ueber die Floodgate-UUID. Der Spieler verbindet
sich einmal bei abgeschalteter Whitelist (`whitelist off`), danach steht er im
User-Cache:
```bash
docker compose exec minecraft cat /data/usercache.json
```
Die UUID hat die Form `00000000-0000-0000-XXXX-XXXXXXXXXXXX`: die oberen 64 Bit
sind null (daran erkennt Floodgate seine eigenen IDs), die hinteren 16
Hex-Stellen sind die Xbox-XUID. Damit:
```bash
docker compose exec minecraft rcon-cli "fwhitelist add 00000000-0000-0000-XXXX-XXXXXXXXXXXX"
```
In `whitelist list` taucht der Eintrag danach als **`unknown`** auf, nicht unter
dem Gamertag - Floodgate traegt beim Hinzufuegen per UUID fest `"unknown"` als
Namen ein. Rein kosmetisch, geprueft wird gegen die UUID.
`EXISTING_WHITELIST_FILE: MERGE` in der `docker-compose.yml` sorgt dafuer, dass
diese Eintraege einen Neustart ueberleben. Ohne das wuerde die `whitelist.json`
bei jedem Start auf den Inhalt von `MC_WHITELIST` zusammengestrichen - und die
Bedrock-Spieler waeren wieder draussen.
**Reihenfolge beachten:** erst eintragen, dann scharf schalten. Operatoren
umgehen die Whitelist zwar, aber darauf sollte man sich nicht verlassen.
## 10. Troubleshooting
**Von aussen keine Verbindung, lokal auf dem Server aber schon** — dann liegt es
an OCI. Auf dem Server gegenpruefen:
```bash
docker compose exec minecraft rcon-cli list
```
Antwortet das, laeuft der Server und es fehlt die Security-List-Regel oder die
Instanz-Firewall blockt.
**Bedrock timeoutet, Java geht** — die UDP-Regel fehlt. Sicher der haeufigste
Fall.
**IP hat sich geaendert** — A-Record bei helloly nachziehen und die Public IP
auf reserved umstellen.
**Container startet in Schleife, Log sagt „requires Java 25 or above"** — das
Image-Tag wurde auf eine aeltere JVM zurueckgesetzt. Es muss
`stable-java25` oder neuer bleiben.
+202
View File
@@ -0,0 +1,202 @@
# Minecraft Crossplay-Server (Java + Bedrock)
Ein PaperMC-Server im Container, der per **GeyserMC** zusaetzlich Bedrock-Clients
(Konsole, Handy, Windows-10-Edition) annimmt. **Floodgate** authentifiziert diese
Spieler ueber Xbox Live, ein gekaufter Java-Account ist fuer sie nicht noetig.
Es bleibt ein **Java-Server**: Redstone, Kampfsystem und Mob-Verhalten folgen den
Java-Mechaniken. Geyser sitzt als Uebersetzungsschicht dazwischen.
```
Java-Client ──TCP 25565──┐
├──► PaperMC (Java-Server)
Bedrock-Client ─UDP 19132─┴──► Geyser ──► Floodgate ──► Xbox-Live-Auth
```
## Dateien
| Datei | Zweck |
|---|---|
| `docker-compose.yml` | Der Stack: Image, Ports, Plugins, server.properties |
| `.env` | Alle Stellschrauben (Version, RAM, Ports, MOTD, Ops, RCON-Passwort) |
| `config/plugins/Geyser-Spigot/config.yml` | Geyser-Vorgabe, u.a. `auth-type: floodgate` |
Die Welt und alle Plugin-Daten liegen im Docker-Volume `mc-crossplay_mc-data`,
nicht im Projektordner (deutlich schnellere I/O unter Docker Desktop).
## Schnellstart
```bash
docker compose up -d
```
Der erste Start dauert ein paar Minuten: Paper-Jar, Geyser und Floodgate werden
geladen und die Welt wird generiert.
Logs mitlesen:
```bash
docker compose logs -f
```
Fertig ist der Server, sobald `Started Geyser on UDP port 19132` und danach
`Done (…)! For help, type "help"` in den Logs stehen.
Getestet mit Paper 26.2, Geyser 2.11.1 und Floodgate 2.2.5: Java-Ping auf
25565/TCP und RakNet-Ping auf 19132/UDP antworten beide, MOTD und Spielerzahl
werden vom Java-Server an die Bedrock-Serverliste durchgereicht.
## Verbinden
* **Java Edition:** Adresse `<Server-IP>`, Port `25565`
* **Bedrock Edition:** Server hinzufuegen, Adresse `<Server-IP>`, Port `19132`
* **Vom selben Rechner:** `localhost` bzw. `127.0.0.1`
Auf Konsolen (Switch, PS, Xbox) laesst sich oft keine freie Serveradresse
eintragen. Ueblicher Weg dort: DNS-Umleitung im Heimnetz oder ein Dienst wie
BedrockConnect.
Bedrock-Spieler erscheinen mit einem Punkt vor dem Namen, z.B. `.MeinGamertag`.
Das ist der Floodgate-Praefix; er verhindert Namenskollisionen mit Java-Accounts.
Aendern laesst er sich in `plugins/floodgate/config.yml` (`username-prefix`).
## Firewall (Windows)
Docker Desktop oeffnet die Ports nur lokal. Fuer Zugriff aus dem LAN muessen
beide Ports durch die Windows-Firewall — in einer **Admin-PowerShell**:
```powershell
New-NetFirewallRule -DisplayName "Minecraft Java 25565" -Direction Inbound -Protocol TCP -LocalPort 25565 -Action Allow
```
```powershell
New-NetFirewallRule -DisplayName "Minecraft Bedrock 19132" -Direction Inbound -Protocol UDP -LocalPort 19132 -Action Allow
```
Fuer Zugriff aus dem Internet zusaetzlich im Router portforwarden:
**25565/TCP** und **19132/UDP**. Bedrock laeuft ueber UDP — ein reines
TCP-Forwarding reicht nicht.
## Erreichbarkeit pruefen
Sobald der Router-Port offen ist, testet Geyser die Erreichbarkeit von aussen
selbst — das fragt einen oeffentlichen GeyserMC-Testdienst an, uebermittelt also
deine oeffentliche IP und den Port dorthin:
```bash
docker compose exec minecraft rcon-cli "geyser connectiontest"
```
## Betrieb
```bash
docker compose ps
```
Serverkonsole (Befehle wie `op`, `whitelist`, `save-all`):
```bash
docker compose exec minecraft rcon-cli
```
Einzelner Befehl:
```bash
docker compose exec minecraft rcon-cli op DeinName
```
Sauber stoppen (die Welt wird gespeichert, bis zu 2 Minuten Zeit):
```bash
docker compose down
```
## Updates
* **Geyser/Floodgate:** werden bei jedem Containerstart in der aktuellsten
Version geladen. Nach einem Bedrock-Update also einfach
`docker compose restart` — Geyser zieht dann den passenden Build nach.
* **Paper/Minecraft:** `MC_VERSION` in der `.env` steht auf `LATEST`. Fuer einen
planbaren Betrieb besser fest pinnen, z.B. `MC_VERSION=26.2`.
* **Image:** `docker compose pull && docker compose up -d`
## Weitere Plugins
URL in `docker-compose.yml` unter `PLUGINS` als weitere Zeile ergaenzen, dann
`docker compose up -d`. Aus der Liste entfernte Plugins werden beim naechsten
Start auch aus dem `plugins`-Ordner geloescht.
Fuer Bedrock-Spieler gilt: Plugins moeglichst simpel halten. Komplexe
Chest-GUI-Menues (Auktionshaeuser, Shop-Menues) funktionieren ueber Geyser oft
nur fehlerhaft. Plugins mit Formular-Unterstuetzung fuer Bedrock sind die
bessere Wahl.
## Konfiguration aendern
* **server.properties:** ueber die `.env` bzw. die Environment-Variablen in
`docker-compose.yml`. Direkte Aenderungen an der `server.properties` im Volume
werden bei diesen Werten beim naechsten Start ueberschrieben.
* **Geyser:** `config/plugins/Geyser-Spigot/config.yml` bearbeiten, dann
`docker compose restart`. Die Datei aus dem Projektordner gewinnt, sobald sie
neuer ist als die im Volume.
* **Alles andere (Floodgate, weitere Plugins):** liegt im Volume. Anschauen mit
```bash
docker compose exec minecraft cat /data/plugins/floodgate/config.yml
```
Dauerhaft vorgeben laesst es sich, indem man die Datei unter
`config/plugins/<Plugin>/` im gleichen Pfadaufbau ablegt.
## Backup
Welt und Plugin-Daten aus dem Volume in eine Datei sichern:
```bash
docker run --rm -v mc-crossplay_mc-data:/data -v "$PWD":/backup alpine tar czf /backup/mc-backup.tar.gz -C /data .
```
Vorher `docker compose exec minecraft rcon-cli save-all` ausfuehren oder den
Server stoppen, damit die Welt konsistent auf der Platte liegt.
## Deployment in die Oracle Cloud
Der Umzug auf die OCI-Instanz hinter `minecraft.djnizz.at` ist in
[DEPLOY-ORACLE.md](DEPLOY-ORACLE.md) beschrieben.
## Bekannte Einschraenkungen
* Bedrock unterstuetzt nicht alle Java-Features grafisch 1:1 (bestimmte
Bannermuster, manche Offhand-Interaktionen) — kleine optische Abweichungen
sind normal.
* Geyser erzeugt einen leichten CPU-Overhead pro Bedrock-Spieler. Auf moderner
Hardware faellt das nicht ins Gewicht.
* Bedrock-Spieler haben keine Java-Chatsignatur, deshalb ist
`enforce-secure-profile=false` gesetzt. Ohne das fliegen sie beim Chatten raus.
## Troubleshooting
**Bedrock-Client findet den Server nicht** — meist die Firewall oder ein
fehlendes UDP-Forwarding. Test vom Host aus:
```bash
docker compose exec minecraft sh -c "netstat -uln | grep 19132"
```
**„Unable to connect to world"** — pruefen, ob Geyser sauber gestartet ist:
```bash
docker compose logs | grep -i geyser
```
**Container startet in einer Schleife neu, Log sagt „requires Java 25 or
above"** — dann zeigt das Image-Tag auf eine zu alte JVM. Minecraft 26.x braucht
Java 25, das Tag in `docker-compose.yml` muss `stable-java25` (oder neuer)
bleiben.
**Bedrock-Spieler wird nach Java-Account gefragt** — dann greift `auth-type`
nicht. Kontrolle:
```bash
docker compose exec minecraft grep -A2 "^java:" /data/plugins/Geyser-Spigot/config.yml
```
+45
View File
@@ -0,0 +1,45 @@
# Geyser-Konfiguration (Vorgabe aus dem Projektordner)
# ----------------------------------------------------
# Diese Datei wird beim Containerstart nach
# /data/plugins/Geyser-Spigot/config.yml kopiert. Geyser ergaenzt beim ersten
# Start alle hier nicht gesetzten Optionen mit ihren Standardwerten (samt
# Kommentaren) in der Datei im Volume.
#
# Aenderungen hier + `docker compose restart` uebersteuern die Datei im Volume,
# weil diese Datei dann neuer ist.
bedrock:
# 0.0.0.0 = im Container an allen Interfaces lauschen; die Absicherung
# macht das Port-Mapping in docker-compose.yml bzw. die Firewall.
address: 0.0.0.0
# Container-interner Bedrock-Port. Der nach aussen sichtbare Port wird in
# der .env ueber BEDROCK_PORT gesetzt - diesen Wert hier nicht anfassen.
port: 19132
clone-remote-port: false
java:
# Der Kern des Ganzen: Bedrock-Spieler werden ueber Floodgate/Xbox Live
# authentifiziert und brauchen keinen gekauften Java-Account.
# Moegliche Werte: floodgate | online | offline
auth-type: floodgate
motd:
# MOTD und Spielerzahl vom Java-Server uebernehmen, damit beide Editionen
# in der Serverliste dasselbe anzeigen.
passthrough-motd: true
passthrough-player-counts: true
# Zeile 1 wird bei passthrough-motd: true vom Java-Server ueberschrieben,
# Zeile 2 nicht - die kommt immer von hier.
primary-motd: "Crossplay Server"
secondary-motd: "Java & Bedrock"
gameplay:
# Name, den Bedrock-Clients im Pause-/Einstellungsmenue sehen.
server-name: "Crossplay Server"
# Bedrock nutzt weiterhin 1.8-Kampfmechanik; blendet einen simulierten
# Cooldown-Indikator ein, damit das Java-Kampfsystem bedienbar bleibt.
show-cooldown: title
show-coordinates: true
# Wird von Geyser beim Start ggf. hochgezaehlt/migriert - nicht manuell aendern.
config-version: 5
Binary file not shown.
+97
View File
@@ -0,0 +1,97 @@
# Minecraft Crossplay-Server: Java Edition + Bedrock Edition
# ----------------------------------------------------------
# Basis : PaperMC (Java-Server, Plugin-faehig)
# Bruecke: GeyserMC uebersetzt Bedrock-Pakete -> Java-Protokoll
# Auth : Floodgate authentifiziert Bedrock-Spieler ueber Xbox Live,
# d.h. Konsolen-/Mobile-Spieler brauchen KEINEN Java-Account.
#
# Alle Stellschrauben liegen in .env
name: mc-crossplay
services:
minecraft:
# Minecraft 26.x setzt Java 25 voraus - Tag nicht auf java21 zuruecksetzen.
image: itzg/minecraft-server:stable-java25
container_name: mc-crossplay
restart: unless-stopped
# noetig, damit die Server-Konsole via `docker attach` bedienbar bleibt
stdin_open: true
tty: true
ports:
- "${JAVA_PORT:-25565}:25565/tcp" # Java Edition (TCP)
- "${BEDROCK_PORT:-19132}:19132/udp" # Bedrock Edition via Geyser (UDP!)
environment:
# --- Mojang EULA -------------------------------------------------
EULA: "TRUE"
# --- Server-Software ---------------------------------------------
TYPE: "PAPER"
VERSION: "${MC_VERSION:-LATEST}"
# --- Speicher / JVM ----------------------------------------------
INIT_MEMORY: "${MC_MEMORY:-4G}"
MAX_MEMORY: "${MC_MEMORY:-4G}"
USE_AIKAR_FLAGS: "true"
# --- Plugins ------------------------------------------------------
# Immer der aktuellste Build. Wichtig, weil Bedrock-Clients (Konsole,
# Handy) sich selbst updaten und Geyser hinterherziehen muss.
# Eigene Plugins hier einfach als weitere Zeile (URL) ergaenzen.
PLUGINS: |
https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest/downloads/spigot
https://download.geysermc.org/v2/projects/floodgate/versions/latest/builds/latest/downloads/spigot
# --- Konfigurations-Seeding ---------------------------------------
# Inhalt von ./config wird nach /data kopiert, also z.B.
# ./config/plugins/Geyser-Spigot/config.yml -> /data/plugins/Geyser-Spigot/config.yml
COPY_CONFIG_DEST: "/data"
# --- server.properties --------------------------------------------
MOTD: "${MC_MOTD:-Crossplay Server - Java & Bedrock}"
DIFFICULTY: "${MC_DIFFICULTY:-normal}"
MAX_PLAYERS: "${MC_MAX_PLAYERS:-20}"
VIEW_DISTANCE: "${MC_VIEW_DISTANCE:-10}"
SIMULATION_DISTANCE: "${MC_SIMULATION_DISTANCE:-8}"
SPAWN_PROTECTION: "0"
OPS: "${MC_OPS:-}"
# --- Whitelist ----------------------------------------------------
# MC_WHITELIST nimmt ausschliesslich *Java*-Profilnamen auf - itzg loest
# sie beim Start via PlayerDB/Mojang in UUIDs auf. Xbox-Gamertags von
# Bedrock-Spielern lassen sich hier NICHT eintragen, die kommen ueber
# `rcon-cli "fwhitelist add <Gamertag>"` direkt von Floodgate dazu.
ENABLE_WHITELIST: "${MC_WHITELIST_ENABLED:-false}"
ENFORCE_WHITELIST: "${MC_WHITELIST_ENABLED:-false}"
WHITELIST: "${MC_WHITELIST:-}"
# MERGE ist hier entscheidend: ohne das wuerde die whitelist.json bei
# jedem Start auf MC_WHITELIST reduziert - und alle per fwhitelist
# eingetragenen Bedrock-Spieler waeren wieder raus.
EXISTING_WHITELIST_FILE: "MERGE"
# Muss TRUE bleiben: Floodgate haengt sich in die Online-Auth ein.
ONLINE_MODE: "TRUE"
# Muss false sein: Bedrock-Spieler haben keine Java-Chat-Signatur und
# wuerden sonst beim Chatten rausfliegen.
ENFORCE_SECURE_PROFILE: "false"
# --- Fernsteuerung / Betrieb ---------------------------------------
ENABLE_RCON: "true"
RCON_PASSWORD: "${RCON_PASSWORD:?RCON_PASSWORD fehlt - bitte in .env setzen}"
TZ: "${TZ:-Europe/Berlin}"
# Zeit fuers saubere Speichern der Welt beim Stoppen
STOP_DURATION: "120"
volumes:
# Welt + Plugin-Daten in einem Named Volume (schnelle I/O, auch unter
# Docker Desktop/WSL2). Zugriff via `docker compose cp` oder Backup s. README.
- mc-data:/data
# Vorgegebene Konfigurationsdateien, versionierbar im Projektordner
- ./config:/config:ro
stop_grace_period: 2m
volumes:
mc-data:
+130
View File
@@ -0,0 +1,130 @@
#!/usr/bin/env python3
"""Prueft, ob Java- und Bedrock-Endpunkt eines Crossplay-Servers antworten.
python scripts/check-crossplay.py # localhost
python scripts/check-crossplay.py minecraft.djnizz.at # Live-Server
Der Java-Test spricht das Server-List-Ping-Protokoll, der Bedrock-Test einen
RakNet Unconnected Ping - also genau das, was die echten Clients tun.
"""
import json
import os
import socket
import struct
import sys
import time
HOST = sys.argv[1] if len(sys.argv) > 1 else "127.0.0.1"
JAVA_PORT = int(sys.argv[2]) if len(sys.argv) > 2 else 25565
BEDROCK_PORT = int(sys.argv[3]) if len(sys.argv) > 3 else 19132
TIMEOUT = 10
def _varint(n):
out = b""
while True:
b = n & 0x7F
n >>= 7
out += bytes([b | (0x80 if n else 0)])
if not n:
return out
def _read_varint(sock):
n = 0
for i in range(5):
b = sock.recv(1)
if not b:
raise EOFError("Verbindung vorzeitig geschlossen")
n |= (b[0] & 0x7F) << (7 * i)
if not b[0] & 0x80:
return n
raise ValueError("VarInt zu lang")
def java_ping(host, port):
s = socket.create_connection((host, port), timeout=TIMEOUT)
try:
host_b = host.encode()
# Handshake: Protokollversion ist beim Status-Ping beliebig
payload = (b"\x00" + _varint(772) + _varint(len(host_b)) + host_b
+ struct.pack(">H", port) + _varint(1))
s.sendall(_varint(len(payload)) + payload)
s.sendall(_varint(1) + b"\x00") # Status Request
_read_varint(s) # Paketlaenge
_read_varint(s) # Paket-ID
n = _read_varint(s) # JSON-Laenge
data = b""
while len(data) < n:
chunk = s.recv(n - len(data))
if not chunk:
break
data += chunk
return json.loads(data.decode("utf-8"))
finally:
s.close()
RAKNET_MAGIC = bytes([0x00, 0xFF, 0xFF, 0x00, 0xFE, 0xFE, 0xFE, 0xFE,
0xFD, 0xFD, 0xFD, 0xFD, 0x12, 0x34, 0x56, 0x78])
def bedrock_ping(host, port):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(TIMEOUT)
try:
pkt = (b"\x01" + struct.pack(">Q", int(time.time() * 1000))
+ RAKNET_MAGIC + os.urandom(8))
s.sendto(pkt, (host, port))
data, _ = s.recvfrom(4096)
if data[0] != 0x1C:
raise ValueError(f"unerwartete Antwort-ID 0x{data[0]:02x}")
strlen = struct.unpack(">H", data[33:35])[0]
return data[35:35 + strlen].decode("utf-8", "replace")
finally:
s.close()
def main():
print(f"Ziel: {HOST} (Java {JAVA_PORT}/TCP, Bedrock {BEDROCK_PORT}/UDP)\n")
failed = False
print("=== Java Edition (TCP) ===")
try:
j = java_ping(HOST, JAVA_PORT)
version = j.get("version", {})
desc = j.get("description")
if isinstance(desc, dict):
desc = desc.get("text", "") or "".join(
e.get("text", "") for e in desc.get("extra", []))
players = j.get("players", {})
print(f" OK {version.get('name')} (Protokoll {version.get('protocol')})")
print(f" MOTD {desc!r}")
print(f" Spieler {players.get('online')}/{players.get('max')}")
except Exception as e:
failed = True
print(f" FEHLER {type(e).__name__}: {e}")
print(" -> Security List (TCP 25565), Instanz-Firewall, Container laeuft?")
print("\n=== Bedrock Edition (UDP, RakNet) ===")
try:
fields = bedrock_ping(HOST, BEDROCK_PORT).split(";")
labels = ["Edition", "MOTD Zeile 1", "Protokoll", "Version", "Spieler",
"Max. Spieler", "Server-GUID", "MOTD Zeile 2", "Gamemode"]
print(" OK")
for label, value in zip(labels, fields):
print(f" {label:<13} {value}")
except Exception as e:
failed = True
print(f" FEHLER {type(e).__name__}: {e}")
print(" -> Security List (UDP 19132!), Geyser gestartet?")
print()
if failed:
print("Mindestens ein Endpunkt antwortet nicht.")
sys.exit(1)
print("Beide Editionen erreichbar.")
if __name__ == "__main__":
main()