-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added MelonLoader and BepInEx installer and uninstaller plus a button to open the game folder.
- Loading branch information
Showing
5 changed files
with
287 additions
and
0 deletions.
There are no files selected for viewing
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,152 @@ | ||
import requests | ||
import zipfile | ||
import json | ||
import os | ||
import io | ||
import platform | ||
import subprocess | ||
import shutil | ||
|
||
def load_json(file): | ||
with open(file, 'r') as f: | ||
return json.load(f) | ||
|
||
def open_settings(): | ||
return load_json(".\\settings.json") | ||
|
||
def open_folder(path): | ||
if path is None: | ||
print(f"Error: Attempted to open a folder with a None path: {path}") | ||
return | ||
|
||
if not os.path.exists(path): | ||
print(f"Error: The specified path does not exist: {path}") | ||
return | ||
|
||
if platform.system() == "Windows": | ||
os.startfile(str(path)) | ||
elif platform.system() == "Darwin": | ||
subprocess.run(["open", str(path)]) | ||
else: | ||
subprocess.run(["xdg-open", str(path)]) | ||
|
||
def download_and_extract(url, extract_to): | ||
response = requests.get(url) | ||
if response.status_code == 200: | ||
with zipfile.ZipFile(io.BytesIO(response.content)) as zip_ref: | ||
zip_ref.extractall(extract_to) | ||
print(f"Extracted {url} to {extract_to}") | ||
return True | ||
else: | ||
print(f"Failed to download {url}") | ||
return False | ||
|
||
def get_latest_bepinex_url(): | ||
try: | ||
response = requests.get("https://api.github.com/repos/BepInEx/BepInEx/releases/latest") | ||
response.raise_for_status() | ||
release_info = response.json() | ||
assets = release_info.get('assets', []) | ||
|
||
user_os = platform.system().lower() | ||
if user_os == "windows": | ||
user_os = "win" | ||
elif user_os == "darwin": | ||
user_os = "macos" | ||
else: | ||
user_os = "linux" | ||
|
||
for asset in assets: | ||
if f"{user_os}_x64" in asset['name'] and asset['name'].endswith('.zip'): | ||
return asset['browser_download_url'] | ||
except requests.exceptions.RequestException as e: | ||
print(f"Failed to fetch release info from https://api.github.com/repos/BepInEx/BepInEx/releases/latest: {e}") | ||
return None | ||
|
||
class bind_func: | ||
settings = open_settings() | ||
game_directory = settings["gameDir"] | ||
|
||
@staticmethod | ||
def print(txt): | ||
print(txt) | ||
|
||
@staticmethod | ||
def open_folder(dir): | ||
open_folder(dir) | ||
|
||
@staticmethod | ||
def open_game_folder(): | ||
if not bind_func.game_directory: | ||
return False, "Game directory is not set" | ||
if not os.path.exists(bind_func.game_directory): | ||
return False, f"Game directory does not exist: {bind_func.game_directory}" | ||
open_folder(bind_func.game_directory) | ||
return True, f"Opened game folder: {bind_func.game_directory}" | ||
|
||
@staticmethod | ||
def open_settings(): | ||
return open_settings() | ||
|
||
@staticmethod | ||
def get_os(): | ||
return platform.system() | ||
|
||
@staticmethod | ||
def set_game_directory(dir): | ||
bind_func.game_directory = dir | ||
return True | ||
|
||
@staticmethod | ||
def install_modloader(): | ||
success = True | ||
messages = [] | ||
|
||
# Install BepInEx | ||
bepinex_url = get_latest_bepinex_url() | ||
if bepinex_url: | ||
if download_and_extract(bepinex_url, bind_func.game_directory): | ||
os.remove(f"{bind_func.game_directory}\\.doorstop_version") | ||
os.remove(f"{bind_func.game_directory}\\doorstop_config.ini") | ||
os.remove(f"{bind_func.game_directory}\\changelog.txt") | ||
os.remove(f"{bind_func.game_directory}\\winhttp.dll") | ||
messages.append("BepInEx installed successfully") | ||
else: | ||
success = False | ||
messages.append("Failed to install BepInEx") | ||
else: | ||
success = False | ||
messages.append("Failed to get the latest BepInEx download URL") | ||
|
||
# Install MelonLoader | ||
melonloader_url = "https://github.com/LavaGang/MelonLoader/releases/latest/download/MelonLoader.x64.zip" | ||
if download_and_extract(melonloader_url, bind_func.game_directory): | ||
messages.append("MelonLoader installed successfully") | ||
else: | ||
success = False | ||
messages.append("Failed to install MelonLoader") | ||
|
||
return success, "\n".join(messages) | ||
|
||
@staticmethod | ||
def uninstall_modloader(): | ||
folders = [ | ||
f"{bind_func.game_directory}\\BepInEx", | ||
f"{bind_func.game_directory}\\MelonLoader", | ||
f"{bind_func.game_directory}\\Plugins", | ||
f"{bind_func.game_directory}\\Mods", | ||
f"{bind_func.game_directory}\\UserData", | ||
f"{bind_func.game_directory}\\UserLibs" | ||
] | ||
|
||
files = [ | ||
f"{bind_func.game_directory}\\dobby.dll", | ||
f"{bind_func.game_directory}\\NOTICE.txt", | ||
f"{bind_func.game_directory}\\version.dll" | ||
] | ||
|
||
for folder in folders: | ||
shutil.rmtree(folder) | ||
|
||
for file in files: | ||
os.remove(file) |
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,64 @@ | ||
import webview | ||
import os | ||
import sys | ||
import ctypes | ||
import json | ||
import inspect | ||
import platform | ||
|
||
def start_window(type, error=""): | ||
import functions | ||
if type == "normal": | ||
window = webview.create_window(title='AL2 Factory Mod Manager', url='win/index.html', resizable=False, width=800, height=600) | ||
for name, method in inspect.getmembers(functions.bind_func, predicate=inspect.isfunction): | ||
window.expose(method) | ||
webview.start() | ||
elif type == "error": | ||
ctypes.windll.user32.MessageBoxW(0, error, "AL2 Factory Mod Manager - Error", 0) | ||
sys.exit(1) | ||
|
||
def check_program_files(): | ||
programFiles = { | ||
".\\win\\": False, | ||
".\\settings.json": False | ||
} | ||
|
||
if not os.path.exists(".\\win\\") or not os.listdir(".\\win\\"): | ||
return programFiles, "Error: Missing essential files in 'win' directory! Please reinstall the application." | ||
|
||
for item in programFiles: | ||
if not os.path.exists(item): | ||
try: | ||
if item == ".\\settings.json": | ||
with open(item, 'w') as f: | ||
gameDir = "" | ||
if platform.system() == "Windows": | ||
gameDir = "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Assembly Line 2" | ||
elif platform.system() == "Darwin": | ||
gameDir = "/Applications/Assembly Line 2" | ||
else: | ||
gameDir = "/home/user/.steam/steam/steamapps/common/Assembly Line 2" | ||
json.dump({"theme": "dark", "gameDir": f"{gameDir}"}, f, indent=4) | ||
elif "." in os.path.basename(item): | ||
with open(item, 'w') as f: | ||
pass | ||
else: | ||
os.mkdir(item) | ||
programFiles[item] = True | ||
except Exception as e: | ||
return programFiles, f"Error creating '{item}': {str(e)}" | ||
else: | ||
programFiles[item] = True | ||
return programFiles, "" | ||
|
||
if __name__ == '__main__': | ||
program_status, error_msg = check_program_files() | ||
|
||
if error_msg: | ||
print(error_msg, file=sys.stderr) | ||
start_window("error", error_msg) | ||
elif all(program_status.values()): | ||
start_window("normal") | ||
else: | ||
print("Error: Unknown error occurred during initialization.", file=sys.stderr) | ||
start_window("error", "Unknown error occurred during initialization.") |
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,19 @@ | ||
html, | ||
body { | ||
width: 100%; | ||
height: 100%; | ||
padding: 0; | ||
margin: 0; | ||
display: block; | ||
overflow: hidden; | ||
} | ||
|
||
.content { | ||
width: 100%; | ||
height: 100%; | ||
} | ||
|
||
#title { | ||
width: 100%; | ||
text-align: center; | ||
} |
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,16 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<link rel="stylesheet" href="./css/index.css"> | ||
</head> | ||
<body> | ||
<div class="content"> | ||
<h1 id="title">AL2 Factory Mod Manager</h1> | ||
<button onclick="install_modloader()">Install Modloader</button> | ||
<button onclick="uninstall_modloader()">Uninstall Modloader</button> | ||
<button onclick="open_game_folder()">Open Game Folder</button> | ||
</div> | ||
</body> | ||
</html> | ||
<script src="./js/index.js"></script> |
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,36 @@ | ||
var settings = "" | ||
|
||
function print(txt) { | ||
pywebview.api.print(txt).then(() => {}).catch((error) => {}) | ||
} | ||
|
||
function get_settings() { | ||
pywebview.api.open_settings().then((settingsFile) => { | ||
settings = settingsFile | ||
}).catch((error) => {}) | ||
} | ||
|
||
function open_folder(dir) { | ||
pywebview.api.open_folder(dir).then(() => {}).catch((error) => {}) | ||
} | ||
|
||
function open_game_folder() { | ||
pywebview.api.open_game_folder().then(() => {}).catch((error) => {}) | ||
} | ||
|
||
function install_modloader() { | ||
pywebview.api.install_modloader().then((result) => { | ||
print(result[1]); | ||
if (!result[0]) { | ||
print("Failed to install one or more modloaders"); | ||
} | ||
}).catch((error) => { | ||
print("Error during modloader installation:", error); | ||
}); | ||
} | ||
|
||
function uninstall_modloader() { | ||
pywebview.api.uninstall_modloader().then(() => {}).catch((error) => {}) | ||
} | ||
|
||
get_settings() |