diff --git a/Dockerfile b/Dockerfile index c1b93f9..3bc659c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,6 +9,8 @@ ENV WINEARCH win64 ENV DISPLAY :0 ENV WINEPREFIX /root/prefix32 ENV AOTDIR "$WINEPREFIX/drive_c/Program Files (x86)/AgeOfTime" +ENV RESOLUTION "1024x768x24" +ENV AOT_CPU_LIMIT="0" WORKDIR /root/ COPY webaudio.js /root/ @@ -66,7 +68,7 @@ RUN dpkg --add-architecture i386 && \ document.getElementsByTagName('canvas')[0].addEventListener('keydown', e => { wa.start(); });" \ /root/novnc/vnc_lite.html -COPY restart-aot-crash.sh /root/restart-aot-crash.sh +COPY aot-monitor.sh /root/aot-monitor.sh COPY start-aot.sh /root/start-aot.sh COPY start.sh /root/start.sh RUN chmod +x /root/*.sh && \ diff --git a/README.md b/README.md index d680e1c..9556e35 100644 --- a/README.md +++ b/README.md @@ -17,16 +17,48 @@ This container runs: * audiostream & websockify_audio - these are supposed to pass audio to the VNC web session but currently doesn't work * Fluxbox - a small window manager * AgeOfTime.exe - The game executable +* aot-monitor.sh - Bash script that runs in the background to kill the game process if any `Program Error` dialogs pop up (since the game doesn't fully exit supervisor wont detect it crashed). This script also applies `cpulimit` if `AOT_CPU_LIMIT` env variable is used. This is a [trusted build](https://registry.hub.docker.com/u/skylord123/aot-wine-x11-novnc-docker/) on the Docker Hub. +## env + +There are a few env variables that can be set on the container: + +- `AOT_CPU_LIMIT` (default value: `0`) + - This is essentially the `-l` option for `cpulimit` which description states: + > percentage of CPU allowed from 1 up. Usually 1 - 100, but can be higher on multi- + core CPUs. + - By default, the `docker-compose.yaml` file limits the cpus to 2. You should be limiting how many CPUs the container has access to (one or two cores should work fine) and not just relying on this env variable. + - If set to a number this will be written to a file in the AgeOfTime directory called `aot_cpu_limit` that will be used for controlling the cpu limit dynamically (updated every second). +- `RESOLUTION` (default value: `1024x768x24`) + - Sets the resolution for x11 and AgeOfTime. On container startup this variable is read before starting supervisor and used to update the `baes/client/prefs.cs` AgeOfTime file with the resolution. + ## Run It -Modify your docker-compose.yml file then: +Modify your `docker-compose.yml` file to your liking then: docker compose up +Or using docker run + + docker run \ + -p 8080:8080 \ + -e "AOT_CPU_LIMIT=0" \ + -e "RESOLUTION=640x480x24" \ + skylord123/aot-wine-x11-novnc-docker + + +Run with existing AgeOfTime folder (replace `/path/on/host`): + + docker run \ + -p 8080:8080 \ + -e "AOT_CPU_LIMIT=0" \ + -e "RESOLUTION=640x480x24" \ + --volume "/path/on/host:/root/prefix32/drive_c/Program Files (x86)/AgeOfTime" \ + skylord123/aot-wine-x11-novnc-docker + Go to `http://localhost:8080` in your browser and you should see AgeOfTime boot up. ## Issues diff --git a/restart-aot-crash.sh b/aot-monitor.sh similarity index 56% rename from restart-aot-crash.sh rename to aot-monitor.sh index e3dc55a..2017c67 100644 --- a/restart-aot-crash.sh +++ b/aot-monitor.sh @@ -1,4 +1,13 @@ #!/bin/bash +# the purpose of this script is to run in the background and check if +# winedbg "Program Error" window popped up and if so kill AgeOfTime.exe and winedbg +# so supervisor will restart it + +# this file also applies cpulimit to the AgeOfTime.exe process by monitoring +# a $CPU_LIMIT_FILE file and applying cpulimit whenever this value changes +# if the value changes to zero then the cpulimit process is removed +# entirely (or never started if initial value is zero) + export DISPLAY=":0.0" CPU_LIMIT_FILE="$AOTDIR/aot_cpu_limit" CURRENT_LIMIT=0 @@ -12,8 +21,34 @@ fi sleep 5; while true; do + AOT_PID=$(pidof AgeOfTime.exe) + + # if "Program Error" dialog found kill aot and winedbg + # as supervisor thinks it's still running + if wmctrl -l | awk '{$3=""; $2=""; $1=""; print $0}' | grep '^\s*Program Error$'; then + kill $AOT_PID + kill $(pidof winedbg) + sleep 2 + continue + fi + + # wait AgeOfTime.exe is not running + if [ -z "$AOT_PID" ]; then + CURRENT_LIMIT=0 + sleep 1 + continue + fi + + # if our $CPU_LIMIT_FILE exists.. if [ -f "$CPU_LIMIT_FILE" ]; then NEW_LIMIT=$(cat "$CPU_LIMIT_FILE") + + # Check if cpulimit is not running + CPULIMIT_PID=$(pgrep -fl "cpulimit.*\-p $(pidof AgeOfTime.exe)" | awk '{print $1}') + if [ -z "$CPULIMIT_PID" ]; then + CURRENT_LIMIT=0 + fi + # Validate NEW_LIMIT is a number greater than zero if [[ "$NEW_LIMIT" =~ ^[0-9]+$ ]]; then # Check if the limit has changed @@ -21,8 +56,6 @@ while true; do # If so, update CURRENT_LIMIT CURRENT_LIMIT="$NEW_LIMIT" # Kill existing cpulimit process if any - AOT_PID=$(pidof AgeOfTime.exe) - CPULIMIT_PID=$(pgrep -fl "cpulimit.*\-p $(pidof AgeOfTime.exe)" | awk '{print $1}') if [ ! -z "$CPULIMIT_PID" ]; then echo "Killing cpulimit process $CPULIMIT_PID" kill "$CPULIMIT_PID" @@ -36,10 +69,5 @@ while true; do fi fi - if wmctrl -l | awk '{$3=""; $2=""; $1=""; print $0}' | grep '^\s*Program Error$'; then - kill $(pidof AgeOfTime.exe) - kill $(pidof winedbg) - fi - sleep 1 done \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index 185cbea..18d9e60 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -4,8 +4,15 @@ services: ports: - "8080:8080" environment: - # + # limit cpu usage to 50% of a single core AOT_CPU_LIMIT: 0 + # sets the resolution of x11 + # on container startup this is parsed into + # AgeOfTime's client/prefs.cs file to match + # good resolution options: + # - 640x480x24 + # - 1024x768x24 + RESOLUTION: 640x480x24 # uncomment if you want to volume mount AgeOfTime # volumes: # - '/path/on/host/AgeOfTime:/root/prefix32/drive_c/Program Files (x86)/AgeOfTime' diff --git a/start-aot.sh b/start-aot.sh index b1a26c1..5277228 100644 --- a/start-aot.sh +++ b/start-aot.sh @@ -6,7 +6,6 @@ export HOME=/root export LANG=en_US.UTF-8 #export WINEDEBUG="+alsa,+pulse,+winealsa,+winepulse,+d3d,+ddraw,+opengl,+winediag", cd "/root/prefix32/drive_c/Program Files (x86)/AgeOfTime" -sleep 5 +sleep 5 # give the x server time to start # have to start with wineconsole as wine will cause a crash -# for some weird reason. wine works fine outside of supervisor though strangely wineconsole AgeOfTime.exe \ No newline at end of file diff --git a/start.sh b/start.sh index 71b5fd4..6993b14 100644 --- a/start.sh +++ b/start.sh @@ -1,3 +1,13 @@ #!/bin/bash +# set age of time to use fullscreen +sed -i 's/\$pref::Video::fullScreen = "0";/\$pref::Video::fullScreen = "1";/g' /root/AgeOfTime/base/client/prefs.cs + +# Extract the width and height from RESOLUTION env variable +WIDTH=$(echo $RESOLUTION | cut -d'x' -f1) +HEIGHT=$(echo $RESOLUTION | cut -d'x' -f2) + +# Use sed to replace the line in settings.txt +sed -i "s/\$pref::Video::resolution = \".*\";/\$pref::Video::resolution = \"${WIDTH} ${HEIGHT} 32\";/g" /root/AgeOfTime/base/client/prefs.cs + /usr/bin/supervisord \ No newline at end of file diff --git a/supervisord.conf b/supervisord.conf index 5e48ec0..5970833 100644 --- a/supervisord.conf +++ b/supervisord.conf @@ -2,7 +2,7 @@ nodaemon=true [program:X11] -command=/usr/bin/Xvfb :0 -screen 0 1024x768x24 +command=/usr/bin/Xvfb :0 -screen 0 %(ENV_RESOLUTION)s autorestart=true stdout_logfile=/dev/fd/1 stdout_logfile_maxbytes=0 @@ -66,7 +66,7 @@ redirect_stderr=true priority=1 [program:restart-aot-if-crashed] -command=/bin/bash -c "/root/restart-aot-crash.sh" +command=/bin/bash -c "/root/aot-monitor.sh" umask=0022 stderr_logfile = /var/log/supervisor/restart-aot-if-crashed-stderr.log stdout_logfile = /var/log/supervisor/restart-aot-if-crashed-stdout.log @@ -94,7 +94,7 @@ redirect_stderr=true priority=200 environment= LANGUAGE="en_US.UTF-8", - WINEARCH="win32", + WINEARCH="win64", HOME="/root", LANG="en_US.UTF-8", WINEPREFIX="/root/prefix32",