Skip to content

Commit

Permalink
Merge pull request #17 from slonkazoid/main
Browse files Browse the repository at this point in the history
improved linux & user flatpak support
  • Loading branch information
slonkazoid authored Jan 16, 2025
1 parent 1d58436 commit e65ba03
Show file tree
Hide file tree
Showing 8 changed files with 446 additions and 59 deletions.
6 changes: 6 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"printWidth": 120,
"trailingComma": "none",
"tabWidth": 2,
"singleQuote": false
}
45 changes: 45 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ clap_complete = "4.5.41"
serde = { version = "1.0.217", features = ["derive"] }
log = "0.4.22"
env_logger = "0.11.6"
nix = { version = "0.29.0", features = ["user"] }
serde_ini = "0.2.0"
serde-value = "0.7.0"
3 changes: 3 additions & 0 deletions crates/libmoonlight/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ serde_json = { workspace = true }
serde = { workspace = true }
clap = { workspace = true }
thiserror = { workspace = true }
nix = { workspace = true }
serde_ini = { workspace = true }
serde-value = { workspace = true }
27 changes: 27 additions & 0 deletions crates/libmoonlight/src/injector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const os = require("node:os");
const fs = require("node:fs");
const path = require("node:path");
const process = require("node:process");

function getInjector(override) {
if (override !== null && fs.existsSync(override)) return override;

// resolve default path
switch (os.platform()) {
case "windows":
return path.join(process.env.APPDATA, "moonlight-mod", DOWNLOAD_DIR, "injector.js");
case "darwin":
return path.join(os.homedir(), "Library", "Application Support", "moonlight-mod", DOWNLOAD_DIR, "injector.js");
case "linux":
default:
return path.join(
process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config"),
"moonlight-mod",
DOWNLOAD_DIR,
"injector.js"
);
}
}

const injector = getInjector(MOONLIGHT_INJECTOR);
require(injector).inject(path.resolve(__dirname, `../${PATCHED_ASAR}`));
173 changes: 115 additions & 58 deletions crates/libmoonlight/src/installer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use super::types::{Branch, DetectedInstall, GitHubRelease, InstallInfo, MoonlightBranch};
use super::util::get_download_dir;
use crate::{get_app_dir, get_moonlight_dir, PATCHED_ASAR};
use super::util::{get_download_dir, get_home_dir};
use crate::types::{
FlatpakFilesystemOverride, FlatpakFilesystemOverridePermission, FlatpakOverrides,
};
use crate::{get_app_dir, get_local_share, get_moonlight_dir, DOWNLOAD_DIR, PATCHED_ASAR};
use std::path::PathBuf;

