Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5cd69b1cbc |
73
retroarch_playlist_generator.py
Normal file
73
retroarch_playlist_generator.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
|
||||||
|
args = sys.argv
|
||||||
|
|
||||||
|
if len(args) < 2:
|
||||||
|
print('need to pass path!')
|
||||||
|
exit()
|
||||||
|
|
||||||
|
if len(args) < 3:
|
||||||
|
print('need to pass dbname!')
|
||||||
|
|
||||||
|
path = args[1]
|
||||||
|
dbname = args[2]
|
||||||
|
|
||||||
|
extfilter = []
|
||||||
|
if len(args) >= 4:
|
||||||
|
extfilters = args[3].split(',')
|
||||||
|
|
||||||
|
print('Arg Path: {}'.format(path))
|
||||||
|
print('DB Name: {}'.format(dbname))
|
||||||
|
print('Ext Filters: {}'.format(extfilters))
|
||||||
|
|
||||||
|
output = '''{
|
||||||
|
"version": "1.2",
|
||||||
|
"default_core_path": "",
|
||||||
|
"default_core_name": "",
|
||||||
|
"label_display_mode": 0,
|
||||||
|
"right_thumbnail_mode": 0,
|
||||||
|
"left_thumbnail_mode": 0,
|
||||||
|
"items": [\n%ITEMS%]
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
itemstr = ''
|
||||||
|
|
||||||
|
for root, dirs, files in os.walk(path):
|
||||||
|
for filename in sorted(files):
|
||||||
|
filepath = '{}\\{}'.format(root, filename)
|
||||||
|
filepath = filepath.replace('/', '\\')
|
||||||
|
filepath = filepath.replace('\\', '\\\\')
|
||||||
|
|
||||||
|
if len(extfilters) != 0 and not os.path.splitext(os.path.basename(filepath))[1] in extfilters:
|
||||||
|
continue
|
||||||
|
|
||||||
|
label = os.path.splitext(os.path.basename(filepath))[0]
|
||||||
|
label = label.replace(' [!]', '')
|
||||||
|
label = re.sub(r'^\d{4} - ', '', label)
|
||||||
|
core_path = 'DETECT'
|
||||||
|
core_name = 'DETECT'
|
||||||
|
crc32 = '00000000|crc'
|
||||||
|
|
||||||
|
itemstr += ''' {
|
||||||
|
"path": "%PATH%",
|
||||||
|
"label": "%LABEL%",
|
||||||
|
"core_path": "%COREPATH%",
|
||||||
|
"core_name": "%CORENAME%",
|
||||||
|
"crc32": "%CRC%",
|
||||||
|
"db_name": "%DBNAME%"
|
||||||
|
},\n'''
|
||||||
|
itemstr = itemstr.replace('%PATH%', filepath)
|
||||||
|
itemstr = itemstr.replace('%LABEL%', label)
|
||||||
|
itemstr = itemstr.replace('%COREPATH%', core_path)
|
||||||
|
itemstr = itemstr.replace('%CORENAME%', core_name)
|
||||||
|
itemstr = itemstr.replace('%CRC%', crc32)
|
||||||
|
itemstr = itemstr.replace('%DBNAME%', dbname)
|
||||||
|
|
||||||
|
output = output.replace('%ITEMS%', itemstr)
|
||||||
|
|
||||||
|
with open('{}'.format(dbname), 'wt') as out_file:
|
||||||
|
out_file.write(output)
|
||||||
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
## 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)
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
requests==2.27.1
|
|
||||||
@@ -1,334 +0,0 @@
|
|||||||
import argparse
|
|
||||||
from gettext import install
|
|
||||||
from http import server
|
|
||||||
from logging import exception
|
|
||||||
import os
|
|
||||||
from posixpath import split
|
|
||||||
import requests
|
|
||||||
import shutil
|
|
||||||
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",
|
|
||||||
SERVER_APP_ID,
|
|
||||||
"validate",
|
|
||||||
"+quit"
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def read_steam_credentials(credentials_file: str) -> tuple:
|
|
||||||
print("Reading Steam credentials file...")
|
|
||||||
with open(credentials_file, "rt") as creds:
|
|
||||||
credentials = creds.readline()
|
|
||||||
|
|
||||||
split_creds = credentials.split(" ", 1)
|
|
||||||
if len(split_creds) != 2:
|
|
||||||
print("Error: Could not parse username and password!")
|
|
||||||
return (False, [])
|
|
||||||
|
|
||||||
return (True, split_creds)
|
|
||||||
|
|
||||||
def read_workshop(content_file: str) -> tuple:
|
|
||||||
print("Reading Steam Workshop content file...")
|
|
||||||
|
|
||||||
out_content = []
|
|
||||||
|
|
||||||
with open(content_file, "rt") as content:
|
|
||||||
content_ids = content.readlines()
|
|
||||||
for content_id in content_ids:
|
|
||||||
stripped_id = content_id.strip()
|
|
||||||
if stripped_id == "":
|
|
||||||
break
|
|
||||||
|
|
||||||
if not stripped_id.isnumeric():
|
|
||||||
print(f"Error: Invalid content ID '{stripped_id}', all IDs should be numeric!")
|
|
||||||
return (False, [])
|
|
||||||
|
|
||||||
if stripped_id in SERVER_SETUP_CONTENT:
|
|
||||||
continue
|
|
||||||
|
|
||||||
out_content.append(stripped_id)
|
|
||||||
|
|
||||||
print(f"Found {len(out_content)} content IDs")
|
|
||||||
|
|
||||||
return (True, out_content)
|
|
||||||
|
|
||||||
def download_workshop(steamcmd_path: str, workshop_content: list, credentials: list) -> bool:
|
|
||||||
args = [
|
|
||||||
f"{steamcmd_path}/steamcmd.exe",
|
|
||||||
"+login",
|
|
||||||
credentials[0],
|
|
||||||
credentials[1]
|
|
||||||
]
|
|
||||||
|
|
||||||
for content_id in workshop_content:
|
|
||||||
args.append("+workshop_download_item")
|
|
||||||
args.append(GAME_APP_ID)
|
|
||||||
args.append(content_id)
|
|
||||||
args.append("validate")
|
|
||||||
|
|
||||||
args.append("+quit")
|
|
||||||
|
|
||||||
subprocess.run(args)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def move_workshop(server_path: str, steamcmd_path: str, workshop_content: list) -> tuple:
|
|
||||||
print("Moving workshop files...")
|
|
||||||
workshop_path = f"{steamcmd_path}/steamapps/workshop/content/{GAME_APP_ID}"
|
|
||||||
packages_path = f"{server_path}/Packages"
|
|
||||||
|
|
||||||
moved_files = []
|
|
||||||
|
|
||||||
for content_id in workshop_content:
|
|
||||||
content_path = f"{workshop_path}/{content_id}"
|
|
||||||
if not os.path.exists(content_path):
|
|
||||||
print(f"Error: '{content_path}' does not exist!")
|
|
||||||
return (False, [])
|
|
||||||
|
|
||||||
files = os.listdir(content_path)
|
|
||||||
|
|
||||||
for file in files:
|
|
||||||
if os.path.splitext(file)[1] == ".rfcmp":
|
|
||||||
file_path = f"{content_path}/{file}"
|
|
||||||
print(f"Moving '{file}'...")
|
|
||||||
|
|
||||||
shutil.move(file_path, f"{packages_path}/{file}")
|
|
||||||
|
|
||||||
moved_files.append(file)
|
|
||||||
|
|
||||||
return (True, moved_files)
|
|
||||||
|
|
||||||
def install_workshop(server_path: str, workshop_files: list) -> bool:
|
|
||||||
print("Installing workshop files...")
|
|
||||||
|
|
||||||
mod_mgr_path = f"{server_path}/Bin64/ModMgr.exe"
|
|
||||||
working_dir = server_path
|
|
||||||
packages_dir = f"{server_path}/Packages"
|
|
||||||
install_dir = f"{server_path}/Installed"
|
|
||||||
|
|
||||||
args = [
|
|
||||||
mod_mgr_path,
|
|
||||||
f"-c{working_dir}",
|
|
||||||
f"-p{packages_dir}",
|
|
||||||
f"-d{install_dir}"
|
|
||||||
]
|
|
||||||
|
|
||||||
for file in workshop_files:
|
|
||||||
args.append(f"-i{file}")
|
|
||||||
|
|
||||||
subprocess.run(args)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def uninstall_workshop(server_path: str, workshop_content: list) -> bool:
|
|
||||||
print("Can't uninstall yet!")
|
|
||||||
return True
|
|
||||||
|
|
||||||
def open_mas2(server_path: str) -> bool:
|
|
||||||
# MIGHT be able to create MAS files using the ModMgr it seems
|
|
||||||
print("Opening MAS2 tool...")
|
|
||||||
mas2_path = f"{server_path}/Support/Tools/MAS2_x64.exe"
|
|
||||||
|
|
||||||
subprocess.run(
|
|
||||||
[
|
|
||||||
mas2_path
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def start_server(server_path: str) -> bool:
|
|
||||||
print("Starting server...")
|
|
||||||
|
|
||||||
subprocess.run(
|
|
||||||
[
|
|
||||||
f"{server_path}/Bin64/rFactor2 Dedicated.exe"
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def server_keys(server_path: str, steamcmd_path: str, credentials: list) -> bool:
|
|
||||||
print("Downloading setup content so a server can be created...")
|
|
||||||
if not download_workshop(steamcmd_path, SERVER_SETUP_CONTENT, credentials):
|
|
||||||
return False
|
|
||||||
|
|
||||||
result, workshop_files = move_workshop(server_path, steamcmd_path, SERVER_SETUP_CONTENT)
|
|
||||||
if not result:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not install_workshop(server_path, workshop_files):
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not open_mas2(server_path):
|
|
||||||
return False
|
|
||||||
|
|
||||||
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_path}/UserData/ServerKeys.bin")
|
|
||||||
print("\t3 - Close the server")
|
|
||||||
print("\t4 - Copy this file to your main rFactor 2 installation which owns the paid DLC you are installing")
|
|
||||||
print("\t\t<steam_games_dir>\\steamapps\\common\\rFactor 2\\UserData\\ServerKeys.bin")
|
|
||||||
print("\t5 - Start a single player race (any content I think)")
|
|
||||||
print("\t6 - There should not be a ServerUnlock.bin file in the folder you copied ServerKeys.bin to")
|
|
||||||
print("\t\t<steam_games_dir>\\steamapps\\common\\rFactor 2\\UserData\\ServerUnlock.bin")
|
|
||||||
print(f"\t7 - Copy ServerUnlock.bin back to {server_path}/UserData/ServerUnlock.bin")
|
|
||||||
|
|
||||||
if not start_server(server_path):
|
|
||||||
return False
|
|
||||||
|
|
||||||
input("\nPress Enter once you have completed those steps: ")
|
|
||||||
return True
|
|
||||||
|
|
||||||
def setup_server(server_name: str, parent_path: str, content_file: str, credentials_file: str) -> bool:
|
|
||||||
server_base_path = f"{parent_path}/{server_name}"
|
|
||||||
steamcmd_path = f"{server_base_path}/steamcmd"
|
|
||||||
server_path = f"{steamcmd_path}/steamapps/common/rFactor 2 Dedicated Server"
|
|
||||||
|
|
||||||
if not create_server_dir(server_base_path, parent_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
|
|
||||||
|
|
||||||
result, credentials = read_steam_credentials(credentials_file)
|
|
||||||
if not result:
|
|
||||||
return False
|
|
||||||
|
|
||||||
result, content = read_workshop(content_file)
|
|
||||||
if not result:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not server_keys(server_path, steamcmd_path, credentials):
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not download_workshop(steamcmd_path, content, credentials):
|
|
||||||
return False
|
|
||||||
|
|
||||||
result, workshop_files = move_workshop(server_path, steamcmd_path, content)
|
|
||||||
if not result:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not install_workshop(server_path, workshop_files):
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not open_mas2(server_path):
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not start_server(server_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)
|
|
||||||
Reference in New Issue
Block a user