diff --git a/configuration.nix b/configuration.nix index 32369ff..66ae9d0 100644 --- a/configuration.nix +++ b/configuration.nix @@ -8,18 +8,19 @@ let stateVersion = "25.05"; env = import ./.env.nix { inherit pkgs; }; + + mpkgs = import ./pkgs/default.nix { + config = { + system.stateVersion = stateVersion; + }; + }; in { imports = [ # Include the results of the hardware scan. ./hardware-configuration.nix (import (./. + "/${env.hostname}.nix")) - ( - let - home-manager = builtins.fetchTarball "https://github.com/nix-community/home-manager/archive/release-${stateVersion}.tar.gz"; - in - import "${home-manager}/nixos" - ) + mpkgs.home-manager.module ]; nixpkgs.config.allowUnfree = true; @@ -59,6 +60,7 @@ in clang llvmPackages.bintools stylua + go ]; fonts.packages = with pkgs; [ @@ -69,7 +71,7 @@ in nerd-fonts.symbols-only inter times-newer-roman - (import ./pkgs/monaco-font/default.nix { inherit pkgs; }) + (mpkgs.font.monaco) ]; fonts.fontconfig = { @@ -153,9 +155,12 @@ in ]; }; - home-manager.useUserPackages = true; - home-manager.useGlobalPkgs = true; - home-manager.users.anton = import ./home.nix; + home-manager = { + useUserPackages = true; + useGlobalPkgs = true; + sharedModules = [ ] ++ (mpkgs.home-manager.sharedModules); + users.anton = import ./home.nix; + }; # programs.home-manager.enable = true; programs.dconf.enable = true; @@ -165,7 +170,15 @@ in style = "adwaita-dark"; }; - hardware.graphics.enable = true; + hardware.graphics = { + enable = true; + extraPackages = with pkgs; [ + intel-media-driver + intel-vaapi-driver + libvdpau-va-gl + mesa + ]; + }; hardware.bluetooth = { enable = true; diff --git a/home.nix b/home.nix index e9bc27a..8639d92 100644 --- a/home.nix +++ b/home.nix @@ -5,14 +5,16 @@ }: let - neovim = pkgs.callPackage ./pkgs/neovim/default.nix { }; - fish = pkgs.callPackage ./pkgs/fish/default.nix { }; + mpkgs = import ./pkgs/default.nix { }; + inherit (mpkgs.config) neovim fish; env = import ./.env.nix { inherit pkgs; }; home = /home/anton; in { + imports = [ ]; + home.username = lib.mkDefault "anton"; home.homeDirectory = lib.mkDefault home; @@ -25,7 +27,7 @@ in krita davinci-resolve vscode - godot + godotPackages_4_5.godot neovim btop @@ -133,7 +135,67 @@ in }; }; - programs.vesktop = import ./home/vesktop.nix; + programs.tetrio-desktop = { + enable = true; + package = mpkgs.tetrio.desktop; + + plus = { + enable = true; + package = mpkgs.tetrio.plus; + skin.package = mpkgs.tetrio.skins.simple-connected; + }; + + settings = { + handling = { + auto_repeat_rate = 0; + delayed_auto_shift = 7; + soft_drop_factor = 14; + }; + audio = { + scroll_adjust_volume = false; + stereo = 60; + + music.preferences = { + kaze-no-sanpomichi = "-"; + muscat-to-shiroi-osara = "-"; + akindo = "+"; + yoru-no-niji = "+"; + burari-tokyo = "+"; + fuyu-no-jinkoueisei = "+"; + honemi-ni-shimiiru-karasukaze = "-"; + "21seiki-no-hitobito" = "+"; + haru-wo-machinagara = "++"; + go-go-go-summer = "-"; + sasurai-no-hitoritabi = "++"; + wakana = "-"; + zange-no-ma = "-"; + asphalt = "-"; + madobe-no-hidamari = "--"; + sora-no-sakura = "-"; + suiu = "-"; + burning-heart = "+"; + hayate-no-sei = "-"; + ima-koso = "+"; + chiheisen-wo-koete = "--"; + moyase-toushi-yobisamase-tamashii = "-"; + uchuu-5239 = "+"; + ultra-super-heros = "-"; + }; + }; + visual = { + board_bounciness = 20; + background_opacity = 0; + }; + multiplayer.notifications.suppress_while_playing = true; + + skip_login_screen = "by-url"; + advertisments.i_support_the_devs.i_cannot_play_with_ads.and_i_really_want_to.disable = true; + + devtools = true; + }; + }; + + programs.vesktop = import ./home/vesktop.nix; services.gpg-agent = { enable = true; @@ -157,5 +219,5 @@ in NIXPKGS_ALLOW_UNFREE = 1; }; - home.stateVersion = (pkgs.callPackage { }).system.stateVersion; + home.stateVersion = mpkgs.system.stateVersion; } diff --git a/pkgs/default.nix b/pkgs/default.nix new file mode 100644 index 0000000..5d2608f --- /dev/null +++ b/pkgs/default.nix @@ -0,0 +1,55 @@ +{ + pkgs ? import { config.allowUnfree = true; }, + config ? (import { }).config, + fetchzip ? pkgs.fetchzip, + ... +}: + +let + system = { + stateVersion = config.system.stateVersion; + }; + use = path: pkgs.callPackage (import path) { inherit pkgs; }; +in +{ + inherit system; + + wallpaper = use ./wallpaper/default.nix; + tetrio.desktop = use ./tetrio/default.nix; + tetrio.plus = + let + repo = { + owner = "UniQMG"; + name = "tetrio-plus"; + job = "11675178434"; + hash = "sha256-j3ACcnT64eMQtWYDGOE2oGXpnN5EUqk+lyV6ARBEtU8="; + }; + src = fetchzip { + url = "https://gitlab.com/${repo.owner}/${repo.name}/-/jobs/${repo.job}/artifacts/raw/app.asar.zip"; + hash = repo.hash; + }; + in + "${src}/app.asar"; + tetrio.skins = { + simple-connected = use ./tetrio/skins/simple-connected.nix; + }; + + leveldb-cli = use ./leveldb/default.nix; + + config.neovim = use ./neovim/default.nix; + config.fish = use ./fish/default.nix; + + font.monaco = use ./monaco-font/default.nix; + + home-manager = { + module = + let + home-manager = fetchzip { + url = "https://github.com/nix-community/home-manager/archive/release-${system.stateVersion}.tar.gz"; + hash = "sha256-WHkdBlw6oyxXIra/vQPYLtqY+3G8dUVZM8bEXk0t8x4="; + }; + in + import "${home-manager}/nixos"; + sharedModules = [ ./home-manager/initialFile.nix ./tetrio/module.nix ]; + }; +} diff --git a/pkgs/home-manager/initialFile.nix b/pkgs/home-manager/initialFile.nix new file mode 100644 index 0000000..0e5d70f --- /dev/null +++ b/pkgs/home-manager/initialFile.nix @@ -0,0 +1,172 @@ +{ + pkgs, + lib ? pkgs.lib, + config, + ... +}: + +let + inherit (lib) mkOption types; + + cfg = lib.filterAttrs (n: f: f.enable) config.home.initialFile; + home = config.home.homeDirectory; +in +{ + options = { + home.initialFile = mkOption { + type = types.attrsOf ( + types.submodule ( + { name, config, ... }: + { + options = { + enable = mkOption { + type = types.bool; + default = true; + }; + target = mkOption { + type = types.str; + apply = + path: + let + absPath = if lib.hasPrefix "/" path then path else "${home}/${path}"; + in + lib.removePrefix (home + "/") absPath; + description = "Path to target file relative to home directory"; + default = "/homeless-shelter/home"; + }; + text = mkOption { + type = types.nullOr types.lines; + description = "Text of the file, otherwise copies .source file."; + default = null; + }; + source = mkOption { + type = types.path; + description = "Path of file whose source to copy"; + }; + mode = mkOption { + type = types.nullOr types.str; + description = "File mode to apply to target file (sourced from .source / .text if not specified)"; + default = null; + }; + dir_mode = mkOption { + type = types.str; + description = "File mode to apply to directories"; + default = "0755"; + }; + recursive = mkOption { + type = types.bool; + description = "Whether or not to recursively copy .source as a directory instead of as a file"; + default = false; + }; + force = mkOption { + type = types.bool; + description = "Whether to unconditionally replace the target file, even if it already exists."; + default = false; + }; + }; + config = { + target = lib.mkDefault name; + source = lib.mkIf (config.text != null) ( + lib.mkDefault ( + pkgs.writeTextFile { + inherit (config) text; + name = lib.hm.strings.storeFileName name; + } + ) + ); + }; + } + ) + ); + description = "Attribute set of files to write into the user home (if they don't already exist)."; + default = { }; + }; + }; + + config = { + assertions = [ + ( + let + dups = lib.attrNames ( + lib.filterAttrs (n: v: v > 1) ( + lib.foldAttrs (acc: v: acc + v) 0 (lib.mapAttrsToList (n: v: { ${v.target} = 1; }) cfg) + ) + ); + dupsStr = lib.concatStringsSep ", " dups; + in + { + assertion = dups == [ ]; + message = '' + Conflicting managed target files: ${dupsStr} + + This may happen, for example, if you have a configuration similar to + + home.initialFile = { + conflict1 = { source = ./foo.nix; target = "baz"; }; + conflict2 = { source = ./bar.nix; target = "baz"; }; + }''; + } + ) + ]; + + home.activation.copyInitialFiles = lib.hm.dag.entryAfter [ "writeBoundary" ] ( + let + homeArg = lib.escapeShellArg home; + in + '' + function copyFile() { + local source="$1" + local targetRel="$2" + local mode="$3" + local dirMode="$4" + local recursive="$5" + local force="$6" + + local target="${homeArg}/$targetRel" + + if [[ -e "$target" && "$force" != "true" ]]; then + verboseEcho "Skipping existing $target" + return 0 + fi + + run mkdir -p "$(dirname "$target")" + + if [[ -d "$source" ]]; then + if [[ "$recursive" != "true" ]]; then + errorEcho "Source '$source' is a directory but recursive=false" + return 1 + fi + run rm -rf "$target" + run cp -r "$source" "$target" + else + if [[ -e "$target" && "$force" == "true" ]]; then + run rm -f "$target" + fi + run cp "$source" "$target" + fi + + if [[ -n "$mode" ]]; then + if [[ -d "$target" && "$recursive" == "true" ]]; then + run chmod "$dirMode" "$target" + find "$target" -type f -exec chmod "$mode" {} + + else + run chmod "$mode" "$target" + fi + fi + } + '' + + lib.concatMapStrings ( + v: + let + src = lib.escapeShellArg (toString v.source); + tgt = lib.escapeShellArg v.target; + mode = if v.mode == null then "''" else lib.escapeShellArg v.mode; + in + '' + copyFile ${src} ${tgt} ${mode} ${lib.escapeShellArg v.dir_mode} ${lib.trivial.boolToString v.recursive} ${lib.trivial.boolToString v.force} + '' + ) (lib.attrValues cfg) + ); + + }; +} diff --git a/pkgs/leveldb/default.nix b/pkgs/leveldb/default.nix new file mode 100644 index 0000000..5a45d54 --- /dev/null +++ b/pkgs/leveldb/default.nix @@ -0,0 +1,21 @@ +{ + pkgs ? import { }, +}: + +pkgs.buildGoModule { + name = "leveldb-cli"; + version = "1.0.0"; + + src = pkgs.fetchFromGitHub { + owner = "theblueplum"; + repo = "leveldb-cli"; + rev = "main"; + hash = "sha256-Q4BVmmqc6MPrOLy/lSV1FyhAoKBq0U2UcMHYEMOhtpo="; + }; + + vendorHash = "sha256-b25hlPQft9iKyIw6E9jtORBgoLPnNa4+Z5QoeFoayfc="; + + meta = { + mainProgram = "leveldb"; + }; +} diff --git a/pkgs/tetrio/default.nix b/pkgs/tetrio/default.nix new file mode 100644 index 0000000..ce7b3f4 --- /dev/null +++ b/pkgs/tetrio/default.nix @@ -0,0 +1,30 @@ +{ + pkgs ? import { }, + fetchzip ? pkgs.fetchzip, + withTetrioPlus ? false, + tetrio-plus ? pkgs.tetrio-plus, + ... +}: + +let + tetrio-desktop = + let + version = "10"; + in + { + inherit version; + src = fetchzip { + url = "https://tetr.io/about/desktop/builds/${version}/TETR.IO%20Setup.deb"; + hash = "sha256-2FtFCajNEj7O8DGangDecs2yeKbufYLx1aZb3ShnYvw="; + nativeBuildInputs = with pkgs; [ dpkg ]; + }; + }; + +in + +(pkgs.tetrio-desktop.overrideAttrs { + version = tetrio-desktop.version; + src = tetrio-desktop.src; +}).override { + inherit withTetrioPlus tetrio-plus; +} diff --git a/pkgs/tetrio/leveldb.nix b/pkgs/tetrio/leveldb.nix new file mode 100644 index 0000000..bef5d1a --- /dev/null +++ b/pkgs/tetrio/leveldb.nix @@ -0,0 +1,32 @@ +{ + pkgs ? import { }, + mpkgs ? import /etc/nixos/pkgs/default.nix { }, + origin ? "https://tetr.io", + key ? "userConfig", + value, + ... +}: + +let + src = ./.; +in +pkgs.stdenv.mkDerivation { + pname = "tetrio-leveldb"; + version = "1"; + + inherit src; + + nativeBuildInputs = [ mpkgs.leveldb-cli ]; + + buildPhase = '' + runHook preBuild + + ${pkgs.lib.getExe mpkgs.leveldb-cli} \ + $out \ + ${pkgs.lib.escapeShellArg origin} \ + ${pkgs.lib.escapeShellArg key} \ + ${pkgs.lib.escapeShellArg (builtins.toJSON value)} + + runHook postBuild + ''; +} diff --git a/pkgs/tetrio/module.nix b/pkgs/tetrio/module.nix new file mode 100644 index 0000000..e76184e --- /dev/null +++ b/pkgs/tetrio/module.nix @@ -0,0 +1,725 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + inherit (lib) mkOption mkIf types; + inherit (types) either; + + clamp = + x: min: max: + if x < min then + min + else if x > max then + max + else + x; + + leveldb = import ./leveldb.nix; + + cfg = config.programs.tetrio-desktop; +in +{ + options.programs.tetrio-desktop = { + enable = lib.mkEnableOption "tetrio"; + package = lib.mkPackageOption pkgs "tetrio-desktop" { }; + + plus = { + enable = lib.mkEnableOption "tetrio-plus"; + package = lib.mkOption { + type = types.path; + description = "The package to use for TETR.IO PLUS"; + default = pkgs.tetrio-plus; + }; + + skin = mkOption { + type = types.nullOr ( + types.submodule { + options = { + package = mkOption { + type = types.package; + description = "The package to use for the skin"; + example = "mpkgs.tetrio.skin.simple-connected"; + }; + + nearest = mkOption { + type = types.bool; + description = "Force nearest-neighbor scaling"; + default = false; + }; + }; + } + ); + default = null; + description = "In-game tetrimino skin"; + }; + + hideOnStartup = lib.mkOption { + type = types.bool; + default = true; + description = "Disable the TETR.IO PLUS configuration panel showing"; + }; + }; + settings = { + handling = + let + buffering = types.enum [ + "off" + "hold" + "tap" + ]; + in + { + + auto_repeat_rate = mkOption { + type = types.number; + apply = x: clamp x 0 5; + description = "Automatic Repeat Rate: the speed at which tetrominoes move when holding down movement keys, measured in frames per movement."; + default = 2; + }; + delayed_auto_shift = mkOption { + type = types.number; + apply = x: clamp x 1 20; + description = "Delayed Auto Shift: the time between the initial keypress and the start of its automatic repeat movement, measured in frames."; + default = 10; + }; + das_cut_delay = mkOption { + type = types.number; + apply = x: clamp x 0 20; + + description = "DAS Cut Delay (frames): if not 0, any ongoing DAS movement will pause for a set amount of time after dropping/rotating a piece, measured in frames."; + default = 1; + }; + soft_drop_factor = mkOption { + type = types.number; + apply = x: clamp x 5 41; + description = "Soft Drop Factor: the factor with which soft drops change the gravity speed."; + default = 6; + }; + prevent_hard_drops = mkOption { + type = types.bool; + description = "If enabled, when a piece locks on its own, the hard drop key becomes unavailable for a few frames. This prevents accidental hard drops."; + default = true; + }; + direction_cancel_das = mkOption { + type = types.bool; + description = "If enabled, DAS charge is cancelled when you change directions."; + default = false; + }; + soft_drop_over_movement = mkOption { + type = types.bool; + description = "If enabled, at very high speeds soft drop will always take precedence over horizontal movement, for a more consistent game feel."; + default = true; + }; + rotation_buffering = mkOption { + type = buffering; + description = "When to buffer rotations for the next piece (\"hold\": only if you hold the key as the next piece spawns)."; + default = "tap"; + }; + hold_buffering = mkOption { + type = buffering; + description = "When to buffer holding for the next piece (\"hold\": only if you hold the key as the next piece spawns)."; + default = "tap"; + }; + }; + audio = + let + percentage = x: x / 100.0; + in + { + music = { + volume = mkOption { + type = types.number; + apply = x: percentage (clamp x 0 200); + description = "Volume (%) at which BGM and jingles play."; + default = 100; + }; + reset_on_retry = mkOption { + type = types.bool; + apply = x: !x; + description = "If enabled, the background music will reset whenever you retry a game."; + default = false; + }; + preferences = mkOption { + type = + with types; + attrsOf (enum [ + "off" + "--" + "-" + "" + "+" + "++" + ]); + apply = + x: + pkgs.lib.mapAttrs ( + _: value: + { + "off" = "ban"; + "--" = "minmin"; + "-" = "min"; + "" = "base"; + "+" = "plus"; + "++" = "plusplus"; + } + ."${value}" + ) x; + default = { }; + }; + }; + sfx = { + volume = mkOption { + type = types.number; + apply = x: percentage (clamp x 0 200); + description = "Volume (%) at which sound effects play."; + default = 100; + }; + next_pieces = mkOption { + type = types.bool; + description = "Whether to play a sound effect that signifies the next piece that'll come up."; + default = false; + }; + other_players = mkOption { + type = types.bool; + description = "Whether to hear the sounds of other people playing in Multiplayer."; + default = true; + }; + attacks = mkOption { + type = types.bool; + description = "Whether to hear the sounds of attacks going towards and from you."; + default = true; + }; + speed_changes = mkOption { + type = types.bool; + description = "Whether to hear a sound when your Climb Speed changes in Quick Play."; + default = true; + }; + }; + scroll_adjust_volume = mkOption { + type = types.bool; + description = "Whether to allow adjusting the volume via scrolling in-game (or everywhere while holding ALT)."; + default = true; + }; + mute_unfocused = mkOption { + type = types.bool; + description = "If enabled, the background music will be muted when TETR.IO is minimized or tabbed away."; + default = true; + }; + stereo = mkOption { + type = types.number; + apply = x: percentage (clamp x 0 100); + description = "Intensity (%) of stereo effects. 0% -> Centered; 100% -> Directional"; + default = 50; + }; + disable = mkOption { + type = types.bool; + description = "If enabled, no audio will ever play. This will speed up the game, with a rather obvious drawback."; + default = false; + }; + }; + visual = + let + toFloat = x: if builtins.typeOf x == "str" then builtins.fromJSON x else x; + percentage = + x: min: max: + builtins.toString ((clamp (toFloat x) min max) / 100.0); + in + { + graphics = { + pipeline = mkOption { + type = types.enum [ + "compatability" + "webgl-1" + "webgl-2" + ]; + apply = + x: + { + "compatability" = "legacy"; + "webgl-1" = "webgl1"; + "webgl-2" = "webgl2"; + } + ."${x}"; + description = "The WebGL mode to use for rendering"; + default = "webgl-2"; + }; + tier = mkOption { + type = types.enum [ + "minimal" + "low" + "medium" + "high" + "ultra" + ]; + description = "The graphics tier to use for rendering"; + default = "ultra"; + }; + cache = mkOption { + type = types.bool; + apply = x: if x then "medium" else "low"; + description = "Whether or not to enable caching of resources"; + default = true; + }; + particle_count = mkOption { + type = with types; either str number; + apply = x: percentage x 10 150; + description = "How many particles to display (%)."; + default = 150; + }; + + powersave = mkOption { + type = types.bool; + description = "If enabled, prioritize power saving over performance."; + default = false; + }; + low_resolution = mkOption { + type = types.bool; + description = "If enabled, render blurrier graphics. Not very pretty but helps performance."; + default = false; + }; + low_precision_counters = mkOption { + type = types.bool; + description = "If enabled, don't show as much precision on in-game counters. Speeds up the game significantly."; + default = false; + }; + }; + show_unfocus_warning = mkOption { + type = types.bool; + description = "If enabled, a warning is shown when TETR.IO is out of focus."; + default = true; + }; + action_text = mkOption { + type = types.enum [ + "off" + "some" + "all" + ]; + description = "Determines how much action text to display when performing special attacks."; + default = "some"; + }; + board_bounciness = mkOption { + type = with types; either str number; + apply = x: percentage x 0 200; + description = "How much (%) the board reacts when you move pieces around."; + default = 40; + }; + damage_shakiness = mkOption { + type = with types; either str number; + apply = x: percentage x 0 110; + description = "How much (%) the board reacts when you receive damage."; + default = 100; + }; + grid_opacity = mkOption { + type = with types; either str number; + apply = x: percentage x 0 110; + description = "How visible the grid is (%). 0% makes the grid invisible."; + default = 10; + }; + board_opacity = mkOption { + type = with types; either str number; + apply = x: percentage x 0 110; + description = "How visible the board is (%). 0% makes the board invisible."; + default = 85; + }; + shadow_opacity = mkOption { + type = with types; either str number; + apply = x: percentage x 0 110; + description = "How visible the shadow piece is (%). 0% makes the shadow piece invisible."; + default = 15; + }; + background_opacity = mkOption { + type = with types; either str number; + apply = x: percentage x 0 100; + description = "How visible the background images are. 0% makes the background entirely black."; + default = 5; + }; + show_background_in_menus = mkOption { + type = types.bool; + apply = x: !x; + description = "If enabled, do not show the background when in menus. This will majorly improve performance in menus, but you will no longer see the background."; + default = false; + }; + board_zoom = mkOption { + type = with types; either str number; + apply = x: percentage x 0 110; + description = "How large the board displays. Values over 100% may cause some elements to not be visible."; + default = 100; + }; + spin_board = mkOption { + type = types.bool; + description = "When enabled, the board reacts to T-Spins by rotating a little with it."; + default = true; + }; + fire_meter = mkOption { + type = types.bool; + description = "If enabled, fill a fire meter when doing well, illuminating boards that have a lot of fire."; + default = true; + }; + danger_warning = mkOption { + type = types.bool; + description = "If enabled, makes the board red and play a warning sound when you're in danger."; + default = true; + }; + colored_shadow = mkOption { + type = types.bool; + description = "If enabled, colors the shadow piece."; + default = true; + }; + color_locked_hold_piece = mkOption { + type = types.bool; + description = "If enabled, the HOLD piece will be grayed out if it may not be used."; + default = true; + }; + + }; + multiplayer = { + chat = { + enable = mkOption { + type = types.bool; + apply = x: !x; + description = "If disabled, chat will be hidden when ingame."; + default = true; + }; + filter = mkOption { + type = types.bool; + description = "If enabled, profanity in chat will be filtered. Note that such filters are never perfect."; + default = true; + }; + show_emotes = mkOption { + type = types.bool; + description = "If enabled, show emotes in chat."; + default = true; + }; + show_animated_emotes = mkOption { + type = types.bool; + description = "If enabled, show animated emotes in chat."; + default = true; + }; + invert = mkOption { + type = types.bool; + description = "If enabled, chat text shows in black (good for light backgrounds)."; + default = false; + }; + darken = mkOption { + type = types.bool; + description = "If enabled, show a background behind chat messages when typing."; + default = true; + }; + }; + notifications = + let + notification.optional = types.enum [ + "off" + "in-game" + "desktop" + ]; + notification.required = types.enum [ + "in-game" + "desktop" + ]; + apply = + x: + { + "off" = "off"; + "in-game" = "ingame"; + "desktop" = "both"; + } + ."${x}"; + in + { + when = { + friend_online = mkOption { + type = notification.optional; + inherit apply; + description = "Notify me when a friend goes online"; + default = "in-game"; + }; + friend_offline = mkOption { + type = notification.optional; + inherit apply; + description = "Notify me when a friend goes offline"; + default = "off"; + }; + friend_dm_recieved = mkOption { + type = notification.optional; + inherit apply; + description = "Notify me when a friend sends me a direct message"; + default = "desktop"; + }; + dm_recieved = mkOption { + type = notification.optional; + inherit apply; + description = "Notify me when a non-friend sends me a direct message"; + default = "desktop"; + }; + room_invite = mkOption { + type = notification.required; + inherit apply; + description = "Notify me when someone invites me to a room"; + default = "desktop"; + }; + other = mkOption { + type = notification.required; + inherit apply; + description = "Other notifications"; + default = "desktop"; + }; + }; + enable_desktop_notifications = mkOption { + type = types.bool; + description = "If enabled, show notifications outside of the game."; + default = true; + }; + suppress_while_playing = mkOption { + type = types.bool; + description = "If enabled, unimportant notifications don't show ingame, but will be shown after the game."; + default = false; + }; + full_volume = mkOption { + type = types.bool; + description = "If enabled, notification sounds always play at full volume."; + default = true; + }; + }; + hide_room_ids = mkOption { + type = types.bool; + description = "If enabled, room IDs will not be shown, to protect you from streamsniping."; + default = false; + }; + show_network_warnings = mkOption { + type = types.bool; + apply = x: !x; + description = "If disabled, network warning icons will not be shown."; + default = true; + }; + notify_elim = mkOption { + type = types.bool; + description = "If enabled, show a popup when you KO someone or get KO'd."; + default = true; + }; + duels_side_by_side = mkOption { + type = types.bool; + description = "Whether to display a duel side-by-side. This causes your own board to move slightly to the left."; + default = true; + }; + simplify_thumbnails = mkOption { + type = types.bool; + description = "If enabled, always shows the simpler thumbnails for other players. This increases performance in games between 2 and 8 players, but looks less nice."; + default = false; + }; + animate_super_lobby_background = mkOption { + type = types.bool; + apply = x: !x; + description = "If enabled, the background of Super Lobbies (rooms of 100+ players) animate."; + default = true; + }; + animate_background_in_quickplay = mkOption { + type = types.bool; + apply = x: !x; + description = "If enabled, the background of Quick Play animate."; + default = true; + }; + welcome_guide = mkOption { + type = types.bool; + description = "If enabled, show the simple guide with the keybinds in multiplayer lobbies."; + default = true; + }; + }; + keep_replay_tools_open = mkOption { + type = types.bool; + description = "If enabled, the replay tools do not collapse when you're not using them."; + default = false; + }; + skip_login_screen = mkOption { + type = types.enum [ + "never" + "by-url" + "always" + ]; + apply = + x: + { + "never" = "never"; + "by-url" = "quickjoin"; + "always" = "always"; + } + ."${x}"; + description = "When to skip the login screen (\"by-url\": only when joining matches or viewing replays by url)"; + default = "always"; + }; + discord_rpc = mkOption { + type = types.bool; + description = "If enabled, show your current activity in Discord."; + default = true; + }; + flash_taskbar_icon = mkOption { + type = types.bool; + description = "If enabled, the taskbar icon will flash when something important happens."; + default = true; + }; + devtools = mkOption { + type = types.bool; + description = "If enabled, allow opening the developer toolbox."; + default = false; + }; + advertisments.i_support_the_devs.i_cannot_play_with_ads.and_i_really_want_to.disable = mkOption { + type = types.bool; + description = "If enabled, most/all third-party ads will not show."; + default = false; + }; + }; + }; + + config = mkIf cfg.enable ( + lib.mkMerge [ + { + home.packages = [ + (cfg.package.override { + withTetrioPlus = cfg.plus.enable; + tetrio-plus = cfg.plus.package; + }) + ]; + + home.initialFile.".config/tetrio-desktop/Local Storage/leveldb" = { + mode = "0644"; + source = leveldb { + value = { + handling = + let + set = cfg.settings.handling; + in + { + arr = set.auto_repeat_rate; + das = set.delayed_auto_shift; + dcd = set.das_cut_delay; + sdf = set.soft_drop_factor; + safelock = set.prevent_hard_drops; + cancel = set.direction_cancel_das; + may20g = set.soft_drop_over_movement; + irs = set.rotation_buffering; + ihs = set.hold_buffering; + }; + volume = + let + set = cfg.settings.audio; + in + { + music = set.music.volume; + bgmtweak = set.music.preferences; + sfx = set.sfx.volume; + stereo = set.stereo; + scrollable = set.scroll_adjust_volume; + oof = set.mute_unfocused; + next = set.sfx.next_pieces; + others = set.sfx.other_players; + attacks = set.sfx.attacks; + zenithrank = set.sfx.speed_changes; + disable = set.disable; + }; + video = + let + set = cfg.settings.visual; + mul = cfg.settings.multiplayer; + in + { + actionText = set.action_text; + bounciness = set.board_bounciness; + shakiness = set.damage_shakiness; + gridopacity = set.grid_opacity; + boardopacity = set.board_opacity; + shadowopacity = set.shadow_opacity; + zoom = set.board_zoom; + sidebyside = mul.duels_side_by_side; + spin = set.spin_board; + kos = mul.notify_elim; + siren = set.danger_warning; + colorshadow = set.colored_shadow; + + graphics = set.graphics.tier; + caching = set.graphics.cache; + webgl = set.graphics.pipeline; + particles = set.graphics.particle_count; + background = set.background_opacity; + powersave = set.graphics.powersave; + lowres = set.graphics.low_resolution; + lowrescounters = set.graphics.low_precision_counters; + alwaystiny = mul.simplify_thumbnails; + nosuperlobbyanim = mul.animate_super_lobby_background; + nozenithanim = mul.animate_background_in_quickplay; + nobg = set.show_background_in_menus; + chatfilter = mul.chat.filter; + nochat = mul.chat.enable; + hideroomids = mul.hide_room_ids; + emotes = mul.chat.show_emotes; + emotes_anim = mul.chat.show_animated_emotes; + invert = mul.chat.invert; + chatbg = mul.chat.darken; + replaytoolsnocollapse = cfg.settings.keep_replay_tools_open; + hidenetwork = mul.show_network_warnings; + focuswarning = set.show_unfocus_warning; + guide = mul.welcome_guide; + + desktopnotifications = mul.notifications.enable_desktop_notifications; + }; + notifications = + let + set = cfg.settings.multiplayer.notifications; + in + { + suppress = set.suppress_while_playing; + forcesound = set.full_volume; + online = set.when.friend_online; + offline = set.when.friend_offline; + dm = set.when.friend_dm_recieved; + dm_pending = set.when.dm_recieved; + invite = set.when.room_invite; + other = set.when.other; + }; + electron = { + loginskip = cfg.settings.skip_login_screen; + adblock = + cfg.settings.advertisments.i_support_the_devs.i_cannot_play_with_ads.and_i_really_want_to.disable; + taskbarflash = cfg.settings.flash_taskbar_icon; + autoupdate = false; + anglecompat = false; + presence = cfg.settings.discord_rpc; + devtools = cfg.settings.devtools; + }; + }; + }; + recursive = true; + }; + } + (mkIf (cfg.plus.enable) { + home.file.".config/tetrio-desktop/tetrioplus/tpkey-tetrioPlusEnabled.json" = { + text = '' + { "value": true } + ''; + force = true; + }; + home.file.".config/tetrio-desktop/tetrioplus/tpkey-hideTetrioPlusOnStartup.json" = { + text = '' + { "value": ${lib.trivial.boolToString cfg.plus.hideOnStartup} } + ''; + force = true; + }; + }) + (mkIf (cfg.plus.enable && cfg.plus ? skin) { + home.file.".config/tetrio-desktop/tetrioplus/tpkey-skin.json" = { + source = cfg.plus.skin.package; + force = true; + }; + home.file.".config/tetrio-desktop/tetrioplus/tpkey-forceNearestScaling.json" = { + text = '' + { "value": ${lib.trivial.boolToString cfg.plus.skin.nearest} } + ''; + force = true; + }; + }) + ] + ); +} diff --git a/pkgs/tetrio/skins/simple-connected.nix b/pkgs/tetrio/skins/simple-connected.nix new file mode 100644 index 0000000..7d5f7d5 --- /dev/null +++ b/pkgs/tetrio/skins/simple-connected.nix @@ -0,0 +1,31 @@ +{ + pkgs ? import { }, + ... +}: + +let + src = pkgs.fetchurl { + url = "https://you.have.fail/tetrioplus/data/tpsefiles/skin/SpooKoArts/simple_connected.zip.tpse"; + hash = "sha256-dIrEpEV9Gy2iU6K6rMrNX4XFQEchkJqSmOuQwVF4EQQ="; + }; +in +pkgs.stdenv.mkDerivation { + name = "simple-connected"; + version = "2022-06-26"; + inherit src; + + dontUnpack = true; + dontBuild = true; + + installPhase = '' + runHook preInstall + cp ${src} $out + runHook postInstall + ''; + + fixupPhase = '' + runHook preFixup + sed -i 's/\bskin\b/value/' $out + runHook postFixup + ''; +}