const USER_AGENT: &str =
Expand Down Expand Up @@ -127,23 +130,15 @@ impl Installer {
match std::env::consts::OS {
"windows" => {
let appdata = std::env::var("LocalAppData").unwrap();
let dirs = vec![
"Discord",
"DiscordPTB",
"DiscordCanary",
"DiscordDevelopment",
let dirs = [
("Discord", Branch::Stable),
("Discord PTB", Branch::PTB),
("Discord Canary", Branch::Canary),
("Discord Development", Branch::Development),
];
let mut installs = vec![];

for dir in dirs {
let branch = match dir {
"Discord" => Branch::Stable,
"DiscordPTB" => Branch::PTB,
"DiscordCanary" => Branch::Canary,
"DiscordDevelopment" => Branch::Development,
_ => unreachable!(),
};

for (dir, branch) in dirs {
let path = PathBuf::from(appdata.clone()).join(dir);
if path.exists() {
// app-(version)
Expand All @@ -162,6 +157,7 @@ impl Installer {
installs.push(DetectedInstall {
branch,
path: most_recent_install.path(),
flatpak_id: None,
});
}
}
Expand All @@ -173,28 +169,20 @@ impl Installer {
"macos" => {
let apps_dirs = vec![
PathBuf::from("/Applications"),
PathBuf::from(std::env::var("HOME").unwrap()).join("Applications"),
get_home_dir().join("Applications"),
];

let branch_names = vec![
"Discord",
"Discord PTB",
"Discord Canary",
"Discord Development",
let branches = [
("Discord", Branch::Stable),
("Discord PTB", Branch::PTB),
("Discord Canary", Branch::Canary),
("Discord Development", Branch::Development),
];

let mut installs = vec![];

for apps_dir in apps_dirs {
for branch_name in branch_names.clone() {
let branch = match branch_name {
"Discord" => Branch::Stable,
"Discord PTB" => Branch::PTB,
"Discord Canary" => Branch::Canary,
"Discord Development" => Branch::Development,
_ => unreachable!(),
};

for (branch_name, branch) in branches {
let macos_app_dir = apps_dir.join(format!("{branch_name}.app"));

if !macos_app_dir.exists() {
Expand All @@ -206,6 +194,7 @@ impl Installer {
installs.push(DetectedInstall {
branch,
path: app_dir,
flatpak_id: None,
});
}
}
Expand All @@ -214,31 +203,26 @@ impl Installer {
}

"linux" => {
let home = std::env::var("HOME").unwrap();
let local_share = std::env::var_os("MOONLIGHT_DISCORD_SHARE_LINUX")
.map(PathBuf::from)
.unwrap_or(PathBuf::from(home).join(".local/share"));

let dirs = vec![
"Discord",
"DiscordPTB",
"DiscordCanary",
"DiscordDevelopment",
let local_share = get_local_share();
let dirs = [
("Discord", Branch::Stable, None),
("DiscordPTB", Branch::PTB, None),
("DiscordCanary", Branch::Canary, None),
("DiscordDevelopment", Branch::Development, None),
// flatpak user installations
("flatpak/app/com.discordapp.Discord/current/active/files/discord", Branch::Stable, Some("com.discordapp.Discord")),
("flatpak/app/com.discordapp.DiscordCanary/current/active/files/discord-canary", Branch::Canary, Some("com.discordapp.DiscordCanary")),
];

let mut installs = vec![];
for dir in dirs {
let branch = match dir {
"Discord" => Branch::Stable,
"DiscordPTB" => Branch::PTB,
"DiscordCanary" => Branch::Canary,
"DiscordDevelopment" => Branch::Development,
_ => unreachable!(),
};

for (dir, branch, id) in dirs {
let path = local_share.join(dir);
if path.exists() {
installs.push(DetectedInstall { branch, path });
installs.push(DetectedInstall {
branch,
path,
flatpak_id: id.map(Into::into),
});
}
}

Expand All @@ -255,6 +239,72 @@ impl Installer {
Ok(!get_app_dir(&install.path)?.join("app.asar").exists())
}

fn get_flatpak_overrides(&self, id: &str) -> crate::Result<Option<FlatpakOverrides>> {
let overrides = get_local_share().join("flatpak").join("overrides");

std::fs::create_dir_all(&overrides)?;

let app_overrides = overrides.join(id);

let file = match std::fs::OpenOptions::new().read(true).open(&app_overrides) {
Ok(v) => v,
Err(err) => match err.kind() {
std::io::ErrorKind::NotFound => return Ok(None),
_ => return Err(err.into()),
},
};

serde_ini::from_read(file).or(Ok(None))
}

fn ensure_flatpak_overrides(&self, id: &str) -> crate::Result<()> {
let overrides = self.get_flatpak_overrides(id)?;

let has = overrides
.as_ref()
.and_then(|v| v.context.as_ref())
.and_then(|v| v.filesystems.as_ref())
.is_some_and(|v| {
v.iter().any(|entry| {
entry.path == "xdg-config/moonlight-mod"
&& entry.permission == FlatpakFilesystemOverridePermission::ReadWrite
})
});

if has {
return Ok(());
}

let mut overrides = overrides.unwrap_or_default();

if overrides.context.is_none() {
overrides.context = Some(Default::default());
}
let context = overrides.context.as_mut().unwrap();

if context.filesystems.is_none() {
context.filesystems = Some(Default::default());
}
let filesystem = context.filesystems.as_mut().unwrap();

filesystem.push(FlatpakFilesystemOverride {
path: String::from("xdg-config/moonlight-mod"),
permission: FlatpakFilesystemOverridePermission::ReadWrite,
});

let app_overrides = get_local_share().join("flatpak").join("overrides").join(id);
let mut file = std::fs::OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.append(false)
.open(&app_overrides)?;

serde_ini::to_writer(&mut file, &overrides).expect("ini serialization to succeed");

Ok(())
}

pub fn patch_install(
&self,
install: &DetectedInstall,
Expand All @@ -273,16 +323,23 @@ impl Installer {
});
std::fs::write(app_dir.join("app/package.json"), json.to_string())?;

let moonlight_dir = moonlight_dir.unwrap_or_else(get_download_dir);
let moonlight_injector = moonlight_dir.join("injector.js");
let moonlight_injector_str = serde_json::to_string(&moonlight_injector).unwrap();
let moonlight_injector = moonlight_dir.map(|m| m.join("injector.js"));
let injector = format!(
r#"require({moonlight_injector_str}).inject(
require("path").resolve(__dirname, "../{PATCHED_ASAR}")
);
"#
r#"const MOONLIGHT_INJECTOR = {};
const PATCHED_ASAR = {};
const DOWNLOAD_DIR = {};
{}"#,
serde_json::to_string(&moonlight_injector).unwrap(),
serde_json::to_string(PATCHED_ASAR).unwrap(),
serde_json::to_string(DOWNLOAD_DIR).unwrap(),
include_str!("injector.js")
);
std::fs::write(app_dir.join("app/injector.js"), injector)?;

if let Some(flatpak_id) = install.flatpak_id.as_deref() {
self.ensure_flatpak_overrides(flatpak_id)?;
}

Ok(())
}

Expand Down
Loading

0 comments on commit e65ba03

Please sign in to comment.