Macintosh / Mac / macOS Support 2024

Kaytis

Member
macOS support on modern 64-bit machines is available again using free software. It would probably take SSG less than a day to update the installer for the Mac client, but since they may not have much incentive to do so, I will provide instructions here for those wanting to try.

It will require using the Terminal (/Applications/Utilities/Terminal). From a high level you will be installing brew, wine, and DDO itself. The game will generally have to be launched from the Terminal whenever you want to play. I do intend to update my old script to skip the launcher and go straight into the game. But first things first.

Note: I have only tested this on Ventura 13.6.4, and only on rather old Intel hardware. There could certainly be complications on different OSes and M1,2,3 hardware.

Installation:

Open the Terminal and paste the following lines one at a time. Hit "return" after each line. Answer "yes" anytime you are asked to allow permission for something (this lets wine apps access necessary folders). You will also need your password one time to remove a "quarantine" flag after wine is installed:

Code:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

brew update

(echo; echo 'eval "$(/usr/local/bin/brew shellenv)"') >> "/Users/$USER/.zprofile"

eval "$(/usr/local/bin/brew shellenv)"

brew install wine-stable

sudo xattr -r -d com.apple.quarantine "/Applications/Wine Stable.app"

curl https://files.ddo.com/ddo/installers/ddolive.exe --output ~/Downloads/ddolive.exe

wine64 ~/Downloads/ddolive.exe

After the game finishes installing, you will be asked to log in. After logging in, the game client will launch. After a few seconds, most of you will be met by a black screen and no further progress. If you have a Touch Bar you will see that something is asking for permission. This is the Terminal asking for permission to use the mic. Go ahead and allow it. For everyone else, press command-option-escape. The client will quit, and you will see the Terminal is indeed asking for permission to use the mic. Go ahead and allow it. I spent several hours trying to find a way to avoid this, unfortunately to no avail. If you had to quit the app, proceed to the next section.

Running the game after installation:

Open the Terminal and paste the following line. Hit "return":

Code:
wine64 ~"/.wine/drive_c/Program Files (x86)/StandingStoneGames/Dungeons & Dragons Online/DNDLauncher.exe"

Happy questing!

Notes
  • I haven't found anything that doesn't work. I have not tried buying anything from the store, but it looks like it should work.
  • In the Launcher there are "Options..." in one of the menus, from which I was able to select a display adapter. I was able to pick the Radeon card in my machine, but I am not convinced it made any difference.
  • Loading into new zones seems a bit choppy the first time. However, going back to the zone seems smooth enough.
  • 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.
  • Wine made all this possible with an update in January 2024 that enabled support for 32-bit Windows apps on 64-bit Macs. This allowed the 32-bit Launcher to run and update the game Client. Interestingly, the game Client has been able to run for years now because it is a 64-bit app. But without the ability to update it easily, there was no way to build a useful script around it.
 
Last edited:

Kaytis

Member
I shouldnt write stuff at 3 in the morning. I have added the lines to install brew. "brew update" is only needed if you had brew installed already, but it wont hurt anything to run it right after a fresh install.
 

GrayJedi AntiProPaladin

Well-known member
Niiiiice write-up! (y)

BTW Please let us know back when u get a chance after try buying DDO points (or subscribing VIP) if successful using WINE... I discovered it doesn't work to buy DDO points in CrossOver by CodeWeavers (another WINE implementation)... and I had to buy DDO points by either using an older Win10 laptop I had around, or run Windows in a VM (it's kinda choppy to actually play like that) to run DDO in order to buy the DDOpts packages... it's Not Optimal/Ideal but it's an OK/"Not too Bad" workaround just for buying the Points...
 

Kaytis

Member
I updated my macOS launching script to improve the help and to default to using the 64 bit client on Catalina and beyond. There are instructions in the text describing how to create the script. It no longer requires any modification. You can run it by typing something like:
Code:
/Applications/DNDLauncher.sh -u username -p password -w world -c character
The option to modify it is still there. I personally use my modified version like so:
Code:
/Applications/DNDLauncher.sh Jolette
This takes me straight into the game, on Orien, as Jolette, bypassing the launcher.

