├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── issue.md ├── release-drafter.yml └── workflows │ ├── dockerimage.yml │ ├── for-next.yml │ ├── pr-release.yml │ └── wine-update.yml ├── Dockerfile ├── Makefile ├── README.md ├── extra ├── PAUSE.wav ├── host-webbrowser ├── live-mtgo ├── mscoreei.dll ├── mtgo-no-startupsound.sh ├── mtgo-replace-sounds.sh └── mtgo.sh ├── live-run-mtgo ├── run-mtgo └── sound ├── Dockerfile └── pulse-client.conf /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [pauleve] 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Issue 3 | about: General issue 4 | title: '' 5 | labels: '' 6 | assignees: pauleve 7 | 8 | --- 9 | 10 | 11 | 12 | OS: 13 | Desktop environment (for Linux): 14 | Graphics card: ... 15 | Output of `docker info`: 16 | ``` 17 | 18 | ``` 19 | 20 | output.log`> 21 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | change-template: '- $TITLE' 2 | name-template: panard/mtgo: 3 | template: | 4 | $CHANGES 5 | 6 | ``` 7 | ./run-mtgo --update 8 | ``` 9 | -------------------------------------------------------------------------------- /.github/workflows/dockerimage.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | pull_request: 5 | 6 | jobs: 7 | validate: 8 | env: 9 | IMAGE: panard/mtgo:pr${{ github.event.pull_request.number }} 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v1 13 | - run: make image DOCKER=docker BASE=$IMAGE 14 | - name: Login to docker hub 15 | uses: actions-hub/docker/login@master 16 | env: 17 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} 18 | DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} 19 | - name: Push image 20 | uses: actions-hub/docker@master 21 | with: 22 | args: push $IMAGE 23 | 24 | -------------------------------------------------------------------------------- /.github/workflows/for-next.yml: -------------------------------------------------------------------------------- 1 | name: sync for-next 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | rebase-for-next: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | with: 12 | ref: for-next 13 | fetch-depth: 0 14 | token: ${{ secrets.PAT }} 15 | - run: git fetch origin master 16 | - run: | 17 | git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" 18 | git config --local user.name "github-actions[bot]" 19 | git rebase origin/master 20 | - run: git push -f 21 | -------------------------------------------------------------------------------- /.github/workflows/pr-release.yml: -------------------------------------------------------------------------------- 1 | name: Automatic release from PR 2 | on: 3 | pull_request: 4 | types: [labeled] 5 | jobs: 6 | merge-and-release: 7 | env: 8 | BASENAME: panard/mtgo 9 | name: Merge and release 10 | if: contains(github.event.pull_request.labels.*.name, 'merge-and-release') 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Dump GitHub context 14 | env: 15 | GITHUB_CONTEXT: ${{ toJson(github) }} 16 | run: echo "$GITHUB_CONTEXT" 17 | - name: Tags 18 | run: | 19 | echo PR_TAG=pr${{ github.event.pull_request.number }} >> $GITHUB_ENV 20 | echo TIMESTAMP=$(date +%F) >> $GITHUB_ENV 21 | - name: Login to docker hub 22 | uses: actions-hub/docker/login@master 23 | env: 24 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} 25 | DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} 26 | - name: Pull image 27 | uses: actions-hub/docker@master 28 | with: 29 | args: pull $BASENAME:$PR_TAG 30 | - name: automerge 31 | uses: pascalgn/automerge-action@v0.14.2 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.PAT }} 34 | MERGE_LABELS: "" 35 | MERGE_METHOD: "rebase" 36 | - uses: actions/checkout@v2 37 | with: 38 | ref: master 39 | - name: Git tag 40 | run: | 41 | git tag $TIMESTAMP 42 | git push --tags https://${GITHUB_ACTOR}:${{ secrets.PAT }}@github.com/${GITHUB_REPOSITORY}.git HEAD:master 43 | echo WINE_TAG=wine-$(grep FROM Dockerfile|cut -d: -f2) >> $GITHUB_ENV 44 | - run: | 45 | docker tag $BASENAME:$PR_TAG $BASENAME:$TIMESTAMP 46 | docker tag $BASENAME:$PR_TAG $BASENAME:$WINE_TAG 47 | - name: Push image :timestamp 48 | uses: actions-hub/docker@master 49 | with: 50 | args: push $BASENAME:$TIMESTAMP 51 | - name: Push image :wine 52 | uses: actions-hub/docker@master 53 | with: 54 | args: push $BASENAME:$WINE_TAG 55 | - run: docker tag $BASENAME:$PR_TAG $BASENAME:latest 56 | - name: Push image :latest 57 | uses: actions-hub/docker@master 58 | with: 59 | args: push $BASENAME:latest 60 | - name: Update sound image 61 | run: docker build -t $BASENAME:sound sound 62 | - name: Push image :sound 63 | uses: actions-hub/docker@master 64 | with: 65 | args: push $BASENAME:sound 66 | default: 67 | name: default 68 | runs-on: ubuntu-latest 69 | steps: 70 | - run: echo 1 71 | -------------------------------------------------------------------------------- /.github/workflows/wine-update.yml: -------------------------------------------------------------------------------- 1 | name: Automated Wine update 2 | on: 3 | schedule: 4 | - cron: '0 12 * * Sat' 5 | workflow_dispatch: 6 | 7 | jobs: 8 | updateWine: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | with: 13 | ref: master 14 | token: ${{ secrets.PAT }} 15 | - name: follow for-next 16 | run: | 17 | git fetch origin for-next 18 | git merge --ff-only origin/for-next 19 | - name: Get Latest Wine Version 20 | id: wine 21 | run: | 22 | echo LATEST_TAG=$(curl -sL https://hub.docker.com/v2/namespaces/panard/repositories/wine/tags | jq -r '.results[].name' | grep '\-wow64$' | head -n1) >> $GITHUB_ENV 23 | echo CURRENT_TAG=$(grep FROM Dockerfile|cut -d: -f2) >> $GITHUB_ENV 24 | - name: Update Wine 25 | if: env.CURRENT_TAG != env.LATEST_TAG 26 | run: | 27 | sed -i "s,FROM .*$,FROM panard/wine:$LATEST_TAG," Dockerfile 28 | - name: Create Pull Request 29 | uses: peter-evans/create-pull-request@v3.5.0 30 | with: 31 | token: ${{ secrets.PAT }} 32 | commit-message: Update wine to ${{ env.LATEST_TAG }} 33 | title: Update wine to ${{ env.LATEST_TAG }} 34 | body: | 35 | Updates wine to ${{ env.LATEST_TAG }} using Docker image from https://github.com/pauleve/docker-wine-wow64 36 | 37 | ``` 38 | ./run-mtgo --update --test panard/mtgo:pr<#ID> 39 | ``` 40 | assignees: pauleve 41 | labels: autodocker 42 | branch: auto/wine-updates 43 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM panard/wine:9.14-wow64 2 | CMD mtgo 3 | 4 | ENV WINE_USER wine 5 | ENV WINE_UID 1000 6 | ENV WINEPREFIX /home/wine/.wine 7 | RUN useradd -u $WINE_UID -d /home/wine -m -s /bin/bash $WINE_USER 8 | WORKDIR /home/wine 9 | 10 | COPY extra/host-webbrowser /usr/local/bin/xdg-open 11 | COPY extra/live-mtgo /usr/local/bin/live-mtgo 12 | 13 | USER wine 14 | 15 | RUN wineboot -i \ 16 | && for f in arial32 times32 trebuc32 verdan32; do \ 17 | curl -fL --output-dir /home/wine/.cache/winetricks/corefonts --create-dirs\ 18 | -O https://github.com/pauleve/docker-mtgo/releases/download/artifacts/$f.exe; done \ 19 | && curl -fL --output-dir /home/wine/.cache/winetricks/PowerPointViewer --create-dirs\ 20 | -O https://github.com/pauleve/docker-mtgo/releases/download/artifacts/PowerPointViewer.exe \ 21 | && winetricks -q corefonts calibri tahoma \ 22 | && taskset -c 0 winetricks -f -q dotnet48 \ 23 | && winetricks win7 sound=alsa \ 24 | && winetricks renderer=gdi \ 25 | && wineboot -s \ 26 | && rm -rf /home/wine/.cache 27 | 28 | ENV WINEDEBUG -all,err+all,warn+chain,warn+cryptnet 29 | 30 | COPY extra/mtgo.sh /usr/local/bin/mtgo 31 | 32 | ADD --chown=wine:wine https://mtgo.patch.daybreakgames.com/patch/mtg/live/client/setup.exe?v=8 /opt/mtgo/mtgo.exe 33 | 34 | USER wine 35 | 36 | # hack to allow mounting of user.reg and system.reg from host 37 | # see https://github.com/pauleve/docker-mtgo/issues/6 38 | RUN cd .wine && mkdir host \ 39 | && mv user.reg system.reg host/ \ 40 | && ln -s host/*.reg . 41 | RUN mkdir -p \ 42 | /home/wine/.wine/drive_c/users/wine/Documents\ 43 | /home/wine/.wine/host/wine/Documents 44 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | TIMESTAMP=$(shell date +%F) 3 | 4 | BASE=panard/mtgo:$(TIMESTAMP) 5 | 6 | DOCKER=docker 7 | 8 | image: 9 | $(DOCKER) build -t $(BASE) . 10 | 11 | test: 12 | ./run-mtgo --test --reset $(BASE) 13 | 14 | try: 15 | ./run-mtgo $(BASE) 16 | 17 | push: 18 | $(DOCKER) push $(BASE) 19 | 20 | tag: 21 | git tag $(TIMESTAMP) 22 | 23 | validate: push tag 24 | $(DOCKER) tag $(BASE) panard/mtgo:latest 25 | $(DOCKER) push panard/mtgo:latest 26 | 27 | .PHONY: sound 28 | sound: 29 | $(DOCKER) build -t panard/mtgo:sound sound/ 30 | 31 | push-sound: 32 | $(DOCKER) push panard/mtgo:sound 33 | 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MTGO docker image 2 | 3 | 4 | 5 | > :warning: :warning: 6 | > **With the recent transition to Daybreak, you need to perform once** 7 | > ``` 8 | > ./run-mtgo --reset --update 9 | > ``` 10 | > 11 | 12 | This image provides a ready-to-play Magic The Gathering Online (MTGO) for Linux 13 | and macOS. 14 | 15 | Join the "WineHQ Players" clan! (Account > Buddies, Clan, Chat > Look for WineHQ Players and contact the captain, or send a mail to panard at inzenet dot org with your MTGO user name) 16 | 17 | It is based on an amd64 [debian:stable-slim](https://hub.docker.com/r/_/debian/) Linux distribution and [WineHQ](https://www.winehq.org/) with WOW64 support. 18 | 19 | See https://appdb.winehq.org/objectManager.php?sClass=version&iId=32007 for more information. 20 | 21 | You can buy me a beer using either 22 | - Github [![Github-sponsors](https://img.shields.io/badge/sponsor-30363D?style=for-the-badge&logo=GitHub-Sponsors&logoColor=#EA4AAA)](https://github.com/sponsors/pauleve) 23 | - Paypal [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Y27XTWGY3ZZFY) 24 | 25 | 26 | ## Installation 27 | 28 | A necessary prerequisite is to install **docker**: https://www.docker.com/community-edition#/download. 29 | You do _not_ need wine. 30 | 31 | ### Linux 32 | 33 | Open a terminal and install the `run-mtgo` script: 34 | ``` 35 | wget -O run-mtgo https://raw.githubusercontent.com/pauleve/docker-mtgo/master/run-mtgo 36 | chmod +x run-mtgo 37 | ``` 38 | 39 | Make sure your user is in the `docker` group (command `groups`). If not, add yourself to the docker group: 40 | ``` 41 | sudo usermod -aG docker $USER 42 | ``` 43 | You need to logout/login for the changes to take effect. 44 | 45 | ### macOS 46 | 47 | Support for macOS is still under test. 48 | Using [Homebrew](https://brew.sh/), install XQuartz, socat, and the GNU version of getopt. 49 | 50 | ``` 51 | brew install xquartz 52 | brew install socat 53 | brew install gnu-getopt 54 | brew install wget 55 | ``` 56 | Then **restart your session** (or reboot) and, install the `run-mtgo` script: 57 | ``` 58 | curl -o run-mtgo https://raw.githubusercontent.com/pauleve/docker-mtgo/master/run-mtgo 59 | chmod +x run-mtgo 60 | ``` 61 | 62 | *Important for macOS users*: depending on your configuration the Docker image may not work properly. You can consider installing MTGO using Wine directly, following the instructions here: https://github.com/pauleve/docker-mtgo/wiki/macOS:-installing-MTGO-using-Wine 63 | 64 | ## Usage 65 | 66 | Run the docker image using the [run-mtgo](./run-mtgo?raw=true) helper script 67 | ``` 68 | ./run-mtgo 69 | ``` 70 | 71 | Depending on your configuration, you may want to adjust the resolution of the game, or even switch to desktop emulation which may fix some graphics issues. 72 | ``` 73 | ./run-mtgo --winecfg 74 | ``` 75 | It will launch a configuration tool prior to launching MTGO. There you may be interested in the Graphics tab and use settings like this: 76 | ![](https://private-user-images.githubusercontent.com/228657/289696255-36af1dee-bea0-4363-93af-0d9c7bc849a0.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MTAzMjI1MDUsIm5iZiI6MTcxMDMyMjIwNSwicGF0aCI6Ii8yMjg2NTcvMjg5Njk2MjU1LTM2YWYxZGVlLWJlYTAtNDM2My05M2FmLTBkOWM3YmM4NDlhMC5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwMzEzJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDMxM1QwOTMwMDVaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT00YzAwOGE1YmJlM2Q3OWZhYmM0ZTg5N2Q3MmNjZmQ3MTM4NDY5Yjk0ZjJiMGI1YjNmN2VkNjNhMGU2NjRmZjMyJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.AmIjIl49KCDxX8juEb5wrT_e7CAZ2PogzpPlvDlC5z4) 77 | 78 | Sound is disabled by default, but adventurous users can give a try to 79 | ``` 80 | ./run-mtgo --sound 81 | ``` 82 | do not hesitate to report issues. 83 | 84 | To ensure running the latest docker image, use 85 | ``` 86 | ./run-mtgo --update 87 | ``` 88 | You shoud consider updating the `run-mtgo` script as well by following the 89 | installation procedure. 90 | 91 | 92 | See 93 | ``` 94 | ./run-mtgo --help 95 | ``` 96 | for other options. 97 | 98 | ## Troubleshooting 99 | 100 | * `run-mtgo` prompt for .NET installation: 101 | ``` 102 | ./run-mtgo --reset # only once 103 | ``` 104 | 105 | * `run-mtgo` got permission denied while trying to connect to the Docker daemon socket: 106 | 107 | Add yourself to the docker group: 108 | ``` 109 | sudo usermod -aG docker $USER 110 | ``` 111 | You need to logout/login for the changes to take effect. 112 | 113 | * `run-mtgo` never exits, even after Ctrl+C: 114 | ``` 115 | docker kill mtgo_running 116 | ``` 117 | 118 | 119 | ## FAQ 120 | 121 | * [Change game resolution](https://github.com/pauleve/docker-mtgo/issues/12#issuecomment-355844711) 122 | 123 | ### Import/export deck files 124 | 125 | By default, the folder `~/.local/share/mtgo/files` is bound to the Windows "Documents" folder. 126 | You can change it using the `--bind` option: assuming your decks are in the `~/mtgo` folder 127 | ``` 128 | ./run-mtgo --bind ~/mtgo 129 | ``` 130 | to have access to this folder from the Docker mtgo as the `Documents` folder. 131 | 132 | See also [Access host files](https://github.com/pauleve/docker-mtgo/issues/11#issuecomment-355766306) 133 | 134 | ### Replace Docker with Podman 135 | 136 | If you are using podman as an alternative container manager, change the value of the "docker_client" parameter to podman. Also add the "--userns keepid" to the "opts" parameter, since default Podman behavior doesn't change file ownership when mounting a volume. 137 | -------------------------------------------------------------------------------- /extra/PAUSE.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pauleve/docker-mtgo/2e2afb79fa50ba07ae70580cbde2a329d8e40fe2/extra/PAUSE.wav -------------------------------------------------------------------------------- /extra/host-webbrowser: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "OPENURL ${@}">&2 3 | -------------------------------------------------------------------------------- /extra/live-mtgo: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ref=${1:-master}; shift 3 | set -x 4 | if curl -fL "https://raw.githubusercontent.com/pauleve/docker-mtgo/${ref}/extra/mtgo.sh" -o mtgo.sh; then 5 | bash mtgo.sh "${@}" 6 | else 7 | echo "WARNING: impossible to download mtgo launch script from ${ref}!" 8 | echo "Falling back to pre-installed version" 9 | /usr/local/bin/mtgo "${@}" 10 | fi 11 | -------------------------------------------------------------------------------- /extra/mscoreei.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pauleve/docker-mtgo/2e2afb79fa50ba07ae70580cbde2a329d8e40fe2/extra/mscoreei.dll -------------------------------------------------------------------------------- /extra/mtgo-no-startupsound.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | MTGO_TOKEN="80e21eca2a0e1258_0003.0004_d2d18fb7f6dd1418" # TODO fetch it 4 | MTGO_VERSION="3.4.113.4034" 5 | 6 | LS_BASE="Application Data/Apps/2.0/Data" 7 | 8 | WP="${HOME}/.wine" 9 | echo "Debugging information, please include it in any bug report" 10 | echo "----" 11 | if [ -e "${WINEPREFIX}" ]; then 12 | WP="${WINEPREFIX}" 13 | fi 14 | uname -a 15 | find "${WP}" -iname "local settings" -type d 16 | find "${WP}" -name MTGO.exe 17 | find "${WP}" -name user.config 18 | echo "----" 19 | 20 | set -e 21 | 22 | write_user_config() { 23 | cat >"${1}" < 25 | 26 | 27 | 28 | 29 | False 30 | 31 | 32 | 33 | 34 | EOF 35 | } 36 | 37 | if [ ! -d "${WP}" ]; then 38 | echo "Cannot find your wine prefix, run wineboot first." 39 | exit 1 40 | fi 41 | cd "${WP}" 42 | RND=$(grep StateStore_RandomString user.reg | cut -d= -f2 | tr -d '"') 43 | if [ -z "${RND}" ]; then 44 | echo "Error: no StateStore_RandomString in registry" 45 | exit 1 46 | fi 47 | MTGO_LS=${RND:0:8}.${RND:8:3}/${RND:11:8}.${RND:19:3} 48 | 49 | 50 | LS="$(find -iname 'local settings' -type d|head -n1)" 51 | if [ -z "${LS}" ]; then 52 | echo "Cannot find local settings.. using default" 53 | LS="drive_c/users/${USER}/Local Settings" 54 | mkdir -pv "${LS}" 55 | fi 56 | echo "Local Settings: ${LS}" 57 | cd "${LS}" 58 | USER_CONFIG="${LS_BASE}/${MTGO_LS}/mtgo..tion_${MTGO_TOKEN}/Data/${MTGO_VERSION}/user.config" 59 | if [ -f "${USER_CONFIG}" ]; then 60 | # TODO 61 | OK=$(grep -A1 PlayStartupSound "${USER_CONFIG}"|tail -n1|grep False|wc -l) 62 | if [ ${OK} -eq 0 ]; then 63 | echo "overwritting ${USER_CONFIG}" 64 | write_user_config "${USER_CONFIG}" 65 | else 66 | echo "user.config looks fine" 67 | cat "${USER_CONFIG}" 68 | fi 69 | else 70 | USER_CONFIG_DIR="$(dirname "${USER_CONFIG}")" 71 | if [ ! -d "${USER_CONFIG_DIR}" ]; then 72 | mkdir -p "${USER_CONFIG_DIR}" 73 | fi 74 | echo "creating ${USER_CONFIG}" 75 | write_user_config "${USER_CONFIG}" 76 | fi 77 | 78 | wineserver -kw 79 | 80 | -------------------------------------------------------------------------------- /extra/mtgo-replace-sounds.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | WP="${HOME}/.wine" 3 | if [ -e "${WINEPREFIX}" ]; then 4 | WP="${WINEPREFIX}" 5 | fi 6 | SOUND=`realpath "$1"` 7 | find ${WP}/drive_c -name MTGO.exe -printf '%h\0' | xargs -0 -i find {} -name '*.wav' -exec cp -v ${SOUND} {} \; 8 | -------------------------------------------------------------------------------- /extra/mtgo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | do_winecfg=false 3 | do_sound=false 4 | do_nosound=false 5 | while [ -n "${1:-}" ]; do 6 | case "${1:-}" in 7 | --winecfg) do_winecfg=true ;; 8 | --sound) do_sound=true ;; 9 | --disable-sound) do_nosound=true ;; 10 | esac 11 | shift 12 | done 13 | 14 | if [ ! -d "${HOME}/.wine/drive_c/windows/syswow64" ]; then 15 | echo 16 | echo 17 | echo "IMPORTANT: This image now uses Windows in 64bit mode (WoW64)" 18 | echo "You have to reset your settings: " 19 | echo " ./run-mtgo --reset" 20 | echo 21 | exit 1 22 | fi 23 | 24 | trap "exit" INT 25 | 26 | run() { 27 | echo "${@}" 28 | "${@}" 29 | } 30 | 31 | commontricks="gdiplus=builtin" 32 | 33 | if $do_sound; then 34 | gst-inspect-1.0 # seems to help avoiding wine crash when loading gstreamer 35 | run winetricks ${commontricks} sound=pulse winegstreamer=builtin wmp=builtin 36 | else 37 | run winetricks ${commontricks} sound=alsa winegstreamer=disabled wmp=disabled 38 | fi 39 | $do_winecfg && (run winecfg ; run wineserver -kw; sleep 1) 40 | 41 | run wineboot 42 | 43 | # workaround EULA picture 44 | #find ~/.wine/drive_c/ -name 'EULA_en.rtf' -exec sed '/^{\\pict/,/^}/ d' -i "{}" \; 45 | 46 | # workaround cert verification crash (wine 6.19) 47 | mkdir -pv ~/.wine/host/wine/AppData/LocalLow 48 | 49 | # workaround Z: causing crashes 50 | rm -vf ~/.wine/dosdevices/z\: 51 | 52 | cd ~/.wine/drive_c/ 53 | 54 | workaround_dotnet() { 55 | D="/home/wine/.wine/drive_c/windows/Microsoft.NET/Framework/v4.0.30319" 56 | F="mscoreei.dll" 57 | if [ ! -f "${D}/${F}" ]; then 58 | echo "THERE IS AN ISSUE WITH DOTNET!" 59 | echo "Trying to fix it..., wait a moment" 60 | run wineserver -k 61 | cd ${D} 62 | run curl -fOL https://github.com/pauleve/docker-mtgo/raw/master/extra/mscoreei.dll 63 | return 1 64 | fi 65 | } 66 | workaround_dotnet 67 | 68 | setup="/opt/mtgo/mtgo.exe" 69 | 70 | run wine ${setup} 71 | started=0 72 | s=6 73 | while :; do 74 | sleep $s 75 | winedbg --command "info proc"|grep MTGO.exe >/dev/null 76 | r=$? 77 | if [ $started -eq 0 ] && [ $r -eq 0 ]; then 78 | echo "====== MTGO.exe has started." 79 | started=1 80 | elif [ $started -eq 1 ] && [ $r -eq 1 ]; then 81 | echo "====== shutting down" 82 | run wineserver -kw 83 | exit 84 | fi 85 | done 86 | -------------------------------------------------------------------------------- /live-run-mtgo: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | url="https://raw.githubusercontent.com/pauleve/docker-mtgo/master/run-mtgo" 3 | if command -v curl >/dev/null; then 4 | get_run_mtgo() { 5 | curl -fsSL "${url}" 6 | } 7 | elif command -v wget >/dev/null; then 8 | get_run_mtgo() { 9 | wget -qO - "${url}" 10 | } 11 | else 12 | echo "ERROR: neither curl or wget are installed..." 13 | exit 1 14 | fi 15 | set -x 16 | get_run_mtgo | bash -s - "${@}" 17 | -------------------------------------------------------------------------------- /run-mtgo: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | defaultcmd="mtgo" 4 | 5 | image=docker.io/panard/mtgo:latest 6 | docker_client=docker 7 | limit_cpus=4 8 | name=mtgo_running 9 | cmd=$defaultcmd 10 | cmdargs="" 11 | opts="" 12 | data="mtgo64-data-$USER" 13 | do_sound=false 14 | do_update=false 15 | 16 | local_data="$HOME/.local/share/mtgo" 17 | bind_documents="${local_data}/files" 18 | 19 | usage() { 20 | echo " 21 | Usage: $0 [opts] [image] 22 | 23 | Options: 24 | -h, --help display this message and exit 25 | --sound enable sound (requires PulseAudio and Linux) 26 | --disable-sound ensure that sound is disabled 27 | --cmd CMD initial command (default: $cmd) 28 | --data-volume VOL docker volume to store data (default: ${data}) 29 | --reset reset local data and docker volume 30 | --name NAME container name (default: $name) 31 | --shell start an interactive shell instead of MTGO 32 | --winecfg run winecfg before calling MTGO 33 | --dry-run simply print the docker run command 34 | --no-tz disable timezone detection 35 | --debug enable debug information 36 | --test test environment 37 | --update update docker image before run 38 | --limit-cpus N limit number of CPUs (first N will be used) 39 | --bind PATH bind given path to the user 'Documents' folder 40 | -e docker run option (environment) 41 | -v docker run option (mount volume) 42 | -u docker run option (change user) 43 | " 44 | } 45 | 46 | lopts="dry-run,help,winecfg,shell,cmd:,name:" 47 | lopts="${lopts},bind:" 48 | lopts="${lopts},data-volume:,reset" 49 | lopts="${lopts},sound,disable-sound" 50 | lopts="${lopts},test" 51 | lopts="${lopts},limit-cpus:" 52 | lopts="${lopts},no-tz" 53 | lopts="${lopts},update" 54 | lopts="${lopts},debug" 55 | 56 | getopt=getopt 57 | if [[ $OSTYPE == darwin* ]]; then 58 | set -x 59 | getopt="$(brew ls --verbose gnu-getopt|grep '/bin/getopt')" 60 | if [ $? -ne 0 ]; then 61 | for d in "/opt/homebrew/opt/gnu-getopt/bin" "/usr/local/opt/gnu-getopt/bin"; do 62 | brew_getopt="${d}/getopt" 63 | if [ -x $brew_getopt ]; then 64 | getopt=$brew_getopt 65 | break 66 | fi 67 | done 68 | fi 69 | fi 70 | 71 | args="$($getopt -n "${0}" -o hv:u:e: --longoptions $lopts -- "${0}" "${@}")" 72 | if [ $? -ne 0 ]; then 73 | usage 74 | exit 1 75 | fi 76 | eval set -- $args 77 | 78 | do_run=true 79 | do_reset=false 80 | do_test=false 81 | 82 | mytz=${TZ:-} 83 | detect_tz=false 84 | if [ -z "${mytz}" ]; then 85 | detect_tz=true 86 | fi 87 | 88 | while [ -n "${1:-}" ]; do 89 | case "${1:-}" in 90 | --help|-h) usage && exit 0 ;; 91 | --bind) shift 92 | bind_documents="$(realpath "$1")" ;; 93 | --data-volume) shift 94 | data="$1" ;; 95 | --debug) 96 | opts="${opts} -e WINEDEBUG=warn+all" ;; 97 | --dry-run) 98 | do_run=false ;; 99 | --winecfg) 100 | [[ "$cmd" == "mtgo" ]] && cmdargs="${cmdargs} $1";; 101 | --sound) 102 | [[ "$cmd" == "mtgo" ]] && cmdargs="${cmdargs} $1" 103 | image=panard/mtgo:sound 104 | do_update=true 105 | do_sound=true ;; 106 | --disable-sound) 107 | [[ "$cmd" == "mtgo" ]] && cmdargs="${cmdargs} $1" ;; 108 | --limit-cpus) shift 109 | limit_cpus="$1" ;; 110 | --reset) 111 | do_reset=true ;; 112 | --shell) 113 | cmd="bash" 114 | cmdargs="" 115 | opts="${opts} -it" ;; 116 | --test) 117 | local_data="$HOME/.local/share/mtgo-test" 118 | bind_documents="${local_data}/files" 119 | data="${data}-test" 120 | do_test=true ;; 121 | --cmd) shift; 122 | cmd="$1" 123 | cmdargs="" 124 | opts="${opts} -it" ;; 125 | --no-tz) 126 | detect_tz=false 127 | mytz="" ;; 128 | --name) shift; 129 | name="$1" ;; 130 | --update) 131 | do_update=true ;; 132 | -e|-v|-u) 133 | opts="${opts} $1 $2" ;; 134 | --) shift ; shift 135 | if [ -n "${1:-}" ]; then 136 | while [ -n "${2}" ]; do 137 | opts="${opts} ${1}" 138 | shift 139 | done 140 | image=$1 141 | fi ;; 142 | esac 143 | shift 144 | done 145 | 146 | echo "IMAGE=${image}" 147 | if [ -n "${opts}" ]; then 148 | echo "CUSTOM DOCKER OPTS=${opts}" 149 | fi 150 | 151 | run() { 152 | echo "run(): ${@}" >&2 153 | if $do_run; then 154 | "${@}" 155 | fi 156 | } 157 | 158 | _host_uid=$(id -u) 159 | if [[ ${_host_uid} == 0 ]]; then 160 | echo "Error: you should not run this script as root." >&2 161 | echo "Instead, consider adding your user to the 'docker' group:" >&2 162 | echo ' sudo usermod -aG docker \$USER' >&2 163 | echo "then restarting your session." >&2 164 | exit 1 165 | fi 166 | 167 | if $detect_tz; then 168 | if [ -f /etc/timezone ]; then 169 | mytz=$(&2 186 | echo "Press Enter to continue, CTRL+C to abort" >&2 187 | read 188 | run ${docker_client} volume rm $data 189 | fi 190 | 191 | if $do_update; then 192 | echo "Updating image ${image}..." >&2 193 | run ${docker_client} pull ${image} 194 | fi 195 | 196 | ${docker_client} volume inspect "$data" >/dev/null 2>&1 197 | if [ $? -ne 0 ]; then 198 | set -e 199 | run ${docker_client} volume create "${data}" 200 | 201 | # migration 202 | old_data=mtgo-data 203 | set +e 204 | ${docker_client} volume inspect ${old_data}>/dev/null 2>&1 205 | if [ $? -eq 0 ]; then 206 | set -e 207 | echo "Migrating from previous configuration..." >&2 208 | run ${docker_client} run --rm -v ${data}:/home/wine/.wine/host \ 209 | -v ${old_data}:/data \ 210 | -v ${local_data}:/ldata \ 211 | -i ${image} bash</dev/null | awk '/interface: / {print $2}') 237 | echo "Using network interface '${iface}'" >&2 238 | ip=$(ifconfig $iface | grep inet | awk '$1=="inet" {print $2}') 239 | if [ -z ${ip} ]; then 240 | echo "FATAL: Cannot find your local IP address (iface=$iface)." >&2 241 | exit 1 242 | fi 243 | run open -a XQuartz 244 | echo "socat on $ip forwarding to $DISPLAY" >&2 245 | socat TCP4-LISTEN:6000,bind=$ip,range=$ip/32,reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\" & 246 | socat_pid=$! 247 | sleep 2 248 | export DISPLAY=$ip:0 249 | opts="${opts} -e WINE_X11_NO_MITSHM=1" 250 | WEBBROWSER=open 251 | else 252 | if [ ${_host_uid} -ne 1000 ]; then 253 | run xhost +local:1000 254 | reset_xhost=true 255 | fi 256 | XSOCK="/tmp/.X11-unix" 257 | XAUTH="${local_data}/Xauthority" 258 | > ${XAUTH} 259 | xauth nlist ${DISPLAY} | sed -e 's/^..../ffff/' | xauth -f ${XAUTH} nmerge - 260 | opts="${opts} -v ${XSOCK}:${XSOCK}:rw" 261 | opts="${opts} -v ${XAUTH}:/home/wine/.Xauthority:ro" 262 | WEBBROWSER=xdg-open 263 | fi 264 | 265 | if $do_sound; then 266 | if [[ ${OSTYPE} == darwin* ]]; then 267 | set +e 268 | pulseaudio --version >/dev/null 2>&1 269 | if [ $? -ne 0 ]; then 270 | echo "PulseAudio does not seem to be installed. Attempting install..." >&2 271 | run brew install pulseaudio 272 | fi 273 | set -e 274 | pascript="$(pulseaudio --dump-conf|grep '^default-script-file = '|sed 's/^.*= *//')" 275 | echo "PAScript is $pascript" >&2 276 | pa_modified=false 277 | set +e 278 | pa_has_esound=$(grep "^load-module module-esound-protocol-tcp" "${pascript}") 279 | pa_has_native=$(grep "^load-module module-native-protocol-tcp" "${pascript}") 280 | set -e 281 | pa_enable_module() { 282 | echo "PulseAudio: enabling $1 module" >&2 283 | echo "load-module module-$1" | sudo tee -a "${pascript}" 284 | } 285 | if [ -z "${pa_has_esound}" ]; then 286 | pa_enable_module "esound-protocol-tcp" 287 | pa_modified=true 288 | fi 289 | if [ -z "${pa_has_native}" ]; then 290 | pa_enable_module "native-protocol-tcp" 291 | pa_modified=true 292 | fi 293 | pulseaudio --check && pulseaudio -k || true 294 | run pulseaudio -D 295 | opts="${opts} -v $HOME/.config/pulse:/home/wine/.config/pulse" 296 | opts="${opts} -e PULSE_SERVER=$ip" 297 | else 298 | _host_pulse="/run/user/${_host_uid}/pulse/native" 299 | if [ ! -S $_host_pulse ]; then 300 | echo "PulseAudio does not seem active (${_host_pulse} not found)" >&2 301 | run pulseaudio --start 302 | fi 303 | opts="${opts} -i -v ${_host_pulse}:/run/user/1000/pulse/native" 304 | fi 305 | fi 306 | 307 | opts="${opts} --net=host --ipc=host" 308 | if [ -n "${mytz}" ]; then 309 | opts="${opts} -e TZ=/usr/share/zoneinfo/${mytz}" 310 | fi 311 | 312 | if [ -n "${name}" ]; then 313 | opts="${opts} --name ${name}" 314 | fi 315 | 316 | if [ -n "${bind_documents}" ]; then 317 | opts="${opts} -v ${bind_documents}:/home/wine/.wine/drive_c/users/wine/Documents" 318 | fi 319 | if [ ${limit_cpus} -gt 0 ]; then 320 | opts="${opts} --cpuset-cpus 0-$(( ${limit_cpus} - 1 ))" 321 | fi 322 | 323 | docker_mtgo() { 324 | #run ${docker_client} run --privileged --rm -e DISPLAY \ 325 | run ${docker_client} run --rm -e DISPLAY \ 326 | ${opts} ${image} ${cmd} ${cmdargs} 327 | } 328 | watch_openurl() { 329 | while read -r line; do 330 | echo "$line" 331 | if [[ "${line:0:8}" == "OPENURL " ]]; then 332 | url="${line:8}" 333 | run $WEBBROWSER "${url}" 334 | fi 335 | done 336 | } 337 | 338 | if [[ "${cmd}" == "mtgo" ]]; then 339 | docker_mtgo 2>&1 | watch_openurl 340 | else 341 | docker_mtgo 342 | fi 343 | 344 | if $reset_xhost; then 345 | run xhost -local:1000 346 | fi 347 | 348 | if [[ ${OSTYPE} == darwin* ]]; then 349 | kill ${socat_pid} 350 | fi 351 | -------------------------------------------------------------------------------- /sound/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM panard/mtgo:latest 2 | 3 | USER root 4 | 5 | RUN apt update && apt install -y --no-install-recommends \ 6 | gstreamer1.0-plugins-good \ 7 | gstreamer1.0-tools \ 8 | gstreamer1.0-pulseaudio \ 9 | pulseaudio-utils \ 10 | && apt autoremove -y --purge \ 11 | && apt clean -y && rm -rf /var/lib/apt/lists/* \ 12 | && for x in alpha avi cutter gio; do \ 13 | rm /usr/lib/*/gstreamer-1.0/libgst$x.so; done 14 | 15 | COPY pulse-client.conf /etc/pulse/client.conf 16 | 17 | USER wine 18 | 19 | RUN gst-inspect-1.0 20 | 21 | -------------------------------------------------------------------------------- /sound/pulse-client.conf: -------------------------------------------------------------------------------- 1 | # Connect to the host's server using the mounted UNIX socket 2 | default-server = unix:/run/user/1000/pulse/native 3 | 4 | # Prevent a server running in the container 5 | autospawn = no 6 | daemon-binary = /bin/true 7 | 8 | # Prevent the use of shared memory 9 | enable-shm = false 10 | --------------------------------------------------------------------------------