Skip to content

Commit d47ff55

Browse files
committed
Added reconnect and swagger auto-patching
1 parent 5487369 commit d47ff55

File tree

13 files changed

+267
-118
lines changed

13 files changed

+267
-118
lines changed

README.md

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,7 @@
22

33
----
44

5-
Always up to date documentation for the League of Legends client API.
6-
7-
### Post v8.14 requirements
8-
Since League of Legends v8.14 RIOT hid some developer functionality behind a flag. To make Rift Explorer work with newer versions of the game you need to manually enable the *swagger endpoint* by hand.
9-
10-
To enable *swagger* you need to add the `enable_swagger: true` at the start of the `system.yaml` file before starting the LCU client.
11-
12-
![Swagger explainer](assets/swagger.png?raw=true)
13-
14-
The file can be found here based on your OS
15-
16-
- **Windows**
17-
18-
`C:\Riot Games\League of Legends\RADS\projects\league_client\releases\<LATEST_VERSION>\deploy\system.yaml`
19-
20-
- **MacOS**
21-
22-
`/Applications/League of Legends.app/Contents/LoL/RADS/projects/league_client/releases/<LATEST_VERSION>/deploy/system.yaml`
23-
24-
Some regions may have a different directory structure but the general gist is the same.
25-
26-
**This file will need to be updated everytime there's a new LCU version.**
5+
Always up to date documentation for the League Client API.
276

287
### Prebuilt
298

app.js

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,103 @@
11
const electron = require('electron');
22
const LCUConnector = require('lcu-connector');
3+
const fs = require('fs-extra');
4+
const path = require('path');
5+
const yaml = require('yaml');
6+
const { getLCUPathFromProcess } = require('./util');
37

4-
const connector = new LCUConnector('');
8+
const connector = new LCUConnector();
59
const { app } = electron;
610
const { BrowserWindow } = electron;
711
const root = __dirname + '/app';
812

9-
let mainWindow = null;
10-
1113
app.commandLine.appendSwitch('--ignore-certificate-errors');
1214

1315
app.on('ready', () => {
16+
let mainWindow = null;
17+
let windowLoaded = false;
18+
let LCUData = null;
19+
let swaggerDisabled = true;
1420

1521
mainWindow = new BrowserWindow({
1622
center: true,
1723
height: 720,
1824
show: false,
19-
title: 'Rift Explorer',
20-
width: 1280
25+
width: 1280,
26+
title: 'Rift Explorer'
2127
});
2228

29+
// Check if dev env FIXME
30+
// mainWindow.openDevTools();
31+
2332
// Remove default menu
2433
mainWindow.setMenu(null);
2534
mainWindow.loadURL('file://' + root + '/index.html');
2635

27-
// Check if dev env FIXME
28-
// mainWindow.openDevTools();
29-
3036
// Avoid white page on load.
3137
mainWindow.webContents.on('did-finish-load', () => {
32-
connector.on('connect', async (data) => {
33-
mainWindow.webContents.send('lcu-load', data);
34-
});
38+
windowLoaded = true;
3539

36-
connector.start();
3740
mainWindow.show();
41+
42+
if (!LCUData) {
43+
return;
44+
}
45+
46+
mainWindow.webContents.send(swaggerDisabled ? 'swagger-disabled' : 'swagger-enabled');
47+
mainWindow.webContents.send('lcu-load', LCUData);
3848
});
3949

4050
mainWindow.on('closed', () => {
4151
mainWindow = null;
4252
});
53+
54+
connector.on('connect', async (data) => {
55+
// During multiple restarts of the client the backend server is not instantly
56+
// ready to serve requests so we delay a bit
57+
setTimeout(async () => {
58+
LCUData = data;
59+
60+
try {
61+
const LCUPath = await getLCUPathFromProcess();
62+
const systemFile = path.join(LCUPath, 'system.yaml');
63+
64+
// File doesn't exist, do nothing
65+
if (!(await fs.pathExists(systemFile))) {
66+
throw new Error('system.yaml not found');
67+
}
68+
69+
const file = await fs.readFile(systemFile, 'utf8');
70+
const fileParsed = yaml.parse(file);
71+
72+
swaggerDisabled = !fileParsed.enable_swagger;
73+
mainWindow.webContents.send(swaggerDisabled ? 'swagger-disabled' : 'swagger-enabled');
74+
75+
if (!fileParsed.enable_swagger) {
76+
fileParsed.enable_swagger = true;
77+
const stringifiedFile = yaml.stringify(fileParsed);
78+
79+
// Rito's file is prefixed with --- newline
80+
await fs.outputFile(systemFile, `---\n${stringifiedFile}`);
81+
}
82+
83+
} catch (error) {
84+
console.error(error);
85+
// No error handling for now
86+
}
87+
88+
mainWindow.webContents.send('lcu-load', LCUData);
89+
}, 5000);
90+
});
91+
92+
connector.on('disconnect', () => {
93+
LCUData = null;
94+
95+
if (windowLoaded) {
96+
mainWindow.webContents.send('lcu-unload');
97+
}
98+
});
99+
100+
connector.start();
43101
});
44102

