Added config command, removed requirement for CLI version to have tk installed

This commit is contained in:
Patrick Marsee 2023-01-22 14:35:02 -05:00
parent 5018edb246
commit 61843399b0
2 changed files with 57 additions and 35 deletions

View file

@ -23,7 +23,7 @@ Upon first use, you will be prompted for the locations of your mods folder and
seven-days also allows the user to create separate mod profiles. If, for seven-days also allows the user to create separate mod profiles. If, for
instance, you frequent a multiplayer server with a certain set of mods, but instance, you frequent a multiplayer server with a certain set of mods, but
have a different prefered set of mods for single player, then you could create have a different prefered set of mods for single player, then you could create
two mod profiles, called `multiplayer` and `singleplayer`, for instance. two mod profiles, called `multiplayer` and `singleplayer`.
Loading a mod profile will instantly change the enabled mods to the ones that Loading a mod profile will instantly change the enabled mods to the ones that
were enabled when the profile was last saved. were enabled when the profile was last saved.
@ -36,7 +36,9 @@ The main source file for the CLI version is `seven-mods.py`. General usage is:
The available commands are: The available commands are:
- `list`: show a listing of all installed mods. Disabled mods appear in red. - `list`: show a listing of all installed mods. Disabled mods appear in red.
Enabled ones appear in green, and are followed by an asterisk (`*`). Enabled ones appear in green, and are followed by an asterisk (`*`). `list`
also accepts an optional argument, `profiles`, which tells it to list all saved
profiles instead of mods.
- `enable`: enable mods. A list of mod names can be given, separated by spaces. - `enable`: enable mods. A list of mod names can be given, separated by spaces.
The mod names are the names of their respective folders, *not* the names listed The mod names are the names of their respective folders, *not* the names listed
in their respective `ModInfo.xml` files. Alternatively, to enable all mods, in their respective `ModInfo.xml` files. Alternatively, to enable all mods,
@ -48,6 +50,7 @@ simply type `-a` rather than listing them all out.
that should be given is the name of the profile to save. If the profile already that should be given is the name of the profile to save. If the profile already
exists, then it will be overwritten. exists, then it will be overwritten.
- `load`: sets the enabled mods to those defined by the given profile. - `load`: sets the enabled mods to those defined by the given profile.
- `config`: reconfigure paths to your mod and 7 Days to Die directories.
Tip: If you run the script from the directory in which you store your mods, Tip: If you run the script from the directory in which you store your mods,
then bash autocompletion works for mod names - hence why it uses the ugly then bash autocompletion works for mod names - hence why it uses the ugly

View file

