6

Background: tl;dr at the end

League of Legends has a spectator mode, in which you can watch someone else's game (essentially a replay) with a 3 minute delay. Popular LoL website OP.GG has figured out a clever way of hosting these spectator games on their own servers, thereby making them replayable, as opposed to only being available while the game is on (as Riot does it).

If you request a replay from OP.GG, it sends a batch file which looks for where the League is situated and then the magic happens:

    @start "" "League of Legends.exe" "8394" "LoLLauncher.exe" "" "spectator fspectate.op.gg:4081 tjJbtRLQ/HMV7HuAxWV0XsXoRB4OmFBr 1391881421 NA1"

This works fine on Windows. I'm trying to get it to work on Mac (which has an official client).

First I tried running the same command by hand, (split for convenience)

/Applications/ ... /LeagueOfLegends.app/ ... /LeagueofLegends 8393 LoLLauncher \
/Applications/ ... /LolClient spectator fspectate.op.gg:4081 tjJbtRLQ/HMV7HuAxWV0XsXoRB4OmFBr 1391881421 NA1

Running this, however, just starts the LoLLauncher, which closes all the active League processes. The exactly same thing happens if I just call /Applications/ ... /LeagueOfLegends.app/ ... /LeagueofLegends

Next I tried seeing what actually happens when Spectator mode is initiated so I ran $ ps -axf | grep -i lol which showed

UID   PID  PPID   C STIME   TTY           TIME CMD
503  3085     1   0 Wed02pm ??         0:00.00 (LolClient)
503 24607     1   0  9:19am ??         0:00.98 /Applications/League of Legends.app/Contents/LOL/RADS/system/UserKernel.app/Contents/MacOS/UserKernel updateandrun lol_launcher LoLLauncher.app
503 24610 24607   0  9:19am ??         1:08.76 /Applications/League of Legends.app/Contents/LoL/RADS/projects/lol_launcher/releases/0.0.0.122/deploy/LoLLauncher.app/Contents/MacOS/LoLLauncher
503 24611 24610   0  9:19am ??         1:23.02 /Applications/League of Legends.app/Contents/LoL/RADS/projects/lol_air_client/releases/0.0.0.127/deploy/bin/LolClient -runtime .\ -nodebug META-INF\AIR\application.xml .\ -- 8393
503 24927 24610   0  9:44am ??         0:03.37 /Applications/League of Legends.app/Contents/LoL/RADS/solutions/lol_game_client_sln/releases/0.0.0.117/deploy/LeagueOfLegends.app/Contents/MacOS/LeagueofLegends 8394 LoLLauncher /Applications/League of Legends.app/Contents/LoL/RADS/projects/lol_air_client/releases/0.0.0.127/deploy/bin/LolClient spectator 216.133.234.17:8088 Yn1oMX/n3LpXNebibzUa1i3Z+s2HV0ul 1400781241 NA1

Of Interest:

  • there is (LolClient) which I cannot kill by it's PID.
  • UserKernel updateandrun lol_launcher LoLLauncher.app is launched first.
  • LoLLauncher is launched by the UserKernel (as we can see from the PPID)
  • The very long command (PID: 24927) is how Spectator mode is launched, and is also launched by UserKernel.
  • Spectator mode is launched in exactly the same way that the OP.GG .bat wanted to, with the only difference that Spectator mode connects to Riot instead of OP.GG's spectate server.

