From 46028c3a9243665d1af8cda8f249b35730659748 Mon Sep 17 00:00:00 2001 From: rope Date: Fri, 30 Oct 2026 09:59:54 +0000 Subject: [PATCH] gaming: lightforge Co-Authored-By: Claude Opus 5.5 --- apps/lightforge.nix | 102 ++++++++++++++++++++++++++++ common.nix | 1 + patches/lightforge-nix-origin.patch | 60 ++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 apps/lightforge.nix create mode 100644 patches/lightforge-nix-origin.patch diff --git a/apps/lightforge.nix b/apps/lightforge.nix new file mode 100644 index 0000000..fa25b53 --- /dev/null +++ b/apps/lightforge.nix @@ -0,0 +1,102 @@ +#./apps/lightforge.nix +{ pkgs, lib, config, ... }: +let + lightforge-unwrapped = pkgs.rustPlatform.buildRustPackage (finalAttrs: { + pname = "lightforge-unwrapped"; + version = "1.0.0-beta.8"; + + src = pkgs.fetchFromGitHub { + owner = "LightforgeProject"; + repo = "lightforge"; + tag = "v${finalAttrs.version}"; + hash = "sha256-iVuHYDtMPi0kaV5hK0wsCOYaBMWbqikPbOTw6M/AbfM="; + }; + cargoHash = "sha256-mqePVSoavA34Q7XbPqFXbc71z+85mvCTN4J6dfPUTR0="; + + # Its updater sees a Nix store path as "unknown" and offers the curl + # installer; this makes it say "Nix updates this" instead. + patches = [ ../patches/lightforge-nix-origin.patch ]; + + # These write under a fixed /games and watch stand-in processes; the + # build sandbox allows neither. + checkFlags = map (t: "--skip=preflight::tests::${t}") [ + "a_pass_beside_the_client_leaves_it_running_and_touches_nothing" + "a_game_served_by_another_build_refuses_a_launch_beside_it" + "a_second_launch_goes_beside_a_running_game" + "quitting_the_client_beside_a_game_leaves_the_game_running" + ]; + + nativeBuildInputs = [ pkgs.pkg-config ]; + buildInputs = with pkgs; [ udev fontconfig ]; + + postInstall = '' + install -Dm644 packaging/pro.tuxcore.Lightforge.desktop -t $out/share/applications + install -Dm644 packaging/pro.tuxcore.Lightforge.metainfo.xml -t $out/share/metainfo + cp -r packaging/icons $out/share/icons + ''; + + meta = { + description = "Native Linux launcher for World of Warcraft and Battle.net"; + homepage = "https://github.com/LightforgeProject/lightforge"; + license = lib.licenses.gpl3Plus; + mainProgram = "lightforge-gui"; + platforms = [ "x86_64-linux" ]; + }; + }); + + # Lightforge downloads its own GE-Proton and runs it outside Steam's + # runtime, so Proton's Wine needs a regular FHS with a 32-bit side + # (Battle.net is 32-bit). Steam's env plus the WINE libraries Lutris lists. + fhs = pname: runScript: extraInstallCommands: pkgs.steam.buildRuntimeEnv { + inherit pname runScript extraInstallCommands; + inherit (lightforge-unwrapped) version meta; + + extraPkgs = p: [ lightforge-unwrapped p.mangohud ]; + extraLibraries = p: with p; [ + fontconfig + freetype + gnutls + p11-kit + libkrb5 + libgcrypt + libunwind + libpulseaudio + alsa-lib + dbus + SDL2 + libusb1 + libxkbcommon + wayland + libx11 + libxext + libxrandr + libxrender + libxi + libxcursor + libxcomposite + libxfixes + libxinerama + libxxf86vm + libxcb + gst_all_1.gstreamer + gst_all_1.gst-plugins-base + gst_all_1.gst-plugins-good + ]; + + privateTmp = false; + }; + +in +{ + config = lib.mkIf (config.networking.hostName == "FredOS-Gaming") { + environment.systemPackages = [ + (fhs "lightforge-gui" "lightforge-gui" '' + mkdir -p $out/share + ln -s ${lightforge-unwrapped}/share/applications $out/share + ln -s ${lightforge-unwrapped}/share/icons $out/share + '') + # The CLI: `lightforge doctor`, `lightforge status`, … + (fhs "lightforge" "lightforge" "") + ]; + }; +} diff --git a/common.nix b/common.nix index 5dbaec8..f02a852 100644 --- a/common.nix +++ b/common.nix @@ -93,6 +93,7 @@ in ./settings/shell.nix ./apps/zen.nix ./apps/lookout.nix + ./apps/lightforge.nix # Services # ./services/server-permissions.nix diff --git a/patches/lightforge-nix-origin.patch b/patches/lightforge-nix-origin.patch new file mode 100644 index 0000000..b017a36 --- /dev/null +++ b/patches/lightforge-nix-origin.patch @@ -0,0 +1,60 @@ +diff --git a/crates/core/src/upgrade/mod.rs b/crates/core/src/upgrade/mod.rs +index c5f3b87..c4fdd34 100644 +--- a/crates/core/src/upgrade/mod.rs ++++ b/crates/core/src/upgrade/mod.rs +@@ -56,6 +56,9 @@ pub enum Origin { + Checkout { repo: PathBuf }, + /// `cargo install`. + Cargo, ++ /// A Nix package, run from the store: Nix updates it with the rest of ++ /// the system or profile. ++ Nix, + /// None of these. + Unknown, + } +@@ -73,6 +76,10 @@ impl Origin { + /// where the system's own directories are read from. + pub fn detect_at(exe: &Path, home: &Path, cargo_home: Option<&Path>, root: &Path) -> Self { + let bin_dir = exe.parent().unwrap_or(Path::new("/")); ++ // Read-only and owned by Nix, whichever profile or wrapper started it. ++ if exe.starts_with("/nix/store") { ++ return Self::Nix; ++ } + if bin_dir == home.join(".local/bin") { + return Self::Installer { bin_dir: bin_dir.to_path_buf() }; + } +@@ -154,6 +161,11 @@ impl Origin { + )], + note: "This Lightforge was installed with cargo.".to_owned(), + }, ++ Self::Nix => say( ++ &[], ++ "Lightforge comes from Nix, which updates it: once the package or flake \ ++ input has the new version, rebuild your system or profile.", ++ ), + Self::Unknown => say( + &[INSTALLER_COMMAND], + "Lightforge cannot tell how it was installed here. The installer puts it \ +@@ -326,6 +338,10 @@ mod tests { + ); + assert_eq!(detect("/home/u/.cargo/bin/lightforge-gui", &empty), Origin::Cargo); + assert_eq!(detect("/opt/lightforge/lightforge-gui", &empty), Origin::Unknown); ++ assert_eq!( ++ detect("/nix/store/00000000000000000000000000000000-lightforge/bin/lightforge-gui", &empty), ++ Origin::Nix ++ ); + let _ = std::fs::remove_dir_all(&empty); + } + +diff --git a/crates/launcher-gui/src/tests.rs b/crates/launcher-gui/src/tests.rs +index 79c7465..e089466 100644 +--- a/crates/launcher-gui/src/tests.rs ++++ b/crates/launcher-gui/src/tests.rs +@@ -1190,6 +1190,7 @@ fn the_update_card_draws_for_every_route_and_stage() { + Origin::Deb, + Origin::Copr { atomic: true }, + Origin::Checkout { repo: "/home/u/lightforge".into() }, ++ Origin::Nix, + Origin::Unknown, + ]; + let stages = [