252 lines
8.4 KiB
Python
252 lines
8.4 KiB
Python
# find minecraft installation
|
|
# install forge client
|
|
# create install if one doesn't already exist
|
|
# copy mods to folder
|
|
|
|
import os
|
|
from sys import stdout
|
|
import json
|
|
import uuid
|
|
import logging
|
|
import webbrowser
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
from distutils.dir_util import copy_tree
|
|
import zipfile
|
|
# from StickyPiston import StickyPiston
|
|
|
|
|
|
DT_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ'
|
|
PROFILE_NAME = "William's Modded 1.19.2"
|
|
|
|
|
|
class MinecraftProfile:
|
|
def __init__(
|
|
self,
|
|
profile_id: str,
|
|
dt_created: datetime,
|
|
icon: str,
|
|
last_used: datetime,
|
|
last_version_id: str,
|
|
profile_name: str = '',
|
|
profile_type: str = 'custom',
|
|
java_args: str = '',
|
|
game_dir: Path = None
|
|
):
|
|
self.profile_id = profile_id
|
|
self.dt_created = dt_created
|
|
self.icon = icon
|
|
self.last_used = last_used
|
|
self.last_version_id = last_version_id
|
|
self.profile_name = profile_name
|
|
self.profile_type = profile_type
|
|
self.java_args = java_args
|
|
self.game_dir = game_dir
|
|
|
|
@classmethod
|
|
def from_json_obj(cls, obj, profile_id):
|
|
return cls(
|
|
profile_id=profile_id,
|
|
dt_created=datetime.strptime(obj['created'], DT_FORMAT) if obj.get('created') else None,
|
|
icon=obj.get('icon', 'Crafting_Table'),
|
|
last_used=datetime.strptime(obj['lastUsed'], DT_FORMAT) if obj.get('lastUsed') else None,
|
|
last_version_id=obj.get('lastVersionId', 'latest-release'),
|
|
profile_name=obj.get('name', ''),
|
|
profile_type=obj.get('type', 'latest-release'),
|
|
java_args=obj.get('javaArgs', ''),
|
|
game_dir=Path(obj['gameDir']) if obj.get('gameDir') else None
|
|
)
|
|
|
|
def to_json(self) -> dict:
|
|
return {
|
|
"created": self.dt_created.strftime(DT_FORMAT),
|
|
"gameDir": str(self.game_dir),
|
|
'icon': self.icon,
|
|
'javaArgs': self.java_args,
|
|
'lastUsed': self.last_used.strftime(DT_FORMAT),
|
|
'lastVersionId': self.last_version_id,
|
|
'name': self.profile_name,
|
|
'type': self.profile_type
|
|
}
|
|
|
|
|
|
def get_minecraft_folder(**kwargs):
|
|
|
|
mc_path = kwargs.get('minecraft_root', Path(os.getenv('APPDATA')) / '.minecraft')
|
|
|
|
if mc_path.exists():
|
|
return mc_path
|
|
else:
|
|
return None
|
|
|
|
|
|
def load_profiles(**kwargs):
|
|
|
|
mc_path = kwargs.get('minecraft_path', get_minecraft_folder())
|
|
|
|
profiles_file = mc_path / 'launcher_profiles.json'
|
|
|
|
profiles_list = []
|
|
if profiles_file.exists():
|
|
with open(profiles_file, 'r') as json_file:
|
|
profiles_json = json.load(json_file)
|
|
for profile in profiles_json['profiles']:
|
|
profile_obj = profiles_json['profiles'][profile]
|
|
profiles_list.append(
|
|
MinecraftProfile.from_json_obj(profile_obj, profile)
|
|
)
|
|
|
|
return profiles_list
|
|
|
|
|
|
def add_profile(profile: MinecraftProfile):
|
|
mc_path = get_minecraft_folder()
|
|
|
|
profiles_file = mc_path / 'launcher_profiles.json'
|
|
|
|
profiles_list = load_profiles()
|
|
|
|
if profile.profile_id in [p.profile_id for p in profiles_list]:
|
|
return 0
|
|
|
|
with open(profiles_file, 'r') as json_file:
|
|
json_raw = json.load(json_file)
|
|
|
|
json_raw['profiles'][profile.profile_id] = profile.to_json()
|
|
|
|
with open(profiles_file, 'w') as json_file:
|
|
json.dump(json_raw, json_file, indent=2)
|
|
|
|
return 1
|
|
|
|
|
|
def any_key():
|
|
return input("Press enter to continue...")
|
|
|
|
|
|
def package_mods(profile_name: str):
|
|
"""
|
|
Packages mods into a zip file for distribution
|
|
:param profile_name: The name of the profile to be deployed
|
|
:return:
|
|
"""
|
|
|
|
profiles_list = load_profiles()
|
|
|
|
profile_to_load = [profile for profile in profiles_list if profile.profile_name == profile_name]
|
|
|
|
if not profile_to_load:
|
|
raise IndexError(f"Profile {profile_name} does not exist!")
|
|
|
|
if len(profile_to_load) == 1:
|
|
profile_to_load = profile_to_load[0]
|
|
else:
|
|
raise IndexError(f"Multiple profiles match the name {profile_name}!")
|
|
|
|
# Create the zip
|
|
zip_file_name = profile_name.strip().lower().replace(' ', '_') + '.zip'
|
|
with zipfile.ZipFile(zip_file_name, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
|
for root, dirs, files in os.walk(profile_to_load.game_dir):
|
|
for file in files:
|
|
if not file.endswith('.jar'):
|
|
# Skip all non-jar files
|
|
continue
|
|
zipf.write(
|
|
os.path.join(root, file),
|
|
os.path.relpath(
|
|
os.path.join(root, file),
|
|
os.getcwd()
|
|
)
|
|
)
|
|
|
|
|
|
def main():
|
|
log = logging.getLogger()
|
|
log.setLevel(logging.DEBUG)
|
|
log.addHandler(logging.StreamHandler(stream=stdout))
|
|
|
|
profiles = load_profiles()
|
|
forge_profiles = [profile for profile in profiles if 'forge' in profile.last_version_id]
|
|
log.info(f"Hello and welcome! I'm going to install the modpack this script was packaged with.")
|
|
log.info(f"You shouldn't have to do anything - I'll let you know if I found anything weird that I need help with.")
|
|
log.debug(f"Found {len(forge_profiles)} modded profiles")
|
|
|
|
if PROFILE_NAME in [profile_name.profile_name for profile_name in forge_profiles]:
|
|
log.info(f"Modpack is already installed, exiting!")
|
|
return 0
|
|
|
|
# Before we add the profile, we need to make sure forge is installed
|
|
forge_profile = [profile for profile in profiles if profile.profile_name == 'forge'
|
|
and profile.last_version_id == '1.19.2-forge-43.1.42']
|
|
if not forge_profile:
|
|
# We need to install the forge version we were packaged with!
|
|
# First, make sure java is installed and on the path:
|
|
java_version_output = os.system('java --version')
|
|
if java_version_output != 0:
|
|
# Java is not installed!
|
|
log.info(f"Java is not installed! Please install OpenJDK and run this script again.")
|
|
webbrowser.open("https://aka.ms/download-jdk/microsoft-jdk-17.0.4.1-windows-x64.msi")
|
|
any_key()
|
|
return 1
|
|
|
|
log.info(f"Forge needs to be installed - select \"install client\" and follow the on-screen prompts.")
|
|
log.info(f"Check back here when you're done!")
|
|
forge_install_output = os.system('java -jar forge-1.19.2-43.1.42-installer.jar')
|
|
if forge_install_output != 0:
|
|
log.error(f"The install command failed! Is java installed?")
|
|
any_key()
|
|
return 1
|
|
|
|
log.info(f"Modpack is not already installed - just needed to check that before continuing!")
|
|
modded_folder = Path(os.getenv('APPDATA')) / '.moddedminecraft' / '1.19.2' / PROFILE_NAME
|
|
if modded_folder.exists():
|
|
log.warning(f"Profile path already exists! Continuing, but this is weird!")
|
|
else:
|
|
try:
|
|
modded_folder.mkdir(parents=True, exist_ok=True)
|
|
except BaseException as e:
|
|
log.error(f"I ran into an error while trying to make the profile path... can you make it for me?\n"
|
|
f"The path needs to be here: {modded_folder}\n"
|
|
f"Once you create it, run this script again, and I should be good to go.",
|
|
exc_info=e)
|
|
any_key()
|
|
raise e
|
|
log.info(f"Created profile path successfully!")
|
|
modded_profile = MinecraftProfile(
|
|
profile_id=str(uuid.uuid4()).replace('-', ''),
|
|
profile_name=PROFILE_NAME,
|
|
dt_created=datetime.utcnow(),
|
|
icon='Furnace_On',
|
|
java_args=" ".join([
|
|
"-Xmx8G",
|
|
"-XX:+UnlockExperimentalVMOptions",
|
|
"-XX:+UseG1GC",
|
|
"-XX:G1NewSizePercent=20",
|
|
"-XX:G1ReservePercent=20",
|
|
"-XX:MaxGCPauseMillis=50",
|
|
"-XX:G1HeapRegionSize=32M"
|
|
]),
|
|
last_used=datetime.utcnow(),
|
|
last_version_id='1.19.2-forge-43.1.42',
|
|
profile_type='custom',
|
|
game_dir=modded_folder
|
|
)
|
|
|
|
log.debug(f"Profile to be added:\n{json.dumps(modded_profile.to_json(), indent=4)}")
|
|
|
|
add_profile(modded_profile)
|
|
log.info(f"Profile added! Copying mod files...")
|
|
|
|
mods_folder = Path('mods')
|
|
dest_mods_folder = modded_profile.game_dir / 'mods'
|
|
dest_mods_folder.mkdir(parents=True, exist_ok=True)
|
|
|
|
copy_tree(str(mods_folder), str(dest_mods_folder))
|
|
|
|
log.info("All done!")
|
|
any_key()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|