I tried attaching GDB to the LolClient, but I couldn't get anything meaningful from it since it's an Adobe AIR application (and I've never used GDB with code other than mine own).

Next I ran dtruss -a -b 100m -f -p $PID on everything I could think of: the LolClient, the LolLauncher and the UserKernel and skimmed the half a million lines produced. I found stuff like the GET request used to get the information of the game to spectate, but I could not see any launch of the equivalent of League of Legends.exe with spectator options.

Finally, I ran lsof | grep -i lol to see if anything else was opened in the process, but didn't find anything that seemed appropriate. Open were UserKernel, LolLauncher, LolClient, Adobe AIR, LeagueofLegends and then Bugsplat, all of which are expected. None of this seemed especially relevant to figuring out how LeagueofLegends was opened into spectator mode.

It obviously can be done, since Spectator mode is accessible from within the client. It seems likely that it can be done from the CLI, since Windows can do it and the clients are supposed to equals. Unless I'm missing something in the difference between how UNIX and Windows handle CLI application launches.

My question is if there are any other things I can try to figure out how to launch Spectator mode myself.

tl;dr:

Trying to get into spectator mode from the CLI. It's possible on Windows (see first code block) but it just restarts League on Mac. What else can I try to find what call is made, and how to reproduce it?

EDIT 1: I've found the exact line (in GDB) when the spectator mode gets launched. This is everything that dtruss turned up for that one step.

PS: Please let me know how I can improve this question or its formatting, I'd love to use StackOverflow/SuperUser, but as the guys said on the podcast this week (Ep. 59) it's very intimidating. Sorry for posting this on StackOverflow the first time :(

Alex Popov
  • 183
  • 1
  • 6

2 Answers2

3

Expanding on dossy's answer, I made a bash function to make this easy to do:

spectator() { ( export LOL_APP="/Applications/League*of*Legends.app" && export LOL_GAME_CLIENT_DIR="$LOL_APP/Contents/LoL/RADS/solutions/lol_game_client_sln/releases/*/deploy/LeagueOfLegends.app/Contents/MacOS" && cd $LOL_GAME_CLIENT_DIR && riot_launched=true nohup "$(ls -1 $LOL_GAME_CLIENT_DIR/LeagueofLegends)" 8394 LoLLauncher "$LOL_APP/Contents/LoL/RADS/projects/lol_air_client/releases/*/deploy/bin/LolClient" "spectator $1 $2 $3 $4" >/dev/null 2>&1 & ) }

Add this to your ~/.bash_profile and the spectator function will be available every time you open a new terminal.

I'm doing some wildcard magic to automatically be compatible with future versions too.

Usage:

spectator [ip:port] [token] [number] [region]

To test, use lolquickfind to find a live game of a popular streamer to spectate.

(I had some problems with environment variables + wild cards + directories with spaces. No matter how I escaped the spaces it didn't seem to work. Using wildcards for spaces fixed it though.)

Cheers.

Eric Boehs
  • 236
  • 1
  • 4
  • Excellent job! I'd recommend making it a separate script instead of putting it into the bash_profile. I called mine `spectate.sh` and ran `ln -s /usr/local/bin/spectate` to have it be globally available. I put your code into a script here: http://cl.ly/YWOk – Alex Popov Nov 13 '14 at 23:22
  • I'm assuming future readers not being technical enough to run a script (since you have to be in the right directory or put it in a dir that's in your PATH). Copy/pasting an alias into `~/.bash_profile` will make the `spectator` command work from anywhere. I guess the link works too assuming you keep the script in a safe place that you won't delete. Btw, you may want to add `set -e` to your script as I'm `&&`ing my commands in case a command fails. – Eric Boehs Nov 15 '14 at 15:12
2

I was wondering this same thing, and you were SO close ... but, here's the answer, at least for 4.14.14_08_11_13_42 (see below). FYI, I did a bunch of searching and NO ONE else seems to have figured this out for Mac, so I hope I get credit for being the first ... ;-)

First, start the League client and log in as normal. Then, in terminal:

$ cd "/Applications/League of Legends.app/Contents/LoL/RADS/solutions/lol_game_client_sln/releases/0.0.0.131/deploy/LeagueOfLegends.app/Contents/MacOS"

$ riot_launched=true "/Applications/League of Legends.app/Contents/LoL/RADS/solutions/lol_game_client_sln/releases/0.0.0.131/deploy/LeagueOfLegends.app/Contents/MacOS/LeagueofLegends" 8394 LoLLauncher "/Applications/League of Legends.app/Contents/LoL/RADS/projects/lol_air_client/releases/0.0.0.140/deploy/bin/LolClient" "spectator [ip:port] [token] [number] [region]"

This worked for me. The only problem is after spectating the game, the lobby doesn't reset properly -- the "game has ended" event doesn't seem to register properly when the spectator client is launched this way. So, I just close the app and relaunch ... but, at least this way I can spectate games of people who I'm not "friends" with.

dossy
  • 278
  • 2
  • 7
  • 1
    You win the internet today. I can't believe there's just a variable that stops it from relaunching on passing in arguments. Curious how you found it. Unfortunately, I quit League 2 months ago: it was killing my creativity and giving me insomnia, but this is excellent work. – Alex Popov Aug 27 '14 at 20:16
  • I spectated a game, then did a "ps -Ewww" and looked for the spectator processor. The "-Ewww" giives you the full environment variables for the process. I spotted the "riot_launched=true" and figured that had to be relevant. :-) Oh, and I totally get what you mean about League being a huge time-suck. But, it's great fun, too. – dossy Aug 28 '14 at 22:48
  • I don't imagine either of you have revisited this lately, have you? I tried the above substituting the latest version and it crashes with a "bus error". – pcg79 Oct 23 '14 at 22:10
  • Nevermind, I'm a dummy and must have had a typo. Works great substituting `0.0.0.141` for `0.0.0.131`. Thank you! – pcg79 Oct 23 '14 at 22:21
  • 1
    For anyone else that finds this, turns out I didn't have a typo. I just thought CD'ing into the MacOS dir was optional. It is not. – pcg79 Oct 24 '14 at 03:17