From 10e59f084110bcbe6c4b5e0759bad57df4797ff2 Mon Sep 17 00:00:00 2001 From: Chris Bessent Date: Fri, 1 Oct 2021 17:17:17 -0700 Subject: [PATCH] Add Mass Effect Legendary Edition plugin All the weird stuff here is done to get around the Origin DRM. The only way I've found to make MO2 and Origin play nicely together is to run Origin through MO2. Therefore, the plugin makes sure Origin is dead when running an executable. Then because Origin is still alive and hooked by MO2 (probably bad), it tries to kill Origin after the game is closed. However, the onFinishedRun callback is only called when all hooked processes are closed so a a thread is used to monitor the processes. Psutil is included from PyPi for accessing process data. --- games/game_masseffectlegendary.py | 81 +++++++++++++++++++++++++++++++ plugin-requirements.txt | 1 + setup.cfg | 4 ++ 3 files changed, 86 insertions(+) create mode 100644 games/game_masseffectlegendary.py diff --git a/games/game_masseffectlegendary.py b/games/game_masseffectlegendary.py new file mode 100644 index 0000000..ff28e07 --- /dev/null +++ b/games/game_masseffectlegendary.py @@ -0,0 +1,81 @@ +from ..basic_game import BasicGame + +import mobase +import sys +import psutil +import threading +import time + + +class MassEffectLegendaryGame(BasicGame): + + Name = "Mass Effect Legendary Edition Support Plugin" + Author = "LostDragonist" + Version = "1.0.0" + + GameName = "Mass Effect: Legendary Edition" + GameShortName = "masseffectlegendaryedition" + GameBinary = "Game/Launcher/MassEffectLauncher.exe" + GameLauncher = "MassEffectLauncher.exe" + GameDataPath = "%GAME_PATH%" + GameDocumentsDirectory = ( + "%USERPROFILE%/Documents/BioWare/Mass Effect Legendary Edition/Save" + ) + GameSaveExtension = "pcsav" + GameSteamId = 1328670 + + def init(self, organizer: mobase.IOrganizer) -> bool: + if not super().init(organizer): + return False + if not self._organizer.onAboutToRun(lambda appName: self._spawnOriginWatcher()): + print("Failed to register onAboutToRun callback!", file=sys.stderr) + return False + if not self._organizer.onFinishedRun( + lambda appName, result: self._stopOriginWatcher() + ): + print("Failed to register onFinishedRun callback!", file=sys.stderr) + return False + return True + + def _spawnOriginWatcher(self) -> bool: + _killOrigin() + self.worker = threading.Thread(target=_workerFunc) + self.worker.start() + return True + + def _stopOriginWatcher(self) -> None: + self.worker.join(10.0) + + +def _killOrigin(): + # Kill Origin if it's alive + for proc in psutil.process_iter(): + if proc.name().lower() == "origin.exe": + proc.kill() + + +def _workerFunc(): + gameAliveCount = 300 # Large number to allow Origin and the game to launch + keepGoing = True + while keepGoing: + gameAlive = False + # See if the game is alive + for proc in psutil.process_iter(): + if proc.name().lower() in [ + "masseffectlauncher.exe", + "masseffect1.exe", + "masseffect2.exe", + "masseffect3.exe", + ]: + gameAlive = True + break + if gameAlive: + # Game is alive, sleep and keep monitoring + time.sleep(1) + gameAliveCount = 10 + else: + gameAliveCount -= 1 + if gameAliveCount <= 0: + _killOrigin() + # Exit the thread + keepGoing = False diff --git a/plugin-requirements.txt b/plugin-requirements.txt index e630ebd..21e84fa 100644 --- a/plugin-requirements.txt +++ b/plugin-requirements.txt @@ -1 +1,2 @@ +psutil==5.8.0 vdf==3.4 diff --git a/setup.cfg b/setup.cfg index 733bf95..42cce2d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,6 +16,9 @@ sections=FUTURE,STDLIB,THIRDPARTY,MOBASE,FIRSTPARTY,LOCALFOLDER warn_return_any = True warn_unused_configs = True +[mypy-psutil.*] +ignore_missing_imports = True + [mypy-vdf.*] ignore_missing_imports = True @@ -28,6 +31,7 @@ skip_install = true deps = PyQt5-stubs mobase-stubs + psutil==5.8.0 vdf==3.4 black flake8