-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🔌 Support for Garena client and Windows Boot options (#3)
* Added some clear error messages, made sure application runs only once. * Minor style changes * Added support for Garena client, and renamed Utils to LeagueMonitor for slight clarity * Fix error where League client has to be running, plus make the popups less obnoxious * Added option to start on boot. - Startup message is surpressed when the setting is on. - Checks whether the app is moved if the setting is on, and sets the new app location as the startup application * Most minor nitpick ever * 🎉 Version bump to 1.1.0
- Loading branch information
1 parent
5c7bb96
commit 02825ee
Showing
4 changed files
with
276 additions
and
142 deletions.
There are no files selected for viewing
335 changes: 202 additions & 133 deletions
335
conduit/Utils.cs → conduit/LeagueMonitor.cs
100755 → 100644
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,133 +1,202 @@ | ||
using Microsoft.WindowsAPICodePack.Dialogs; | ||
using System; | ||
using System.IO; | ||
using System.Text; | ||
using System.Windows.Forms; | ||
|
||
namespace MimicConduit | ||
{ | ||
static class Utils | ||
{ | ||
private static string dataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Mimic"); | ||
|
||
static Utils() | ||
{ | ||
if (!Directory.Exists(dataDir)) Directory.CreateDirectory(dataDir); | ||
} | ||
|
||
/** | ||
* Utility method that calls the specified argument functions whenever League starts or stops. | ||
* This is done by observing the lockfile, and the start function gets the contents of the lockfile as a param. | ||
*/ | ||
public static void MonitorLeague(string lcuExecutablePath, Action<string> onStart, Action onStop) | ||
{ | ||
var leagueDir = Path.GetDirectoryName(lcuExecutablePath) + "\\"; | ||
|
||
var watcher = new FileSystemWatcher(); | ||
watcher.Path = leagueDir; | ||
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastAccess; | ||
watcher.Filter = "lockfile"; | ||
|
||
var active = false; | ||
|
||
// We use the same handler for both lockfile creation and lockfile edits. | ||
// Sometimes we are so fast that the file is still empty when we attempt to read it. | ||
// This way, it will be caught in the edit event instead. | ||
FileSystemEventHandler createdEditHandler = (o, e) => | ||
{ | ||
if (active) return; | ||
var contents = ReadLockfile(leagueDir); | ||
if (!contents.Equals("")) | ||
{ | ||
onStart(contents); | ||
active = true; | ||
} | ||
}; | ||
|
||
watcher.Created += createdEditHandler; | ||
watcher.Changed += createdEditHandler; | ||
|
||
watcher.Deleted += (o, e) => | ||
{ | ||
// Lockfile was deleted, notify. | ||
onStop(); | ||
active = false; | ||
}; | ||
|
||
watcher.EnableRaisingEvents = true; | ||
|
||
// Check if we launched while league was already active. | ||
if (File.Exists(leagueDir + "\\lockfile")) | ||
{ | ||
active = true; | ||
onStart(ReadLockfile(leagueDir)); | ||
} | ||
} | ||
|
||
/** | ||
* Reads the lockfile at the specified location. The lockfile needs to be copied first, since it is locked (heh). | ||
*/ | ||
private static string ReadLockfile(string leagueLocation) | ||
{ | ||
// The lockfile is locked (heh), so we copy it first. | ||
File.Copy(leagueLocation + "lockfile", leagueLocation + "lockfile-temp"); | ||
var contents = File.ReadAllText(leagueLocation + "lockfile-temp", Encoding.UTF8); | ||
File.Delete(leagueLocation + "lockfile-temp"); | ||
return contents; | ||
} | ||
|
||
/** | ||
* Either gets the LCU path from the saved properties, or by prompting the user. | ||
* Returns null if the user does not want to select a folder. | ||
*/ | ||
public static string GetLCUPath() | ||
{ | ||
string configPath = Path.Combine(dataDir, "lcuPath"); | ||
string path = File.Exists(configPath) ? File.ReadAllText(configPath) : "C:/Riot Games/League of Legends/LeagueClient.exe"; | ||
|
||
while (!IsValidLCUPath(path)) | ||
{ | ||
// Notify that the path is invalid. | ||
MessageBox.Show( | ||
"Mimic could not find the League client at " + path + ". Please select the location of 'LeagueClient.exe' manually.", | ||
"LCU not found", | ||
MessageBoxButtons.OK, | ||
MessageBoxIcon.Exclamation | ||
); | ||
|
||
// Ask for new path. | ||
CommonOpenFileDialog dialog = new CommonOpenFileDialog(); | ||
dialog.Title = "Select LeagueClient.exe location."; | ||
dialog.InitialDirectory = "C:\\Riot Games\\League of Legends"; | ||
dialog.EnsureFileExists = true; | ||
dialog.EnsurePathExists = true; | ||
dialog.DefaultFileName = "LeagueClient"; | ||
dialog.DefaultExtension = "exe"; | ||
dialog.Filters.Add(new CommonFileDialogFilter("Executables", ".exe")); | ||
dialog.Filters.Add(new CommonFileDialogFilter("All Files", ".*")); | ||
if (dialog.ShowDialog() == CommonFileDialogResult.Cancel) | ||
{ | ||
// User wants to cancel. Exit | ||
return null; | ||
} | ||
|
||
path = dialog.FileName; | ||
} | ||
|
||
// Store choice so we don't have to ask for it again. | ||
File.WriteAllText(configPath, path); | ||
|
||
return path; | ||
} | ||
|
||
/** | ||
* Checks if the provided path is most likely a path where the LCU is installed. | ||
*/ | ||
private static bool IsValidLCUPath(string path) | ||
{ | ||
string folder = Path.GetDirectoryName(path); | ||
return File.Exists(path) && Directory.Exists(folder + "/RADS") && Directory.Exists(folder + "/RADS/projects/league_client"); | ||
} | ||
} | ||
} | ||
using Microsoft.WindowsAPICodePack.Dialogs; | ||
using System; | ||
using System.Diagnostics; | ||
using System.Management; | ||
using System.IO; | ||
using System.Text; | ||
using System.Windows.Forms; | ||
using System.ComponentModel; | ||
using System.Linq; | ||
|
||
namespace MimicConduit | ||
{ | ||
class LeagueMonitor | ||
{ | ||
private static string dataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Mimic"); | ||
private FileSystemWatcher watcher = new FileSystemWatcher(); | ||
|
||
static LeagueMonitor() | ||
{ | ||
if (!Directory.Exists(dataDir)) Directory.CreateDirectory(dataDir); | ||
} | ||
|
||
/** | ||
* Utility method that calls the specified argument functions whenever League starts or stops. | ||
* This is done by observing the lockfile, and the start function gets the contents of the lockfile as a param. | ||
*/ | ||
public LeagueMonitor(string lcuExecutablePath, Action<string> onStart, Action onStop) | ||
{ | ||
var leagueDir = Path.GetDirectoryName(lcuExecutablePath) + "\\"; | ||
|
||
watcher.Path = leagueDir; | ||
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastAccess; | ||
watcher.Filter = "lockfile"; | ||
|
||
var active = false; | ||
|
||
// We use the same handler for both lockfile creation and lockfile edits. | ||
// Sometimes we are so fast that the file is still empty when we attempt to read it. | ||
// This way, it will be caught in the edit event instead. | ||
FileSystemEventHandler createdEditHandler = (o, e) => | ||
{ | ||
if (active) return; | ||
var contents = ReadLockfile(leagueDir); | ||
if (!contents.Equals("")) | ||
{ | ||
onStart(contents); | ||
active = true; | ||
} | ||
}; | ||
|
||
watcher.Created += createdEditHandler; | ||
watcher.Changed += createdEditHandler; | ||
|
||
watcher.Deleted += (o, e) => | ||
{ | ||
// Lockfile was deleted, notify. | ||
onStop(); | ||
active = false; | ||
}; | ||
|
||
watcher.EnableRaisingEvents = true; | ||
|
||
// Check if we launched while league was already active. | ||
if (File.Exists(leagueDir + "\\lockfile")) | ||
{ | ||
active = true; | ||
onStart(ReadLockfile(leagueDir)); | ||
} | ||
} | ||
|
||
/** | ||
* Reads the lockfile at the specified location. The lockfile needs to be copied first, since it is locked (heh). | ||
*/ | ||
private static string ReadLockfile(string leagueLocation) | ||
{ | ||
// The lockfile is locked (heh), so we copy it first. | ||
File.Copy(leagueLocation + "lockfile", leagueLocation + "lockfile-temp"); | ||
var contents = File.ReadAllText(leagueLocation + "lockfile-temp", Encoding.UTF8); | ||
File.Delete(leagueLocation + "lockfile-temp"); | ||
return contents; | ||
} | ||
|
||
/** | ||
* Either gets the LCU path from the saved properties, or by prompting the user. | ||
* Returns null if the user does not want to select a folder. | ||
*/ | ||
public static string GetLCUPath() | ||
{ | ||
string configPath = Path.Combine(dataDir, "lcuPath"); | ||
string path = File.Exists(configPath) ? File.ReadAllText(configPath) : "C:/Riot Games/League of Legends/LeagueClient.exe"; | ||
|
||
if (!IsValidLCUPath(path)) | ||
{ | ||
var leaguePath = GetLCUPathWithRunningLeagueClient(); | ||
if (leaguePath == null) | ||
{ | ||
// Ask the user to run the League client | ||
MessageBox.Show( | ||
"Mimic could not find the League client at " + path + ". Make sure the League client runs, and press OK.", | ||
"LCU not found", | ||
MessageBoxButtons.OK, | ||
MessageBoxIcon.Exclamation | ||
); | ||
|
||
// Now try again | ||
leaguePath = GetLCUPathWithRunningLeagueClient(); | ||
} | ||
|
||
path = leaguePath != null ? leaguePath + "LeagueClient.exe" : path; | ||
|
||
// Store choice so we don't have to look for it again. | ||
if (IsValidLCUPath(path)) | ||
File.WriteAllText(configPath, path); | ||
} | ||
|
||
while (!IsValidLCUPath(path)) | ||
{ | ||
// Notify that the path is invalid. | ||
MessageBox.Show( | ||
"Mimic could not find the running League client. Please select the location of 'LeagueClient.exe' manually.", | ||
"LCU not found", | ||
MessageBoxButtons.OK, | ||
MessageBoxIcon.Exclamation | ||
); | ||
|
||
// Ask for new path. | ||
CommonOpenFileDialog dialog = new CommonOpenFileDialog(); | ||
dialog.Title = "Select LeagueClient.exe location."; | ||
dialog.InitialDirectory = "C:\\Riot Games\\League of Legends"; | ||
dialog.EnsureFileExists = true; | ||
dialog.EnsurePathExists = true; | ||
dialog.DefaultFileName = "LeagueClient"; | ||
dialog.DefaultExtension = "exe"; | ||
dialog.Filters.Add(new CommonFileDialogFilter("Executables", ".exe")); | ||
dialog.Filters.Add(new CommonFileDialogFilter("All Files", ".*")); | ||
if (dialog.ShowDialog() == CommonFileDialogResult.Cancel) | ||
{ | ||
// User wants to cancel. Exit | ||
return null; | ||
} | ||
|
||
path = dialog.FileName; | ||
|
||
// Store choice so we don't have to ask for it again. | ||
File.WriteAllText(configPath, path); | ||
} | ||
|
||
return path; | ||
} | ||
|
||
/* | ||
* Find the league client location by checking for the process, | ||
* then look for an argument with the --install-directory, | ||
* in which the lockfile should be placed. | ||
*/ | ||
public static string GetLCUPathWithRunningLeagueClient() | ||
{ | ||
var leagueProcesses = Process.GetProcesses().Where(p => p.ProcessName.Contains("League")); | ||
foreach (var process in leagueProcesses) | ||
{ | ||
try | ||
{ | ||
string commandLine = process.GetCommandLine(); | ||
var indexOfInstallDirectory = commandLine.IndexOf("--install-directory"); | ||
if (indexOfInstallDirectory == -1) | ||
continue; | ||
|
||
// Index started at "--league-directory=", but we now go to the start of the directory in the string | ||
indexOfInstallDirectory = commandLine.IndexOf("=", indexOfInstallDirectory) + 1; | ||
|
||
// Take everything until the " behind the directory | ||
return commandLine.Substring(indexOfInstallDirectory, commandLine.IndexOf("\"", indexOfInstallDirectory) - indexOfInstallDirectory); | ||
} | ||
catch (Win32Exception ex) when ((uint)ex.ErrorCode == 0x80004005) | ||
{ | ||
// Intentionally empty. | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
/** | ||
* Checks if the provided path is most likely a path where the LCU is installed. | ||
*/ | ||
private static bool IsValidLCUPath(string path) | ||
{ | ||
try | ||
{ | ||
if (String.IsNullOrEmpty(path)) | ||
return false; | ||
|
||
string folder = Path.GetDirectoryName(path); | ||
return File.Exists(folder + "/LeagueClient.exe") && Directory.Exists(folder + "/Config") && Directory.Exists(folder + "/Logs"); | ||
} | ||
catch | ||
{ | ||
return false; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
using System.Diagnostics; | ||
using System.Management; | ||
using System.Text; | ||
|
||
namespace MimicConduit | ||
{ | ||
public static class ProcessExtensions | ||
{ | ||
public static string GetCommandLine(this Process process) | ||
{ | ||
var commandLine = new StringBuilder(process.MainModule.FileName); | ||
|
||
commandLine.Append(" "); | ||
using (var searcher = new ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + process.Id)) | ||
{ | ||
foreach (var @object in searcher.Get()) | ||
{ | ||
commandLine.Append(@object["CommandLine"]); | ||
commandLine.Append(" "); | ||
} | ||
} | ||
|
||
return commandLine.ToString(); | ||
} | ||
} | ||
} |
Oops, something went wrong.