Script follows (first line is '#!/bin/bash'):
Code:
#!/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: Personalize the script
#
# Note that this script cannot update the game. If it fails to work, run the
# original DDO Launcher to allow it to perform any pending updates first.
#
# (C) 2007-2011 SNy <SNy@bmx-chemnitz.de>
#
# AtomicMew     6/3/12
#   -modded to take command line args and windoze use
#
# Kaytis        5/31/13     v 1.1
#   -modded to work on Mac OS X
#
# Kaytis        2/18/15     v 1.2
#   -updated to match new login protocols
#
# Kaytis        11/30/15    v 1.2.1
#   -disabled peer verification to bypass invalid certificates
#
# Kaytis        4/4/19      v 1.3
#   -simplified instructions
#
# Kaytis        10/19/19    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/20     v 1.5
#   -added support for Lamannia world
#   -added support for 64-bit client
#       -x64
# Kaytis        2/10/24     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

#############################################################################
# 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.

#############################################################################
# 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.
#
# Note: 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.
#
# NOTE: If you don't personalize the script, you MUST provide at least the
# username, password and world on the command line.
# Version 1.4 and higher of this script can be customized in EXACTLY
# the same way as earlier versions, including personalizing it per character and
# invoking the script with just the character name (the "-c" is optional).
# If you want to personalize the script please skip to the
# "Personalize the script" section below.

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
    ;;
    -h|--help)
    HELP=YES
    shift # past argument
    ;;
    *)    # unknown option -assume character name
    ARG_CHARACTER="$1"
    shift # past argument
    ;;
esac
done

#############################################################################
# Personalize the script
#
# 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 provide your username, password and primary world here. Note that
# if you choose to enter this information, you should NOT share this file with
# anyone. Note: arguments provided on the command line will 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. No spaces anywhere.
subscription=0                          # Main account subscription. Usually 0.
world=world_name_here                   # Main world name e.g. 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 character names by duplicating an "elif" block.

if [ "${ARG_CHARACTER}" == "alt_account_character_name_here" ] ; then

username=alt_account_username_here      # Alt account username
password=alt_account_password_here      # Alt account password
subscription=0                          # Alt account subscription. Usually 0
world=alt_world_name_here               # Alt server name e.g. Orien

elif [ "${ARG_CHARACTER}" == "alt_account_character_name_here" ] ; then

username=alt_account_username_here      # Alt account username
password=alt_account_password_here      # Alt account password
subscription=0                          # Alt account subscription. Usually 0
world=alt_world_name_here               # Alt server name e.g. Orien

elif [ "${ARG_CHARACTER}" == "alt_account_character_name_here" ] ; then

username=alt_account_username_here      # Alt account username
password=alt_account_password_here      # Alt account password
subscription=0                          # Alt account subscription. Usually 0
world=alt_world_name_here               # Alt server name e.g. Orien

fi

#############################################################################
# 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"
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 ""
echo "Example:"
echo ""
echo "  /Applications/DNDLauncher.sh -u username -p password -w Orien -c Kaytis"
echo ""
echo "or with appropriate script customization (see script for details):"
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, use this command"
echo "to allow the original DDO Launcher to perform any pending updates first:"
echo "wine64 ~\"/.wine/drive_c/Program Files (x86)/StandingStoneGames/Dungeons & Dragons Online/DNDLauncher.exe\""
echo -e "\033[0m"
echo ""
exit 0
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

echo "-----------------------------------------------------------------------"
echo -e "Welcome to the CLI launcher for DDO for Mac OS X version 2.0"
echo "-----------------------------------------------------------------------"

exe() { echo "> $@" ; "$@" ; }

# Some common directories
gameWineBin_DIR="/usr/local/bin"
gameWinePrefix_DIR="$HOME/.wine"

# Production client
gameClient_DIR="$gameWinePrefix_DIR/drive_c/Program Files (x86)/StandingStoneGames/Dungeons & Dragons Online"
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 client
if [ "${world}" == "Lamannia" ] ; then
gameClient_DIR="$gameWinePrefix_DIR/drive_c/Program Files (x86)/StandingStoneGames/Dungeons & Dragons Online (Preview)"
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


# make this script be callable from elsewhere (desktop shortcuts etc)
oldDir=`pwd`

# 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 "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"
    cd "${oldDir}"
    exit
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/"`

