mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-08-20 09:57:09 +00:00
add: add Docker support for Dedicated Server
add with entrypoint and build scripts
This commit is contained in:
parent
392dfdad1f
commit
06bff53e4d
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -442,3 +442,4 @@ Minecraft.Client/Saves/
|
|||
|
||||
tmp*/
|
||||
_server_asset_probe/
|
||||
server-data/
|
||||
|
|
|
|||
45
README.md
45
README.md
|
|
@ -58,6 +58,51 @@ Minecraft.Client.exe -server -ip 0.0.0.0 -port 25565
|
|||
|
||||
The headless server also reads and writes `server.properties` in the working directory. If `-ip` / `-port` are omitted in `-server` mode, it falls back to `server-ip` / `server-port` from that file. Dedicated-server host options such as `trust-players`, `pvp`, `fire-spreads`, `tnt`, `difficulty`, `gamemode`, `spawn-animals`, and `spawn-npcs` are persisted there as well.
|
||||
|
||||
## Dedicated Server in Docker (Wine)
|
||||
|
||||
This repository includes a lightweight Docker setup for running the Windows dedicated server under Wine.
|
||||
**To build the image, a release build of `Minecraft.Server` is required.**
|
||||
|
||||
1. Build the dedicated server (`Minecraft.Server`) first.
|
||||
2. Start with Docker Compose (server runtime files are copied into the image at build time):
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.dedicated-server.yml up -d
|
||||
```
|
||||
|
||||
or use the helper script:
|
||||
|
||||
```bash
|
||||
./start-dedicated-server.sh
|
||||
```
|
||||
|
||||
You can explicitly choose runtime:
|
||||
**However, debug builds often fail to run because some debug-related libraries are missing.**
|
||||
|
||||
```bash
|
||||
./start-dedicated-server.sh release
|
||||
./start-dedicated-server.sh debug
|
||||
```
|
||||
|
||||
By default, build input is `x64/Minecraft.Server/Release`. Override it when needed (path must be inside this repository):
|
||||
|
||||
```bash
|
||||
MC_RUNTIME_DIR=x64/Minecraft.Server/Debug docker compose -f docker-compose.dedicated-server.yml up -d --build
|
||||
```
|
||||
|
||||
Useful environment variables:
|
||||
- `XVFB_DISPLAY` (default: `:99`)
|
||||
- `XVFB_SCREEN` (default: `64x64x16`, tiny virtual display used by Wine)
|
||||
|
||||
Fixed server runtime behavior in container:
|
||||
- executable path: `/srv/mc/Minecraft.Server.exe`
|
||||
- bind IP: `0.0.0.0`
|
||||
- server port: `25565`
|
||||
|
||||
Persistent files are bind-mounted to host:
|
||||
- `./server-data/server.properties` -> `/srv/mc/server.properties`
|
||||
- `./server-data/GameHDD` -> `/srv/mc/Windows64/GameHDD`
|
||||
|
||||
## Controls (Keyboard & Mouse)
|
||||
|
||||
- **Movement**: `W` `A` `S` `D`
|
||||
|
|
|
|||
1
docker-build-dedicated-server.sh
Normal file
1
docker-build-dedicated-server.sh
Normal file
|
|
@ -0,0 +1 @@
|
|||
docker compose -f docker-compose.dedicated-server.yml build
|
||||
27
docker-compose.dedicated-server.yml
Normal file
27
docker-compose.dedicated-server.yml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
services:
|
||||
minecraft-lce-dedicated-server:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/dedicated-server/Dockerfile
|
||||
args:
|
||||
MC_RUNTIME_DIR: ${MC_RUNTIME_DIR:-x64/Minecraft.Server/Release}
|
||||
container_name: minecraft-lce-dedicated-server
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
TZ: ${TZ:-Asia/Tokyo}
|
||||
WINEARCH: win64
|
||||
WINEPREFIX: /var/opt/wineprefix64
|
||||
WINEDEBUG: -all
|
||||
# minimum required virtual screen
|
||||
XVFB_DISPLAY: ${XVFB_DISPLAY:-:99}
|
||||
XVFB_SCREEN: ${XVFB_SCREEN:-64x64x16}
|
||||
volumes:
|
||||
# - wineprefix64:/var/opt/wineprefix64
|
||||
- ./server-data:/srv/persist
|
||||
ports:
|
||||
- "25565:25565/tcp"
|
||||
- "25565:25565/udp"
|
||||
stop_grace_period: 30s
|
||||
|
||||
# volumes:
|
||||
# wineprefix64:
|
||||
33
docker/dedicated-server/Dockerfile
Normal file
33
docker/dedicated-server/Dockerfile
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
FROM debian:bookworm-slim
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
# basically, it only works with a Release build.(libs are not included in Debug build)
|
||||
ARG MC_RUNTIME_DIR=x64/Minecraft.Server/Release
|
||||
|
||||
RUN dpkg --add-architecture i386 \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
wine \
|
||||
wine64 \
|
||||
wine32:i386 \
|
||||
xvfb \
|
||||
tini \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV WINEARCH=win64
|
||||
ENV WINEPREFIX=/var/opt/wineprefix64
|
||||
|
||||
WORKDIR /srv/mc
|
||||
|
||||
COPY ${MC_RUNTIME_DIR}/Minecraft.Server.exe /srv/mc/Minecraft.Server.exe
|
||||
COPY ${MC_RUNTIME_DIR}/iggy_w64.dll /srv/mc/iggy_w64.dll
|
||||
COPY ${MC_RUNTIME_DIR}/Common /srv/mc/Common
|
||||
COPY ${MC_RUNTIME_DIR}/Windows64 /srv/mc/Windows64
|
||||
COPY docker/dedicated-server/entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||
|
||||
RUN chmod 0755 /usr/local/bin/entrypoint.sh \
|
||||
&& mkdir -p /var/opt/wineprefix64 /srv/mc \
|
||||
&& test -f /srv/mc/Minecraft.Server.exe
|
||||
|
||||
ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/entrypoint.sh"]
|
||||
74
docker/dedicated-server/entrypoint.sh
Normal file
74
docker/dedicated-server/entrypoint.sh
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SERVER_DIR="/srv/mc"
|
||||
SERVER_EXE="Minecraft.Server.exe"
|
||||
# ip & port are fixed since they run inside the container
|
||||
SERVER_PORT="25565"
|
||||
SERVER_BIND_IP="0.0.0.0"
|
||||
|
||||
PERSIST_DIR="/srv/persist"
|
||||
WINE_CMD=""
|
||||
|
||||
if [ ! -d "$SERVER_DIR" ]; then
|
||||
echo "[error] Server directory not found: $SERVER_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$SERVER_DIR"
|
||||
|
||||
if [ ! -f "$SERVER_EXE" ]; then
|
||||
echo "[error] ${SERVER_EXE} not found in ${SERVER_DIR}" >&2
|
||||
echo "[hint] Rebuild image with a valid MC_RUNTIME_DIR build arg that contains dedicated server runtime files." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# created because it is not implemented on the server side
|
||||
mkdir -p "${PERSIST_DIR}/GameHDD"
|
||||
|
||||
if [ ! -f "${PERSIST_DIR}/server.properties" ]; then
|
||||
if [ -f "server.properties" ] && [ ! -L "server.properties" ]; then
|
||||
cp -f "server.properties" "${PERSIST_DIR}/server.properties"
|
||||
else
|
||||
: > "${PERSIST_DIR}/server.properties" # the server writes the default configuration if the file is empty, so it works
|
||||
fi
|
||||
fi
|
||||
|
||||
# differs from the structure, but it’s reorganized into a more manageable structure to the host side
|
||||
if [ -e "Windows64/GameHDD" ] && [ ! -L "Windows64/GameHDD" ]; then
|
||||
rm -rf "Windows64/GameHDD"
|
||||
fi
|
||||
ln -sfn "${PERSIST_DIR}/GameHDD" "Windows64/GameHDD"
|
||||
ln -sfn "${PERSIST_DIR}/server.properties" "server.properties"
|
||||
|
||||
# for compatibility with other images
|
||||
if command -v wine64 >/dev/null 2>&1; then
|
||||
WINE_CMD="wine64"
|
||||
elif [ -x "/usr/lib/wine/wine64" ]; then
|
||||
WINE_CMD="/usr/lib/wine/wine64"
|
||||
elif command -v wine >/dev/null 2>&1; then
|
||||
WINE_CMD="wine"
|
||||
else
|
||||
echo "[error] No Wine executable found (wine64/wine)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "${WINEPREFIX}" ] || [ -z "$(ls -A "${WINEPREFIX}" 2>/dev/null)" ]; then
|
||||
mkdir -p "${WINEPREFIX}"
|
||||
fi
|
||||
|
||||
# in the current implementation, a virtual screen is required because the client-side logic is being called for compatibility
|
||||
if [ -z "${DISPLAY:-}" ]; then
|
||||
export DISPLAY="${XVFB_DISPLAY:-:99}"
|
||||
XVFB_SCREEN="${XVFB_SCREEN:-64x64x16}"
|
||||
Xvfb "${DISPLAY}" -nolisten tcp -screen 0 "${XVFB_SCREEN}" >/tmp/xvfb.log 2>&1 &
|
||||
sleep 0.2
|
||||
fi
|
||||
|
||||
args=(
|
||||
-port "${SERVER_PORT}"
|
||||
-bind "${SERVER_BIND_IP}"
|
||||
)
|
||||
|
||||
echo "[info] Starting ${SERVER_EXE} on ${SERVER_BIND_IP}:${SERVER_PORT}"
|
||||
exec "${WINE_CMD}" "${SERVER_EXE}" "${args[@]}"
|
||||
90
start-dedicated-server.sh
Normal file
90
start-dedicated-server.sh
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
COMPOSE_FILE="${SCRIPT_DIR}/docker-compose.dedicated-server.yml"
|
||||
SERVICE_NAME="minecraft-lce-dedicated-server"
|
||||
PERSIST_DIR="${SCRIPT_DIR}/mc-server-data"
|
||||
|
||||
if [[ ! -f "${COMPOSE_FILE}" ]]; then
|
||||
echo "[error] docker-compose file not found: ${COMPOSE_FILE}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if command -v docker >/dev/null 2>&1; then
|
||||
COMPOSE_CMD=(docker compose)
|
||||
elif command -v docker-compose >/dev/null 2>&1; then
|
||||
COMPOSE_CMD=(docker-compose)
|
||||
else
|
||||
echo "[error] docker compose is not available." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# can choose between release and debug, but honestly there isn’t much use
|
||||
if [[ "${#}" -gt 1 ]]; then
|
||||
echo "Usage: $0 [release|debug|<runtime-dir-in-repo>]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "${PERSIST_DIR}/GameHDD"
|
||||
if [[ ! -f "${PERSIST_DIR}/server.properties" ]]; then
|
||||
: > "${PERSIST_DIR}/server.properties"
|
||||
fi
|
||||
|
||||
if [[ "${#}" -eq 1 ]]; then
|
||||
case "${1}" in
|
||||
release)
|
||||
MC_RUNTIME_DIR_INPUT="x64/Minecraft.Server/Release"
|
||||
;;
|
||||
debug)
|
||||
MC_RUNTIME_DIR_INPUT="x64/Minecraft.Server/Debug"
|
||||
;;
|
||||
*)
|
||||
MC_RUNTIME_DIR_INPUT="${1}"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
if [[ -f "${SCRIPT_DIR}/x64/Minecraft.Server/Release/Minecraft.Server.exe" ]]; then
|
||||
MC_RUNTIME_DIR_INPUT="x64/Minecraft.Server/Release"
|
||||
else
|
||||
MC_RUNTIME_DIR_INPUT="x64/Minecraft.Server/Debug"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${MC_RUNTIME_DIR_INPUT}" = /* ]]; then
|
||||
if [[ "${MC_RUNTIME_DIR_INPUT}" != "${SCRIPT_DIR}"/* ]]; then
|
||||
echo "[error] runtime-dir must be inside repository: ${MC_RUNTIME_DIR_INPUT}" >&2
|
||||
exit 1
|
||||
fi
|
||||
MC_RUNTIME_DIR_REL="${MC_RUNTIME_DIR_INPUT#${SCRIPT_DIR}/}"
|
||||
else
|
||||
MC_RUNTIME_DIR_REL="${MC_RUNTIME_DIR_INPUT#./}"
|
||||
fi
|
||||
|
||||
if [[ -z "${MC_RUNTIME_DIR_REL}" || "${MC_RUNTIME_DIR_REL}" = "." ]]; then
|
||||
echo "[error] runtime-dir is invalid: ${MC_RUNTIME_DIR_INPUT}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! RUNTIME_ABS="$(cd "${SCRIPT_DIR}" && cd "${MC_RUNTIME_DIR_REL}" 2>/dev/null && pwd)"; then
|
||||
echo "[error] runtime-dir not found: ${MC_RUNTIME_DIR_INPUT}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${RUNTIME_ABS}" != "${SCRIPT_DIR}"/* ]]; then
|
||||
echo "[error] runtime-dir must resolve inside repository: ${MC_RUNTIME_DIR_INPUT}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MC_RUNTIME_DIR_REL="${RUNTIME_ABS#${SCRIPT_DIR}/}"
|
||||
|
||||
if [[ ! -f "${RUNTIME_ABS}/Minecraft.Server.exe" ]]; then
|
||||
echo "[error] Minecraft.Server.exe not found in: ${RUNTIME_ABS}" >&2
|
||||
echo "[hint] Build dedicated server first, then retry." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[info] Using runtime (build arg): ${MC_RUNTIME_DIR_REL}"
|
||||
echo "[info] Persistent data dir: ${PERSIST_DIR}"
|
||||
MC_RUNTIME_DIR="${MC_RUNTIME_DIR_REL}" "${COMPOSE_CMD[@]}" -f "${COMPOSE_FILE}" up -d --build "${SERVICE_NAME}"
|
||||
echo "[info] Dedicated server started."
|
||||
Loading…
Reference in a new issue