# panel-native Windrose runtime image. # # Windrose's dedicated server is a Windows-only UE5 build, so we bring along # just enough Wine + Xvfb to host it headlessly. SteamCMD is NOT installed # here — the panel's generic updater sidecar uses the official steamcmd # image and writes into our /game volume. # # Expected runtime layout inside the container: # /game — SteamCMD-installed Windows build (panel volume) # /game/WindroseServer.exe — dedicated server binary (preferred path) # /game/R5/Binaries/Win64/WindroseServer.exe — fallback path (UE5 convention) # /game-saves — ServerDescription.json, R5/Saved save tree # /entrypoint.sh — boot Xvfb, launch exe under Wine FROM debian:12-slim ENV DEBIAN_FRONTEND=noninteractive \ TZ=Etc/UTC \ LANG=en_US.UTF-8 \ LC_ALL=en_US.UTF-8 \ WINEDEBUG=-all \ WINEPREFIX=/home/panel/.wine \ DISPLAY=:99 # Wine: install winehq-stable from dl.winehq.org rather than Debian's # wine 8.0~repack. Reason: Windrose's UE5 game uses gRPC for its R5 coop # session. gRPC's Windows event engine asserts on partial-transfer IOCP # completions ("ASSERTION FAILED: result.bytes_transferred == # buffer_->Length() at windows_endpoint.cc:355"). Wine 8.0's IOCP can # return partial completions for WSARecv that the assertion rejects, so # the server *crashes* the moment a player session reaches the gRPC # transport layer (~12 seconds after a join). Wine 9+ on winehq's stable # repo has the IOCP fix; matches what CubeCoders' AMP wine-stable image # ships. cabextract + winbind are commonly needed by Wine's installer # helpers even though we don't call winetricks directly. RUN dpkg --add-architecture i386 \ && apt-get update \ && apt-get install -y --no-install-recommends \ ca-certificates \ locales \ tini \ curl \ gnupg \ xvfb \ xauth \ winbind \ cabextract \ procps \ && mkdir -pm755 /etc/apt/keyrings \ && curl -fsSL https://dl.winehq.org/wine-builds/winehq.key \ | gpg --dearmor -o /etc/apt/keyrings/winehq-archive.key \ && curl -fsSL https://dl.winehq.org/wine-builds/debian/dists/bookworm/winehq-bookworm.sources \ -o /etc/apt/sources.list.d/winehq-bookworm.sources \ && apt-get update \ && apt-get install -y --no-install-recommends winehq-stable \ && sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \ && locale-gen \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* RUN groupadd -g 1000 panel \ && useradd -u 1000 -g 1000 -m -s /bin/bash panel \ && mkdir -p /game /game-saves \ && chown -R panel:panel /game /game-saves /home/panel # Pre-warm the Wine prefix at /home/panel/.wine during the image build. # # Why: Wine 11's first-boot wineboot pops `appwiz.cpl install_mono` to # install Wine Mono. In a headless xvfb container that step blocks # indefinitely, and any interrupt (SIGINT, container restart) leaves the # prefix half-built — kernel32.dll never finishes registering, so the # next launch dies with "could not load kernel32.dll, status c0000135" # in a tight crash-loop. UE5 dedicated servers don't need .NET, so we # skip Mono entirely via WINEDLLOVERRIDES=mscoree= and the Gecko HTML # engine via mshtml=. Result: first boot is fast and idempotent; # entrypoint can skip wineboot since system.reg is already present. USER panel ENV HOME=/home/panel WINEPREFIX=/home/panel/.wine WINEDEBUG=-all # wineboot --init under xvfb-run hangs on Wine 11 because xvfb-run kills # its X server the moment wineboot's main thread returns, but wineboot # spawns explorer.exe / services.exe in the background that keep trying # to reach :99 and emit "X connection broken" errors that kill the build. # Run Xvfb directly, give it a moment, point DISPLAY at it, init the # prefix, then `wineserver -w` to flush state and explicitly stop Xvfb. RUN Xvfb :99 -screen 0 1024x768x24 -nolisten tcp & XVFB_PID=$! \ && sleep 2 \ && DISPLAY=:99 WINEDLLOVERRIDES="mscoree=;mshtml=" wineboot --init \ && wineserver -w \ && kill "$XVFB_PID" 2>/dev/null || true USER root COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh EXPOSE 7777/udp 27015/udp ENTRYPOINT ["/usr/bin/tini", "--", "/entrypoint.sh"]