reorganize Minecraft client internal structure, add missing error handling in update_mod

This commit is contained in:
espeon65536 2021-08-08 11:57:50 -05:00 committed by Fabian Dill
parent 8d4be10fd7
commit fa8531022d
1 changed files with 74 additions and 50 deletions

View File

@ -1,18 +1,21 @@
import argparse import argparse
import os, sys import os, sys
import re import re
import requests
import atexit import atexit
import logging
from subprocess import Popen from subprocess import Popen
from shutil import copyfile from shutil import copyfile
from base64 import b64decode from base64 import b64decode
from json import loads from json import loads
import requests
import Utils import Utils
atexit.register(input, "Press enter to exit.") atexit.register(input, "Press enter to exit.")
logger = logging.getLogger(__name__)
# 1 or more digits followed by m or g, then optional b
max_heap_re = re.compile(r"^\d+[mMgG][bB]?$")
def prompt_yes_no(prompt): def prompt_yes_no(prompt):
yes_inputs = {'yes', 'ye', 'y'} yes_inputs = {'yes', 'ye', 'y'}
@ -26,63 +29,51 @@ def prompt_yes_no(prompt):
else: else:
print('Please respond with "y" or "n".') print('Please respond with "y" or "n".')
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("apmc_file", default=None, help="Path to an Archipelago Minecraft data file (.apmc)")
args = parser.parse_args() # Find Forge jar file; raise error if not found
options = Utils.get_options() def find_forge_jar(forge_dir):
max_heap_re = re.compile(r"^\d+[mMgG][bB]?$")
apmc_file = args.apmc_file
forge_dir = options["minecraft_options"]["forge_directory"]
max_heap = options["minecraft_options"]["max_heap_size"]
if apmc_file is not None and not os.path.isfile(apmc_file):
raise FileNotFoundError(f"Path {apmc_file} does not exist or could not be accessed.")
if not os.path.isdir(forge_dir):
raise NotADirectoryError(f"Path {forge_dir} does not exist.")
if not max_heap_re.match(max_heap):
raise Exception(f"Max heap size {max_heap} in incorrect format. Use a number followed by M or G, e.g. 512M or 2G.")
# Find forge .jar
forge_server = None
for entry in os.scandir(forge_dir): for entry in os.scandir(forge_dir):
if ".jar" in entry.name and "forge" in entry.name: if ".jar" in entry.name and "forge" in entry.name:
forge_server = entry.name print(f"Found forge .jar: {entry.name}")
print(f"Found forge .jar: {forge_server}") return entry.name
if forge_server is None: raise FileNotFoundError(f"Could not find forge .jar in {forge_dir}.")
raise FileNotFoundError(f"Could not find forge .jar in {forge_dir}.")
# Find current randomizer mod, make mod dir if it doesn't already exist
# Create mods folder if needed; find AP randomizer jar; return None if not found.
def find_ap_randomizer_jar(forge_dir):
mods_dir = os.path.join(forge_dir, 'mods') mods_dir = os.path.join(forge_dir, 'mods')
ap_randomizer = None
if os.path.isdir(mods_dir): if os.path.isdir(mods_dir):
ap_mod_re = re.compile(r"^aprandomizer-[\d\.]+\.jar$") ap_mod_re = re.compile(r"^aprandomizer-[\d\.]+\.jar$")
for entry in os.scandir(mods_dir): for entry in os.scandir(mods_dir):
match = ap_mod_re.match(entry.name) match = ap_mod_re.match(entry.name)
if ap_mod_re.match(entry.name): if match:
ap_randomizer = match.group() print(f"Found AP randomizer mod: {match.group()}")
print(f"Found AP randomizer mod: {ap_randomizer}") return match.group()
break return None
else: else:
os.mkdir(mods_dir) os.mkdir(mods_dir)
print(f"Created mods folder in {forge_dir}") print(f"Created mods folder in {forge_dir}")
return None
# If given an apmc file, remove any apmc files in APData and copy the new one in
if apmc_file is not None:
apdata_dir = os.path.join(forge_dir, 'APData')
if not os.path.isdir(apdata_dir):
os.mkdir(apdata_dir)
print(f"Created APData folder in {forge_dir}")
for entry in os.scandir(apdata_dir):
if ".apmc" in entry.name and entry.is_file():
os.remove(entry.path)
print(f"Removed existing .apmc files in {apdata_dir}")
copyfile(apmc_file, os.path.join(apdata_dir, os.path.basename(apmc_file)))
print(f"Copied new .apmc file to {apdata_dir}")
# Download new client if needed # Create APData folder if needed; clean .apmc files from APData; copy given .apmc into directory.
def replace_apmc_files(forge_dir, apmc_file):
if apmc_file is None:
return
apdata_dir = os.path.join(forge_dir, 'APData')
if not os.path.isdir(apdata_dir):
os.mkdir(apdata_dir)
print(f"Created APData folder in {forge_dir}")
for entry in os.scandir(apdata_dir):
if ".apmc" in entry.name and entry.is_file():
os.remove(entry.path)
print(f"Removed existing .apmc files in {apdata_dir}")
copyfile(apmc_file, os.path.join(apdata_dir, os.path.basename(apmc_file)))
print(f"Copied {apmc_file} to {apdata_dir}")
# Check mod version, download new mod from GitHub releases page if needed.
def update_mod(forge_dir, ap_randomizer):
client_releases_endpoint = "https://api.github.com/repos/KonoTyran/Minecraft_AP_Randomizer/releases" client_releases_endpoint = "https://api.github.com/repos/KonoTyran/Minecraft_AP_Randomizer/releases"
resp = requests.get(client_releases_endpoint) resp = requests.get(client_releases_endpoint)
if resp.status_code == 200: # OK if resp.status_code == 200: # OK
@ -106,10 +97,18 @@ if __name__ == '__main__':
os.remove(old_ap_mod) os.remove(old_ap_mod)
print(f"Removed old mod file from {old_ap_mod}") print(f"Removed old mod file from {old_ap_mod}")
else: else:
print(f"Error retrieving the randomizer mod (status code {apmod_resp.status_code}).\nPlease report this issue on the Archipelago Discord server.") print(f"Error retrieving the randomizer mod (status code {apmod_resp.status_code}).")
print(f"Please report this issue on the Archipelago Discord server.")
sys.exit(1) sys.exit(1)
else:
print(f"Error checking for randomizer mod updates (status code {resp.status_code}).")
print(f"If this was not expected, please report this issue on the Archipelago Discord server.")
if not prompt_yes_no("Continue anyways?"):
sys.exit(1)
# Run forge server
# Run the Forge server. Return process object
def run_forge_server(forge_dir, forge_server, heap_arg):
java_exe = os.path.abspath(os.path.join('jre8', 'bin', 'java.exe')) java_exe = os.path.abspath(os.path.join('jre8', 'bin', 'java.exe'))
if not os.path.isfile(java_exe): if not os.path.isfile(java_exe):
java_exe = "java" # try to fall back on java in the PATH java_exe = "java" # try to fall back on java in the PATH
@ -122,5 +121,30 @@ if __name__ == '__main__':
argstring = ' '.join([java_exe, heap_arg, "-jar", forge_server, "-nogui"]) argstring = ' '.join([java_exe, heap_arg, "-jar", forge_server, "-nogui"])
print(f"Running Forge server: {argstring}") print(f"Running Forge server: {argstring}")
os.chdir(forge_dir) os.chdir(forge_dir)
server = Popen(argstring) return Popen(argstring)
server.wait()
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("apmc_file", default=None, help="Path to an Archipelago Minecraft data file (.apmc)")
args = parser.parse_args()
options = Utils.get_options()
apmc_file = args.apmc_file
forge_dir = options["minecraft_options"]["forge_directory"]
max_heap = options["minecraft_options"]["max_heap_size"]
if apmc_file is not None and not os.path.isfile(apmc_file):
raise FileNotFoundError(f"Path {apmc_file} does not exist or could not be accessed.")
if not os.path.isdir(forge_dir):
raise NotADirectoryError(f"Path {forge_dir} does not exist or could not be accessed.")
if not max_heap_re.match(max_heap):
raise Exception(f"Max heap size {max_heap} in incorrect format. Use a number followed by M or G, e.g. 512M or 2G.")
forge_server = find_forge_jar(forge_dir)
ap_randomizer = find_ap_randomizer_jar(forge_dir)
replace_apmc_files(forge_dir, apmc_file)
update_mod(forge_dir, ap_randomizer)
server_process = run_forge_server(forge_dir, forge_server, max_heap)
server_process.wait()