Skip to content

Commit

Permalink
Updated to 1.0.0
Browse files Browse the repository at this point in the history
Added MelonLoader and BepInEx installer and uninstaller plus a button to open the game folder.
  • Loading branch information
Neuxs0 authored Aug 2, 2024
1 parent 2a499af commit f16cc57
Show file tree
Hide file tree
Showing 5 changed files with 287 additions and 0 deletions.
152 changes: 152 additions & 0 deletions functions.py
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)
64 changes: 64 additions & 0 deletions main.py
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.")
19 changes: 19 additions & 0 deletions win/css/index.css
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;
}
16 changes: 16 additions & 0 deletions win/index.html
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>
36 changes: 36 additions & 0 deletions win/js/index.js
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()

0 comments on commit f16cc57

Please sign in to comment.