Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
b15c6850c3
|
|||
|
b38e5db90b
|
|||
|
e3a9a4bd1a
|
|||
|
65fc3e47b4
|
|||
|
c9e6ec7b23
|
Generated
+12
-12
@@ -44,11 +44,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1778105658,
|
"lastModified": 1778887053,
|
||||||
"narHash": "sha256-PxurBCejSjB99wxkpiPTUi4aHSYlfiHzrCwAGuDTRb8=",
|
"narHash": "sha256-0tSElqIlp+tbsMrbxVwzKlF3wrRO2GPGHmDqrMbaphs=",
|
||||||
"owner": "sadjow",
|
"owner": "sadjow",
|
||||||
"repo": "claude-code-nix",
|
"repo": "claude-code-nix",
|
||||||
"rev": "f065f848c6a11f93124b09164fa92e8253a0264d",
|
"rev": "faa8786775fa232366f95a6f8dc2de67a1fea0e5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -182,11 +182,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1778144356,
|
"lastModified": 1779143091,
|
||||||
"narHash": "sha256-dGM+QCstz/DyLB68+JK5GWyMx4QSqmOJEVgZmy63d/g=",
|
"narHash": "sha256-qxiUVquHM09KOV1aD4We9q0ooPWO4qOc0v9J9NDWSAo=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "e4419d3123b780d5f4c0bceeace450424387638c",
|
"rev": "736c2084ea750a049ecdbdd7ff5817d2eeeef444",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -298,11 +298,11 @@
|
|||||||
},
|
},
|
||||||
"nixos-hardware": {
|
"nixos-hardware": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1778143761,
|
"lastModified": 1779099457,
|
||||||
"narHash": "sha256-lkesY6x2X2qxlqLM7CT2iM/0rP2JB7fruPN3h8POXmI=",
|
"narHash": "sha256-u73aVD/lUmmT3JV+kPDztl7zPwQKd0eobD1AbJltaGs=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixos-hardware",
|
"repo": "nixos-hardware",
|
||||||
"rev": "3bcaa367d4c550d687a17ac792fd5cda214ee871",
|
"rev": "8792fab9d4a6454a9201675f01326f827ce35ead",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -314,11 +314,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1777954456,
|
"lastModified": 1778869304,
|
||||||
"narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=",
|
"narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1",
|
"rev": "d233902339c02a9c334e7e593de68855ad26c4cb",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -41,6 +41,11 @@
|
|||||||
];
|
];
|
||||||
programs.librewolf.enable = true;
|
programs.librewolf.enable = true;
|
||||||
services.easyeffects.enable = true;
|
services.easyeffects.enable = true;
|
||||||
|
systemd.user.services.easyeffects.Service = lib.mkIf osConfig.lumpiasty.audioRt.cpuPartitioning {
|
||||||
|
# Move easyeffects into audio.slice (defined in modules/desktop/audio-rt.nix)
|
||||||
|
# which has AllowedCPUs=<audioCpus> — pins all DSP work to the reserved cores.
|
||||||
|
Slice = "audio.slice";
|
||||||
|
};
|
||||||
|
|
||||||
programs.chromium.enable = true;
|
programs.chromium.enable = true;
|
||||||
programs.chromium.package = pkgs.ungoogled-chromium;
|
programs.chromium.package = pkgs.ungoogled-chromium;
|
||||||
|
|||||||
+3
-2
@@ -23,8 +23,8 @@ rec {
|
|||||||
|
|
||||||
# Kernel
|
# Kernel
|
||||||
# boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ];
|
# boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ];
|
||||||
boot.kernelPackages = nixpkgs-linuxeol.legacyPackages."x86_64-linux".linuxKernel.packages.linux_6_19;
|
boot.kernelPackages = pkgs.linuxKernel.packages.linux_7_0;
|
||||||
boot.zfs.package = pkgs.zfs_unstable;
|
boot.zfs.package = pkgs.zfs_2_4;
|
||||||
|
|
||||||
# Swap
|
# Swap
|
||||||
swapDevices = [
|
swapDevices = [
|
||||||
@@ -71,6 +71,7 @@ rec {
|
|||||||
amdCpu = true;
|
amdCpu = true;
|
||||||
noMitigations = false;
|
noMitigations = false;
|
||||||
enablePulseaudio = true;
|
enablePulseaudio = true;
|
||||||
|
audioRt.enable = true;
|
||||||
sshd = true;
|
sshd = true;
|
||||||
users.user = true;
|
users.user = true;
|
||||||
# users.drugi = true;
|
# users.drugi = true;
|
||||||
|
|||||||
+1
-1
@@ -53,7 +53,7 @@
|
|||||||
|
|
||||||
# Kernel
|
# Kernel
|
||||||
# boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ];
|
# boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ];
|
||||||
boot.kernelPackages = nixpkgs-linuxeol.legacyPackages."x86_64-linux".linuxKernel.packages.linux_6_19;
|
boot.kernelPackages = pkgs.linuxKernel.packages.linux_7_0;
|
||||||
|
|
||||||
# Swap
|
# Swap
|
||||||
zramSwap = {
|
zramSwap = {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
desktop/plasma.nix
|
desktop/plasma.nix
|
||||||
desktop/touchpad.nix
|
desktop/touchpad.nix
|
||||||
desktop/pulseaudio.nix
|
desktop/pulseaudio.nix
|
||||||
|
desktop/audio-rt.nix
|
||||||
desktop/tailscale.nix
|
desktop/tailscale.nix
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,233 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
# Workarounds for audio xruns under CPU load.
|
||||||
|
#
|
||||||
|
# Each optimization is independently toggleable so behavior can be bisected.
|
||||||
|
# `lumpiasty.audioRt.enable` is the master switch; individual sub-flags default
|
||||||
|
# to `true` when the master is on and can be flipped per-host to test impact.
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.lumpiasty.audioRt;
|
||||||
|
|
||||||
|
marchFlags = " -march=znver4 -O3";
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Per-build-system helpers (see commit history for rationale on LTO choices).
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
withMarch = pkg: pkg.overrideAttrs (old: {
|
||||||
|
env = (old.env or {}) // {
|
||||||
|
NIX_CFLAGS_COMPILE =
|
||||||
|
((old.env or {}).NIX_CFLAGS_COMPILE or old.NIX_CFLAGS_COMPILE or "")
|
||||||
|
+ marchFlags;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
cmakePkg = pkg: pkg.overrideAttrs (old: {
|
||||||
|
env = (old.env or {}) // {
|
||||||
|
NIX_CFLAGS_COMPILE =
|
||||||
|
((old.env or {}).NIX_CFLAGS_COMPILE or old.NIX_CFLAGS_COMPILE or "")
|
||||||
|
+ marchFlags;
|
||||||
|
};
|
||||||
|
cmakeFlags = (old.cmakeFlags or []) ++ [ "-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON" ];
|
||||||
|
preConfigure = (old.preConfigure or "") + "\nexport AR=gcc-ar\n";
|
||||||
|
});
|
||||||
|
|
||||||
|
rustPkg = pkg: pkg.overrideAttrs (old: {
|
||||||
|
RUSTFLAGS = (old.RUSTFLAGS or "") + " -C target-cpu=znver4";
|
||||||
|
});
|
||||||
|
|
||||||
|
in
|
||||||
|
|
||||||
|
{
|
||||||
|
options.lumpiasty.audioRt = {
|
||||||
|
enable = lib.mkEnableOption "Audio RT scheduling and CPU isolation";
|
||||||
|
|
||||||
|
audioCpus = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "12-15";
|
||||||
|
description = "CPU list reserved for audio services (systemd cpuset syntax).";
|
||||||
|
};
|
||||||
|
|
||||||
|
nonAudioCpus = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "0-11";
|
||||||
|
description = "CPU list for everything else.";
|
||||||
|
};
|
||||||
|
|
||||||
|
# ------ Individual optimization toggles ------
|
||||||
|
|
||||||
|
cpuPartitioning = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = cfg.enable;
|
||||||
|
description = ''
|
||||||
|
Cgroup-based CPU partitioning via dedicated audio.slice and
|
||||||
|
restricted app/session/background slices.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
rtLimits = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = cfg.enable;
|
||||||
|
description = ''
|
||||||
|
Raise rlimits (RTPRIO=95, MEMLOCK=infinity) for the audio group
|
||||||
|
so PipeWire's module-rt can set SCHED_FIFO 88 directly instead
|
||||||
|
of going through RTKit's priority-10 ceiling.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
performanceGovernor = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = cfg.enable;
|
||||||
|
description = ''
|
||||||
|
Keep cpufreq governor `performance` on the audio cores so they
|
||||||
|
stay boosted regardless of measured utilization.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
ananicy = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = cfg.enable;
|
||||||
|
description = ''
|
||||||
|
Run ananicy-cpp with a rule that pins easyeffects to nice -12 so
|
||||||
|
its non-RT DSP threads get scheduler preference under load.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
optimisedBinaries = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = cfg.enable;
|
||||||
|
description = ''
|
||||||
|
Rebuild easyeffects and its DSP dependencies with -march=znver4 -O3
|
||||||
|
(and LTO for cmake builds, target-cpu for rust builds).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkMerge [
|
||||||
|
|
||||||
|
# --- Optimised binary builds ---------------------------------------------
|
||||||
|
(lib.mkIf (cfg.enable && cfg.optimisedBinaries) {
|
||||||
|
nixpkgs.overlays = [
|
||||||
|
(final: prev: {
|
||||||
|
easyeffects = cmakePkg (prev.easyeffects.override {
|
||||||
|
fftw = withMarch prev.fftw;
|
||||||
|
fftwFloat = withMarch prev.fftwFloat;
|
||||||
|
speexdsp = withMarch prev.speexdsp;
|
||||||
|
rubberband = withMarch prev.rubberband;
|
||||||
|
soundtouch = withMarch prev.soundtouch;
|
||||||
|
zita-convolver = withMarch prev.zita-convolver;
|
||||||
|
webrtc-audio-processing = withMarch prev.webrtc-audio-processing;
|
||||||
|
rnnoise = withMarch prev.rnnoise;
|
||||||
|
libebur128 = cmakePkg prev.libebur128;
|
||||||
|
libbs2b = withMarch prev.libbs2b;
|
||||||
|
lilv = withMarch prev.lilv;
|
||||||
|
onetbb = cmakePkg prev.onetbb;
|
||||||
|
calf = cmakePkg prev.calf;
|
||||||
|
lsp-plugins = withMarch prev.lsp-plugins;
|
||||||
|
zam-plugins = withMarch prev.zam-plugins;
|
||||||
|
mda_lv2 = withMarch prev.mda_lv2;
|
||||||
|
deepfilternet = rustPkg prev.deepfilternet;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
];
|
||||||
|
})
|
||||||
|
|
||||||
|
# --- RT scheduling rlimits ----------------------------------------------
|
||||||
|
(lib.mkIf (cfg.enable && cfg.rtLimits) {
|
||||||
|
security.pam.loginLimits = [
|
||||||
|
{ domain = "@audio"; type = "-"; item = "rtprio"; value = "95"; }
|
||||||
|
{ domain = "@audio"; type = "-"; item = "memlock"; value = "unlimited"; }
|
||||||
|
{ domain = "@audio"; type = "-"; item = "nice"; value = "-20"; }
|
||||||
|
];
|
||||||
|
systemd.user.extraConfig = ''
|
||||||
|
DefaultLimitRTPRIO=95
|
||||||
|
DefaultLimitMEMLOCK=infinity
|
||||||
|
'';
|
||||||
|
})
|
||||||
|
|
||||||
|
# --- CPU partitioning (cgroup-based) ------------------------------------
|
||||||
|
#
|
||||||
|
# Cgroup hierarchy under user@.service:
|
||||||
|
# ├── app.slice AllowedCPUs=<nonAudioCpus> (Steam-launched apps)
|
||||||
|
# ├── session.slice AllowedCPUs=<nonAudioCpus> (kwin, plasmashell, kded)
|
||||||
|
# ├── background.slice AllowedCPUs=<nonAudioCpus> (akonadi, polkit)
|
||||||
|
# └── audio.slice AllowedCPUs=<audioCpus> (pipewire, easyeffects)
|
||||||
|
#
|
||||||
|
# Reasoning:
|
||||||
|
# - No isolcpus= : breaks scheduler load balancing on the rest of the system.
|
||||||
|
# - No nohz_full= : amd-pstate can't sample utilization in tickless mode
|
||||||
|
# so cores get clamped at minimum frequency.
|
||||||
|
# - No rcu_nocbs= : microsecond-scale jitter is irrelevant at 21ms quantum.
|
||||||
|
(lib.mkIf (cfg.enable && cfg.cpuPartitioning) {
|
||||||
|
systemd.user.extraConfig = ''
|
||||||
|
CPUAffinity=${cfg.nonAudioCpus}
|
||||||
|
'';
|
||||||
|
systemd.settings.Manager.CPUAffinity = cfg.nonAudioCpus;
|
||||||
|
|
||||||
|
# Delegate the cpuset controller to user managers so user-level slices
|
||||||
|
# can use AllowedCPUs=.
|
||||||
|
systemd.services."user@".serviceConfig.Delegate = "cpu cpuset io memory pids";
|
||||||
|
|
||||||
|
systemd.user.slices = {
|
||||||
|
app.sliceConfig.AllowedCPUs = cfg.nonAudioCpus;
|
||||||
|
session.sliceConfig.AllowedCPUs = cfg.nonAudioCpus;
|
||||||
|
background.sliceConfig.AllowedCPUs = cfg.nonAudioCpus;
|
||||||
|
audio = {
|
||||||
|
description = "Audio services pinned to reserved CPU cores";
|
||||||
|
sliceConfig.AllowedCPUs = cfg.audioCpus;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# easyeffects.service Slice= is set in home-modules/pc.nix.
|
||||||
|
systemd.user.services.pipewire.serviceConfig.Slice = "audio.slice";
|
||||||
|
systemd.user.services.pipewire-pulse.serviceConfig.Slice = "audio.slice";
|
||||||
|
systemd.user.services.wireplumber.serviceConfig.Slice = "audio.slice";
|
||||||
|
})
|
||||||
|
|
||||||
|
# --- Performance governor on audio cores --------------------------------
|
||||||
|
(lib.mkIf (cfg.enable && cfg.performanceGovernor) {
|
||||||
|
systemd.services.audio-cores-performance = {
|
||||||
|
description = "Keep performance governor on audio cores";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
after = [ "multi-user.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "simple";
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = "5s";
|
||||||
|
# Expand systemd CPU list ("12-15" / "12,13,14,15") into a flat list.
|
||||||
|
ExecStart = pkgs.writeShellScript "audio-cores-performance" ''
|
||||||
|
cpus=$(echo "${cfg.audioCpus}" | ${pkgs.coreutils}/bin/tr ',' ' ' | \
|
||||||
|
${pkgs.gawk}/bin/awk '{
|
||||||
|
for (i=1; i<=NF; i++) {
|
||||||
|
if (match($i, /^([0-9]+)-([0-9]+)$/, m))
|
||||||
|
for (j=m[1]; j<=m[2]; j++) print j
|
||||||
|
else print $i
|
||||||
|
}
|
||||||
|
}')
|
||||||
|
while true; do
|
||||||
|
for cpu in $cpus; do
|
||||||
|
cur=$(cat /sys/devices/system/cpu/cpu$cpu/cpufreq/scaling_governor)
|
||||||
|
if [ "$cur" != "performance" ]; then
|
||||||
|
echo performance > /sys/devices/system/cpu/cpu$cpu/cpufreq/scaling_governor
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
# --- Ananicy rule for easyeffects ---------------------------------------
|
||||||
|
(lib.mkIf (cfg.enable && cfg.ananicy) {
|
||||||
|
services.ananicy = {
|
||||||
|
enable = true;
|
||||||
|
package = pkgs.ananicy-cpp;
|
||||||
|
extraRules = [
|
||||||
|
{ name = "easyeffects"; type = "Audio"; nice = -12; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -58,6 +58,9 @@
|
|||||||
# Exclude audiocd-kio due to build failure
|
# Exclude audiocd-kio due to build failure
|
||||||
(builtins.filter (pkg: pkg.pname != "audiocd-kio"))
|
(builtins.filter (pkg: pkg.pname != "audiocd-kio"))
|
||||||
|
|
||||||
|
# Exclude audiotube due to build failure
|
||||||
|
(builtins.filter (pkg: pkg.pname != "audiotube"))
|
||||||
|
|
||||||
# Exclude plasma-mobile
|
# Exclude plasma-mobile
|
||||||
(builtins.filter (pkg: pkg.pname != "plasma-mobile"))
|
(builtins.filter (pkg: pkg.pname != "plasma-mobile"))
|
||||||
]) ++ [
|
]) ++ [
|
||||||
|
|||||||
@@ -20,13 +20,21 @@
|
|||||||
# no need to redefine it in your config for now)
|
# no need to redefine it in your config for now)
|
||||||
#media-session.enable = true;
|
#media-session.enable = true;
|
||||||
|
|
||||||
|
extraConfig.pipewire."99-quantum" = {
|
||||||
|
"context.properties" = {
|
||||||
|
"default.clock.quantum" = 1024;
|
||||||
|
"default.clock.min-quantum" = 1024;
|
||||||
|
"default.clock.max-quantum" = 8192;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
wireplumber.configPackages = [
|
wireplumber.configPackages = [
|
||||||
(pkgs.writeTextDir "share/wireplumber/wireplumber.conf.d/99-alsa-nova-3.conf" ''
|
(pkgs.writeTextDir "share/wireplumber/wireplumber.conf.d/99-alsa-nova-3.conf" ''
|
||||||
monitor.alsa.rules = [
|
monitor.alsa.rules = [
|
||||||
{
|
{
|
||||||
matches = [
|
matches = [
|
||||||
{
|
{
|
||||||
node.name = "alsa_output.usb-SteelSeries_Arctis_Nova_3-00.analog-stereo"
|
node.name = "alsa_output.usb-SteelSeries_Arctis_Nova_7-00.analog-stereo"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
actions = {
|
actions = {
|
||||||
@@ -34,7 +42,7 @@
|
|||||||
audio.format = "S24LE"
|
audio.format = "S24LE"
|
||||||
audio.rate = 96000
|
audio.rate = 96000
|
||||||
api.alsa.period-size = 1024
|
api.alsa.period-size = 1024
|
||||||
api.alsa.period-num = 4
|
api.alsa.period-num = 8
|
||||||
api.alsa.disable-batch = false
|
api.alsa.disable-batch = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
stdenv.mkDerivation {
|
stdenv.mkDerivation {
|
||||||
pname = "opencode-claude-auth";
|
pname = "opencode-claude-auth";
|
||||||
version = "1.5.0";
|
version = "1.5.4";
|
||||||
|
|
||||||
src = fetchurl {
|
src = fetchurl {
|
||||||
url = "https://registry.npmjs.org/opencode-claude-auth/-/opencode-claude-auth-1.5.0.tgz";
|
url = "https://registry.npmjs.org/opencode-claude-auth/-/opencode-claude-auth-1.5.4.tgz";
|
||||||
hash = "sha512-5NSL+x++VTe2ZrFSznXKv7imiKObIBz0QXPuL+g1NAXAcdTGcbEbQBvvHZeIaSBNjmwpY2MR67Yez1f3LlPl7w==";
|
hash = "sha256-9iByuNTg/MTD3VGeqpBaBCBaooXm97BuvP0fPXDoPGc=";
|
||||||
};
|
};
|
||||||
|
|
||||||
dontBuild = true;
|
dontBuild = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user