thoughts | THEDEN

Running Linux games on MacOS with Docker

gnome-mahjongg on MacOS

Setting up X

Install XQuartz (X.Org X Window System that runs on MacOS)

brew cask install xquartz

Then in the XQuartz preferences, under the security tab, check the allow connections from network clients button

Quickstart (tldr)

Given XQuartz is set up

Find your IP address and allow access to the X server (assuming en0 is your network device)

export localIP=$(ipconfig getifaddr en0)
xhost + $localIP 

pull the container

docker pull theden/linux-games

run it with the following environment variables (passing in your localIP to DISPLAY)

docker run --hostname linux-games --user ubuntu -it -e DISPLAY=$localIP:0 -e XAUTHORITY=~/.Xauthority -v /tmp/.X11-unix:/tmp/.X11-unix -v ~/.Xauthority:/.Xauthority theden/linux-games

Now that’s fun, but let’s go through how the Dockerfile was built, and add a generic script that would ensure it would run on any MacBook.

Building the Dockerfile

Using ubuntu as the base image. Let’s work out the RUN commands we’ll need.

Base software

Let’s install the packages required, xauth, xorg, and sudo since we want your non-root user to be able to escalate to root.

apt-get install -y --no-install-recommends sudo xauth xorg

For the games, we’ll install gnome-games and the KDE games. the kdegames package doesn’t exist anymore, so we’ll individually install all the known packages.

apt-get install -y --no-install-recommends gnome-games bomber bovo granatier kajongg kapman katomic kblackbox kblocks kbounce kbreakout kdiamond kfourinline kgoldrunner kigo killbots kiriki kjumpingcube klickety klines kmahjongg
kmines knavalbattle knetwalk kolf kollision konquest kpat kreversi kshisen ksirk ksnakeduel kspaceduel ksquares ksudoku ktuberling kubrick lskat palapeli picmi

User configuration

We’ll create the user ubuntu with sudo privledges

adduser --disabled-password --gecos '' ubuntu
adduser ubuntu sudo
echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers

Then set it as default with its home as the WORKDIR, add the /usr/games to PATH

USER ubuntu
ENV PATH $PATH:/usr/games/
WORKDIR /home/ubuntu

Game list

I’d like the user to have a list of games avaiable with a description for each one. A trick is to use apt-cache search ^packagename$ to get the description, e.g.,

apt-cache search ^ksudoku$
ksudoku - Sudoku puzzle game and solver

What we can do is run this against all the games in the /usr/games directory and redirect it to a file gamelist that we can output when the container is run. Using some xargs magic, we can speed it up during the build

ls -A /usr/games | xargs -n 1 -I {} -P $(nproc) sh -c "apt-cache search ^{}$" > ${HOME}/gamelist

where the -P flag is for Parallel mode and nproc will print the number of processing units available to the current process, so we can parallelise the process as much as possible.

Entrypoint

We’ll set the entrypoint to be a script

ENTRYPOINT ["./start.sh"]

where it will print the list of games, cd in /user/games, and exec whatever command is passed to the container

#!/bin/bash

cat gamelist
cd /usr/games/

exec $@

Putting it all together

Optimising for layers and image size we now have our Dockerfile

FROM ubuntu:16.04

