diff --git a/rf2/README.md b/rf2/README.md new file mode 100644 index 0000000..aff6701 --- /dev/null +++ b/rf2/README.md @@ -0,0 +1,5 @@ +## rFactor 2 scripts +RF2 is a pain to manage so doing it with scripts now. + +### setup_server.py +- All in one script to download server + content via SteamCMD (also downloaded as part of this script) diff --git a/rf2/requirements.txt b/rf2/requirements.txt new file mode 100644 index 0000000..a743bbe --- /dev/null +++ b/rf2/requirements.txt @@ -0,0 +1 @@ +requests==2.27.1 diff --git a/rf2/setup_server.py b/rf2/setup_server.py new file mode 100644 index 0000000..34d806e --- /dev/null +++ b/rf2/setup_server.py @@ -0,0 +1,183 @@ +import argparse +from logging import exception +import os +import requests +import subprocess +import zipfile + +# Download / extract SteamCMD +# Download game server +# Download workshop - config for this +# Move workshop files to game server folder +# Can MAS2 etc be scripted? + +# Also need to deal with keys +# Run server with some basic content (have default car / track to download) +# Once the server closes, ServerKeys.bin (or whatever its called) should be there +# Optionally copy this somewhere? + +# Possible args: +# Server name +# Path to install +# Car list +# Track list + +STEAM_CMD_URL = "https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip" + +SERVER_APP_ID = 400300 +GAME_APP_ID = 365960 + +# 917387157 - Silverstone 2012 +# 1515650133 - McLaren MP4/13 +SERVER_SETUP_CONTENT = ["917387157", "1515650133"] + +def create_server_dir(server_base_path: str, server_path: str) -> bool: + if not os.path.exists(server_path): + response = input(f"'{server_path}' does not exist. Create? y/N: ") + if response.upper == "Y": + try: + os.makedirs(server_path) + except Exception as e: + print(f"\nError: Could not create '{server_path}' - {e}") + return False + else: + return False + + if os.path.exists(server_base_path): + print(f"\nError: '{server_base_path}' already exists!") + return False + + try: + os.mkdir(server_base_path) + except Exception as e: + print(f"\nError: Could not create '{server_base_path}' - {e}") + + return True + +def download_steamcmd(steamcmd_path: str) -> bool: + # Make dir for steamcmd + print(f"Creating directory for SteamCMD at '{steamcmd_path}'...") + if os.path.exists(steamcmd_path): + print(f"\nError: '{steamcmd_path}' already exists!") + return False + + try: + os.mkdir(steamcmd_path) + except Exception as e: + print(f"\nError: Could not create '{steamcmd_path}' - {e}") + return False + + print("Downloading SteamCMD...") + print(f"\t{STEAM_CMD_URL}") + + response = requests.get(STEAM_CMD_URL) + + print("Writing downloaded file...") + with open(f"{steamcmd_path}/steamcmd.zip", "wb") as steamcmd_file: + steamcmd_file.write(response.content) + + return True + +def extract_steamcmd(steamcmd_path: str) -> bool: + print("Extracting SteamCMD zip...") + with zipfile.ZipFile(f"{steamcmd_path}/steamcmd.zip", "r") as steamcmd_zip: + steamcmd_zip.extractall(steamcmd_path) + + # Remove steamcmd.zip folder + print("Removing downloaded zip...") + os.remove(f"{steamcmd_path}/steamcmd.zip") + + return True + +def download_server(steamcmd_path: str) -> bool: + print("Downloading rFactor 2 server...") + + subprocess.run( + [ + f"{steamcmd_path}/steamcmd.exe", + "+login", + "anonymous", + "+app_update", + "400300", + "validate", + "+quit" + ] + ) + + return True + +def read_steam_credentials(credentials_file: str) -> tuple: + return (False, {}) + +def read_workshop(content_file: str) -> tuple: + return (False, []) + +def download_workshop(server_base_path: str, workshop_content: list) -> bool: + return False + +def move_workshop(server_base_path: str, workshop_content: list) -> bool: + return False + +def install_workshop(server_base_path: str, workshop_content: list) -> bool: + return False + +def uninstall_workshop(server_base_path: str, workshop_content: list) -> bool: + return False + +def open_mas2(server_base_path: str) -> bool: + # MIGHT be able to create MAS files using the ModMgr it seems + return False + +def start_server(server_base_path: str) -> bool: + return False + +def server_keys(server_base_path: str) -> bool: + # download + install basic content here + + print("Starting server to get server keys file") + print("\t1 - Create a server using basic content") + print(f"\t2 - Once the server is running, ServerKeys.bin should exist at {server_base_path}/UserData/ServerKeys.bin") + print("\t3 - Copy this file to your main rFactor 2 installation which owns the paid DLC you are installing") + print("\t\t\\steamapps\\common\\rFactor 2\\UserData\\ServerKeys.bin") + print("\t4 - Start a single player race (any content I think)") + print("\t5 - There should not be a ServerUnlock.bin file in the folder you copied ServerKeys.bin to") + print("\t\t\\steamapps\\common\\rFactor 2\\UserData\\ServerUnlock.bin") + print(f"\t6 - Copy ServerUnlock.bin back to {server_base_path}/UserData/ServerUnlock.bin") + + # start server here - dont think this can be scripted? + + input("\nPress Enter once you have completed those steps...") + return True + +def setup_server(server_name: str, server_path: str, content_file: str, credentials_file: str) -> bool: + server_base_path = f"{server_path}/{server_name}" + steamcmd_path = f"{server_base_path}/steamcmd" + + if not create_server_dir(server_base_path, server_path): + return False + + if not download_steamcmd(steamcmd_path): + return False + + if not extract_steamcmd(steamcmd_path): + return False + + if not download_server(steamcmd_path): + return False + + if not server_keys(server_base_path): + return False + + return True + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Set up rFactor 2 server :)") + + parser.add_argument("name", type=str, help="Friendly name for the server") + parser.add_argument("path", type=str, help="Path to download the server to") + parser.add_argument("content_file", type=str, help="Path to a list of workshop IDs to download and install onto the server") + parser.add_argument("credentials_file", type=str, help="Path to file containing Steam username and password in format username:password") + + args = parser.parse_args() + + setup_server(args.name, os.path.abspath(args.path), args.content_file, args.credentials_file)