echo "Game Name:                    ${game}"
echo "Game Login Server:            ${glsDataCenter_URL}"
echo "-----------------------------------------------------------------------"

# 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

echo "Querying Game Login Server..."

#   wget \
#       --no-check-certificate -q \
#       --header 'Content-Type: text/xml; charset=utf-8' \
#       --header 'SOAPAction: "http://www.turbine.com/SE/GLS/GetDatacenters"' \
#       --post-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}" -O .launcher/GLSDataCenter.config

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 fetch GLS data center configuration.\n"
    cd "${oldDir}"
    exit
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"
    cd "${oldDir}"
    exit
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'
i=0
for name in ${worlds} ; do
    serverNames[$i]=${name}
    i=$(($i + 1))
done
serverNames[$i]="end-of-list"
unset IFS

echo "Launch Configuration Server:  ${launcherCfg_URL}"
echo "Patch Server:                 ${patchServer_ADR}"
echo "Authorization Server:         ${authServer_URL}"
if [[ "${serverNames[0]}" != "end-of-list" ]] ; then
    echo "Available Worlds:             ${serverNames[0]}"
    i=1
    while [[ "${serverNames[$i]}" != "end-of-list" ]] ; do
        echo -e "                              ${serverNames[$i]}"
        i=$(($i + 1))
    done
else
    echo "Available Worlds:             None"
    cd "${oldDir}"
    exit
fi
echo "${world} Chat Server:            ${serverChat_URL}"
echo "${world} Status Server:          ${serverStatus_URL}"
echo "-----------------------------------------------------------------------"

echo "Querying Launch Configuration Server..."

#   wget --no-check-certificate \
#       -q "${launcherCfg_URL}" \
#       -O .launcher/launcher.config

curl --silent \
    --insecure \
    "${launcherCfg_URL}" \
    --output .launcher/launcher.config

if ! [ -r .launcher/launcher.config ] ; then
    echo -e "\nError: Could not fetch dynamic launcher configuration.\n"
    cd "${oldDir}"
    exit
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/"`

echo "World Queue Server:           ${worldQueue_URL}"
echo "World Queue Args:             ${worldQueue_ARGTMPL}"
echo "GLS Ticket Lifetime:          ${glsTicketLifetime}"
echo "Support URL:                  ${support_URL}"
echo "Bug URL:                      ${bug_URL}"
echo "Support Service URL:          ${supportService_URL}"
echo "Game Client File:             ${gameClient_FILE}"
echo "Game Client Args:             ${gameClient_ARGTMPL}"
echo "-----------------------------------------------------------------------"

########### CHOOSE LANGUAGE
languages[0]=english
selectedLanguage=0

########### PATCHING
# Note: This script cannot patch the game. Use the Launcher to do that.

########### 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...

    #    wget \
    #        --no-check-certificate -q \
    #        --header 'Content-Type: text/xml; charset=utf-8' \
    #        --header 'SOAPAction: "http://www.turbine.com/SE/GLS/LoginAccount"' \
    #        --post-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}" -O .launcher/GLSAuthServer.config

    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"
        cd "${oldDir}"
        exit
    fi
}

echo "Requesting account details and authentication token from the Authorization Server..."

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'
i=0
for sub in ${subNames} ; do
    subs[$i]="${sub}"
    i=$(($i + 1))
done
subs[$i]="end-of-list"
i=0
for desc in ${subDescs} ; do
    descs[$i]="${desc}"
    i=$(($i + 1))
done
descs[$i]="end-of-list"
unset IFS

# Check for at least one active subscription.
if [[ $i == 0 ]] ; then
    echo -e "\nError: There appears to be no subscription for ${game}.\n"
    cd "${oldDir}"
    exit
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 authetication token from the Authorization Server response.\n"
    cd "${oldDir}"
    exit
fi

if [[ "${subs[0]}" != "end-of-list" ]] ; then
    echo "Subscription Names:           ${subs[0]}"
    i=1
    while [[ "${subs[$i]}" != "end-of-list" ]] ; do
        echo -e "                              ${subs[$i]}"
        i=$(($i + 1))
    done
else
    echo "Subscription Names:           None"
    cd "${oldDir}"
    exit