RUN apt-get -y update \
      && apt-get install -y --no-install-recommends sudo xauth xorg \
      && apt-get install -y --no-install-recommends gnome-games bomber bovo granatier kajongg kapman katomic kblackbox kblocks kbounce kbreakout kdiamond kfourinline kgoldrunner kigo killbots kiriki kjumpingcube klickety klines kmahjongg kmines knavalbattle knetwalk kolf kollision konquest kpat kreversi kshisen ksirk ksnakeduel kspaceduel ksquares ksudoku ktuberling kubrick lskat palapeli picmi \
      && adduser --disabled-password --gecos '' ubuntu \
      && adduser ubuntu sudo \
      && echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers \
      && rm -rf /var/lib/apt/lists/*

USER ubuntu
ENV PATH $PATH:/usr/games/
WORKDIR /home/ubuntu
COPY --chown=ubuntu start.sh /home/ubuntu
RUN ls -A /usr/games | xargs -n 1 -I {} -P $(nproc) sh -c "apt-cache search ^{}$" > ${HOME}/gamelist \
      && touch ~/.hushlogin \
      && chmod a+x $HOME/start.sh

ENTRYPOINT ["./start.sh"]

Running the container

I wrote a script that finds the MacBook’s active network device and the local IP, gives it access to the X server, and runs the container

#!/bin/bash

IMAGENAME=theden/linux-games

getip() {
  localIP=$(ipconfig getifaddr en0)

  if [ -z "$localIP" ] ; then
    localIP=$(ipconfig getifaddr en1)
  else
    return
  fi

  if [ -z "$localIP" ] ; then
    for i in $(networksetup -listallhardwareports | grep Device | awk '{print $2}' | grep -v "en0\|en1"); do
      localIP=$(ipconfig getifaddr $i)
      if [ ! -z "$localIP" ] ; then
        return
      fi
    done
  fi
  echo "can't find local IP"
  exit 1
}

startcontainer() {
  docker run --hostname linux-games --user ubuntu -it -e DISPLAY=$localIP:0 -e XAUTHORITY=~/.Xauthority -v /tmp/.X11-unix:/tmp/.X11-unix -v ~/.Xauthority:/.Xauthority $IMAGENAME $1
}

getip
xhost + $localIP &> /dev/null

if [ -z "$@" ] ; then
  startcontainer bash
else
  startcontainer $@
fi

it works as expected

./run-container.sh
five-or-more - make color lines of five or more length
bovo - gomoku (five in line) board game
bomber - arcade spaceship game
four-in-a-row - Four in a Row game for GNOME
gnome-chess - chess game with 3D graphics
gnome-mines - popular minesweeper puzzle game for GNOME
gnome-nibbles - snake game, up to four players
gnome-klotski - Klotski puzzle game for GNOME
gnome-mahjongg - classic Eastern tile game for GNOME
gnome-robots - improved old BSD robots game
granatier - Bomberman clone
gnome-sudoku - Sudoku puzzle game for GNOME
gnome-tetravex - put tiles on a board and match their edges together
hitori - logic puzzle game similar to sudoku
hoichess - xboard compatible chess engine to play chess with
kajongg - classical Mah Jongg game
iagno - popular Othello game for GNOME
kapman - Pac-Man clone
kblackbox - Black Box puzzle game
kblocks - falling blocks game
katomic - atomix puzzle game
kbounce - Jezzball arcade game
kfourinline - Connect Four game
kdiamond - three-in-a-row game
kbreakout - ball and paddle game
kgoldrunner - Lode Runner arcade game
killbots - port of the classic BSD console game robots
kigo - go game
kiriki - Yahtzee dice game
klickety - SameGame puzzle game
kjumpingcube - simple tactical game
kmahjongg - mahjongg solitaire game
klines - color lines game
knavalbattle - battleship board game
kmines - minesweeper game
knetwalk - wire puzzle game
kollision - simple ball dodging game
kolf - miniature golf game
konquest - simple turn-based strategy game
kpat - solitaire card games
kshisen - Shisen-Sho solitaire game
kreversi - reversi board game
ksirk - Risk strategy game
ksnakeduel - snake race game
ksquares - Dots and Boxes game
kspaceduel - SpaceWar! arcade game
ksudoku - Sudoku puzzle game and solver
ktuberling - stamp drawing toy
kubrick - game based on Rubik's Cube
lskat - Lieutnant Skat card game
palapeli - jigsaw puzzle game
lightsoff - Light puzzle game
picmi - Number logic game
swell-foop - Colored ball puzzle game
quadrapassel - popular Russian game, similar to Tetris
tali - sort of poker with dice and less money
ubuntu@linux-games:/usr/games$

With no arguments passed, the container will cat the gamelist file and give the user an interactive shell in the /usr/games directory. Passing a game name as an argument, the container will simply run the game.

Nice and simple!  👏

GitHub repository

https://github.com/TheDen/xserver-games-docker

Written July 2018.

← On Withings & making my daily sleeping patterns public