How to play AoE2DE multiplayer on linux without steam
Overview
I recently purchased the remaining DLCs for Age of Empires II: Definitive Edition (steam) and I wanted to make sure that my ability to play this game survives Steam, my relationship with them, and even the world wide web.
After a lot of research and the benefit of many Free Software tools, I can play multiplayer AoE2DE with my mods without Steam, on Linux! Here is my method for assembling a package for distributing to my systems without Internet. I have tested only for a few minutes between two machines in the same room. I don't know how this would scale to a vpn or wireless clients.
This does not use system Wine, which crashes immediately with some opengl32 problem.
Building the package
This first asset is the proper Steam directory with all my DLCs installed.
tar -zcf aoe2de-alldlcs-raw.2026-06-28.tgz "${HOME}/.steam/steam/steamapps/common/AoE2DE"
-
Explode my asset
aoe2de-alldlcs-raw.2026-06-28.tgzto ~/aoe2de.tar -zx -C ~ -f aoe2de-alldlcs-raw.2026-06-28.tgz
-
Download dependency files to
~/aoe2de/prepare, which is a temporary spot.wget https://github.com/GloriousEggroll/proton-ge-custom/releases/download/GE-Proton11-1/GE-Proton11-1.tar.gz https://github.com/Open-Wine-Components/umu-launcher/releases/download/1.4.0/python3-umu-launcher_1.4.0-1_amd64_debian-13.deb https://github.com/Open-Wine-Components/umu-launcher/releases/download/1.4.0/umu-launcher_1.4.0-1_all_debian-13.deb https://github.com/luskaner/ageLANServerLauncherCompanion/releases/download/v1.3.0.0/ageLANServerLauncherCompanion_Age2FakeOnline_1.0.0.0.zip https://github.com/Detanup01/gbe_fork/releases/download/release-2026_05_30/emu-win-release.7z https://github.com/luskaner/ageLANServer/releases/download/v1.14.1/ageLANServer_full_1.14.1_linux_x86-64.tar.gz
-
Move debs to
~/aoe2de/debs. They will need to be installed on any system that uses this package. -
Explode GE-Proton11-1 to ~/aoe2de.
tar -zx -C ~/aoe2de -f ~/aoe2de/prepare/GE-Proton11-1.tar.gz
-
Prepare files from Goldberg Emulator fork.
cd ~/aoe2de/prepare 7za x emu-win-release.7z cd ~/aoe2de/ cp -pi prepare/release/steamclient_experimental/{ColdClientLoader.ini,steamclient*.dll,steamclient_loader_x64.exe} .
Prepare the ColdClientLoader.ini. Here is the non-comment values I used.
[SteamClient] Exe=AoE2DE\AoE2DE_s.exe ExeRunDir= ExeCommandLine=NOSTARTUP SKIPINTRO AppId=813780 SteamClientDll=steamclient.dll SteamClient64Dll=steamclient64.dll [Injection] ForceInjectSteamClient=0 ForceInjectGameOverlayRenderer=0 DllsToInjectFolder=dlls IgnoreInjectionError=1 IgnoreLoaderArchDifference=0 [Persistence] Mode=0 [Debug] ResumeByDebugger=0
-
Prepare configs for steam emulator
cd ~/aoe2de mkdir -p dlls steam_settings saves
-
Load dlls.
cd ~/aoe2de/dlls 7za x ~/aoe2de/prepare/ageLANServerLauncherCompanion_Age2FakeOnline_1.0.0.0.zip cp -pi ../prepare/release/steamclient_experimental/GameOverlayRenderer64.dll ../prepare/release/steamclient_experimental/extra_dlls/steamclient_extra_x64.dll .
-
Prepare steam_settings.
Prepare these based on Reference 5.
File
achievements.jsonis lengthy and not shown here.The DLCs that I own are these, for file
configs.app.ini.[app::dlcs] unlock_all=0 # Expansions 2141580=Age of Empires II: Definitive Edition - Return of Rome 2555420=Age of Empires II: Definitive Edition - The Mountain Royals #2805510=Age of Empires II: Definitive Edition - Victors and Vanquished 2805520=Age of Empires II: DE - Chronicles: Battle for Greece 3080080=Age of Empires II: DE - The Three Kingdoms #3793540=Age of Empires II: DE - Chronicles: Alexander the Great 3219700=Age of Empires II: DE – The Last Chieftains 1389240=Age of Empires II: Definitive Edition - Lords of the West 1869820=Age of Empires II: Definitive Edition - Dynasties of India 1557210=Age of Empires II: Definitive Edition - Dawn of the Dukes # Animated Icons #2672800=Age of Empires II: Definitive Edition – La Hire’s Dry Humor Animated Icons #2672810=Age of Empires II: Definitive Edition – Joan’s Marvelous Moving Masterpieces Animated Icons #2672820=Age of Empires II: Definitive Edition – Barbarossa’s Barrel of Pickled Goods Animated Icons # Other #1039811=Enhanced Graphics Pack
Prepare file
configs.user.ini.[user::general] # Fill your account name as you would normally see. account_name=SpacesAreEvil65 # Fill with your real steamid or leave empty for it to be auto-generated. account_steamid= # Choose one from steam_settings\supported_languages.txt (described in https://partner.steamgames.com/doc/store/localization/languages) or leave empty to be set as 'english' language= # Choose from 'Alpha-2' country code: https://www.iban.com/country-codes or leave empty to be set as 'US' ip_country= [user::saves] local_save_path=saves
File
supported_languages.txtis exactly as is from Reference 5.english french italian german japanese koreana brazilian russian schinese latam tchinese turkish vietnamese spanish polish hindi malay
-
Prepare launcher script
aoe2de.sh.#!/bin/shPROTON_DISABLE_LSTEAMCLIENT=1 WINEPREFIX=~/.wine-aoe2de PROTONPATH=~/aoe2de/GE-Proton11-1 umu-run ~/aoe2de/steamclient_loader_x64.exe NOSTARTUP SKIPINTROThe first time you run, umu wants to download components from the Internet for your user. Client scripts
set-initial-umu.shmight be able to prevent that requirement. -
Prepare scripts/ files.
files/2026/listings/aoe2de-get-account-steamid.sh (Source)
files/2026/listings/aoe2de-get-username.sh (Source)1 2 3 4 5 6 7 8 9
#!/bin/sh if test -z "${INFILE}" ; then scriptdir="$( dirname "$( readlink -f "${0}" )" )" # find first one. IGNORE the saves/settings/configs.user.ini which is the auto-generated one. INFILE="$( find "${scriptdir}" "${scriptdir}/.." "${scriptdir}/../.." -ipath '*/steam_settings/configs.user.ini' -print -quit )" fi final_steamid="$( awk -F'=' '$1~/account_steamid/{print $NF}' "${INFILE}" )" echo "account_steamid = ${final_steamid}"
files/2026/listings/aoe2de-set-account-steamid.sh (Source)1 2 3 4 5 6 7 8 9
#!/bin/sh if test -z "${INFILE}" ; then scriptdir="$( dirname "$( readlink -f "${0}" )" )" # find first one INFILE="$( find "${scriptdir}" "${scriptdir}/.." "${scriptdir}/../.." -ipath '*/steam_settings/configs.user.ini' -print -quit )" fi awk -F'=' '$1~/account_name/{print $NF}' "${INFILE}"
files/2026/listings/aoe2de-set-cert-from-server.sh (Source)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
#!/bin/sh # Useful because the emulator seems to pick the same number on every system when it needs to generate its own account_steamid. # Dependencies: # dep-devuan: moreutils # Env vars: APPLY VERBOSE if test -z "${INFILE}" ; then scriptdir="$( dirname "$( readlink -f "${0}" )" )" # find first one. IGNORE the saves/settings/configs.user.ini which is the auto-generated one. INFILE="$( find "${scriptdir}" "${scriptdir}/.." "${scriptdir}/../.." -ipath '*/steam_settings/configs.user.ini' -print -quit )" fi # Steam id is 17 digits long, decimal, and starts with 765611982 new_steamid="765611982$( </dev/random tr -dc '0-9' | head -c8 )" # modconf.py breaks on set account_steamid because of the underscore. I will have to do this with awk or sed or similar 2026-06-30-3 23:55 #if test -n "${VERBOSE}" ; then echo "/usr/libexec/bgscripts/py/modconf.py ${APPLY:+-a} ${VERBOSE:+-v} -s '[user::general]' \"${INFILE}\" set account_steamid \"${new_steamid}\"" ; fi #/usr/libexec/bgscripts/py/modconf.py ${APPLY:+-a} ${VERBOSE:+-v} -s '[user::general]' "${INFILE}" set account_steamid "${new_steamid}" # So use basic awk instead if test -n "${VERBOSE}" ; then echo "awk -v \"b=${new_steamid}\" -F'=' 'BEGIN{OFS=\"=\"} \$1~/account_steamid/{\$2=b} {print}' \"${INFILE}\" | sponge \"${INFILE}\"" fi if test -n "${APPLY}" ; then awk -v "b=${new_steamid}" -F'=' 'BEGIN{OFS="="} $1~/account_steamid/{$2=b} {print}' "${INFILE}" | sponge "${INFILE}" # Regardless if it worked, read the value and show it. final_steamid="$( awk -F'=' '$1~/account_steamid/{print $NF}' "${INFILE}" )" echo "account_steamid = ${final_steamid}" fi # modconf.py has a bug where it adds an additional newline after that updated line, so # collapse multiple empty lines into a single empty line: ref https://unix.stackexchange.com/a/72749 awk '!NF {if (++n <= 1) print; next}; {n=0;print}' "${INFILE}" | sponge "${INFILE}" if test -z "${APPLY}" ; then echo "Dry run only! Use APPLY=1" 1>&2 ; fi
files/2026/listings/aoe2de-set-etc-hosts.sh (Source)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
#!/bin/sh IPADDRESS="${IPADDRESS:-${1}}" if test -z "${INFILE}" ; then scriptdir="$( dirname "$( readlink -f "${0}" )" )" INFILE="$( find "${scriptdir}" "${scriptdir}/.." "${scriptdir}/../.." -ipath '*/certificates/cacert.pem' -print -quit )" fi INFILE="$( readlink -f "${INFILE}" )" if test -z "${IPADDRESS}" ; then echo "Fatal! Provide \$1 or IPADDRESS (IP address or hostname) to fetch certificate. Aborted." 1>&2 exit 1 fi cert="$( </dev/null openssl s_client -connect "${IPADDRESS}:443" 2>/dev/null | openssl x509 -in /dev/stdin )" # Exit if the connection failed. Openssl x509 already printed an error, so do not print another. if test -z "${cert}" ; then exit 1 ; fi next_next_to_last="$( echo "${cert}" | awk '{a[NR]=$0} END {print a[NR-2]}' )" if ! grep -q -e "${next_next_to_last}" "${INFILE}" ; then echo "You need the cert! File ${INFILE} does not have the cert." if test -n "${APPLY}" ; then echo "Applying the the cert." 1>&2 echo "${cert}" >> "${INFILE}" else echo "Dry run only! Please run with APPLY=1 to add the cert." fi else echo "File ${INFILE} has the cert already." fi
files/2026/listings/aoe2de-set-initial-mods.sh (Source)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
#!/bin/sh # Dependencies: # dep-devuan: bgscripts # TODO: # clean up #ageLANServer entries scriptdir="$( dirname "$( readlink -f "${0}" )" )" INFILE="${INFILE:-/etc/hosts}" mkdir -p "${scriptdir}/backups" # needs bgscripts sudo bup "${INFILE}" "${scriptdir}/backups" IPADDRESS="${IPADDRESS:-${1}}" if test -z "${IPADDRESS}" ; then echo "Fatal! Need \$1 or IPADDRESS. Aborted." 1>&1 exit 1 fi # I need to remove the loopback address for this host, only if this is the server echo "WARNING: for server only, please modify /etc/hosts and remove 127.0.0.1-type entries. Not implemented automatically yet." 1>&2 # nice idea, but I don't want to depend on this # This only adds the relevant api.ageofempires.com entries. #sudo "${scriptdir}/../ageLANServer/linux/launcher/bin/config-admin" setup --game age2 --ip "${IPADDRESS}" _resolved="$( getent hosts "${IPADDRESS}" | awk '{print $1}' )" echo "Using resolved ip address ${_resolved}" 1>&2 for word in \ aoe-api.reliclink.com \ aoe-api.worldsedgelink.com \ api.ageofempires.com \ cdn.ageofempires.com \ pb-live-release1-api.worldsedgelink.com \ pb-live-release2-api.worldsedgelink.com \ pb-live-release3-api.worldsedgelink.com \ pb-live-release4-api.worldsedgelink.com \ pb-live-release5-api.worldsedgelink.com \ pb-live-release6-api.worldsedgelink.com \ pb-live-release7-api.worldsedgelink.com \ pb-live-release8-api.worldsedgelink.com \ pb-live-release9-api.worldsedgelink.com \ ; do long="${word}#ageLANServer" # depends on bgscripts if test -n "${VERBOSE}" ; then echo "sudo updateval -v ${INFILE}" \"${_resolved} ${long}\" \"${_resolved} ${long}\" fi if test -n "${APPLY}" ; then sudo updateval -a "${INFILE}" "${_resolved} ${long}" "${_resolved} ${long}" fi done
files/2026/listings/aoe2de-set-initial-profile.sh (Source)1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#!/bin/sh scriptdir="$( dirname "$( readlink -f "${0}" )" )" INFILE="${INFILE:-/mnt/public/Support/Games/aoe2de-on-linux/aoe2de-mods-subscribed.2026-06-26.tgz}" steamid="$( "${scriptdir}/get-account-steamid.sh" | awk '{print $NF}' )" WINEPREFIX="${WINEPREFIX:-${HOME}/.wine-aoe2de}" destdir="${WINEPREFIX}/drive_c/users/steamuser/Games/Age of Empires 2 DE/${steamid}/mods" if test -n "${VERBOSE}" ; then echo "tar -zx -C \"${destdir}\" -f \"${INFILE}\"" fi if test -n "${APPLY}" ; then mkdir -p "${destdir}" tar -zx -C "${destdir}" -f "${INFILE}" else echo "Dry run only! Please run with APPLY=1 to apply the initial mods." fi
files/2026/listings/aoe2de-set-initial-registry.sh (Source)1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#!/bin/sh scriptdir="$( dirname "$( readlink -f "${0}" )" )" indir="$( readlink -f "${scriptdir}/../initial-files" )" steamid="$( "${scriptdir}/get-account-steamid.sh" | awk '{print $NF}' )" WINEPREFIX="${WINEPREFIX:-${HOME}/.wine-aoe2de}" destdir="${WINEPREFIX}/drive_c/users/steamuser/Games/Age of Empires 2 DE/${steamid}" if test -n "${VERBOSE}" ; then echo "cp -pr \"${indir}/profile\" \"${destdir}\"" fi if test -n "${APPLY}" ; then mkdir -p "${destdir}" cp -pr "${indir}/profile" "${destdir}" else echo "Dry run only! Please run with APPLY=1 to apply the initial profile." fi
files/2026/listings/aoe2de-set-initial-umu.sh (Source)1 2
#!/bin/sh PROTON_DISABLE_LSTEAMCLIENT=1 WINEPREFIX=~/.wine-aoe2de PROTONPATH=~/aoe2de/GE-Proton11-1 umu-run regedit "x:\aoe2de\initial-files\aoe2de-partial.reg"
files/2026/listings/aoe2de-set-username.sh (Source)1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#!/bin/sh scriptdir="$( dirname "$( readlink -f "${0}" )" )" indir="$( readlink -f "${scriptdir}/../initial-files" )" destdir="${HOME}/.local/share" if test -n "${VERBOSE}" ; then echo "cp -pr \"${indir}/umu\" \"${destdir}\"" fi if test -n "${APPLY}" ; then mkdir -p "${destdir}" cp -pr "${indir}/umu" "${destdir}" else echo "Dry run only! Please run with APPLY=1 to apply the initial umu files." fi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
#!/bin/sh # Dependencies: # dep-devuan: bgscripts, moreutils # Env vars: APPLY VERBOSE USERNAME="${USERNAME:-${1}}" if test -z "${USERNAME}" ; then echo "Fatal! Need \$1 or USERNAME. Aborted." 1>&2 ; exit 1 ; fi if test -z "${INFILE}" ; then scriptdir="$( dirname "$( readlink -f "${0}" )" )" # find first one INFILE="$( find "${scriptdir}" "${scriptdir}/.." "${scriptdir}/../.." -ipath '*/steam_settings/configs.user.ini' -print -quit )" fi # This depends on bgscripts. /usr/libexec/bgscripts/py/modconf.py ${APPLY:+-a} ${VERBOSE:+-v} -s '[user::general]' "${INFILE}" set account_name "${USERNAME}" # modconf.py has a bug where it adds an additional newline after that updated line, so # collapse multiple empty lines into a single empty line: ref https://unix.stackexchange.com/a/72749 awk '!NF {if (++n <= 1) print; next}; {n=0;print}' "${INFILE}" | sponge "${INFILE}" if test -z "${APPLY}" ; then echo "Dry run only! Use APPLY=1" 1>&2 ; fi
-
Prepare ageLANServer assets for server.
These assets are only needed for the machine that will run the server.
mkdir -p ~/aoe2de/ageLANServer/linux tar -zx -C ~/aoe2de/ageLANServer/linux -f ~/aoe2de/prepare/ageLANServer_full_1.14.1_linux_x86-64.tar.gz
Adjust
battle-server-manager/resources/config.age2.tomlPath = '../../../AoE2DE/BattleServer/BattleServer.exe'
Adjust
server/resources/config/config.tomlEnabled = ["age2"]
I could have written sed statements, but I didn't bother.
-
Prepare initial files.
I copied what I think is a reasonably configured "low graphics" user profile from
~/.wine-aoe2de.attempt2/drive_c/users/steamuser/Games/Age\ of\ Empires\ 2\ DE/7656119EXAMPLE892. Place it as~/aoe2de/initial-files/profile.Prepare
initial-files/README.md.# Profile This initial profile already sets graphics to low. Most, but probably not all, my preferred settings are loaded already. The destination for `profile/` is the following. ${WINEPREFIX}/drive_c/users/steamuser/Games/Age of Empires 2 DE/7656119EXAMPLE892 # Umu This is a snapshot of `${HOME}/.local/share/umu` from 2026-07-01. # Registry key It appears some values are in registry, and some are in `${WINEPREFIX}/drive_c/users/steamuser/Games/Age\ of\ Empires\ 2\ DE/${account_steamid}/profile/Player.nfp`. So this is the ones from the registry. However, it also appears to use key `HKEY_USERS\{sid}-{UID}-1000\Software\Microsoft\Microsoft Games\Age of Empires II DE` which is not predictable.
Prepare the initial umu from a working environment. I found one that works for clients that is less than 1GB, with only steamrt4/.
cp -pr ~/.local/share/umu ~/aoe2de/initial-files/
NOTE: It appears some values are in registry, and some are in
${WINEPREFIX}/drive_c/users/steamuser/Games/Age\ of\ Empires\ 2\ DE/${account_steamid}/profile/Player.nfp.This command exports the registry keys for the game to
${HOME}\aoe2de-partial.reg. This depends on symlink${WINEPREFIX}/dosdevices/x: -> ${HOME}PROTON_DISABLE_LSTEAMCLIENT=1 WINEPREFIX=~/.wine-aoe2de PROTONPATH=~/aoe2de/GE-Proton11-1 umu-run regedit /E "x:\aoe2de\initial-files\aoe2de-partial.reg" "HKEY_CURRENT_USER\Software\Microsoft\Microsoft Games\Age of Empires II DE"
-
Build release asset #2.
tar -zc -C ~ -f ~/aoe2de-other-components-2026-07-01.tgz --exclude 'AoE2DE' --exclude 'prepare' aoe2de
This tar operation only took about r0:03.30.
Description of this working environment
~/aoe2de-
ageLANServer/linuxexploded from https://github.com/luskaner/ageLANServer/releases/download/v1.14.1/ageLANServer_full_1.14.1_linux_x86-64.tar.gz-
battle-server-manager/resources/config.age2.tomlcustomized -
server/resources/config/config.tomlcustomized
-
-
AoE2DEproper Steam game with all my DLCs, named aoe2de-on-linux/aoe2de-alldlcs-raw.2026-06-28.tgz -
aoe2de.shlauncher script - ColdClientLoader.ini customized
- steamclient64.dll
- steamclient.dll
-
steamclient_loader_x64.exeThese four from https://github.com/Detanup01/gbe_fork/releases/download/release-2026_05_30/emu-win-release.7zrelease/steamclient_experimental/ - debs/ Two files from https://github.com/Open-Wine-Components/umu-launcher/releases/tag/1.4.0
-
python3-umu-launcher_1.4.0-1_amd64_debian-13.debhttps://github.com/Open-Wine-Components/umu-launcher/releases/download/1.4.0/python3-umu-launcher_1.4.0-1_amd64_debian-13.deb -
umu-launcher_1.4.0-1_all_debian-13.debhttps://github.com/Open-Wine-Components/umu-launcher/releases/download/1.4.0/umu-launcher_1.4.0-1_all_debian-13.deb
-
- dlls/
-
ageLANServerLauncherCompanion_Age2FakeOnline_1.0.0.0.dllfrom https://github.com/luskaner/ageLANServerLauncherCompanion/releases/download/v1.3.0.0/ageLANServerLauncherCompanion_Age2FakeOnline_1.0.0.0.zip - GameOverlayRenderer64.dll
-
steamclient_extra_x64.dllThese two from https://github.com/Detanup01/gbe_fork/releases/download/release-2026_05_30/emu-win-release.7zrelease/steamclient_experimental/andextra_dlls/
-
- GE-Proton11-1/ exploded from https://github.com/GloriousEggroll/proton-ge-custom/releases/download/GE-Proton11-1/GE-Proton11-1.tar.gz
- initial-files/
- aoe2de-partial.reg Generated myself, in an attempt to store my settings.
- profile/ Generated myself, in an attempt to store my settings.
- umu/ Copy of ~/.local/share/umu to avoid needing the Internet for umu to fetch its components
- saves/ Empty directory that will be used by clients
- scripts/ Generated myself, useful scripts for this package.
- steam_settings/ Minimal configs for steam emulator, described by https://github.com/luskaner/ageLANServerLauncherCompanion
- configs.app.ini Customized for the DLCs I own
- configs.user.ini Will be customized for each client, for
account_steamidandaccount_username.
Operations
See also /mnt/public/Support/Games/aoe2de-on-linux/aoe2de-without-steam.md
Installing this offline bundle
A server and client get the same assets. The server needs some additional steps.
-
Explode tarball of AoE2DE itself, to ~/aoe2de.
tar -zxf /mnt/public/Support/Games/aoe2de-on-linux/aoe2de-alldlcs-raw.2026-06-28.tgz -C ~
-
Explode the other components.
tar -zxf /mnt/public/Support/Games/aoe2de-on-linux/aoe2de-other-components-2026-07-01.tgz -C ~
Installing a client
In ~/aoe2de, run some of the `scripts/set-*.sh`. APPLY=1 ./set-account-steamid.sh # randomize the steamid in the emulator APPLY=1 ./set-initial-profile.sh APPLY=1 ./set-initial-umu.sh APPLY=1 ./set-initial-mods.sh # optional APPLY=1 ./set-username.sh "Steve Harvee 3" ./set-initial-registry.sh Some can only be run after the server is up and running; see section "Running a client".
Installing a server
Run each step from the "Installing a client" first, except the ones for after a server is running. When you adjust /etc/hosts, remove or comment any 127.0.0.1 <hostname>-style records.
It is possible that on a new system, you have to run this.
sudo setcap CAP_NET_BIND_SERVICE=+eip ~/aoe2de/ageLANServer/linux/server/server
Initialize a certificate.
~/aoe2de/ageLANServer/linux/server/bin/genCert.sh
Running a client
If you will play multiplayer, you need to know the IP address or hostname of the server. This sets the entries in /etc/hosts which can be done manually if necessary.
APPLY=1 ./set-etc-hosts.sh 192.168.1.45 # IP address or hostname of server APPLY=1 ./set-cert-from-server.sh 192.168.1.45
Run the short launcher.
~/aoe2de/aoe2de.sh
Running a server
Run the battle server manager in a terminal. It will spawn a new one to run, and return this prompt to you for the next command.
~/aoe2de/ageLANServer/linux/battle-server-manager/start_age2.sh
Run the server.
~/aoe2de/ageLANServer/linux/server/start_age2.sh
Then run the client as described above in "Running a client".
Adding mods
With script
From the second asset, use ~/aoe2de/scripts/set-initial-mods.sh. It depends on this network path for this file.
/mnt/public/Support/Games/aoe2de-on-linux/aoe2de-mods-subscribed.2026-06-26.tgz
Manually
Find the generated userid from ~/aoe2de/saves/settings.configs.user.ini.
eval $( awk '/account_steamid/' ~/aoe2de/saves/settings/configs.user.ini | tr -dc 'a-zA-Z=0-9_' )
Visit the user's mods directory and copy in the mods I have provided.
cd ~/".wine-aoe2de/drive_c/users/steamuser/Games/Age of Empires 2 DE/${account_steamid}/mods" tar -zxf /mnt/public/Support/Games/aoe2de-on-linux/aoe2de-mods-subscribed.2026-06-26.tgz
Updating the game
A full copy of the official game from steam needs to be updated, and then built into a tarball. The path would be similar to /mnt/vm2/pcb-009-steam/steamapps/common/AoE2DE.
cd /mnt/vm2/pcb-009-steam/steamapps/common tar -zcf "AoE2DE-$( date "+%F" ).tgz" AoE2DE
Note that this would not have the exact same directory structure as the original first asset which was aoe2de/AoE2DE/.
Updating "other components"
These dependencies might have newer versions. Check the references, and follow the "Building the package" steps.
Updating mods
Build a new tarball from the upstream machine that has the desired mods.
tar -zc -C "/mnt/vm2/pcb-009-steam/steamapps/compatdata/813780/pfx/drive_c/users/steamuser/Games/Age of Empires 2 DE/${account_steamid}/mods" -f /mnt/public/Support/Games/aoe2de-on-linux/aoe2de-mods-subscribed.$( date "+%F" ).tgz
Related files
- aoe2de-alldlcs-raw.2026-06-28.tgz
- aoe2de-other-components-2026-07-01.tgz
- aoe2de-mods-subscribed.2026-06-26.tgz
References
- Installing Proton on Linux: A Comprehensive Guide — linuxvox.com
- proton ge: https://github.com/GloriousEggroll/proton-ge-custom/releases/download/GE-Proton11-1/GE-Proton11-1.tar.gz from Releases · GloriousEggroll/proton-ge-custom
- Open-Wine-Components/umu-launcher: Unified launcher for Windows games on Linux
- UMU Database Search shows nothing for "Empires" about AoE2, so GAMEID can be left undefined.
- luskaner/ageLANServerLauncherCompanion: Libraries to enhance or fix the game behaviour when using a custom launcher in Age LAN Server
- Releases · Detanup01/gbe_fork
- luskaner/ageLANServer: Web Server (and its launcher) for AoE 1, 2, 3 (all DE), AoE 4 and AoM: RT supporting offline multiplayer
- Age of Empires II: Definitive Edition DLCs · SteamDB to find ids for the two DLCs I own that were not demonstrated in the configs.app.ini
Comments