fi
if [[ "${descs[0]}" != "end-of-list" ]] ; then
    echo "Subscription Descriptions:    ${descs[0]}"
    i=1
    while [[ "${descs[$i]}" != "end-of-list" ]] ; do
        echo -e "                              ${descs[$i]}"
        i=$(($i + 1))
    done
else
    echo "Subscription Descriptions:    None"
    cd "${oldDir}"
    exit
fi
echo "Authentication Token:         ${glsTicket}"
echo "-----------------------------------------------------------------------"

# 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 [[ $i > 1 && "${subscription}" == "" ]] ; then
        i=0
        echo "You have the following subscriptions for $game:"
        while [[ "${subs[$i]}" != "end-of-list" ]] ; do
            echo -e "    $i: ${subs[$i]}\t'${descs[$i]}'"
            i=$(($i + 1))
        done
        echo -n "Please select the one you wish to use (enter the number on the left): "
        read subscription
    else
        subscription=0
    fi
fi

echo "Requesting world information for ${world} from the Status Server..."

# 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

#   wget --no-check-certificate \
#       -q "$serverStatus_URL" \
#       -O .launcher/server.config

curl --silent \
    --insecure \
    "$serverStatus_URL" \
    --output .launcher/server.config

# 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"
    cd "${oldDir}"
    exit
fi

# Create array of servers
IFS=";"
i=0
for adr in ${loginServers} ; do
    serverAddresses[$i]="${adr}"
    i=$(($i + 1))
done
serverAddresses[$i]="end-of-list"
i=0
for adr in ${queueUrls} ; do
    serverQueues[$i]="${adr}"
    i=$(($i + 1))
done
serverQueues[$i]="end-of-list"
unset IFS

# 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
serverAddress="${serverAddresses[0]}"
serverQueue="${serverQueues[0]}"

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/\&amp\;/\&/g"`

if [[ "${serverAddresses[0]}" != "end-of-list" ]] ; then
    echo "${world} Login Servers:          ${serverAddresses[0]}"
    i=1
    while [[ "${serverAddresses[$i]}" != "end-of-list" ]] ; do
        echo -e "                              ${serverAddresses[$i]}"
        i=$(($i + 1))
    done
else
    echo "${world} Login Servers:          None"
    cd "${oldDir}"
    exit
fi
if [[ "${serverQueues[0]}" != "end-of-list" ]] ; then
    echo "${world} Login Servers:          ${serverQueues[0]}"
    i=1
    while [[ "${serverQueues[$i]}" != "end-of-list" ]] ; do
        echo -e "                              ${serverQueues[$i]}"
        i=$(($i + 1))
    done
else
    echo "${world} Login Servers:          None"
    cd "${oldDir}"
    exit
fi
echo "Selecting queue:              ${serverQueue}"
echo "Queue Arguments:              ${worldQueue_ARGS}"
echo "-----------------------------------------------------------------------"

########### 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

        #   wget --no-check-certificate \
        #       -q --post-data="${worldQueue_ARGS}" \
        #       "${worldQueue_URL}" \
        #       -O .launcher/WorldQueue.config

        curl --silent \
            --insecure \
            --data "${worldQueue_ARGS}" \
            "${worldQueue_URL}" \
            --output .launcher/WorldQueue.config
        if ! [ -r .launcher/WorldQueue.config ] ; then
            echo -e "\nError: World login queue request failed.\n"
            cd "${oldDir}"
            exit
        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."
            cd "${oldDir}"
            exit
        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
}

echo "Joining ${world} Login Server queue..."

# 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."

# OK, so 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 "Launching client:"
echo -e "\033[0;32m"
echo "WINEPREFIX=\"$gameWinePrefix_DIR\" \"$gameWineBin_DIR/wine64\" \"$gameClient_EXE\" ${gameClient_ARGS} &>/dev/null &"
echo -e "\033[0m"
WINEPREFIX="$gameWinePrefix_DIR" "$gameWineBin_DIR"/wine64 "$gameClient_EXE" ${gameClient_ARGS} &>/dev/null &
disown

echo "-----------------------------------------------------------------------"
echo "Done. Please allow the DDO client a few seconds to launch."
echo "-----------------------------------------------------------------------"

# Get back to where the caller was
cd "${oldDir}"

exit
 
Last edited:
Top