@ -22,12 +22,11 @@
# #
# #
from tkinter import *
from tkinter import ttk
import os import os
import os.path import os.path
import json import json
import re import re
import sys
PROFILE_DB = "profile_db.json" PROFILE_DB = "profile_db.json"
HOME = os.getenv("HOME", os.getcwd()) # Use relative path in attmpt to salvage things HOME = os.getenv("HOME", os.getcwd()) # Use relative path in attmpt to salvage things
@ -36,6 +35,27 @@ STEAM_LIBRARIES_DIR = os.path.join(HOME, ".local/share/Steam/config/libraryfolde
SEVEN_DIR = os.path.join(HOME, "/.steam/steam/steamapps/common/7 Days To Die") SEVEN_DIR = os.path.join(HOME, "/.steam/steam/steamapps/common/7 Days To Die")
CONFIG_FILE = "seven-mods.cfg" CONFIG_FILE = "seven-mods.cfg"
HELP_TEXT="""Usage: seven-mods.py <command> <args ...>
Valid commands are:
- help : usage guide.
- list : show a listing of all installed mods. Disabled mods appear in red.
Enabled ones appear in green, and are followed by an asterisk (`*`).
`list` also accepts an optional argument, `profiles`, which tells it
to list all saved profiles instead of mods.
- enable : enable mods. A list of mod names can be given, separated by spaces.
The mod names are the names of their respective folders, *not* the
names listed in their respective `ModInfo.xml` files. Alternatively,
to enable all mods, simply type `-a` rather than listing them all
out.
- disable : disable mods. Arguments are the same as for `enable`, including `-a`
to disable all mods.
- toggle : toggles mods on or off. Does *not* support `-a`.
- save : saves the currently enabled mods to a profile. The only argument
that should be given is the name of the profile to save. If the
profile already exists, then it will be overwritten.
- load : sets the enabled mods to those defined by the given profile.
- config : reconfigure paths to your mod and 7 Days to Die directories."""
class Config: class Config:
def __init__(self, **kwargs): def __init__(self, **kwargs):
@ -70,21 +90,14 @@ class ModProfiles:
except: except:
print("Could not save profiles.") print("Could not save profiles.")
class AppFrame(ttk.Frame):
def __init__(self, root):
self.super(SevenDaysToMod, self).__init__(root)
self.grid(column = 0, row = 0)
self.initialize_widgets()
def initialize_widgets(self):
pass
VAL_RE = re.compile(r'\s*"([^"]*)"', re.MULTILINE) VAL_RE = re.compile(r'\s*"([^"]*)"', re.MULTILINE)
DICT_START_RE = re.compile(r'\s*{', re.MULTILINE) DICT_START_RE = re.compile(r'\s*{', re.MULTILINE)
DICT_END_RE = re.compile(r'\s*}', re.MULTILINE) DICT_END_RE = re.compile(r'\s*}', re.MULTILINE)
class VDF_Error(RuntimeError): class SevenModsError(RuntimeError):
pass
class VDF_Error(SevenModsError):
pass pass
def parse_vdf_val(text: str) -> tuple: def parse_vdf_val(text: str) -> tuple:
@ -165,10 +178,9 @@ def get_internal_mods_path(cfg: Config) -> str:
ret = os.path.join(cfg.seven_dir, "Mods") ret = os.path.join(cfg.seven_dir, "Mods")
if not os.path.exists(ret): if not os.path.exists(ret):
try: try:
os.makedirs(ret) os.mkdir(ret)
except: except:
print(f"Could not create directory at {ret}.") raise SevenModsError(f"Could not create directory at {ret}.")
raise
elif not os.path.isdir(ret): elif not os.path.isdir(ret):
os.remove(ret) os.remove(ret)
os.makedirs(ret) os.makedirs(ret)
@ -184,7 +196,7 @@ def prompt_configuration_cli():
seven_dir = SEVEN_DIR seven_dir = SEVEN_DIR
cfg = Config(mods_dir=mods_dir, seven_dir=seven_dir) cfg = Config(mods_dir=mods_dir, seven_dir=seven_dir)
if not save_config(cfg): if not save_config(cfg):
print("There was a problem writing the configuration file.") raise SevenModsError("There was a problem writing the configuration file.")
return cfg return cfg
def save_config(cfg: Config) -> bool: def save_config(cfg: Config) -> bool:
@ -192,8 +204,7 @@ def save_config(cfg: Config) -> bool:
with open(CONFIG_FILE, 'w') as config_file: with open(CONFIG_FILE, 'w') as config_file:
config_file.write(f"mods_dir={cfg.mods_dir}\nseven_dir={cfg.seven_dir}\n") config_file.write(f"mods_dir={cfg.mods_dir}\nseven_dir={cfg.seven_dir}\n")
return True return True
except RuntimeError as e: except RuntimeError:
print(e)
return False return False
def load_config(): def load_config():
@ -234,18 +245,15 @@ def enable_mod(cfg: Config, mod_name: str):
link_source = os.path.join(cfg.mods_dir, mod_name) link_source = os.path.join(cfg.mods_dir, mod_name)
link_dest = os.path.join(get_internal_mods_path(cfg), mod_name) link_dest = os.path.join(get_internal_mods_path(cfg), mod_name)
if not os.path.exists(link_source): if not os.path.exists(link_source):
print(f"Could not enable {mod_name}: mod not available.") raise SevenModsError(f"Could not enable {mod_name}: mod not available.")
return
if os.path.exists(link_dest): if os.path.exists(link_dest):
print(f"Could not enable {mod_name}: already enabled.") raise SevenModsError(f"Could not enable {mod_name}: already enabled.")
return
os.symlink(link_source, link_dest) os.symlink(link_source, link_dest)
def disable_mod(cfg: Config, mod_name: str): def disable_mod(cfg: Config, mod_name: str):
link_dest = os.path.join(get_internal_mods_path(cfg), mod_name) link_dest = os.path.join(get_internal_mods_path(cfg), mod_name)
if not os.path.exists(link_dest): if not os.path.exists(link_dest):
print(f"Could not disable {mod_name}: already disabled.") raise SevenModsError(f"Could not disable {mod_name}: already disabled.")
return
os.remove(link_dest) os.remove(link_dest)
def toggle_mod(cfg: Config, mod_name: str): def toggle_mod(cfg: Config, mod_name: str):
@ -255,8 +263,7 @@ def toggle_mod(cfg: Config, mod_name: str):
else: else:
link_source = os.path.join(cfg.mods_dir, mod_name) link_source = os.path.join(cfg.mods_dir, mod_name)
if not os.path.exists(link_source): if not os.path.exists(link_source):
print(f"Could not enable {mod_name}: mod not available.") raise SevenModsError(f"Could not enable {mod_name}: mod not available.")
return
os.symlink(link_source, link_dest) os.symlink(link_source, link_dest)
def command_list(args: list, cfg: Config, profiles: ModProfiles): def command_list(args: list, cfg: Config, profiles: ModProfiles):
@ -274,7 +281,7 @@ def command_list(args: list, cfg: Config, profiles: ModProfiles):
print(profile) print(profile)
else: else:
print(f"Usage: python {args[0]} {args[1]} [profiles]") print(f"Usage: {args[0]} {args[1]} [profiles]")
def command_enable(args: list, cfg: Config, profiles: ModProfiles): def command_enable(args: list, cfg: Config, profiles: ModProfiles):
for mod in args[2:]: for mod in args[2:]:
@ -308,14 +315,20 @@ def command_save(args: list, cfg: Config, profiles: ModProfiles):
profiles.profiles[prof] = get_loaded_mods(cfg) profiles.profiles[prof] = get_loaded_mods(cfg)
profiles.save_mod_profiles() profiles.save_mod_profiles()
else: else:
print(f"Usage: python {args[0]} {args[1]} <profile-name>") print(f"Usage: {args[0]} {args[1]} <profile-name>")
def command_load(args: list, cfg: Config, profiles: ModProfiles): def command_load(args: list, cfg: Config, profiles: ModProfiles):
if len(args) == 3 and args[2] in profiles.profiles: if len(args) == 3 and args[2] in profiles.profiles:
command_disable(args[:2] + ["*"], cfg, profiles) command_disable(args[:2] + ["*"], cfg, profiles)
command_enable(args[:2] + profiles.profiles[args[2]], cfg, profiles) command_enable(args[:2] + profiles.profiles[args[2]], cfg, profiles)
else: else:
print(f"Usage: python {args[0]} {args[1]} <profile-name>") print(f"Usage: {args[0]} {args[1]} <profile-name>")
def command_configure(args: list, cfg: Config, profiles: ModProfiles):
prompt_configuration_cli()
def command_help(args: list, cfg: Config, profiles: ModProfiles):
print(HELP_TEXT)
def parse_input(args: list, cfg: Config, profiles: ModProfiles): def parse_input(args: list, cfg: Config, profiles: ModProfiles):
if len(args) > 1: if len(args) > 1:
@ -325,7 +338,10 @@ def parse_input(args: list, cfg: Config, profiles: ModProfiles):
"disable" : command_disable, "disable" : command_disable,
"toggle" : command_toggle, "toggle" : command_toggle,
"save" : command_save, "save" : command_save,
"load" : command_load} "load" : command_load,
"config" : command_configure,
"help" : command_help,
"--help" : command_help}
if args[1] in commands: if args[1] in commands:
commands[args[1]](args, cfg, profiles) commands[args[1]](args, cfg, profiles)
else: else:
@ -337,9 +353,12 @@ def main(args):
cfg = load_config() cfg = load_config()
profiles = ModProfiles() profiles = ModProfiles()
profiles.load_mod_profiles() profiles.load_mod_profiles()
parse_input(args, cfg, profiles) try:
parse_input(args, cfg, profiles)
except SevenModsError as err:
print(err, file = sys.stderr)
return 1
return 0 return 0
if __name__ == '__main__': if __name__ == '__main__':
import sys
sys.exit(main(sys.argv)) sys.exit(main(sys.argv))