mirror of
https://github.com/gensokyo-zone/infrastructure.git
synced 2026-02-10 04:49:19 -08:00
1085 lines
31 KiB
Nix
1085 lines
31 KiB
Nix
{
|
|
config,
|
|
pkgs,
|
|
gensokyo-zone,
|
|
lib,
|
|
...
|
|
}: let
|
|
inherit (gensokyo-zone.lib) userIs;
|
|
inherit (config.lib.steam) mkSharePath;
|
|
inherit (lib.options) mkOption mkEnableOption;
|
|
inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault;
|
|
inherit (lib.strings) hasSuffix replaceStrings optionalString concatStringsSep escapeShellArg makeBinPath versionOlder;
|
|
inherit (lib.attrsets) attrValues filterAttrs mapAttrs mapAttrs' mapAttrsToList listToAttrs nameValuePair;
|
|
inherit (lib.lists) concatLists head last filter sort singleton;
|
|
inherit (config.services.steam) accountSwitch;
|
|
cfg = config.services.steam.beatsaber;
|
|
sortedVersions = sort (a: b: versionOlder a.version b.version) (attrValues cfg.versions);
|
|
prevVersionFor = version: let
|
|
olderVersions = filter (v: versionOlder v.version version) sortedVersions;
|
|
in
|
|
if olderVersions != []
|
|
then last olderVersions
|
|
else null;
|
|
versionModule = {
|
|
config,
|
|
name,
|
|
...
|
|
}: {
|
|
options = with lib.types; {
|
|
version = mkOption {
|
|
type = str;
|
|
default = name;
|
|
};
|
|
previousVersion = mkOption {
|
|
type = nullOr str;
|
|
};
|
|
__toString = mkOption {
|
|
type = functionTo str;
|
|
};
|
|
};
|
|
config = {
|
|
__toString = mkOptionDefault (
|
|
config: config.version
|
|
);
|
|
previousVersion = mkOptionDefault (
|
|
prevVersionFor config.version
|
|
);
|
|
};
|
|
};
|
|
fileModule = {
|
|
config,
|
|
name,
|
|
...
|
|
}: {
|
|
options = with lib.types; {
|
|
relativePath = mkOption {
|
|
type = str;
|
|
default = name;
|
|
};
|
|
type = mkOption {
|
|
type = enum ["file" "directory"];
|
|
default = "file";
|
|
};
|
|
versioned = mkOption {
|
|
type = bool;
|
|
default = false;
|
|
};
|
|
target = mkOption {
|
|
type = enum ["user" "shared" "game"];
|
|
default = "user";
|
|
};
|
|
mode = {
|
|
file = mkOption {
|
|
type = str;
|
|
default =
|
|
if hasSuffix ".exe" config.relativePath || hasSuffix ".dll" config.relativePath
|
|
then "775"
|
|
else "664";
|
|
};
|
|
dir = mkOption {
|
|
type = str;
|
|
default = "2775";
|
|
};
|
|
};
|
|
ownerFor = mkOption {
|
|
type = functionTo str;
|
|
};
|
|
srcPathFor = mkOption {
|
|
type = functionTo path;
|
|
};
|
|
srcStyle = mkOption {
|
|
type = enum ["empty" "copy" "symlink" "symlink-shallow"];
|
|
default = "symlink";
|
|
};
|
|
workingPathFor = mkOption {
|
|
type = functionTo path;
|
|
};
|
|
init = mkOption {
|
|
type = nullOr path;
|
|
};
|
|
initFor = mkOption {
|
|
type = functionTo (nullOr path);
|
|
};
|
|
initStyle = mkOption {
|
|
type = enum ["none" "copy" "symlink" "symlink-shallow"];
|
|
default = "copy";
|
|
};
|
|
setup = {
|
|
shared = mkOption {
|
|
type = functionTo lines;
|
|
internal = true;
|
|
};
|
|
script = mkOption {
|
|
type = functionTo lines;
|
|
internal = true;
|
|
};
|
|
};
|
|
};
|
|
config = let
|
|
versionPathFor = version: optionalString config.versioned "/${version}";
|
|
in {
|
|
init = mkOptionDefault (
|
|
if config.target == "game"
|
|
then null
|
|
else if config.type == "directory"
|
|
then "${emptyDir}"
|
|
else if hasSuffix ".json" config.relativePath
|
|
then "${emptyJson}"
|
|
else if hasSuffix ".dll" config.relativePath || hasSuffix ".exe" config.relativePath
|
|
then "${emptyExecutable}"
|
|
else "${emptyFile}"
|
|
);
|
|
initFor = mkOptionDefault (
|
|
{
|
|
user,
|
|
version,
|
|
}:
|
|
config.init
|
|
);
|
|
ownerFor = mkOptionDefault (
|
|
user:
|
|
if config.target == "user"
|
|
then user
|
|
else "admin"
|
|
);
|
|
srcPathFor = mkOptionDefault (
|
|
{
|
|
user,
|
|
version,
|
|
}:
|
|
{
|
|
shared = cfg.sharedDataDir + versionPathFor version;
|
|
user = cfg.dataDirFor user + versionPathFor version;
|
|
game = cfg.gameDirFor version;
|
|
}
|
|
.${config.target}
|
|
or (throw "unsupported target")
|
|
+ "/${config.relativePath}"
|
|
);
|
|
workingPathFor = mkOptionDefault (
|
|
{
|
|
user,
|
|
version,
|
|
}:
|
|
cfg.workingDirFor {inherit user version;}
|
|
+ "/${config.relativePath}"
|
|
);
|
|
# TODO: setup.shared and do inits seperately!
|
|
setup.script = {
|
|
user,
|
|
version,
|
|
} @ args: let
|
|
owner = config.ownerFor user;
|
|
srcPath = config.srcPathFor args;
|
|
workingPath = config.workingPathFor args;
|
|
initPath = config.initFor args;
|
|
parentWorkingPath = dirOf workingPath;
|
|
mkdir = dest: ''
|
|
if [[ -L ${escapeShellArg dest} ]]; then
|
|
rm -f ${escapeShellArg dest}
|
|
fi
|
|
if [[ ! -d ${escapeShellArg dest} ]]; then
|
|
mkdir -m${config.mode.dir} ${escapeShellArg dest}
|
|
else
|
|
chmod ${config.mode.dir} ${escapeShellArg dest}
|
|
fi
|
|
chown ${owner}:${cfg.group} ${escapeShellArg dest}
|
|
'';
|
|
mkStyle = {
|
|
style,
|
|
src,
|
|
}:
|
|
if
|
|
style
|
|
!= "none"
|
|
&& src
|
|
== {
|
|
file = "${emptyFile}";
|
|
directory = "${emptyDir}";
|
|
}
|
|
.${config.type}
|
|
then "empty"
|
|
else style;
|
|
doInit = {
|
|
style,
|
|
src,
|
|
dest,
|
|
}:
|
|
{
|
|
none = "true";
|
|
copy =
|
|
{
|
|
file = ''
|
|
if [[ -L ${escapeShellArg dest} ]]; then
|
|
rm -f ${escapeShellArg dest}
|
|
elif [[ -e ${escapeShellArg dest} ]]; then
|
|
echo ERR: something is in the way of copying ${escapeShellArg dest} >&2
|
|
exit 1
|
|
fi
|
|
cp -TP --no-preserve=all ${escapeShellArg src} ${escapeShellArg dest}
|
|
chmod ${config.mode.file} ${escapeShellArg dest}
|
|
chown ${owner}:${cfg.group} ${escapeShellArg dest}
|
|
'';
|
|
directory = ''
|
|
${mkdir dest}
|
|
cp -rTP --no-preserve=all ${escapeShellArg src} ${escapeShellArg dest}
|
|
chown -R ${owner}:${cfg.group} ${escapeShellArg dest}
|
|
find ${escapeShellArg dest} -type f -exec chmod -m${config.mode.file} "{}" \;
|
|
'';
|
|
}
|
|
.${config.type};
|
|
empty =
|
|
{
|
|
directory = ''
|
|
${mkdir dest}
|
|
'';
|
|
file = ''
|
|
touch ${escapeShellArg dest}
|
|
chmod ${config.mode.file} ${escapeShellArg dest}
|
|
chown ${owner}:${cfg.group} ${escapeShellArg dest}
|
|
'';
|
|
}
|
|
.${config.type};
|
|
symlink = ''
|
|
if [[ -e ${escapeShellArg dest} && ! -L ${escapeShellArg dest} ]]; then
|
|
echo ERR: something is in the way of linking ${escapeShellArg dest} >&2
|
|
exit 1
|
|
fi
|
|
ln -sfT ${escapeShellArg src} ${escapeShellArg dest}
|
|
'';
|
|
symlink-shallow =
|
|
{
|
|
directory = ''
|
|
${mkdir dest}
|
|
ln -sf ${escapeShellArg src}/* ${escapeShellArg dest}/
|
|
'';
|
|
}
|
|
.${config.type};
|
|
}
|
|
.${mkStyle {inherit style src;}};
|
|
doSetup = {
|
|
style,
|
|
src,
|
|
dest,
|
|
}:
|
|
rec {
|
|
none = "true";
|
|
copy =
|
|
{
|
|
file = ''
|
|
${empty}
|
|
'';
|
|
directory = ''
|
|
${empty}
|
|
if [[ ${escapeShellArg dest}/* != ${escapeShellArg dest}/\* ]]; then
|
|
chmod -m${config.mode.file} ${escapeShellArg dest}/*
|
|
fi
|
|
'';
|
|
}
|
|
.${config.type};
|
|
empty =
|
|
{
|
|
directory = ''
|
|
chmod ${config.mode.dir} ${escapeShellArg dest}
|
|
chown ${owner}:${cfg.group} ${escapeShellArg dest}
|
|
'';
|
|
file = ''
|
|
chmod ${config.mode.file} ${escapeShellArg dest}
|
|
chown ${owner}:${cfg.group} ${escapeShellArg dest}
|
|
'';
|
|
}
|
|
.${config.type};
|
|
symlink = "true";
|
|
symlink-shallow =
|
|
{
|
|
directory = ''
|
|
${mkdir.directory}
|
|
'';
|
|
}
|
|
.${config.type};
|
|
}
|
|
.${mkStyle {inherit style src;}};
|
|
init = doInit {
|
|
style = config.initStyle;
|
|
src = initPath;
|
|
dest = srcPath;
|
|
};
|
|
setup = doSetup {
|
|
style = config.initStyle;
|
|
src = initPath;
|
|
dest = srcPath;
|
|
};
|
|
src = doInit {
|
|
style = config.srcStyle;
|
|
src = srcPath;
|
|
dest = workingPath;
|
|
};
|
|
checkFlag =
|
|
{
|
|
file =
|
|
{
|
|
none = "e";
|
|
copy = "f";
|
|
symlink = "L";
|
|
}
|
|
.${config.initStyle};
|
|
directory =
|
|
{
|
|
none = "e";
|
|
copy = "d";
|
|
symlink-shallow = "d";
|
|
symlink = "L";
|
|
}
|
|
.${config.initStyle};
|
|
}
|
|
.${config.type};
|
|
checkParent = ''
|
|
if [[ ! -d ${escapeShellArg parentWorkingPath} ]]; then
|
|
echo ERR: parent of ${escapeShellArg workingPath} does not exist >&2
|
|
exit 1
|
|
fi
|
|
'';
|
|
check =
|
|
if initPath != null
|
|
then ''
|
|
if [[ ! -${checkFlag} ${escapeShellArg srcPath} ]]; then
|
|
${init}
|
|
else
|
|
${setup}
|
|
fi
|
|
''
|
|
else ''
|
|
if [[ ! -${checkFlag} ${escapeShellArg srcPath} ]]; then
|
|
echo ERR: src ${escapeShellArg srcPath} for ${escapeShellArg workingPath} does not exist >&2
|
|
exit 1
|
|
fi
|
|
'';
|
|
in ''
|
|
${checkParent}
|
|
${check}
|
|
${src}
|
|
'';
|
|
};
|
|
};
|
|
userModule = {
|
|
config,
|
|
name,
|
|
...
|
|
}: {
|
|
options = with lib.types; {
|
|
name = mkOption {
|
|
type = str;
|
|
default = name;
|
|
};
|
|
preferredVersion = mkOption {
|
|
type = str;
|
|
default = cfg.defaultVersion;
|
|
};
|
|
};
|
|
};
|
|
emptyFile = pkgs.writeText "empty.txt" "";
|
|
emptyJson = pkgs.writeText "empty.json" "{}";
|
|
emptyDir = pkgs.runCommand "empty" {} ''
|
|
mkdir $out
|
|
'';
|
|
emptyExecutable = pkgs.writeTextFile {
|
|
name = "empty.exe";
|
|
executable = true;
|
|
text = "";
|
|
};
|
|
|
|
bsdata = "Beat Saber_Data";
|
|
|
|
vars = let
|
|
bsUserData = mkSharePath (cfg.dataDirFor "%GENSO_STEAM_USER%");
|
|
bsWorkingData = mkSharePath (cfg.workingDirFor {
|
|
user = "%GENSO_STEAM_USER%";
|
|
version = "%GENSO_STEAM_BS_VERSION%";
|
|
});
|
|
in ''
|
|
if "%GENSO_STEAM_MACHINE%" == "" set "GENSO_STEAM_MACHINE=%COMPUTERNAME%"
|
|
if "%GENSO_STEAM_LOCAL_DATA%" == "" set "GENSO_STEAM_LOCAL_DATA=C:\Program Files\GensokyoZone"
|
|
if "%GENSO_STEAM_LOCAL_DATA_BS%" == "" set "GENSO_STEAM_LOCAL_DATA_BS=%GENSO_STEAM_LOCAL_DATA%\${cfg.dirName}"
|
|
if "%GENSO_STEAM_INSTALL%" == "" set "GENSO_STEAM_INSTALL=C:\Program Files (x86)\Steam"
|
|
if "%GENSO_STEAM_LIBRARY_BS%" == "" set "GENSO_STEAM_LIBRARY_BS=%GENSO_STEAM_INSTALL%"
|
|
if "%GENSO_STEAM_BS_VERSION%" == "" set "GENSO_STEAM_BS_VERSION=${cfg.defaultVersion}"
|
|
if "%GENSO_SMB_HOST%" == "" set "GENSO_SMB_HOST=smb.${config.networking.domain}"
|
|
if "%GENSO_SMB_SHARED_MOUNT%" == "" set "GENSO_SMB_SHARED_MOUNT=\\%GENSO_SMB_HOST%\shared"
|
|
set "STEAM_BS_LIBRARY=%GENSO_STEAM_LIBRARY_BS%\steamapps\common\Beat Saber"
|
|
set "STEAM_BS_APPDATA=%USERPROFILE%\AppData\LocalLow\Hyperbolic Magnetism\Beat Saber"
|
|
set "STEAM_BS_LOCAL_DATA=%GENSO_STEAM_LOCAL_DATA_BS%\%GENSO_STEAM_BS_VERSION%"
|
|
set "STEAM_BS_USER_DATA=${bsUserData}"
|
|
set "STEAM_BS_WORKING_DATA=${bsWorkingData}"
|
|
if "%GENSO_STEAM_BS_LOCAL%" == "1" (
|
|
set "STEAM_BS_LAUNCH=%STEAM_BS_LOCAL_DATA%"
|
|
set "STEAM_BS_LAUNCH_APPDATA=%STEAM_BS_LOCAL_DATA%\AppData"
|
|
) else (
|
|
set "STEAM_BS_LAUNCH=%STEAM_BS_WORKING_DATA%"
|
|
set "STEAM_BS_LAUNCH_APPDATA=%STEAM_BS_USER_DATA%\AppData"
|
|
)
|
|
if "%GENSO_STEAM_USER%" == "" goto NOUSER
|
|
'';
|
|
eof = ''
|
|
goto:eof
|
|
|
|
:NOUSER
|
|
echo no steam user set
|
|
'';
|
|
mount = ''
|
|
rmdir "%STEAM_BS_APPDATA%"
|
|
mklink /D "%STEAM_BS_APPDATA%" "%STEAM_BS_LAUNCH_APPDATA%"
|
|
|
|
rmdir "%STEAM_BS_LIBRARY%"
|
|
mklink /D "%STEAM_BS_LIBRARY%" "%STEAM_BS_LAUNCH%"
|
|
'';
|
|
launch =
|
|
''
|
|
cd /d "%STEAM_BS_LIBRARY%"
|
|
''
|
|
+ ''"%STEAM_BS_LIBRARY%\Beat Saber.exe"'';
|
|
setup = ''
|
|
rmdir "%STEAM_BS_APPDATA%"
|
|
rmdir "%STEAM_BS_LIBRARY%"
|
|
|
|
mkdir "%GENSO_STEAM_LOCAL_DATA_BS%"
|
|
move /Y "%STEAM_BS_LIBRARY%" "%GENSO_STEAM_LOCAL_DATA_BS%\Vanilla"
|
|
move /Y "%STEAM_BS_APPDATA%" "%GENSO_STEAM_LOCAL_DATA_BS%\Vanilla\AppData"
|
|
'';
|
|
mountbeatsaber = ''
|
|
${vars}
|
|
${mount}
|
|
${eof}
|
|
'';
|
|
launchbeatsaber = ''
|
|
${vars}
|
|
${mount}
|
|
${launch}
|
|
${eof}
|
|
'';
|
|
fpfcbeatsaber = ''
|
|
${vars}
|
|
${launch} fpfc
|
|
${eof}
|
|
'';
|
|
setupbeatsaber = ''
|
|
${vars}
|
|
${setup}
|
|
pause
|
|
${eof}
|
|
'';
|
|
localbeatsaber-mount = ''
|
|
set GENSO_STEAM_BS_LOCAL=1
|
|
${vars}
|
|
${mount}
|
|
if NOT "%GENSO_STEAM_BS_VERSION%" == "Vanilla" (
|
|
rmdir "%STEAM_BS_LIBRARY%\UserData"
|
|
mklink /D "%STEAM_BS_LIBRARY%\UserData" "%STEAM_BS_WORKING_DATA%\UserData"
|
|
)
|
|
${eof}
|
|
'';
|
|
localbeatsaber-vanilla = ''
|
|
set GENSO_STEAM_BS_VERSION=Vanilla
|
|
set GENSO_STEAM_BS_LOCAL=1
|
|
${vars}
|
|
${mount}
|
|
${eof}
|
|
'';
|
|
localbeatsaber-launch = ''
|
|
set GENSO_STEAM_BS_LOCAL=1
|
|
${vars}
|
|
${mount}
|
|
${launch}
|
|
${eof}
|
|
'';
|
|
beatsaber-user = {
|
|
user,
|
|
version,
|
|
}: ''
|
|
set GENSO_STEAM_USER=${user}
|
|
set GENSO_STEAM_BS_VERSION=${version}
|
|
${vars}
|
|
${mount}
|
|
${launch}
|
|
${eof}
|
|
'';
|
|
vanilla = ''
|
|
setx GENSO_STEAM_BS_VERSION Vanilla
|
|
'';
|
|
|
|
mksetupbeatsaber = {
|
|
user,
|
|
version,
|
|
}: let
|
|
setupFiles = mapAttrsToList (_: file: file.setup.script {inherit user version;}) cfg.files;
|
|
in
|
|
pkgs.writeShellScript "setupbeatsaber-${user}-${version}" ''
|
|
set -eu
|
|
export PATH="$PATH:${makeBinPath [pkgs.coreutils]}"
|
|
${concatStringsSep "\n" setupFiles}
|
|
'';
|
|
in {
|
|
options.services.steam.beatsaber = with lib.types; {
|
|
enable = mkEnableOption "beatsaber scripts";
|
|
setup =
|
|
mkEnableOption "beatsaber data"
|
|
// {
|
|
default = accountSwitch.setup;
|
|
};
|
|
group = mkOption {
|
|
type = str;
|
|
default = "beatsaber";
|
|
};
|
|
defaultVersion = mkOption {
|
|
type = str;
|
|
};
|
|
versions = mkOption {
|
|
type = attrsOf (submodule versionModule);
|
|
default = {};
|
|
};
|
|
setupServiceNames = mkOption {
|
|
type = listOf str;
|
|
readOnly = true;
|
|
};
|
|
files = mkOption {
|
|
type = attrsOf (submodule fileModule);
|
|
default = {};
|
|
};
|
|
users = mkOption {
|
|
type = attrsOf (submodule userModule);
|
|
};
|
|
dirName = mkOption {
|
|
type = str;
|
|
default = "BeatSaber";
|
|
};
|
|
binDir = mkOption {
|
|
type = path;
|
|
default = accountSwitch.binDir + "/beatsaber";
|
|
};
|
|
gamesDir = mkOption {
|
|
type = path;
|
|
default = accountSwitch.gamesDir + "/${cfg.dirName}";
|
|
};
|
|
gameDirFor = mkOption {
|
|
type = functionTo path;
|
|
default = version: cfg.gamesDir + "/${version}";
|
|
};
|
|
sharedDataDir = mkOption {
|
|
type = path;
|
|
default = accountSwitch.sharedDataDir + "/${cfg.dirName}";
|
|
};
|
|
userDataDir = mkOption {
|
|
type = path;
|
|
default = accountSwitch.dataDir;
|
|
};
|
|
dataDirFor = mkOption {
|
|
type = functionTo path;
|
|
default = user: cfg.userDataDir + "/${user}/${cfg.dirName}";
|
|
};
|
|
userWorkingDir = mkOption {
|
|
type = path;
|
|
default = accountSwitch.workingDir;
|
|
};
|
|
userWorkingDirFor = mkOption {
|
|
type = functionTo path;
|
|
default = user: cfg.userWorkingDir + "/${user}/${cfg.dirName}";
|
|
};
|
|
workingDirFor = mkOption {
|
|
type = functionTo path;
|
|
default = {
|
|
user,
|
|
version,
|
|
}:
|
|
cfg.userWorkingDirFor user + "/${version}";
|
|
};
|
|
};
|
|
|
|
config = {
|
|
services.steam.beatsaber = let
|
|
bsUsers = filterAttrs (_: userIs cfg.group) config.users.users;
|
|
allVersions = mapAttrsToList (_: version: version.version) cfg.versions;
|
|
gameFiles = {
|
|
"Beat Saber.exe" = {};
|
|
"UnityCrashHandler64.exe" = {};
|
|
"UnityPlayer.dll" = {};
|
|
"MonoBleedingEdge".type = "directory";
|
|
};
|
|
sharedFiles = {
|
|
DynamicOpenVR = {
|
|
type = "directory";
|
|
versioned = true;
|
|
};
|
|
IPA = {
|
|
type = "directory";
|
|
versioned = true;
|
|
};
|
|
Libs = {
|
|
type = "directory";
|
|
versioned = true;
|
|
};
|
|
Plugins = {
|
|
type = "directory";
|
|
versioned = true;
|
|
};
|
|
Logs = {
|
|
type = "directory";
|
|
versioned = true;
|
|
};
|
|
"BeatSaberVersion.txt" = {
|
|
versioned = true;
|
|
initFor = {version, ...}: pkgs.writeText "BeatSaberVersion-${version}.txt" version;
|
|
};
|
|
"IPA.exe".versioned = true;
|
|
"IPA.exe.config".versioned = true;
|
|
"IPA.runtimeconfig.json".versioned = true;
|
|
"winhttp.dll".versioned = true;
|
|
${bsdata} = {
|
|
type = "directory";
|
|
versioned = true;
|
|
#initStyle = "symlink-shallow";
|
|
#initFor = { version, ... }: cfg.gameDirFor version + "/${bsdata}";
|
|
initStyle = "none";
|
|
srcPathFor = {version, ...}: cfg.gameDirFor version + "/${bsdata}";
|
|
srcStyle = "symlink-shallow";
|
|
};
|
|
"${bsdata}/Managed" = {
|
|
type = "directory";
|
|
versioned = true;
|
|
initFor = {version, ...}: cfg.gameDirFor version + "/${bsdata}/Managed";
|
|
};
|
|
# TODO: remove this to use multiple folders
|
|
"${bsdata}/CustomLevels" = {
|
|
type = "directory";
|
|
initStyle = "none";
|
|
srcPathFor = {...}: cfg.sharedDataDir + "/CustomLevels";
|
|
};
|
|
CustomAvatars = {
|
|
type = "directory";
|
|
initStyle = "none";
|
|
};
|
|
CustomNotes = {
|
|
type = "directory";
|
|
initStyle = "none";
|
|
};
|
|
CustomPlatforms = {
|
|
type = "directory";
|
|
initStyle = "none";
|
|
};
|
|
CustomSabers = {
|
|
type = "directory";
|
|
initStyle = "none";
|
|
};
|
|
CustomWalls = {
|
|
type = "directory";
|
|
initStyle = "none";
|
|
};
|
|
Playlists = {
|
|
type = "directory";
|
|
initStyle = "none";
|
|
};
|
|
"UserData/ScoreSaber/Replays" = {
|
|
type = "directory";
|
|
initStyle = "none";
|
|
srcPathFor = {...}: cfg.sharedDataDir + "/Replays";
|
|
};
|
|
"UserData/BeatLeader/" = {
|
|
versioned = true;
|
|
relativePath = "UserData/BeatLeader";
|
|
type = "directory";
|
|
srcStyle = "empty";
|
|
};
|
|
"UserData/BeatLeader/Replays" = {
|
|
type = "directory";
|
|
initStyle = "none";
|
|
srcPathFor = {...}: cfg.sharedDataDir + "/ReplaysBeatLeader";
|
|
};
|
|
"UserData/BeatLeader/ReplayerCache" = {
|
|
type = "directory";
|
|
versioned = true;
|
|
};
|
|
"UserData/BeatLeader/LeaderboardsCache" = {
|
|
versioned = true;
|
|
init = "${emptyJson}";
|
|
};
|
|
"UserData/BeatLeader/ReplayHeadersCache" = {
|
|
versioned = true;
|
|
init = "${emptyJson}";
|
|
};
|
|
"UserData/Beat Saber IPA.json".versioned = true;
|
|
"UserData/SongCore/" = {
|
|
versioned = true;
|
|
relativePath = "UserData/SongCore";
|
|
type = "directory";
|
|
srcStyle = "empty";
|
|
};
|
|
"UserData/SongCore/folders.xml" = {
|
|
versioned = true;
|
|
};
|
|
"UserData/SongCore/SongCoreExtraData.dat" = {
|
|
versioned = true;
|
|
init = "${emptyJson}";
|
|
};
|
|
"UserData/SongCore/SongDurationCache.dat" = {
|
|
versioned = true;
|
|
init = "${emptyJson}";
|
|
};
|
|
"UserData/SongCore/SongHashData.dat" = {
|
|
versioned = true;
|
|
init = "${emptyJson}";
|
|
};
|
|
"UserData/Chroma".type = "directory";
|
|
"UserData/Nya".type = "directory";
|
|
"UserData/SongRankedBadge".type = "directory";
|
|
"UserData/DrinkWater".type = "directory";
|
|
"UserData/Enhancements".type = "directory";
|
|
"UserData/HitScoreVisualizer" = {
|
|
type = "directory";
|
|
# TODO: initStyle = "symlink"; init = "${myhitscorejsons}";
|
|
};
|
|
"UserData/Saber Factory/" = {
|
|
relativePath = "UserData/Saber Factory";
|
|
type = "directory";
|
|
srcStyle = "empty";
|
|
};
|
|
"UserData/Saber Factory/Cache".type = "directory";
|
|
"UserData/Saber Factory/Textures".type = "directory";
|
|
"UserData/BeatSaverDownloader.ini" = {};
|
|
"UserData/BeatSaverUpdater.json" = {};
|
|
"UserData/SongDetailsCache.proto".versioned = true;
|
|
"UserData/SongDetailsCache.proto.Direct.etag".versioned = true;
|
|
};
|
|
userFiles = {
|
|
"UserData" = {
|
|
type = "directory";
|
|
versioned = true;
|
|
srcStyle = "empty";
|
|
};
|
|
"UserData/Camera2".type = "directory";
|
|
"UserData/Saber Factory" = {
|
|
type = "directory";
|
|
srcStyle = "empty";
|
|
};
|
|
"UserData/Saber Factory/Presets".type = "directory";
|
|
"UserData/Saber Factory/TrailConfig.json" = {};
|
|
"UserData/SongCore" = {
|
|
type = "directory";
|
|
versioned = true;
|
|
srcStyle = "empty";
|
|
};
|
|
"UserData/SongCore/SongCore.json" = {
|
|
versioned = true;
|
|
};
|
|
"UserData/BeatLeader" = {
|
|
type = "directory";
|
|
versioned = true;
|
|
srcStyle = "empty";
|
|
};
|
|
"UserData/BeatLeader.json" = {
|
|
versioned = true;
|
|
};
|
|
"UserData/ScoreSaber" = {
|
|
type = "directory";
|
|
versioned = true;
|
|
srcStyle = "empty";
|
|
};
|
|
"UserData/ScoreSaber/ScoreSaber.json" = {
|
|
versioned = true;
|
|
};
|
|
"UserData/Disabled Mods.json".versioned = true;
|
|
"UserData/modprefs.ini".versioned = true;
|
|
"UserData/JDFixer.json".versioned = true;
|
|
};
|
|
userDataFiles = [
|
|
"modprefs.ini"
|
|
"Disabled Mods.json"
|
|
"AutoPauseStealth.json"
|
|
"BeatSaberMarkupLanguage.json"
|
|
"BeatSaviorData.ini"
|
|
"BetterSongList.json"
|
|
"BetterSongSearch.json"
|
|
"bookmarkedSongs.json"
|
|
"votedSongs.json"
|
|
"Chroma.json"
|
|
"Cinema.json"
|
|
"CountersPlus.json"
|
|
"CustomAvatars.CalibrationData.dat"
|
|
"CustomAvatars.json"
|
|
"CustomNotes.json"
|
|
"Custom Platforms.json"
|
|
"CustomWalls.json"
|
|
"DrinkWater.json"
|
|
"EasyOffset.json"
|
|
"Enhancements.json"
|
|
"FasterScroll.json"
|
|
"FastFail.json"
|
|
"Fifth Anniversary Mod.json"
|
|
"Gotta Go Fast.json"
|
|
"Heck.json"
|
|
"HitScoreVisualizer.json"
|
|
"HitsoundTweaks.json"
|
|
"Intro Skip.json"
|
|
"JDFixer.json"
|
|
"ModelDownloader.json"
|
|
"Music Spatializer.json"
|
|
"Nya.json"
|
|
"ParticleOverdrive.ini"
|
|
"PerformanceMeter.json"
|
|
"PlayFirst.json"
|
|
"PlaylistManager.json"
|
|
"PracticePlugin.json"
|
|
"Saber Factory.json"
|
|
"SaberTailor.json"
|
|
"ScorePercentage.json"
|
|
"SiraUtil.json"
|
|
"SmoothCamPlus.json"
|
|
"SongChartVisualizer.json"
|
|
"SongPlayData.json"
|
|
"SongPlayHistory.json"
|
|
"SongRankedBadge.json"
|
|
"Technicolor.json"
|
|
"Tweaks55.json"
|
|
"UITweaks.json"
|
|
];
|
|
mapSharedFile = file:
|
|
file
|
|
// {
|
|
target = "shared";
|
|
};
|
|
mapGameFile = file:
|
|
file
|
|
// {
|
|
target = "game";
|
|
};
|
|
mapUserDataFile = file:
|
|
nameValuePair "UserData/${file}" {
|
|
target = "user";
|
|
};
|
|
in {
|
|
defaultVersion = mkIf (allVersions != []) (mkOptionDefault (
|
|
head allVersions
|
|
));
|
|
users = mapAttrs (_: user: {name = mkDefault user.name;}) bsUsers;
|
|
setupServiceNames = mkOptionDefault (
|
|
mapAttrsToList (_: user: "steam-setup-beatsaber-${user.name}.service") cfg.users
|
|
);
|
|
files = mkMerge [
|
|
userFiles
|
|
(listToAttrs (map mapUserDataFile userDataFiles))
|
|
(mapAttrs (_: mapGameFile) gameFiles)
|
|
(mapAttrs (_: mapSharedFile) sharedFiles)
|
|
];
|
|
};
|
|
systemd.services.steam-setup-beatsaber = mkIf cfg.setup {
|
|
wantedBy = [
|
|
"multi-user.target"
|
|
];
|
|
after = [
|
|
"tmpfiles.service"
|
|
];
|
|
serviceConfig = {
|
|
Type = mkOptionDefault "oneshot";
|
|
RemainAfterExit = mkOptionDefault true;
|
|
ExecStart = mkMerge (mapAttrsToList (
|
|
_: user: (mapAttrsToList (
|
|
_: version: "${mksetupbeatsaber {
|
|
user = user.name;
|
|
inherit (version) version;
|
|
}}"
|
|
)
|
|
cfg.versions)
|
|
)
|
|
cfg.users);
|
|
};
|
|
};
|
|
services.tmpfiles = let
|
|
toplevel = {
|
|
owner = mkDefault "admin";
|
|
group = mkDefault cfg.group;
|
|
mode = mkDefault "3775";
|
|
};
|
|
shared = {
|
|
inherit (toplevel) owner group;
|
|
mode = mkDefault "2775";
|
|
};
|
|
personal = owner: {
|
|
inherit owner;
|
|
inherit (shared) group mode;
|
|
};
|
|
bin = {
|
|
inherit (toplevel) owner group;
|
|
mode = "0755";
|
|
type = "copy";
|
|
};
|
|
sharedFolders = [
|
|
"CustomAvatars"
|
|
"CustomLevels"
|
|
"CustomNotes"
|
|
"CustomPlatforms"
|
|
"CustomSabers"
|
|
"CustomWalls"
|
|
"Playlists"
|
|
"Replays"
|
|
"ReplaysBeatLeader"
|
|
"AppData"
|
|
"UserData"
|
|
];
|
|
setupFiles =
|
|
[
|
|
{
|
|
${cfg.sharedDataDir} = toplevel;
|
|
${cfg.binDir} = shared;
|
|
}
|
|
(listToAttrs (
|
|
map (
|
|
folder:
|
|
nameValuePair "${cfg.sharedDataDir}/${folder}" shared
|
|
)
|
|
sharedFolders
|
|
))
|
|
]
|
|
++ concatLists (mapAttrsToList (
|
|
_: user:
|
|
singleton {
|
|
${cfg.dataDirFor user.name} = personal user.name;
|
|
"${cfg.dataDirFor user.name}/AppData" = personal user.name;
|
|
"${cfg.dataDirFor user.name}/UserData" = personal user.name;
|
|
}
|
|
++ mapAttrsToList (_: version: {
|
|
"${cfg.dataDirFor user.name}/${version.version}" = personal user.name;
|
|
${cfg.userWorkingDirFor user.name} = personal user.name;
|
|
${
|
|
cfg.workingDirFor {
|
|
user = user.name;
|
|
inherit (version) version;
|
|
}
|
|
} =
|
|
personal user.name;
|
|
})
|
|
cfg.versions
|
|
)
|
|
cfg.users)
|
|
++ mapAttrsToList (_: version: {
|
|
"${cfg.sharedDataDir}/${version.version}" = shared;
|
|
})
|
|
cfg.versions;
|
|
versionBinFiles =
|
|
mapAttrs' (
|
|
_: version:
|
|
nameValuePair
|
|
"${cfg.binDir}/${replaceStrings ["."] ["_"] version.version}.bat"
|
|
{
|
|
inherit (bin) owner group mode type;
|
|
src = pkgs.writeTextFile {
|
|
name = "beatsaber-${version.version}.bat";
|
|
executable = true;
|
|
text = ''
|
|
setx GENSO_STEAM_BS_VERSION ${version.version}
|
|
'';
|
|
};
|
|
}
|
|
)
|
|
cfg.versions;
|
|
userBinFiles =
|
|
mapAttrs' (
|
|
_: user:
|
|
nameValuePair
|
|
"${cfg.binDir}/${user.name}.bat"
|
|
{
|
|
inherit (bin) owner group mode type;
|
|
src = pkgs.writeTextFile {
|
|
name = "beatsaber-${user.name}.bat";
|
|
executable = true;
|
|
text = beatsaber-user {
|
|
user = user.name;
|
|
version = user.preferredVersion;
|
|
};
|
|
};
|
|
}
|
|
)
|
|
cfg.users;
|
|
binFiles =
|
|
{
|
|
"${cfg.binDir}/mount.bat" = {
|
|
inherit (bin) owner group mode type;
|
|
src = pkgs.writeTextFile {
|
|
name = "beatsaber-mount.bat";
|
|
executable = true;
|
|
text = mountbeatsaber;
|
|
};
|
|
};
|
|
"${cfg.binDir}/launch.bat" = {
|
|
inherit (bin) owner group mode type;
|
|
src = pkgs.writeTextFile {
|
|
name = "beatsaber-launch.bat";
|
|
executable = true;
|
|
text = launchbeatsaber;
|
|
};
|
|
};
|
|
"${cfg.binDir}/fpfc.bat" = {
|
|
inherit (bin) owner group mode type;
|
|
src = pkgs.writeTextFile {
|
|
name = "beatsaber-fpfc.bat";
|
|
executable = true;
|
|
text = fpfcbeatsaber;
|
|
};
|
|
};
|
|
"${cfg.binDir}/setup.bat" = {
|
|
inherit (bin) owner group mode type;
|
|
src = pkgs.writeTextFile {
|
|
name = "beatsaber-setup.bat";
|
|
executable = true;
|
|
text = setupbeatsaber;
|
|
};
|
|
};
|
|
"${cfg.binDir}/local-launch.bat" = {
|
|
inherit (bin) owner group mode type;
|
|
src = pkgs.writeTextFile {
|
|
name = "beatsaber-local-launch.bat";
|
|
executable = true;
|
|
text = localbeatsaber-launch;
|
|
};
|
|
};
|
|
"${cfg.binDir}/local-mount.bat" = {
|
|
inherit (bin) owner group mode type;
|
|
src = pkgs.writeTextFile {
|
|
name = "beatsaber-local-mount.bat";
|
|
executable = true;
|
|
text = localbeatsaber-mount;
|
|
};
|
|
};
|
|
"${cfg.binDir}/local-vanilla.bat" = {
|
|
inherit (bin) owner group mode type;
|
|
src = pkgs.writeTextFile {
|
|
name = "beatsaber-local-vanilla.bat";
|
|
executable = true;
|
|
text = localbeatsaber-vanilla;
|
|
};
|
|
};
|
|
"${cfg.binDir}/vanilla.bat" = {
|
|
inherit (bin) owner group mode type;
|
|
src = pkgs.writeTextFile {
|
|
name = "beatsaber-version-vanilla.bat";
|
|
executable = true;
|
|
text = vanilla;
|
|
};
|
|
};
|
|
"${cfg.binDir}/ModAssistant.exe" = {
|
|
inherit (bin) owner group mode type;
|
|
src = pkgs.fetchurl {
|
|
url = "https://github.com/Assistant/ModAssistant/releases/download/v1.1.32/ModAssistant.exe";
|
|
hash = "sha256-ozu2gYFiz+2BjptqL80DmUopbahbyGKFO1IPd7BhVPM=";
|
|
executable = true;
|
|
};
|
|
};
|
|
}
|
|
// versionBinFiles
|
|
// userBinFiles;
|
|
in {
|
|
enable = mkIf cfg.setup true;
|
|
files = mkIf cfg.setup (mkMerge (
|
|
singleton binFiles
|
|
++ setupFiles
|
|
));
|
|
};
|
|
};
|
|
}
|