DDO Server Transfers 2025
New 64-bit servers will go live on July 15th and you will have until August 31st to transfer characters off the 32-bit servers before they are closed.
At a later date TBD, SSG will reopen the 32-bit game worlds only for players to prepare their characters for transfer with no questing or other gameplay available for two years.
In preparation for the transfer, use the code 100SLOTS to gain a free 100 Shared Account Bank slots on all servers! edit
Game mechanics • Newbie guide • In development • DDO Store • Social Media
Challenges • Classes • Collectables • Crafting • Enhancements • Epic Destinies • Favor • Feats
Glossary • Items • Maps • Monsters • Places • Quests • Races • Reincarnation • Skills • Spells
Please create an account or log in to remove ads, build a reputation, and unlock more editing privileges and then visit DDO wiki's IRC Chat/Discord if you need any help!
DDO on MacOS
Play DDO |
---|
|
Wine[edit]
The following instructions describe how to install the DDO on macOS 10.15 or higher. It requires using the Terminal which can be found at: /Applications/Utilities/Terminal. Intel and Apple Silicon based Macintosh computers are supported.
From a high level perspective you will be installing the package manager brew, the wine framework, and then DDO itself. The game is subsequently launched from the Terminal whenever you want to play.
Installation[edit]
Open the Terminal and paste the bolded lines one at a time. Hit "return" after each line pasted. Answer "yes" anytime you are asked to grant permissions. You will need your password one time to remove the "quarantine" flag from wine.
Installing the package manager 'brew'[edit]
If you don't have brew already, or are not sure whether you do, execute these commands:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" hb=/usr/local; if [[ $(uname -p) == 'arm' ]]; then; hb=/opt/homebrew; fi (echo; echo 'eval "$('$hb'/bin/brew shellenv)"') >> ~/.zprofile eval "$($hb/bin/brew shellenv)"
If you do have brew already, execute this command:
brew update
Installing the 'wine' framework[edit]
Once you have an up to date installation of brew, execute these commands:
# DDO only works in the first release of Wine 10.0. Remove any newer or older versions of wine. brew uninstall wine-stable # Installing from a URL is no longer supported so download the Wine 10.0 "cask" to your local machine. curl -o wine-stable.rb https://raw.githubusercontent.com/Homebrew/homebrew-cask/3b6f8acd9c1f8aad5616223b781c5be240dc16f8/Casks/w/wine-stable.rb # There were some useful updates in the 10.0_1 cask. Add those. sed -i '' 's/wine64/msidb/g' wine-stable.rb # Install wine 10.0. brew install --no-quarantine --cask wine-stable.rb # If you are on Apple Silicon, install Apple's x86 emulator. Ignore any errors. /usr/sbin/softwareupdate --install-rosetta --agree-to-license
Install DDO[edit]
Execute these commands to install DDO:
curl https://files.ddo.com/ddo/installers/ddolive.exe --output ~/Downloads/ddolive.exe wine ~/Downloads/ddolive.exe
Playing DDO[edit]
This is how you will launch the game directly from the Terminal going forward:
wine ~"/.wine/drive_c/Program Files (x86)/StandingStoneGames/Dungeons & Dragons Online/DNDLauncher.exe"
Hit the "up" arrow in the Terminal to repeat the previous command. This can be quite the time saver once you start launching the game this way.
macOS security considerations[edit]
macOS occasionally tightens security requirements on apps downloaded from the web. One symptom of these restrictions is that the DDO Launcher will not have permission to patch any files in the DDO program folder. Running the Launcher will unexpectedly quit. To resolve the issue, open System Preferences, click "Privacy and Security", and scroll down to where it says "Wine". Choose Open / Open Anyway. Enter your admin password. This will allow the Launcher to complete patching on the next attempt.
DX-11 support[edit]
The standard version of wine described above only supports DX-9. DX-11 can be enabled using Apple's wine variation "Game Porting Toolkit". It is significantly more difficult to install and should not be undertaken lightly. There will also be some lost functionality using this approach. Details are provided in the DDO on macOS using Apple's Game Porting Toolkit section.
Launcher Script[edit]
The Launcher Script allows you to bypass the standard DDO Launcher. You can use it to launch a fresh copy of the game in a new window for as many characters as you have accounts. You must have either the wine installation completed, or the Game Porting Toolkit installed in order for the Launcher Script to work.
The Launcher Script is executed from the /Applications/Utilities/Terminal. For example:
/Applications/DNDLauncher.sh Kaytis /Applications/DNDLauncher.sh Sienlee
These commands launch characters "Kaytis" and "Sienlee" from two different accounts in separate windows. They can join the same party and quest together. You can have as many windows open as you have accounts. Most Macs based on Apple Silicon can easily support up to 6 party members even at the highest graphics settings. There is support for a tool called "Dungeon Helper" in the script, which allows characters to follow any other party member around, greatly simplifying movement to and through quests. Instructions for installing Dungeon Helper on macOS can be found in a later section.
Creating the launcher script[edit]
- Open TextEdit and create a new document.
- Select menu item "Format" > "Make Plain Text" (if you don't see this option, ignore this step).
- Paste all of the text from the "DNDLauncher" script.
- Save the file as /Applications/DNDLauncher.sh
- Open /Applications/Utilities/Terminal and type:
chmod 755 /Applications/DNDLauncher.sh
[[DNDLauncher script]] |
---|
#!/bin/bash
#############################################################################
# General Information
#
# Command Line Interface launcher for Dungeons & Dragons online.
#
# To use this script, follow the instructions in the sections:
# 1) Create the script
# 2) Play the game
# 3) Optional: Create a separate personalization script
#
# Note that this script cannot update the game. If it fails to work, add -l to the
# command line to allow the original DDO Launcher to perform any pending updates.
# After updating, remove the -l from the command line to return to launching the game.
#
# SNy 2007-2011
# -original version
#
# AtomicMew 6/3/2012
# -modded to take command line args and windoze use
#
# Kaytis 5/31/2013 v 1.1
# -modded to work on Mac OS X
#
# Kaytis 2/18/2015 v 1.2
# -updated to match new login protocols
#
# Kaytis 11/30/2015 v 1.2.1
# -disabled peer verification to bypass invalid certificates
#
# Kaytis 4/4/2019 v 1.3
# -simplified instructions
#
# Kaytis 10/19/2019 v 1.4
# -added support for wine client
# -added support for command line arguments:
# -u username
# -p 'password'
# -n subscription
# -w world
# -c character
#
# Kaytis 10/2/2020 v 1.5
# -added support for Lamannia world
# -added support for 64-bit client
# -x64
#
# Kaytis 2/10/2024 v 2.0
# -adapted for macOS 10.15 (Catalina) and higher
# -changed default to 64-bit client. -x64 flag no longer does anything
# -added support for 32-bit clients. Works, but not playable
# -x32
#
# Kaytis 3/16/2024 v 2.1
# -added launcher support. Will run the launcher instead of the client
# -l
#
# Kaytis 7/25/2024 v 2.2
# -added support for Apple Silicon
#
# Kaytis 7/26/2024 v 2.2.1
# -emphasized that passwords should be surrounded by single quotes
#
# Kaytis 2/8/2025 v 2.2.2
# -added support for Wine 10
#
# Kaytis 4/12/2025 v 3.0.0
# -moved personalization information into a separate file
# -improved text alignment when printing in columns
# -removed redundant code
# -improved code consistency
# -added support for Game Porting Toolkit
# -gpt
# -added support for Dungeon Helper
# -d
#
# Kaytis 6/13/2025 v 3.0.1
# -disable gstreamer due to compatibility issues. Does not affect gameplay.
#
#############################################################################
# Create the script
#
# 1) Open TextEdit, create a new document.
# 2) Select menu item "Format" > "Make Plain Text" (if you don't see this option, ignore this step).
# 3) Paste all of this text into it.
# 4) Save the file as /Applications/DNDLauncher.sh
# 5) Open /Applications/Utilities/Terminal and type:
# chmod 755 /Applications/DNDLauncher.sh
# 6) Hit the return key to execute the command.
#
# Version 3.0.0 and higher of this script does not need to be customized. Put
# your account and personalization information in a separate file
# DNDLauncherPersonalization.sh that resides in the same folder as this script.
#############################################################################
# Play the game
#
# 1) Open /Applications/Utilities/Terminal
# 2) To start from the character selection screen, type:
# /Applications/DNDLauncher.sh -u username -p 'password' -w world
# To start in world with a specific character, type:
# /Applications/DNDLauncher.sh -u username -p 'password' -w world -c character
# 3) Hit the return key to execute the command. The game will start.
#
# Handy tip: the up arrow will restore the last command you typed in the Terminal.
# Handy tip: you do not need to leave this file open after creating it.
#
# In certain circumstances a subscription number is required. You can
# specify it using the -n parameter. If one is required, and it is not provided,
# the script will help you figure out what the number is so that it can be
# provided next time.
#############################################################################
# Parse the command line arguments.
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
-u|--username)
ARG_USERNAME="$2"
shift # past argument
shift # past value
;;
-p|--password)
ARG_PASSWORD="$2"
shift # past argument
shift # past value
;;
-n|--subscription)
ARG_SUBSCRIPTION="$2"
shift # past argument
shift # past value
;;
-w|--world)
ARG_WORLD="$2"
shift # past argument
shift # past value
;;
-c|--character)
ARG_CHARACTER="$2"
shift # past argument
shift # past value
;;
-x32|--x32)
X32=YES
unset X64
shift # past argument
;;
-x64|--x64)
unset X32
X64=YES
shift # past argument
;;
-l|--launcher)
LAUNCHER=YES
shift # past argument
;;
-d|--dungeonhelper)
DUNGEONHELPER=YES
shift # past argument
;;
-gpt|--gpt)
USEGPT=YES
shift # past argument
;;
-h|--help)
HELP=YES
shift # past argument
;;
*) # unknown option -assume character name
ARG_CHARACTER="$1"
shift # past argument
;;
esac
done
#############################################################################
# Launcher script
if [ -n "${HELP}" ]; then
echo ""
echo "A macOS command line launcher for DDO. Use this command for fast game launching"
echo "and multiboxing."
echo ""
echo "usage: DNDLauncher [-u username] [-p 'password'] [-n subscription] [-w world] [-c character]"
echo ""
echo "-u username : account username"
echo "-p 'password' : account password. Surround with single quotes."
echo "-n subscription : account subscription. Usually 0. Omit if unknown."
echo "-w world : world e.g. Orien. Capitalize only the first letter"
echo "-c character : character to log in. '-c' is optional"
echo "-x32 : use 32-bit client (not recommended)"
echo "-x64 : use 64-bit client (default)"
echo "-l : run the launcher instead. Use this on update days"
echo "-d : run DungeonHelper before launching the game"
echo "-gpt : use Apple's Game Porting Toolkit (must be installed)"
echo ""
echo "Example:"
echo ""
echo " /Applications/DNDLauncher.sh -u username -p 'password' -w Orien -c Kaytis"
echo ""
echo "or with an appropriate personalization script:"
echo ""
echo " /Applications/DNDLauncher.sh -c Kaytis"
echo ""
echo -e "\033[0;33m"
echo "Note that this script cannot update the game. If it fails to work, add -l to the"
echo "command line to allow the original DDO Launcher to perform any pending updates."
echo "After updating, remove the -l from the command line to return to launching the game."
echo -e "\033[0m"
echo ""
exit 0
fi
# The DNDLauncherPersonalization.sh file can be used to set personal values for username,
# password, subscription and world. It can be used to select different values based on the
# input value of ARG_CHARACTER allowing quick multiboxing using different accounts.
# The DNDLauncherPersonalization.sh file is optional. All the information needed to play
# can be passed in on the command line.
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
if [ -f "${SCRIPT_DIR}/DNDLauncherPersonal.sh" ]; then
source "${SCRIPT_DIR}/DNDLauncherPersonal.sh"
fi
# Snag the character name, if any, and stash it in the "character" variable.
character="${ARG_CHARACTER}"
# Apply overides
if [ -n "${ARG_USERNAME}" ]; then
username="${ARG_USERNAME}"
fi
if [ -n "${ARG_PASSWORD}" ]; then
password="${ARG_PASSWORD}"
fi
if [ -n "${ARG_SUBSCRIPTION}" ]; then
subscription="${ARG_SUBSCRIPTION}"
fi
if [ -n "${ARG_WORLD}" ]; then
world="${ARG_WORLD}"
fi
# Helper function to print a description in column one, and one or more items
# in column two. Warning: function exits the script if nothing is provided
# for column two.
printColumns() {
local description="$1" # Header to display for susequent list
shift # Shift past the first argument
local elements=("$@") # Construct an array from the remaining arguments
local columnWidth=30 # Set a fixed width for the output
# It is a fatal error to pass an empty list for the second column.
# Indicate that nothing was passed and terminate the script.
array_count=${#elements[@]}
if [ $array_count -eq 0 ]; then
printf "%-${columnWidth}sNone\n" "${description}:"
exit 1
fi
# Print the description in the first row of the first column and the elements
# in the second column
printf "%-${columnWidth}s%s\n" "${description}:" "${elements[0]}"
for ((i=1; i<$array_count; i++)); do
printf "%-${columnWidth}s%s\n" "" "${elements[$i]}"
done
}
echo "-----------------------------------------------------------------------"
echo -n -e "\033[0;32m"
echo -e "Welcome to the CLI launcher for DDO for Mac OS X version 3.0.0"
echo -n -e "\033[0m"
exe() { echo "> $@" ; "$@" ; }
# Disable gstreamer for compatibility with Wine 10.0.This will have no impact on any of
# the applications launched by this script. This can be removed once the issue in Wine is
# resolved.
export GST_PLUGIN_PATH=/dev/null
export GST_PLUGIN_SYSTEM_PATH=/dev/null
# Find the best version of wine to use
# ------------------------------------
stdWinePrefix=".wine"
gptWinePrefix=".wine_gpt"
# If the ARM version of wine is installed, default to that.
gameWineBin_DIR="/opt/homebrew/bin"
gameWine_EXE=wine
gameWinePrefix_DIR="$HOME/$stdWinePrefix"
gameWinePlatform_MSG="Apple Silicon wine"
# If the ARM version is not installed, switch to the Intel version of wine.
if [ ! -f "${gameWineBin_DIR}/${gameWine_EXE}" ]; then
gameWineBin_DIR="/usr/local/bin"
gameWine_EXE=wine64
gameWinePrefix_DIR="$HOME/$stdWinePrefix"
gameWinePlatform_MSG="Intel wine"
fi
# If the user wants to run using Apple's Game Porting Toolkit, switch to that.
if [ -n "${USEGPT}" ]; then
# Check that it is present first
if [ ! -d "$HOME/$gptWinePrefix" ]; then
echo "Game Porting Toolkit is misconfigured. Please refer to ddowiki for installation instructions."
else
gameWineBin_DIR="/usr/local/bin"
gameWine_EXE=wine64
gameWinePrefix_DIR="$HOME/$gptWinePrefix"
gameWinePlatform_MSG="Intel Game Porting Toolkit"
fi
else
# If we are not using the Game Porting Toolkit, restore DX 9. If we do not,
# the client will lockup when launching. This also restores the initial prompt
# to use DX 11 the next time the user launches using the Game Porting Toolkit.
sed -i'' -e 's/D3DVersionPromptedForAtStartup=.*$/D3DVersionPromptedForAtStartup=9/' "${HOME}/Documents/Dungeons and Dragons Online/UserPreferences.ini"
sed -i'' -e 's/GraphicsCore=.*$/GraphicsCore=D3D9/' "${HOME}/Documents/Dungeons and Dragons Online/UserPreferences.ini"
fi
# If we can't find the needed installation of wine, bail.
if [[ ! -f "${gameWineBin_DIR}/${gameWine_EXE}" || ! -d "${gameWinePrefix_DIR}" ]]; then
echo "Invalid wine configuration. Please refer to the wiki for installation instructions."
exit 1
fi
echo -n -e "\033[0;32m"
echo "Using ${gameWinePlatform_MSG} with prefix ${gameWinePrefix_DIR}"
echo -n -e "\033[0m"
# Locate the rest of the files needed
# -----------------------------------
# Production launcher and client
gameClient_DIR="$gameWinePrefix_DIR/drive_c/Program Files (x86)/StandingStoneGames/Dungeons & Dragons Online"
gameLauncher_EXE="C:/Program Files (x86)/StandingStoneGames/Dungeons & Dragons Online/DNDLauncher.exe"
gameClient_EXE="C:/Program Files (x86)/StandingStoneGames/Dungeons & Dragons Online/x64/dndclient64.exe"
if [ -n "${X32}" ]; then
gameClient_EXE="C:/Program Files (x86)/StandingStoneGames/Dungeons & Dragons Online/dndclient.exe"
fi
# Lamannia launcher and client
if [ "${world}" == "Lamannia" ]; then
gameClient_DIR="$gameWinePrefix_DIR/drive_c/Program Files (x86)/StandingStoneGames/Dungeons & Dragons Online (Preview)"
gameLauncher_EXE="C:/Program Files (x86)/StandingStoneGames/Dungeons & Dragons Online (Preview)/DNDLauncher.exe"
gameClient_EXE="C:/Program Files (x86)/StandingStoneGames/Dungeons & Dragons Online (Preview)/x64/dndclient64.exe"
if [ -n "${X32}" ]; then
gameClient_EXE="C:/Program Files (x86)/StandingStoneGames/Dungeons & Dragons Online (Preview)/dndclient.exe"
fi
fi
# Run the launcher instead (will update game as needed)
if [ -n "${LAUNCHER}" ]; then
echo "-----------------------------------------------------------------------"
echo "Starting launcher..."
echo -e "\033[0;32m"
echo "WINEPREFIX=\"$gameWinePrefix_DIR\" \"$gameWineBin_DIR/$gameWine_EXE\" \"$gameLauncher_EXE\" &>/dev/null &"
echo -e "\033[0m"
WINEPREFIX="$gameWinePrefix_DIR" "$gameWineBin_DIR"/$gameWine_EXE "$gameLauncher_EXE" &>/dev/null &
echo "-----------------------------------------------------------------------"
echo "Done. Please allow the DDO launcher a few seconds to start."
echo "-----------------------------------------------------------------------"
disown
exit
fi
# Run DungeonHelper first if requested
if [ -n "${DUNGEONHELPER}" ]; then
echo "-----------------------------------------------------------------------"
dungeon_helper_EXE="C:/users/${USER}/AppData/Roaming/Dungeon Helper/DungeonHelper.exe"
echo "Starting Dungeon Helper..."
if [ ! -f "${dungeon_helper_EXE}" ]; then
sed -i'' -e 's/ScreenMode=FullScreen$/ScreenMode=Windowed/' "/Users/${USER}/Documents/Dungeons and Dragons Online/UserPreferences.ini"
echo -n -e "\033[0;32m"
echo "WINEPREFIX=\"${gameWinePrefix_DIR}\" \"${gameWineBin_DIR}/${gameWine_EXE}\" \"${dungeon_helper_EXE}\" &>/dev/null &"
echo -n -e "\033[0m"
WINEPREFIX="${gameWinePrefix_DIR}" "${gameWineBin_DIR}"/${gameWine_EXE} "${dungeon_helper_EXE}" &>/dev/null &
echo "Done starting Dungeon Helper."
else
echo "Dungeon Helper is not installed."
fi
fi
# Change directory to where the launcher resources reside
cd "${gameClient_DIR}"
# cleanup temp directory for configuration files downloaded from the official servers
rm -rf .launcher
mkdir .launcher
echo "-----------------------------------------------------------------------"
echo "Reading game configuration..."
# Get Game and DataCenter settings from launcher config file.
configFile="ddo.launcherconfig"
if ! [ -r "${configFile}" ]; then
echo -e "\nError: ${configFile} cannot be read.\n"
exit 1
fi
game=`grep -h "DataCenter.GameName" "${configFile}" | grep -v '<!--.*-->' | sed -e "s/^.*value=\"\([^\"]*\)\".*$/\1/"`
glsDataCenter_URL=`grep -h "Launcher.DataCenterService.GLS" "${configFile}" | grep -v '<!--.*-->' | sed -e "s/^.*value=\"\([^\"]*\)\".*$/\1/"`
printColumns "Game Name" "${game}"
printColumns "Game Login Server" "${glsDataCenter_URL}"
echo "-----------------------------------------------------------------------"
echo "Querying Game Login Server..."
# get configuration info (xml-file containing auth, patch and game servers with their corresponding settings)
# NOTE: while a normal HTTP GET to /GLS.DataCenterServer/Service.asmx/GetDatacenters?game=LOTROEU
# works fine for the european datacenter, it does not work for the US/AU/... LOTRO one
# instead, we need to send a SOAP request there, ending up with a SOAP answer (no whitespace whatsoever)
# now, to have at least some whitespace we can deal with, sed is used to insert a newline after each closing xml tag below
curl --silent \
--insecure \
--header 'Content-Type: text/xml; charset=utf-8' \
--header 'SOAPAction: "http://www.turbine.com/SE/GLS/GetDatacenters"' \
--data "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><soap:Body><GetDatacenters xmlns=\"http://www.turbine.com/SE/GLS\"><game>${game}</game></GetDatacenters></soap:Body></soap:Envelope>" \
"${glsDataCenter_URL}" \
--output .launcher/GLSDataCenter.config
if ! [ -r .launcher/GLSDataCenter.config ]; then
echo -e "\nError: Could not download GLS data center configuration.\n"
exit 1
fi
# Format the response by adding new lines after the closing xml tags.
sed -e "s#\(</[^>]*>\)#\1_make_newline_#g" -i "" .launcher/GLSDataCenter.config
awk '{ gsub(/_make_newline_/,"\n",$0); print }' .launcher/GLSDataCenter.config >> .launcher/GLSDataCenter.config.tmp
rm -f .launcher/GLSDataCenter.config
mv .launcher/GLSDataCenter.config.tmp .launcher/GLSDataCenter.config
# Grab only the first DataCenter.
echo -e "<!--\n NOTE\n This file is NOT a valid XML file!\n-->" >> .launcher/GLSDataCenter.config."${game}"
cat .launcher/GLSDataCenter.config | sed -n -e "/^.*<Datacenter><Name>${game}<\/Name>/,/<\/Datacenter>.*$/ p" >> .launcher/GLSDataCenter.config."${game}"
# Extract global server URLs
patchServer_ADR=`grep -h "<PatchServer>" .launcher/GLSDataCenter.config.${game} | grep -v '<!--.*-->' | sed -e "s/^.*<PatchServer>//;s/<\/PatchServer>.*$//"`
launcherCfg_URL=`grep -h "<LauncherConfigurationServer>" .launcher/GLSDataCenter.config.${game} | grep -v '<!--.*-->' | sed -e "s/^.*<LauncherConfigurationServer>//;s/<\/LauncherConfigurationServer>.*$//"`
authServer_URL=`grep -h "<AuthServer>" .launcher/GLSDataCenter.config.${game} | grep -v '<!--.*-->' | sed -e "s/^.*<AuthServer>//;s/<\/AuthServer>.*$//"`
# Extract world specific server URLs
serverChat_URL=`grep -h -A 4 "<World>" .launcher/GLSDataCenter.config.${game} | grep -F -A 3 "${world}" | grep -h "<ChatServerUrl>" | grep -v '<!--.*-->' | sed -e "s/^.*<ChatServerUrl>//;s/<\/ChatServerUrl>.*$//"`
serverStatus_URL=`grep -h -A 4 "<World>" .launcher/GLSDataCenter.config.${game} | grep -F -A 3 "${world}" | grep -h "<StatusServerUrl>" | grep -v '<!--.*-->' | sed -e "s/^.*<StatusServerUrl>//;s/<\/StatusServerUrl>.*$//"`
# Look up the server info in the configuration file
# The chat server address is given directly, other stuff needs another file (cache_$REALMNAME.xml) from the server
if [ -z "${launcherCfg_URL}" ] || [ -z "${authServer_URL}" ]; then
echo -e "\nError: Could not extract one or more server URLs from the Game Login Server response.\n"
exit 1
fi
# Extract list of game servers into an array.
worlds=`grep -h -A 4 "<World>" .launcher/GLSDataCenter.config.${game} | grep "<Name>" | grep -v '<!--.*-->' | sed -e "s/^.*<Name>//;s/<\/Name>.*$//"`
IFS=$'\n'
serverNames=""
first=true
for name in ${worlds}; do
if [ "$first" = true ]; then
serverNames="${name}"
first=false
else
serverNames+=", ${name}"
fi
done
unset IFS
printColumns "Launch Configuration Server" "${launcherCfg_URL}"
printColumns "Patch Server" "${patchServer_ADR}"
printColumns "Authorization Server" "${authServer_URL}"
printColumns "$world Chat Server" "${serverChat_URL}"
printColumns "$world Status Server" "${serverStatus_URL}"
printColumns "Available worlds" "${serverNames[@]}"
echo "-----------------------------------------------------------------------"
echo "Querying Launch Configuration Server..."
curl --silent \
--insecure \
"${launcherCfg_URL}" \
--output .launcher/launcher.config
if ! [ -r .launcher/launcher.config ]; then
echo -e "\nError: Could not download launcher configuration.\n"
exit 1
fi
# Extract game settings from launcher configuration.
worldQueue_URL=`grep -h "WorldQueue.LoginQueue.URL" .launcher/launcher.config | grep -v '<!--.*-->' | sed -e "s/^.*value=\"\([^\"]*\)\".*$/\1/"`
worldQueue_ARGTMPL=`grep -h "WorldQueue.TakeANumber.Parameters" .launcher/launcher.config | grep -v '<!--.*-->' | sed -e "s/^.*value=\"\([^\"]*\)\".*$/\1/"`
glsTicketLifetime=`grep -h "GameClient.Arg.glsticketlifetime" .launcher/launcher.config | grep -v '<!--.*-->' | sed -e "s/^.*value=\"\([^\"]*\)\".*$/\1/"`
support_URL=`grep -h "GameClient.Arg.supporturl" .launcher/launcher.config | grep -v '<!--.*-->' | sed -e "s/^.*value=\"\([^\"]*\)\".*$/\1/"`
bug_URL=`grep -h "GameClient.Arg.bugurl" .launcher/launcher.config | grep -v '<!--.*-->' | sed -e "s/^.*value=\"\([^\"]*\)\".*$/\1/"`
supportService_URL=`grep -h "GameClient.Arg.supportserviceurl" .launcher/launcher.config | grep -v '<!--.*-->' | sed -e "s/^.*value=\"\([^\"]*\)\".*$/\1/"`
gameClient_FILE=`grep -h "GameClient.OSX.Filename" .launcher/launcher.config | grep -v '<!--.*-->' | sed -e "s/^.*value=\"\([^\"]*\)\".*$/\1/"`
gameClient_ARGTMPL=`grep -h "GameClient.OSX.ArgTemplate" .launcher/launcher.config | grep -v '<!--.*-->' | sed -e "s/^.*value=\"\([^\"]*\)\".*$/\1/"`
printColumns "World Queue Server" "${worldQueue_URL}"
printColumns "World Queue Args" "${worldQueue_ARGTMPL}"
printColumns "GLS Ticket Lifetime" "${glsTicketLifetime}"
[ -n "$support_URL" ] && printColumns "Support URL" "${support_URL}"
[ -n "$bug_URL" ] && printColumns "Bug URL" "${bug_URL}"
[ -n "$supportService_URL" ] && printColumns "Support Service URL" "${supportService_URL}"
printColumns "Game Client File" "${gameClient_FILE}"
printColumns "Game Client Args" "${gameClient_ARGTMPL}"
echo "-----------------------------------------------------------------------"
echo "Requesting account details and authentication token from the Authorization Server..."
########### CHOOSE LANGUAGE
languages[0]=english
selectedLanguage=0
########### AUTHENTICATION
function GLSAuth() {
# "submit" the login form via POST, will download a file called "LoginAccount"
# NOTE: the same thing as with the DataCenterServer applies here
# the lotroeugls server even has a service description and test form online
# well, at least they provide the SOAP request body as well...
curl --silent \
--insecure \
--header 'Content-Type: text/xml; charset=utf-8' \
--header 'SOAPAction: "http://www.turbine.com/SE/GLS/LoginAccount"' \
--data "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"><soap:Body><LoginAccount xmlns=\"http://www.turbine.com/SE/GLS\"><username>${username}</username><password>${password}</password><additionalInfo></additionalInfo></LoginAccount></soap:Body></soap:Envelope>" \
"${authServer_URL}" \
--output .launcher/GLSAuthServer.config
if ! [ -s .launcher/GLSAuthServer.config ] || grep -qs faultcode .launcher/GLSAuthServer.config ; then
echo -e "\nError: GLS auth server request failed. Wrong username / password?\n"
exit 1
fi
}
GLSAuth
# Check for multiple game subscriptions for the authenticated account.
# Insert whitespace after </GameSubscription> tags here (to have linebreaks as separators for accounts)
sed -e "s#\(</GameSubscription>\)#\1_make_newline_#g" -i "" .launcher/GLSAuthServer.config
awk '{ gsub(/_make_newline_/,"\n",$0); print }' .launcher/GLSAuthServer.config >> .launcher/GLSAuthServer.config.tmp
rm -f .launcher/GLSAuthServer.config
mv .launcher/GLSAuthServer.config.tmp .launcher/GLSAuthServer.config
# Extract each of the ids as well as descriptions for subscriptions to the current game.
subNames=`grep -h "<Game>${game}<\/Game>" .launcher/GLSAuthServer.config | grep "<Name>" | grep -v '<!--.*-->' | sed -e "s/^.*<Name>//;s/<\/Name>.*$//"`
subDescs=`grep -h "<Game>${game}<\/Game>" .launcher/GLSAuthServer.config | grep "<Description>" | grep -v '<!--.*-->' | sed -e "s/^.*<Description>//;s/<\/Description>.*$//"`
IFS=$'\n'
subs=()
for name in ${subNames}; do
subs+=("${name}")
done
descs=()
for desc in ${subDescs}; do
descs+=("${desc}")
done
unset IFS
# Check for at least one active subscription.
if [[ ${#subs[@]} == 0 ]]; then
echo -e "\nError: There appears to be no subscription for ${game}.\n"
exit 1
fi
# Extract the ticket from the GLS auth file, check for failure
# NOTE: only the id (not the ticket) is subscription-specific
glsTicket=`sed -n -e "s/^.*<Ticket>//;s/<\/Ticket>.*$// p" .launcher/GLSAuthServer.config`
if [[ -z "${glsTicket}" ]]; then
echo -e "\nError: Could not extract authentication token from the Authorization Server response.\n"
exit 1
fi
printColumns "Subscription Names" "${subs[@]}"
printColumns "Subscription Descriptions" "${descs[@]}"
printColumns "Authentication Token" "${glsTicket}"
echo "-----------------------------------------------------------------------"
echo "Querying the ${world} Status Server..."
# If there are multiple subscriptions to the current game available for the account,
# and we haven't set the subscription choice, ask which one to use.
if [[ "${subscription}" == "" ]]; then
if [[ "${subscription}" == "" && ${#subs[@]} > 1 ]]; then
echo "You have the following subscriptions for $game:"
for ((i=0; i<${#subs[@]}; i++)); do
echo -e " $i: ${subs[$i]}\t'${descs[$i]}'"
done
echo -n "Please select the one you wish to use (enter the number on the left): "
read subscription
else
subscription=0
fi
fi
# Download the status file and get the server adress and login queue adress to
# establish the connection.
# TODO: this file also contains current availability information, use it
curl --silent \
--insecure \
"$serverStatus_URL" \
--output .launcher/server.config
if ! [ -s .launcher/server.config ]; then
echo -e "\nError: Could not download server status information.\n"
exit 1
fi
# Extract the list of loginServers and queue URLs (two at this time, it seems)
loginServers=`grep -h "<loginservers>" .launcher/server.config | grep -v '<!--.*-->' | sed -e "s/^.*<loginservers>//;s/<\/loginservers>.*$//"`
queueUrls=`grep -h "<queueurls>" .launcher/server.config | grep -v '<!--.*-->' | sed -e "s/^.*<queueurls>//;s/<\/queueurls>.*$//"`
if [ -z "${loginServers}" ] || [ -z "${queueUrls}" ]; then
echo -e "\nError: Could not extract world information for ${world}.\n"
exit 1
fi
# Create array of servers
IFS=";"
serverAddresses=()
for server in ${loginServers}; do
serverAddresses+=("${server}")
done
serverQueues=()
for queue in ${queueUrls}; do
serverQueues+=("${queue}")
done
unset IFS
# Use the first login server's first queue
serverAddress="${serverAddresses[0]}"
serverQueue="${serverQueues[0]}"
# Create the world queue login request for the requested world.
# The ticket can contain special characters and needs to be URL encoded before
# POSTing it (same for queue_url). launcher.config included a parameter template
# for the world queue request, used the same way as the client args below
rawurlencode() {
local string="${1}"
local strlen=${#string}
local encoded=""
for (( pos=0 ; pos<strlen ; pos++ )); do
c=${string:$pos:1}
case "$c" in
[-_.~a-zA-Z0-9] ) o="${c}" ;;
* ) printf -v o '%%%02x' "'$c"
esac
encoded+="${o}"
done
echo "${encoded}"
}
glsTicketURLencoded=$(rawurlencode "${glsTicket}")
loginQueueURLencoded=$(rawurlencode "${serverQueue}")
worldQueue_ARGS=`echo "${worldQueue_ARGTMPL}" | sed -e "s/[{]0[}]/${subs[$subscription]}/;s/[{]1[}]/${glsTicketURLencoded}/;s/[{]2[}]/${loginQueueURLencoded}/;s/\&\;/\&/g"`
printColumns "$world Login Servers" "${serverAddresses[@]}"
printColumns "$world Login Queues" "${serverQueues[@]}"
printColumns "Selecting queue" "${serverQueue}"
printColumns "Queue Arguments" "${worldQueue_ARGS}"
echo "-----------------------------------------------------------------------"
echo "Joining ${world} Login Server queue..."
########### LOGIN QUEUE / CLIENT START
function JoinQueue() {
# Now get a queue number from the world login queue so that the client can enqueue and authenticate
inQueue=1
while [ "${inQueue}" == "1" ]; do
# loop when necessary
curl --silent \
--insecure \
--data "${worldQueue_ARGS}" \
"${worldQueue_URL}" \
--output .launcher/WorldQueue.config
if ! [ -r .launcher/WorldQueue.config ]; then
echo -e "\nError: Could not download world queue information.\n"
exit 1
fi
# check the result, should be HRESULT 0x00000000, indicating success (Windows API madness)
hresult=`grep -h "<HResult>" .launcher/WorldQueue.config | grep -v '<!--.*-->' | sed -e "s/^.*<HResult>//;s/<\/HResult>.*$//"`
if [ "${hresult}" != "0x00000000" ]; then
echo -e "\nError: World login queue response indicates failure."
exit 1
else
# lets see what position we are at
queuePos=`grep -h "<QueueNumber>" .launcher/WorldQueue.config | grep -v '<!--.*-->' | sed -e "s/^.*<QueueNumber>//;s/<\/QueueNumber>.*$//"`
queueSrvd=`grep -h "<NowServingNumber>" .launcher/WorldQueue.config | grep -v '<!--.*-->' | sed -e "s/^.*<NowServingNumber>//;s/<\/NowServingNumber>.*$//"`
queueAhead=$(( ${queuePos} - ${queueSrvd} ))
if [ ${queueAhead} -gt 0 ]; then
# d'oh, need to wait
echo -e "\n${queueAhead} ahead in queue, retrying in 5s..."
sleep 5
else
# hah, ready to go
inQueue=0
fi
fi
done
}
# new: world queue is unused on (some?) EU servers, just like with DDO, skip if no queue URL
if [ -n "${serverQueue}" ]; then
JoinQueue
else
echo -e "\nWorld login queue seems to be disabled, skipping..."
fi
echo "Login to ${world} successful."
echo "-----------------------------------------------------------------------"
echo "Starting DDO..."
# We have a template for the client arguments and we have the arguments.
# Note that for replacing the glsTicket, I use s### instead of s/// due
# to the ticket containing slashes. Hopefully, it doesn't contain any
# sharp characters :)
gameClient_ARGS=`echo "${gameClient_ARGTMPL}" | sed -e "
s/[{]SUBSCRIPTION[}]/${subs[$subscription]}/;
s/[{]LOGIN[}]/${serverAddress}/;
s#[{]GLS[}]#${glsTicket}#;
s/[{]CHAT[}]/${serverChat_URL}/;
s/[{]LANG[}]/${languages[$selectedLanguage]}/;
s#[{]AUTHSERVERURL[}]#${authServer_URL}#;
s/[{]GLSTICKETLIFETIME[}]/${glsTicketLifetime}/;
s#[{]SUPPORTURL[}]#${support_URL}#;
s#[{]BUGURL[}]#${bug_URL}#;
s#[{]SUPPORTSERVICEURL[}]#${supportService_URL}#;"`
if [ -n "${character}" ]; then
gameClient_ARGS="$gameClient_ARGS -u $character"
fi
echo -n -e "\033[0;32m"
echo "WINEPREFIX=\"$gameWinePrefix_DIR\" \"$gameWineBin_DIR/$gameWine_EXE\" \"$gameClient_EXE\" ${gameClient_ARGS} &>/dev/null &"
echo -n -e "\033[0m"
WINEPREFIX="$gameWinePrefix_DIR" "$gameWineBin_DIR"/$gameWine_EXE "$gameClient_EXE" ${gameClient_ARGS} &>/dev/null &
echo "Done starting DDO. Please wait a few seconds for it to fully load."
echo "-----------------------------------------------------------------------"
disown
exit 0
|
Playing the game using the launcher script[edit]
# To start from the character selection screen, type: /Applications/DNDLauncher.sh -u username -p 'password' -w world
# To start in world with a specific character, type: /Applications/DNDLauncher.sh -u username -p 'password' -w world -c character
Personalizing the launcher script[edit]
Earlier versions of the launcher script encouraged users to personalize it by typing defaults for usernames, passwords, worlds, and character names directly in to the script. This made it very difficult to upgrade to a new version because all the customization had to be transcribed from the old version in to the new version. To make this easier, the script will now look for a configuration file named "DNDLauncherPersonal.sh" in the same folder as the script. If found, the configuration file will be loaded before the login process begins. Going forward, upgrading the Launcher Script should now be a matter of pasting the new script over the old one and saving it. The configuration file is created the same way as above. Save it with the name "DNDLauncherPersonal.sh" to the same folder as the Launcher Script. Here is an example configuration file:
[[DNDLauncherPersonal configuration]] |
---|
#!/bin/bash
#############################################################################
# Personalization
#
# Providing these values will allow you to go to the character select screen on
# a specific world without providing any arguments to the script. If you add a
# character name as an argument to the script, you will be taken straight to
# that character on the specified world.
#
# Optionally enter your username, password and primary world here. Note that
# if you choose to enter this information, you should NOT share this file with
# anyone. Arguments provided on the command line will always override these
# values.
#
# 1) Fill in the following, by replacing,
# account_username_here,
# account_password_here, and
# world_name_here
# 2) When you are done typing the values, save the file. Don't share the
# file with anyone. Your password is in it.
username=account_username_here # Main account username. No spaces anywhere.
password='account_password_here' # Main account password. Surround with single quotes.
subscription=0 # Main account subscription. Usually 0.
world=world_name_here # World name e.g. world=Orien. Capitalize the first letter!
#############################################################################
# Advanced instructions
#
# Providing values for:
# alt_account_character_name_here
# alt_account_username_here
# alt_account_password_here
# alt_world_name_here
# will allow you to go to a specific character on any account on any world by
# providing a character name as an argument to the script. The character name is
# used as a key to select the right account and world. You will typically just
# provide the character name and nothing else when multiboxing. Note: arguments
# provided on the command line will override these values.
#
# To play a character on an alternate account:
# 1) Open /Applications/Utilities/Terminal
# 2) Type:
# /Applications/DNDLauncher.sh alt_account_character_name
# 3) Hit the return key -a new copy of the game will start.
#
# Handy tip: you can have as many copies of the game open as you have alternate
# accounts. You can add more accounts by duplicating an "elif" block.
if [ "${ARG_CHARACTER}" == "alt_account_character_name_here" ] || \
[ "${ARG_CHARACTER}" == "alt_account_character_name_two_here" ] || \
[ "${ARG_CHARACTER}" == "alt_account_character_name_three_here" ] ; then
username=alt_account_username_here # Alt account username
password='alt_account_password_here' # Alt account password. Surround with single quotes.
subscription=0 # Alt account subscription. Usually 0
world=alt_world_name_here # World name e.g. world=Orien. Capitalize the first letter!
elif [ "${ARG_CHARACTER}" == "alt_account_two_character_name_here" ] || \
[ "${ARG_CHARACTER}" == "alt_account_two_character_name_two_here" ] || \
[ "${ARG_CHARACTER}" == "alt_account_two_character_name_three_here" ] ; then
username=alt_account_two_username_here # Alt account username
password='alt_account_two_password_here' # Alt account password. Surround with single quotes.
subscription=0 # Alt account subscription. Usually 0
world=alt_world_name_here # World name e.g. world=Orien. Capitalize the first letter!
fi
|
Launcher script command line options[edit]
usage: DNDLauncher [-u username] [-p 'password'] [-n subscription] [-w world] [-c character]
-u username : account username -p 'password' : account password. Surround with single quotes. -n subscription : account subscription. Usually 0. Omit if unknown. -w world : world e.g. Orien. Capitalize only the first letter -c character : character to log in. '-c' is optional -x32 : use 32-bit client (not recommended) -x64 : use 64-bit client (default) -l : run the launcher instead. Use this on update days -d : run DungeonHelper before launching the game -gpt : use Apple's Game Porting Toolkit (must be installed)
Example:
/Applications/DNDLauncher.sh -u username -p 'password' -w Orien -c Kaytis
or with an appropriate personalization script:
/Applications/DNDLauncher.sh Kaytis
DungeonHelper[edit]
About DungeonHelper[edit]
DungeonHelper is a tool that, amongst other things, enables a "follow me" mode for characters in your party. The characters must be open in separate windows on the same computer. When the "followed" character moves, all the other characters in the party will move with them. The tool offers other features as well. Refer to the DungeonFinder website, https://dungeonhelper.com, for more information.
Installing DungeonHelper[edit]
In /Applications/Utilities/Terminal type:
curl https://s3.amazonaws.com/downloads.dungeonhelper.com/public/DH/4.0/DungeonHelper.4.2.0.146.msi --output ~/Downloads/DungeonHelper.4.2.0.146.msi
If that download succeeds, type:
wine ~/Downloads/DungeonHelper.4.2.0.146.msi
This will launch the installer. Follow the onscreen instructions.
DungeonHelper has a self-update feature. If there is a newer release version available, it will offer to install it for you.
Using DungeonHelper[edit]
Make sure you are in windowed mode. If you need to, open a client and switch to "Windowed" mode under the Options>Graphics panel. You must quit the client for the change to be permanent (the client wont write your preference to disk until it closes). If you are not using the Launcher Script, you will need to use the Terminal to launch DungeonFinder before you open any game window. In the Terminal type:
wine "C:/users/${USER}/AppData/Roaming/Dungeon Helper/DungeonHelper.exe"
Now launch a game client for every character you want in the group.
If you are using the Launcher Script, simply add a -d flag to the end of the first character opened. For example:
/Applications/DNDLauncher.sh Kaytis -d
This will open DungeonFinder first, then the specified character.
Now open any other characters you would like to group with. Do not include the -d option:
/Applications/DNDLauncher.sh Sienlee /Applications/DNDLauncher.sh Zalim
Remember each character must belong to a different game account. You cannot group with other characters on the same account.
Arrange the windows so that you can click the title bars or edge of each one so that you can bring it forward quickly. Alternatively, you can use the Dock, but that is fairly tedious.
In each of the game windows, you will see a "DungeonHelper" tool. Click on that to reveal a large list of options. The ones you will be using most are "All Follow Me, Now" and "All Stop". The character clicking these buttons will have all nearby characters start to follow the character, or stop following the character respectively.
The "DungeonHelper" tool has a peculiar habit of disappearing from windows from time to time. If you click on the DungeonHelper application, then back into the game window, it will reappear.
The only way to dismiss the options list is to click the "DungeonHelper" tool again. After doing so, it can take a couple clicks in the game window to get things active again. This might be a quirk of macOS, wine, or both. But it's minor inconvenience compared to the incredible time-savings that this tool offers when multiboxing.
Apple's Game Porting Toolkit[edit]
Apple's Game Porting Toolkit uses an older version of wine adapted for 64-bit architectures. It contains custom extensions written by Apple that convert graphics calls into native "Metal" API calls. Metal is a set of interfaces that allow macOS applications to use the underlying hardware for graphics rendering. The net benefit to using these extensions is lower CPU consumption and DX-11 support allowing higher quality rendering options.
These instructions will guide you through installing the Game Porting Toolkit and installing a fresh copy of DDO into a separate wine prefix to avoid contamination with any existing copy of DDO you might already have.
This is a non-trivial exercise. Follow the instructions to the letter. If you receive an unexpected response from the system that you do not understand, you would be well-advised to proceed no further. Feel free to PM Kaytis on the DDO forums for guidance.
Installation[edit]
Open the Terminal and paste the bolded lines one at a time. Hit "return" after each line pasted. Answer affirmatively anytime you are asked to grant permissions or generally proceed forward.
Preparing to install or upgrade the Intel version of the package manager 'brew'[edit]
If you are on Apple Silicon, and even if you already have brew installed, you will need to install or upgrade the Intel version of brew. The Apple Silicon version of brew is installed in /opt/homebrew. The Intel version of brew is installed in /usr/local.
First determine if you are using Apple Silicon. Type this:
uname -a | awk '{print $NF;}'
If you see:
x86_64
you are on an Intel hardware and should skip to the next section "Installing or upgrading the Intel version of the package manager 'brew'".
However, if you see:
arm64
you are using Apple Silicon and must now execute these commands:
# Install the x86_64 emulator. There is a good chance it is already installed. Ignore any errors. softwareupdate --install-rosetta --agree-to-license # Run a new shell emulated in x86_64: arch -x86_64 zsh
You are now running in an x86_64 emulated zshell. This will cause brew to install into /usr/local instead of /opt/homebrew.
Installing or upgrading the Intel version of the package manager 'brew'[edit]
Double check that you are running Intel hardware OR you are in an x86 emulated zshell. Type:
uname -a | awk '{print $NF;}'
This should return:
x86_64
If it does not, then STOP. Something has gone wrong and it will need to be debugged.
If it does, proceed to install or update brew. Type:
which brew
Read the response carefully. If it returns:
/usr/local/bin/brew
then type:
brew upgrade
If it returns anything else, then type the following to install the x86_64 brew package:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
After finishing installation type:
eval "$(/usr/local/bin/brew shellenv)"
Now type:
which brew
This should return:
/usr/local/bin/brew
If it does not, then STOP. Something has gone wrong and it will need to be debugged.
If you just installed brew for the first time, regardless of what platform you are using, type:
echo >> /Users/${USER}/.zprofile echo 'eval "$(/usr/local/bin/brew shellenv)"' >> /Users/${USER}/.zprofile
If you already had brew installed AND you are on Apple Silicon, you can get some semblance of compatibility by having your /Users/${USER}/.zprofile brew entry look like this:
eval "$(/usr/local/bin/brew shellenv)" eval "$(/opt/homebrew/bin/brew shellenv)"
Intel users should have only eval "$(/usr/local/bin/brew shellenv)" as the first line.
If you got this far, you are still running in an x86_64 zshell, and brew is now installed in /usr/local/bin. You should use the validation commands again to be completely certain:
uname -a | awk '{print $NF;}' # Should return "x86_64" which brew # Should return "/usr/local/bin/brew"
Installing the Game Porting Toolkit[edit]
Building Apple's version of the Game Porting Toolkit is currently not even possible without using an older macOS and older tools. Additionally, the provided instructions are unhelpful to say the least. To be fair, virtually every other source of instructions are outdated, misleading and, in many cases simply wrong. Nevertheless, it is possible to install a working copy of the Game Porting Toolkit using prebuilt artifacts:
Install the "gcenx/homebrew-apple" formulae into brew:
brew tap gcenx/homebrew-apple
Install the prebuilt version of the Game Porting Toolkit:
brew install gcenx/wine/game-porting-toolkit
As of writing, the metal extensions being installed by the prebuilt package are more current than the ones being provided by Apple.
For the sake of completeness, create a symbolic link that the Game Porting Toolkit brew formula neglected to:
ln -s "/Applications/Game Porting Toolkit.app/Contents/Resources/wine" /usr/local/opt/game-porting-toolkit
If, and only if, you are on Apple Silicon, you should now exit the x86_64 shell:
exit
Install DDO in a new wine prefix[edit]
We need to create a new "prefix" (Windows "volume" for want of a better word) for exclusive use by Game Porting Toolkit. The Game Porting Toolkit wine version is old and although it appears to work interchangeably with the standard wine installation, it can get confused when switching from DX-11 to DX-9. It is best to keep them separate.
Create the new wine prefix. When you execute this command a dialog will appear. Choose Windows 10, then dismiss the dialog:
WINEPREFIX=~/.wine_gpt /usr/local/bin/wine64 winecfg
Download the DDO installer. You might already have this from the standard instructions, but it is quick, so you can just do it again:
curl https://files.ddo.com/ddo/installers/ddolive.exe --output ~/Downloads/ddolive.exe
Install DDO into the new wine prefix:
WINEPREFIX=~/.wine_gpt /usr/local/bin/wine64 ~/Downloads/ddolive.exe
Playing DDO[edit]
This is how you will launch the game directly from the Terminal going forward:
WINEPREFIX="/Users/${USER}/.wine_gpt" /usr/local/bin/wine64 "C:/Program Files (x86)/StandingStoneGames/Dungeons & Dragons Online/DNDLauncher.exe"