45103
app.on('window-all-closed', () => {

app/css/style.css

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ html {
44
color: hsla(0, 0%, 0%, 0.87);
55
}
66

7+
.swagger-section .logo {
8+
width: 54px;
9+
height: 54px;
10+
display: block;
11+
float: left;
12+
margin: -6px 12px 0 0;
13+
}
14+
715
.swagger-section .github {
816
width: 24px;
917
height: 24px;
@@ -55,6 +63,10 @@ html {
5563
text-shadow: rgba(0, 0, 0, 0.15) 0 0 1px;
5664
}
5765

66+
.swagger-section .swagger-ui-wrap .info_title {
67+
padding-bottom: 0;
68+
}
69+
5870
@media (max-width: 960px) {
5971
.swagger-section #swagger-ui-container {
6072
margin: 24px auto;

app/fonts/DroidSans-Bold.ttf

0 Bytes
Binary file not shown.

app/fonts/DroidSans.ttf

0 Bytes
Binary file not shown.

app/images/logo.png

58 KB
Loading

app/index.html

Lines changed: 126 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,137 @@
1-
21
<!DOCTYPE html>
32
<html>
4-
<head>
5-
<meta charset="UTF-8">
6-
<title>Rift Explorer</title>
7-
<link rel="icon" type="image/png" href="images/favicon-32x32.png" sizes="32x32" />
8-
<link rel="icon" type="image/png" href="images/favicon-16x16.png" sizes="16x16" />
9-
<link href='css/typography.css' media='screen' rel='stylesheet' type='text/css'/>
10-
<link href='css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
11-
<link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
12-
<link href='css/reset.css' media='print' rel='stylesheet' type='text/css'/>
13-
<link href='css/print.css' media='print' rel='stylesheet' type='text/css'/>
14-
<link href='css/style.css' rel='stylesheet' type='text/css'/>
15-
<script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
16-
<script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
17-
<script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
18-
<script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
19-
<script src='lib/handlebars-2.0.0.js' type='text/javascript'></script>
20-
<script src='lib/underscore-min.js' type='text/javascript'></script>
21-
<script src='lib/backbone-min.js' type='text/javascript'></script>
22-
<script src='swagger-ui.js' type='text/javascript'></script>
23-
<script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>
24-
<script src='lib/jsoneditor.min.js' type='text/javascript'></script>
25-
<script src='lib/marked.js' type='text/javascript'></script>
26-
<script src='lib/swagger-oauth.js' type='text/javascript'></script>
27-
28-
29-
<script type="text/javascript">
30-
31-
const IPC = require('electron').ipcRenderer;
32-
const shell = require('electron').shell;
33-
const REMOTE = require('electron').remote;
34-
35-
IPC.on('lcu-load', (event, data) => {
36-
37-
const { username, password, address, port } = data;
38-
const header = `Basic ${btoa(`${username}:${password}`)}`;
39-
const auth = new SwaggerClient.ApiKeyAuthorization("Authorization", header, "header");
40-
41-
window.swaggerUi = new SwaggerUi({
42-
validatorUrl: null,
43-
url: `https://${username}:${password}@${address}:${port}/swagger/v2/swagger.json`,
44-
authorizations: { riot: auth },
45-
dom_id: "swagger-ui-container",
46-
onComplete(swaggerApi, swaggerUi) {
47-
$('pre code').each(function(i, e) {
48-
hljs.highlightBlock(e)
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Rift Explorer</title>
6+
<link rel="icon" type="image/png" href="images/favicon-32x32.png" sizes="32x32" />
7+
<link rel="icon" type="image/png" href="images/favicon-16x16.png" sizes="16x16" />
8+
<link href='css/typography.css' media='screen' rel='stylesheet' type='text/css'/>
9+
<link href='css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
10+
<link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
11+
<link href='css/reset.css' media='print' rel='stylesheet' type='text/css'/>
12+
<link href='css/print.css' media='print' rel='stylesheet' type='text/css'/>
13+
<link href='css/style.css' rel='stylesheet' type='text/css'/>
14+
<script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
15+
<script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
16+
<script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
17+
<script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
18+
<script src='lib/handlebars-2.0.0.js' type='text/javascript'></script>
19+
<script src='lib/underscore-min.js' type='text/javascript'></script>
20+
<script src='lib/backbone-min.js' type='text/javascript'></script>
21+
<script src='swagger-ui.js' type='text/javascript'></script>
22+
<script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>
23+
<script src='lib/jsoneditor.min.js' type='text/javascript'></script>
24+
<script src='lib/marked.js' type='text/javascript'></script>
25+
<script src='lib/swagger-oauth.js' type='text/javascript'></script>
26+
27+
</head>
28+
29+
<body class="swagger-section">
30+
<div id="message-bar" class="swagger-ui-wrap" data-sw-translate>Waiting for League to start...</div>
31+
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
32+
33+
<script type="text/javascript">
34+
const IPC = require('electron').ipcRenderer;
35+
const shell = require('electron').shell;
36+
const pkg = require('../package');
37+
38+
const swaggerContainer = document.querySelector('#swagger-ui-container');
39+
const messageBar = document.querySelector('#message-bar');
40+
41+
let LCUData = null;
42+
let swaggerDisabled = true;
43+
let loadingSwagger = false;
44+
45+
// We all love global variables
46+
window.RE = {
47+
info: {
48+
title: 'Rift Explorer',
49+
description: pkg.description,
50+
version: pkg.version,
51+
}
52+
};
53+
54+
const loadSwagger = () => {
55+
const { username, password, address, port } = LCUData;
56+
const header = `Basic ${btoa(`${username}:${password}`)}`;
57+
const auth = new SwaggerClient.ApiKeyAuthorization("Authorization", header, "header");
58+
59+
loadingSwagger = true;
60+
61+
window.swaggerUi = new SwaggerUi({
62+
validatorUrl: null,
63+
url: `https://${username}:${password}@${address}:${port}/swagger/v2/swagger.json`,
64+
authorizations: { riot: auth },
65+
dom_id: "swagger-ui-container",
66+
onComplete() {
67+
loadingSwagger = false;
68+
69+
$('pre code').each((i, e) => {
70+
hljs.highlightBlock(e);
71+
});
72+
73+
window.swaggerUi.api.clientAuthorizations.add("riot", auth);
74+
},
75+
// Failure has been taken care of in the swagger-ui file
76+
docExpansion: "none",
77+
jsonEditor: false,
78+
apisSorter: "alpha",
79+
defaultModelRendering: 'schema',
80+
showRequestHeaders: true
4981
});
5082

51-
window.swaggerUi.api.clientAuthorizations.add("riot", auth);
52-
},
53-
onFailure(error) {
54-
// alert('Couldn\'t load the swagger file. Make sure swagger is enabled. More info at https://github.com/Pupix/rift-explorer');
55-
},
56-
docExpansion: "none",
57-
jsonEditor: false,
58-
apisSorter: "alpha",
59-
defaultModelRendering: 'schema',
60-
showRequestHeaders: true
83+
window.swaggerUi.load();
84+
};
85+
86+
IPC.on('lcu-load', (event, data) => {
87+
LCUData = data;
88+
89+
if (!swaggerDisabled && !loadingSwagger) {
90+
loadSwagger();
91+
}
92+
});
93+
94+
IPC.on('lcu-unload', () => {
95+
LCUData = null;
96+
swaggerContainer.innerHTML = '';
97+
messageBar.innerHTML = 'Waiting for League to start...';
6198
});
6299

63-
window.swaggerUi.load();
64-
});
100+
IPC.on('swagger-disabled', () => {
101+
swaggerDisabled = true;
102+
messageBar.innerHTML = 'API has been auto-enabled. Please restart the League Client';
65103

66-
function _handleGithubLink(event) {
67-
shell.openExternal('https://github.com/pupix/rift-explorer');
68-
}
104+
// Allow the DOM to render the new HTML
105+
setTimeout(() => {
106+
const allowedRestart = confirm('Rift Explorer needs to restart the League Client to enable the API documentation');
69107

70-
function _handleDiscordLink(event) {
71-
shell.openExternal('https://discord.gg/hPtrMcx');
72-
}
108+
if (allowedRestart) {
109+
const { username, password, address, port } = LCUData;
110+
111+
fetch(`https://${address}:${port}/process-control/v1/process/restart?delaySeconds=0`, {
112+
method: 'POST',
113+
headers: new Headers({ Authorization: `Basic ${btoa(`${username}:${password}`)}` })
114+
});
115+
}
116+
}, 100);
117+
});
118+
119+
IPC.on('swagger-enabled', () => {
120+
swaggerDisabled = false;
121+
messageBar.innerHTML = 'Waiting for League to start...';
122+
123+
if (LCUData && !loadingSwagger) {
124+
loadSwagger();
125+
}
126+
});
73127

74-
</script>
75-
</head>
128+
function _handleGithubLink(event) {
129+
shell.openExternal('https://github.com/pupix/rift-explorer');
130+
}
76131

77-
<body class="swagger-section">
78-
<div id="message-bar" class="swagger-ui-wrap" data-sw-translate>Waiting for League to start...</div>
79-
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
80-
</body>
132+
function _handleDiscordLink(event) {
133+
shell.openExternal('https://discord.gg/hPtrMcx');
134+
}
135+
</script>
136+
</body>
81137
</html>

0 commit comments

Comments
 (0)