File size: 6,002 Bytes
02f4a63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# ==============================================================================
# Stage 1: Build OpenRA from source (C#/.NET 8.0)
# ==============================================================================
FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim AS openra-build

RUN apt-get update && apt-get install -y --no-install-recommends \
    make \
    git \
    libsdl2-dev \
    libopenal-dev \
    libfreetype-dev \
    liblua5.1-0-dev \
    ca-certificates \
    && rm -rf /var/lib/apt/lists/*

# Clone OpenRA source from GitHub (works on HF Spaces where submodules aren't initialized)
ARG OPENRA_REPO=https://github.com/yxc20089/OpenRA.git
RUN git clone --depth=1 "$OPENRA_REPO" /src/openra
WORKDIR /src/openra

# Fix Windows CRLF line endings in shell scripts (git autocrlf on Windows adds \r)
RUN find . -name '*.sh' -exec sed -i 's/\r$//' {} + && \
    find . -name '*.sh' -exec chmod +x {} +

# Build with system libraries (unix-generic avoids bundled native binaries)
# SKIP_PROTOC=true uses pre-generated protobuf C# files (avoids protoc arm64 crash in Docker)
ENV SKIP_PROTOC=true
RUN make TARGETPLATFORM=unix-generic CONFIGURATION=Release

# Verify critical output (includes Null platform for headless RL operation)
RUN test -f bin/OpenRA.dll && \
    test -f bin/OpenRA.Game.dll && \
    test -f bin/OpenRA.Mods.Common.dll && \
    test -f bin/OpenRA.Platforms.Null.dll

# ==============================================================================
# Stage 2: Install Python dependencies
# ==============================================================================
FROM python:3.11-slim-bookworm AS python-build

RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY pyproject.toml /app/
COPY openra_env/ /app/openra_env/
COPY proto/ /app/proto/
COPY README.md /app/

RUN pip install --upgrade pip && \
    pip install --no-cache-dir .

# ==============================================================================
# Stage 3: Runtime image
# ==============================================================================
FROM mcr.microsoft.com/dotnet/aspnet:8.0-bookworm-slim AS dotnet-runtime

FROM python:3.11-slim-bookworm

LABEL maintainer="OpenRA-RL"
LABEL description="OpenRA RL Environment - headless game engine with gRPC bridge + OpenEnv API"

# Copy ASP.NET Core runtime from official Microsoft image
COPY --from=dotnet-runtime /usr/share/dotnet /usr/share/dotnet
RUN ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet

# Install runtime dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
    xvfb \
    libgl1-mesa-dri \
    libgl1-mesa-glx \
    libegl-mesa0 \
    mesa-vulkan-drivers \
    libvulkan1 \
    libsdl2-2.0-0 \
    libopenal1 \
    libfreetype6 \
    liblua5.1-0 \
    libicu72 \
    curl procps \
    x11vnc novnc websockify \
    && rm -rf /var/lib/apt/lists/*

# Copy Python packages from builder
COPY --from=python-build /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=python-build /usr/local/bin /usr/local/bin

# Copy built OpenRA (bin, mods, glsl shaders, and global mix database for content resolution)
COPY --from=openra-build /src/openra/bin /opt/openra/bin
COPY --from=openra-build /src/openra/mods /opt/openra/mods
COPY --from=openra-build /src/openra/glsl /opt/openra/glsl
COPY --from=openra-build ["/src/openra/global mix database.dat", "/opt/openra/global mix database.dat"]

# Create native library symlinks that OpenRA expects
# (configure-system-libraries.sh points these to system lib paths)
RUN LIBDIR=$( [ "$(dpkg --print-architecture)" = "arm64" ] && echo "/usr/lib/aarch64-linux-gnu" || echo "/usr/lib/x86_64-linux-gnu" ) && \
    ln -sf "$LIBDIR/libSDL2-2.0.so.0" /opt/openra/bin/SDL2.so && \
    ln -sf "$LIBDIR/libopenal.so.1" /opt/openra/bin/soft_oal.so && \
    ln -sf "$LIBDIR/libfreetype.so.6" /opt/openra/bin/freetype6.so && \
    ln -sf "$LIBDIR/liblua5.1.so.0" /opt/openra/bin/lua51.so

# Copy Python application code
COPY openra_env/ /app/openra_env/
COPY proto/ /app/proto/
COPY pyproject.toml /app/

# Create OpenRA support directory and pre-install RA game content (best-effort).
# Only needed for the replay viewer (Game.Platform=Default with full UI).
# The RL environment works without this content (headless mode).
RUN mkdir -p /root/.config/openra/Content/ra/v2/expand /root/.config/openra/Content/ra/v2/cnc && \
    ( curl -sfL --max-time 30 -o /tmp/ra-quickinstall.zip \
        https://openra.baxxster.no/openra/ra-quickinstall.zip && \
    apt-get update && apt-get install -y --no-install-recommends unzip && \
    unzip -o /tmp/ra-quickinstall.zip -d /tmp/ra-content && \
    cp /tmp/ra-content/*.mix /root/.config/openra/Content/ra/v2/ && \
    cp /tmp/ra-content/expand/* /root/.config/openra/Content/ra/v2/expand/ && \
    cp /tmp/ra-content/cnc/* /root/.config/openra/Content/ra/v2/cnc/ && \
    rm -rf /tmp/ra-quickinstall.zip /tmp/ra-content && \
    apt-get purge -y unzip && apt-get autoremove -y && rm -rf /var/lib/apt/lists/* \
    ) || echo "WARNING: RA content download failed (replay viewer will be unavailable)"

# Copy entrypoints (fix Windows CRLF line endings)
COPY docker/entrypoint.sh /entrypoint.sh
COPY docker/replay-viewer.sh /replay-viewer.sh
RUN sed -i 's/\r$//' /entrypoint.sh /replay-viewer.sh && \
    chmod +x /entrypoint.sh /replay-viewer.sh

# Environment
ENV OPENRA_PATH=/opt/openra
ENV PYTHONPATH=/app
ENV PYTHONUNBUFFERED=1
ENV DISPLAY=:99
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
ENV DOTNET_ROLL_FORWARD=LatestMajor
ENV LIBGL_ALWAYS_SOFTWARE=1
ENV MESA_GL_VERSION_OVERRIDE=3.3
# Game configuration (override at runtime with -e)
ENV AI_SLOT=Multi0
ENV BOT_TYPE=normal
ENV RECORD_REPLAYS=true

EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
    CMD curl -f http://localhost:8000/health || exit 1

ENTRYPOINT ["/entrypoint.sh"]
CMD ["python", "-m", "openra_env.server.app"]