fullscreen (#360)
* fullscreen * improvements, and error catching * yarn prettier * discord rpc * fixed uninitialized options.txt * working discord version * incorrect boolean * change * merge issue; regex solution * fixed error * multi line mode * moved \n to start
This commit is contained in:
parent
ce01ee6a2d
commit
c364468ed5
48
Cargo.lock
generated
48
Cargo.lock
generated
@ -575,7 +575,7 @@ checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"fnv",
|
"fnv",
|
||||||
"uuid",
|
"uuid 1.4.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -986,7 +986,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d"
|
checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"uuid",
|
"uuid 1.4.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1107,6 +1107,18 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "discord-rich-presence"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47fc4beffb85ee1461588499073a4d9c20dcc7728c4b13d6b282ab6c508947e5"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"uuid 0.8.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dispatch"
|
name = "dispatch"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -2522,7 +2534,7 @@ dependencies = [
|
|||||||
"crash-handler",
|
"crash-handler",
|
||||||
"minidumper",
|
"minidumper",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"uuid",
|
"uuid 1.4.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3810,7 +3822,7 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
"time 0.3.22",
|
"time 0.3.22",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid 1.4.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4299,7 +4311,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"tao-macros",
|
"tao-macros",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"uuid",
|
"uuid 1.4.0",
|
||||||
"windows 0.39.0",
|
"windows 0.39.0",
|
||||||
"windows-implement",
|
"windows-implement",
|
||||||
"x11-dl",
|
"x11-dl",
|
||||||
@ -4382,7 +4394,7 @@ dependencies = [
|
|||||||
"time 0.3.22",
|
"time 0.3.22",
|
||||||
"tokio",
|
"tokio",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid 1.4.0",
|
||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webview2-com",
|
"webview2-com",
|
||||||
"windows 0.39.0",
|
"windows 0.39.0",
|
||||||
@ -4428,7 +4440,7 @@ dependencies = [
|
|||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"time 0.3.22",
|
"time 0.3.22",
|
||||||
"uuid",
|
"uuid 1.4.0",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4506,7 +4518,7 @@ dependencies = [
|
|||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid 1.4.0",
|
||||||
"webview2-com",
|
"webview2-com",
|
||||||
"windows 0.39.0",
|
"windows 0.39.0",
|
||||||
]
|
]
|
||||||
@ -4524,7 +4536,7 @@ dependencies = [
|
|||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"tauri-runtime",
|
"tauri-runtime",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"uuid",
|
"uuid 1.4.0",
|
||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webview2-com",
|
"webview2-com",
|
||||||
"windows 0.39.0",
|
"windows 0.39.0",
|
||||||
@ -4606,6 +4618,7 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"daedalus",
|
"daedalus",
|
||||||
"dirs 5.0.1",
|
"dirs 5.0.1",
|
||||||
|
"discord-rich-presence",
|
||||||
"dunce",
|
"dunce",
|
||||||
"futures",
|
"futures",
|
||||||
"indicatif",
|
"indicatif",
|
||||||
@ -4633,7 +4646,7 @@ dependencies = [
|
|||||||
"tracing-error 0.1.2",
|
"tracing-error 0.1.2",
|
||||||
"tracing-subscriber 0.2.25",
|
"tracing-subscriber 0.2.25",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid 1.4.0",
|
||||||
"whoami",
|
"whoami",
|
||||||
"winreg 0.50.0",
|
"winreg 0.50.0",
|
||||||
"zip",
|
"zip",
|
||||||
@ -4661,7 +4674,7 @@ dependencies = [
|
|||||||
"tracing-futures",
|
"tracing-futures",
|
||||||
"tracing-subscriber 0.3.17",
|
"tracing-subscriber 0.3.17",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid 1.4.0",
|
||||||
"webbrowser",
|
"webbrowser",
|
||||||
"winreg 0.11.0",
|
"winreg 0.11.0",
|
||||||
]
|
]
|
||||||
@ -4695,7 +4708,7 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
"tracing-error 0.1.2",
|
"tracing-error 0.1.2",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid 1.4.0",
|
||||||
"window-shadows",
|
"window-shadows",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4725,7 +4738,7 @@ dependencies = [
|
|||||||
"tracing-error 0.1.2",
|
"tracing-error 0.1.2",
|
||||||
"tracing-subscriber 0.2.25",
|
"tracing-subscriber 0.2.25",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid 1.4.0",
|
||||||
"webbrowser",
|
"webbrowser",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -5209,6 +5222,15 @@ version = "0.7.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.10",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
|||||||
@ -55,6 +55,8 @@ dunce = "1.0.3"
|
|||||||
|
|
||||||
whoami = "1.4.0"
|
whoami = "1.4.0"
|
||||||
|
|
||||||
|
discord-rich-presence = "0.2.3"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winreg = "0.50.0"
|
winreg = "0.50.0"
|
||||||
|
|
||||||
|
|||||||
@ -27,7 +27,7 @@ pub mod prelude {
|
|||||||
jre, metadata, pack, process,
|
jre, metadata, pack, process,
|
||||||
profile::{self, create, Profile},
|
profile::{self, create, Profile},
|
||||||
settings,
|
settings,
|
||||||
state::JavaGlobals,
|
state::{JavaGlobals, SetFullscreen},
|
||||||
state::{ProfilePathId, ProjectPathId},
|
state::{ProfilePathId, ProjectPathId},
|
||||||
util::{
|
util::{
|
||||||
io::{canonicalize, IOError},
|
io::{canonicalize, IOError},
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
use std::{collections::HashMap, path::PathBuf};
|
use std::{collections::HashMap, path::PathBuf};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::fs;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
event::LoadingBarId,
|
event::LoadingBarId,
|
||||||
@ -106,7 +105,7 @@ pub struct ATLauncherMod {
|
|||||||
// Check if folder has a instance.json that parses
|
// Check if folder has a instance.json that parses
|
||||||
pub async fn is_valid_atlauncher(instance_folder: PathBuf) -> bool {
|
pub async fn is_valid_atlauncher(instance_folder: PathBuf) -> bool {
|
||||||
let instance: String =
|
let instance: String =
|
||||||
fs::read_to_string(&instance_folder.join("instance.json"))
|
io::read_to_string(&instance_folder.join("instance.json"))
|
||||||
.await
|
.await
|
||||||
.unwrap_or("".to_string());
|
.unwrap_or("".to_string());
|
||||||
let instance: Result<ATInstance, serde_json::Error> =
|
let instance: Result<ATInstance, serde_json::Error> =
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::fs;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
prelude::{ModLoader, ProfilePathId},
|
prelude::{ModLoader, ProfilePathId},
|
||||||
@ -42,7 +41,7 @@ pub struct MinecraftInstance {
|
|||||||
// Check if folder has a minecraftinstance.json that parses
|
// Check if folder has a minecraftinstance.json that parses
|
||||||
pub async fn is_valid_curseforge(instance_folder: PathBuf) -> bool {
|
pub async fn is_valid_curseforge(instance_folder: PathBuf) -> bool {
|
||||||
let minecraftinstance: String =
|
let minecraftinstance: String =
|
||||||
fs::read_to_string(&instance_folder.join("minecraftinstance.json"))
|
io::read_to_string(&instance_folder.join("minecraftinstance.json"))
|
||||||
.await
|
.await
|
||||||
.unwrap_or("".to_string());
|
.unwrap_or("".to_string());
|
||||||
let minecraftinstance: Result<MinecraftInstance, serde_json::Error> =
|
let minecraftinstance: Result<MinecraftInstance, serde_json::Error> =
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::fs;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
prelude::{ModLoader, ProfilePathId},
|
prelude::{ModLoader, ProfilePathId},
|
||||||
@ -32,7 +31,7 @@ pub struct GDLauncherLoader {
|
|||||||
// Check if folder has a config.json that parses
|
// Check if folder has a config.json that parses
|
||||||
pub async fn is_valid_gdlauncher(instance_folder: PathBuf) -> bool {
|
pub async fn is_valid_gdlauncher(instance_folder: PathBuf) -> bool {
|
||||||
let config: String =
|
let config: String =
|
||||||
fs::read_to_string(&instance_folder.join("config.json"))
|
io::read_to_string(&instance_folder.join("config.json"))
|
||||||
.await
|
.await
|
||||||
.unwrap_or("".to_string());
|
.unwrap_or("".to_string());
|
||||||
let config: Result<GDLauncherConfig, serde_json::Error> =
|
let config: Result<GDLauncherConfig, serde_json::Error> =
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use serde::{de, Deserialize, Serialize};
|
use serde::{de, Deserialize, Serialize};
|
||||||
use tokio::fs;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
pack::{
|
pack::{
|
||||||
@ -126,7 +125,7 @@ pub async fn is_valid_mmc(instance_folder: PathBuf) -> bool {
|
|||||||
let instance_cfg = instance_folder.join("instance.cfg");
|
let instance_cfg = instance_folder.join("instance.cfg");
|
||||||
let mmc_pack = instance_folder.join("mmc-pack.json");
|
let mmc_pack = instance_folder.join("mmc-pack.json");
|
||||||
|
|
||||||
let mmc_pack = match fs::read_to_string(&mmc_pack).await {
|
let mmc_pack = match io::read_to_string(&mmc_pack).await {
|
||||||
Ok(mmc_pack) => mmc_pack,
|
Ok(mmc_pack) => mmc_pack,
|
||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -66,45 +66,46 @@ pub async fn get_importable_instances(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Import an instance from a launcher type and base path
|
// Import an instance from a launcher type and base path
|
||||||
|
// Note: this *deletes* the submitted empty profile
|
||||||
#[theseus_macros::debug_pin]
|
#[theseus_macros::debug_pin]
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn import_instance(
|
pub async fn import_instance(
|
||||||
profile_path: ProfilePathId,
|
profile_path: ProfilePathId, // This should be a blank profile
|
||||||
launcher_type: ImportLauncherType,
|
launcher_type: ImportLauncherType,
|
||||||
base_path: PathBuf,
|
base_path: PathBuf,
|
||||||
instance_folder: String,
|
instance_folder: String,
|
||||||
) -> crate::Result<()> {
|
) -> crate::Result<()> {
|
||||||
tracing::debug!("Importing instance from {instance_folder}");
|
tracing::debug!("Importing instance from {instance_folder}");
|
||||||
match launcher_type {
|
let res = match launcher_type {
|
||||||
ImportLauncherType::MultiMC | ImportLauncherType::PrismLauncher => {
|
ImportLauncherType::MultiMC | ImportLauncherType::PrismLauncher => {
|
||||||
mmc::import_mmc(
|
mmc::import_mmc(
|
||||||
base_path, // path to base mmc folder
|
base_path, // path to base mmc folder
|
||||||
instance_folder, // instance folder in mmc_base_path
|
instance_folder, // instance folder in mmc_base_path
|
||||||
profile_path, // path to profile
|
profile_path.clone(), // path to profile
|
||||||
)
|
)
|
||||||
.await?;
|
.await
|
||||||
}
|
}
|
||||||
ImportLauncherType::ATLauncher => {
|
ImportLauncherType::ATLauncher => {
|
||||||
atlauncher::import_atlauncher(
|
atlauncher::import_atlauncher(
|
||||||
base_path, // path to atlauncher folder
|
base_path, // path to atlauncher folder
|
||||||
instance_folder, // instance folder in atlauncher
|
instance_folder, // instance folder in atlauncher
|
||||||
profile_path, // path to profile
|
profile_path.clone(), // path to profile
|
||||||
)
|
)
|
||||||
.await?;
|
.await
|
||||||
}
|
}
|
||||||
ImportLauncherType::GDLauncher => {
|
ImportLauncherType::GDLauncher => {
|
||||||
gdlauncher::import_gdlauncher(
|
gdlauncher::import_gdlauncher(
|
||||||
base_path.join("instances").join(instance_folder), // path to gdlauncher folder
|
base_path.join("instances").join(instance_folder), // path to gdlauncher folder
|
||||||
profile_path, // path to profile
|
profile_path.clone(), // path to profile
|
||||||
)
|
)
|
||||||
.await?;
|
.await
|
||||||
}
|
}
|
||||||
ImportLauncherType::Curseforge => {
|
ImportLauncherType::Curseforge => {
|
||||||
curseforge::import_curseforge(
|
curseforge::import_curseforge(
|
||||||
base_path.join("Instances").join(instance_folder), // path to curseforge folder
|
base_path.join("Instances").join(instance_folder), // path to curseforge folder
|
||||||
profile_path, // path to profile
|
profile_path.clone(), // path to profile
|
||||||
)
|
)
|
||||||
.await?;
|
.await
|
||||||
}
|
}
|
||||||
ImportLauncherType::Unknown => {
|
ImportLauncherType::Unknown => {
|
||||||
return Err(crate::ErrorKind::InputError(
|
return Err(crate::ErrorKind::InputError(
|
||||||
@ -112,6 +113,16 @@ pub async fn import_instance(
|
|||||||
)
|
)
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// If import failed, delete the profile
|
||||||
|
match res {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!("Import failed: {:?}", e);
|
||||||
|
let _ = crate::api::profile::remove(&profile_path).await;
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check existing managed packs for potential updates
|
// Check existing managed packs for potential updates
|
||||||
|
|||||||
@ -121,7 +121,7 @@ pub async fn wait_for_by_uuid(uuid: &Uuid) -> crate::Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kill a running child process directly, and wait for it to be killed
|
// Kill a running child process directly
|
||||||
#[tracing::instrument(skip(running))]
|
#[tracing::instrument(skip(running))]
|
||||||
pub async fn kill(running: &mut MinecraftChild) -> crate::Result<()> {
|
pub async fn kill(running: &mut MinecraftChild) -> crate::Result<()> {
|
||||||
running
|
running
|
||||||
@ -131,7 +131,7 @@ pub async fn kill(running: &mut MinecraftChild) -> crate::Result<()> {
|
|||||||
.kill()
|
.kill()
|
||||||
.await
|
.await
|
||||||
.map_err(IOError::from)?;
|
.map_err(IOError::from)?;
|
||||||
wait_for(running).await
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Await on the completion of a child process directly
|
// Await on the completion of a child process directly
|
||||||
|
|||||||
@ -7,7 +7,9 @@ use crate::event::LoadingBarType;
|
|||||||
use crate::pack::install_from::{
|
use crate::pack::install_from::{
|
||||||
EnvType, PackDependency, PackFile, PackFileHash, PackFormat,
|
EnvType, PackDependency, PackFile, PackFileHash, PackFormat,
|
||||||
};
|
};
|
||||||
use crate::prelude::{JavaVersion, ProfilePathId, ProjectPathId};
|
use crate::prelude::{
|
||||||
|
JavaVersion, ProfilePathId, ProjectPathId, SetFullscreen,
|
||||||
|
};
|
||||||
use crate::state::ProjectMetadata;
|
use crate::state::ProjectMetadata;
|
||||||
|
|
||||||
use crate::util::io::{self, IOError};
|
use crate::util::io::{self, IOError};
|
||||||
@ -838,9 +840,23 @@ pub async fn run_credentials(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Any options.txt settings that we want set, add here
|
||||||
|
let mut mc_set_options: Vec<(String, String)> = Vec::new();
|
||||||
|
match profile.force_fullscreen {
|
||||||
|
SetFullscreen::LeaveUnset => {}
|
||||||
|
SetFullscreen::SetWindowed => {
|
||||||
|
mc_set_options
|
||||||
|
.push(("fullscreen".to_string(), "false".to_string()));
|
||||||
|
}
|
||||||
|
SetFullscreen::SetFullscreen => {
|
||||||
|
mc_set_options.push(("fullscreen".to_string(), "true".to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mc_process = crate::launcher::launch_minecraft(
|
let mc_process = crate::launcher::launch_minecraft(
|
||||||
java_args,
|
java_args,
|
||||||
env_args,
|
env_args,
|
||||||
|
&mc_set_options,
|
||||||
wrapper,
|
wrapper,
|
||||||
&memory,
|
&memory,
|
||||||
&resolution,
|
&resolution,
|
||||||
|
|||||||
@ -49,6 +49,9 @@ pub enum ErrorKind {
|
|||||||
#[error("Incorrect Sha1 hash for download: {0} != {1}")]
|
#[error("Incorrect Sha1 hash for download: {0} != {1}")]
|
||||||
HashError(String, String),
|
HashError(String, String),
|
||||||
|
|
||||||
|
#[error("Regex error: {0}")]
|
||||||
|
RegexError(#[from] regex::Error),
|
||||||
|
|
||||||
#[error("Paths stored in the database need to be valid UTF-8: {0}")]
|
#[error("Paths stored in the database need to be valid UTF-8: {0}")]
|
||||||
UTFError(std::path::PathBuf),
|
UTFError(std::path::PathBuf),
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
use crate::event::emit::{emit_loading, init_or_edit_loading};
|
use crate::event::emit::{emit_loading, init_or_edit_loading};
|
||||||
use crate::event::{LoadingBarId, LoadingBarType};
|
use crate::event::{LoadingBarId, LoadingBarType};
|
||||||
use crate::jre::{self, JAVA_17_KEY, JAVA_18PLUS_KEY, JAVA_8_KEY};
|
use crate::jre::{self, JAVA_17_KEY, JAVA_18PLUS_KEY, JAVA_8_KEY};
|
||||||
|
use crate::launcher::io::IOError;
|
||||||
use crate::prelude::JavaVersion;
|
use crate::prelude::JavaVersion;
|
||||||
use crate::state::ProfileInstallStage;
|
use crate::state::ProfileInstallStage;
|
||||||
use crate::util::io;
|
use crate::util::io;
|
||||||
@ -164,6 +165,16 @@ pub async fn install_minecraft(
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
// Test jre version
|
||||||
|
let java_version = jre::check_jre(java_version.path.clone().into())
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| {
|
||||||
|
crate::ErrorKind::LauncherError(format!(
|
||||||
|
"Java path invalid or non-functional: {}",
|
||||||
|
java_version.path
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
// Download minecraft (5-90)
|
// Download minecraft (5-90)
|
||||||
download::download_minecraft(
|
download::download_minecraft(
|
||||||
&state,
|
&state,
|
||||||
@ -246,6 +257,7 @@ pub async fn install_minecraft(
|
|||||||
)?)
|
)?)
|
||||||
.output()
|
.output()
|
||||||
.await
|
.await
|
||||||
|
.map_err(|e| IOError::with_path(e, &java_version.path))
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
crate::ErrorKind::LauncherError(format!(
|
crate::ErrorKind::LauncherError(format!(
|
||||||
"Error running processor: {err}",
|
"Error running processor: {err}",
|
||||||
@ -291,6 +303,7 @@ pub async fn install_minecraft(
|
|||||||
pub async fn launch_minecraft(
|
pub async fn launch_minecraft(
|
||||||
java_args: &[String],
|
java_args: &[String],
|
||||||
env_args: &[(String, String)],
|
env_args: &[(String, String)],
|
||||||
|
mc_set_options: &[(String, String)],
|
||||||
wrapper: &Option<String>,
|
wrapper: &Option<String>,
|
||||||
memory: &st::MemorySettings,
|
memory: &st::MemorySettings,
|
||||||
resolution: &st::WindowSize,
|
resolution: &st::WindowSize,
|
||||||
@ -440,6 +453,33 @@ pub async fn launch_minecraft(
|
|||||||
}
|
}
|
||||||
command.envs(env_args);
|
command.envs(env_args);
|
||||||
|
|
||||||
|
// Overwrites the minecraft options.txt file with the settings from the profile
|
||||||
|
// Uses 'a:b' syntax which is not quite yaml
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
let options_path = instance_path.join("options.txt");
|
||||||
|
let mut options_string = String::new();
|
||||||
|
|
||||||
|
if options_path.exists() {
|
||||||
|
options_string = io::read_to_string(&options_path).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (key, value) in mc_set_options {
|
||||||
|
let re = Regex::new(&format!(r"(?m)^{}:.*$", regex::escape(key)))?;
|
||||||
|
// check if the regex exists in the file
|
||||||
|
if !re.is_match(&options_string) {
|
||||||
|
// The key was not found in the file, so append it
|
||||||
|
options_string.push_str(&format!("\n{}:{}", key, value));
|
||||||
|
} else {
|
||||||
|
let replaced_string = re
|
||||||
|
.replace_all(&options_string, &format!("{}:{}", key, value))
|
||||||
|
.to_string();
|
||||||
|
options_string = replaced_string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
io::write(&options_path, options_string).await?;
|
||||||
|
|
||||||
// Get Modrinth logs directories
|
// Get Modrinth logs directories
|
||||||
let datetime_string =
|
let datetime_string =
|
||||||
chrono::Local::now().format("%Y%m%d_%H%M%S").to_string();
|
chrono::Local::now().format("%Y%m%d_%H%M%S").to_string();
|
||||||
@ -501,6 +541,14 @@ pub async fn launch_minecraft(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Add game played to discord rich presence
|
||||||
|
let _ = state
|
||||||
|
.discord_rpc
|
||||||
|
.set_activity(&format!("Playing {}", profile.metadata.name), true)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
// Create Minecraft child by inserting it into the state
|
// Create Minecraft child by inserting it into the state
|
||||||
// This also spawns the process and prepares the subsequent processes
|
// This also spawns the process and prepares the subsequent processes
|
||||||
let mut state_children = state.children.write().await;
|
let mut state_children = state.children.write().await;
|
||||||
|
|||||||
@ -145,6 +145,13 @@ impl Children {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Clear game played for Discord RPC
|
||||||
|
// May have other active processes, so we clear to the next running process
|
||||||
|
let state = crate::State::get().await?;
|
||||||
|
let _ = state.discord_rpc.clear_to_default(true).await;
|
||||||
|
}
|
||||||
|
|
||||||
// If in tauri, window should show itself again after process exists if it was hidden
|
// If in tauri, window should show itself again after process exists if it was hidden
|
||||||
#[cfg(feature = "tauri")]
|
#[cfg(feature = "tauri")]
|
||||||
{
|
{
|
||||||
|
|||||||
167
theseus/src/state/discord.rs
Normal file
167
theseus/src/state/discord.rs
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
use std::sync::{atomic::AtomicBool, Arc};
|
||||||
|
|
||||||
|
use discord_rich_presence::{
|
||||||
|
activity::{Activity, Assets},
|
||||||
|
DiscordIpc, DiscordIpcClient,
|
||||||
|
};
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
|
use crate::State;
|
||||||
|
|
||||||
|
pub struct DiscordGuard {
|
||||||
|
client: Arc<RwLock<DiscordIpcClient>>,
|
||||||
|
connected: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiscordGuard {
|
||||||
|
/// Initialize discord IPC client, and attempt to connect to it
|
||||||
|
/// If it fails, it will still return a DiscordGuard, but the client will be unconnected
|
||||||
|
pub async fn init() -> crate::Result<DiscordGuard> {
|
||||||
|
let mut dipc =
|
||||||
|
DiscordIpcClient::new("1084015525241311292").map_err(|e| {
|
||||||
|
crate::ErrorKind::OtherError(format!(
|
||||||
|
"Could not create Discord client {}",
|
||||||
|
e,
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
let res = dipc.connect(); // Do not need to connect to Discord to use app
|
||||||
|
let connected = if res.is_ok() {
|
||||||
|
Arc::new(AtomicBool::new(true))
|
||||||
|
} else {
|
||||||
|
Arc::new(AtomicBool::new(false))
|
||||||
|
};
|
||||||
|
|
||||||
|
let client = Arc::new(RwLock::new(dipc));
|
||||||
|
Ok(DiscordGuard { client, connected })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If the client failed connecting during init(), this will check for connection and attempt to reconnect
|
||||||
|
/// This MUST be called first in any client method that requires a connection, because those can PANIC if the client is not connected
|
||||||
|
/// (No connection is different than a failed connection, the latter will not panic and can be retried)
|
||||||
|
pub async fn retry_if_not_ready(&self) -> bool {
|
||||||
|
let mut client = self.client.write().await;
|
||||||
|
if !self.connected.load(std::sync::atomic::Ordering::Relaxed) {
|
||||||
|
if client.connect().is_ok() {
|
||||||
|
self.connected
|
||||||
|
.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the activity to the given message
|
||||||
|
pub async fn set_activity(
|
||||||
|
&self,
|
||||||
|
msg: &str,
|
||||||
|
reconnect_if_fail: bool,
|
||||||
|
) -> crate::Result<()> {
|
||||||
|
// Attempt to connect if not connected. Do not continue if it fails, as the client.set_activity can panic if it never was connected
|
||||||
|
if !self.retry_if_not_ready().await {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let activity = Activity::new().state(msg).assets(
|
||||||
|
Assets::new()
|
||||||
|
.large_image("modrinth_simple")
|
||||||
|
.large_text("Modrinth Logo"),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Attempt to set the activity
|
||||||
|
// If the existing connection fails, attempt to reconnect and try again
|
||||||
|
let mut client: tokio::sync::RwLockWriteGuard<'_, DiscordIpcClient> =
|
||||||
|
self.client.write().await;
|
||||||
|
let res = client.set_activity(activity.clone());
|
||||||
|
let could_not_set_err = |e: Box<dyn serde::ser::StdError>| {
|
||||||
|
crate::ErrorKind::OtherError(format!(
|
||||||
|
"Could not update Discord activity {}",
|
||||||
|
e,
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
if reconnect_if_fail {
|
||||||
|
if let Err(_e) = res {
|
||||||
|
client.reconnect().map_err(|e| {
|
||||||
|
crate::ErrorKind::OtherError(format!(
|
||||||
|
"Could not reconnect to Discord IPC {}",
|
||||||
|
e,
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
return Ok(client
|
||||||
|
.set_activity(activity)
|
||||||
|
.map_err(could_not_set_err)?); // try again, but don't reconnect if it fails again
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.map_err(could_not_set_err)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear the activity
|
||||||
|
pub async fn clear_activity(
|
||||||
|
&self,
|
||||||
|
reconnect_if_fail: bool,
|
||||||
|
) -> crate::Result<()> {
|
||||||
|
// Attempt to connect if not connected. Do not continue if it fails, as the client.clear_activity can panic if it never was connected
|
||||||
|
if !self.retry_if_not_ready().await {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to clear the activity
|
||||||
|
// If the existing connection fails, attempt to reconnect and try again
|
||||||
|
let mut client = self.client.write().await;
|
||||||
|
let res = client.clear_activity();
|
||||||
|
|
||||||
|
let could_not_clear_err = |e: Box<dyn serde::ser::StdError>| {
|
||||||
|
crate::ErrorKind::OtherError(format!(
|
||||||
|
"Could not clear Discord activity {}",
|
||||||
|
e,
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
if reconnect_if_fail {
|
||||||
|
if res.is_err() {
|
||||||
|
client.reconnect().map_err(|e| {
|
||||||
|
crate::ErrorKind::OtherError(format!(
|
||||||
|
"Could not reconnect to Discord IPC {}",
|
||||||
|
e,
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
return Ok(client
|
||||||
|
.clear_activity()
|
||||||
|
.map_err(could_not_clear_err)?); // try again, but don't reconnect if it fails again
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.map_err(could_not_clear_err)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear the activity, but if there is a running profile, set the activity to that instead
|
||||||
|
pub async fn clear_to_default(
|
||||||
|
&self,
|
||||||
|
reconnect_if_fail: bool,
|
||||||
|
) -> crate::Result<()> {
|
||||||
|
let state: Arc<tokio::sync::RwLockReadGuard<'_, State>> =
|
||||||
|
State::get().await?;
|
||||||
|
if let Some(existing_child) = state
|
||||||
|
.children
|
||||||
|
.read()
|
||||||
|
.await
|
||||||
|
.running_profile_paths()
|
||||||
|
.await?
|
||||||
|
.first()
|
||||||
|
{
|
||||||
|
self.set_activity(
|
||||||
|
&format!("Playing {}", existing_child),
|
||||||
|
reconnect_if_fail,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
} else {
|
||||||
|
self.clear_activity(reconnect_if_fail).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -48,6 +48,9 @@ pub use self::java_globals::*;
|
|||||||
mod safe_processes;
|
mod safe_processes;
|
||||||
pub use self::safe_processes::*;
|
pub use self::safe_processes::*;
|
||||||
|
|
||||||
|
mod discord;
|
||||||
|
pub use self::discord::*;
|
||||||
|
|
||||||
// Global state
|
// Global state
|
||||||
// RwLock on state only has concurrent reads, except for config dir change which takes control of the State
|
// RwLock on state only has concurrent reads, except for config dir change which takes control of the State
|
||||||
static LAUNCHER_STATE: OnceCell<RwLock<State>> = OnceCell::const_new();
|
static LAUNCHER_STATE: OnceCell<RwLock<State>> = OnceCell::const_new();
|
||||||
@ -81,6 +84,9 @@ pub struct State {
|
|||||||
/// Launcher processes that should be safely exited on shutdown
|
/// Launcher processes that should be safely exited on shutdown
|
||||||
pub(crate) safety_processes: RwLock<SafeProcesses>,
|
pub(crate) safety_processes: RwLock<SafeProcesses>,
|
||||||
|
|
||||||
|
/// Discord RPC
|
||||||
|
pub discord_rpc: DiscordGuard,
|
||||||
|
|
||||||
/// File watcher debouncer
|
/// File watcher debouncer
|
||||||
pub(crate) file_watcher: RwLock<Debouncer<RecommendedWatcher>>,
|
pub(crate) file_watcher: RwLock<Debouncer<RecommendedWatcher>>,
|
||||||
}
|
}
|
||||||
@ -156,6 +162,9 @@ impl State {
|
|||||||
let children = Children::new();
|
let children = Children::new();
|
||||||
let auth_flow = AuthTask::new();
|
let auth_flow = AuthTask::new();
|
||||||
let safety_processes = SafeProcesses::new();
|
let safety_processes = SafeProcesses::new();
|
||||||
|
|
||||||
|
let discord_rpc = DiscordGuard::init().await?;
|
||||||
|
|
||||||
emit_loading(&loading_bar, 10.0, None).await?;
|
emit_loading(&loading_bar, 10.0, None).await?;
|
||||||
|
|
||||||
Ok::<RwLock<Self>, crate::Error>(RwLock::new(Self {
|
Ok::<RwLock<Self>, crate::Error>(RwLock::new(Self {
|
||||||
@ -175,6 +184,7 @@ impl State {
|
|||||||
children: RwLock::new(children),
|
children: RwLock::new(children),
|
||||||
auth_flow: RwLock::new(auth_flow),
|
auth_flow: RwLock::new(auth_flow),
|
||||||
tags: RwLock::new(tags),
|
tags: RwLock::new(tags),
|
||||||
|
discord_rpc,
|
||||||
safety_processes: RwLock::new(safety_processes),
|
safety_processes: RwLock::new(safety_processes),
|
||||||
file_watcher: RwLock::new(file_watcher),
|
file_watcher: RwLock::new(file_watcher),
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -149,6 +149,8 @@ pub struct Profile {
|
|||||||
pub memory: Option<MemorySettings>,
|
pub memory: Option<MemorySettings>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub resolution: Option<WindowSize>,
|
pub resolution: Option<WindowSize>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub force_fullscreen: SetFullscreen,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub hooks: Option<Hooks>,
|
pub hooks: Option<Hooks>,
|
||||||
pub projects: HashMap<ProjectPathId, Project>,
|
pub projects: HashMap<ProjectPathId, Project>,
|
||||||
@ -223,6 +225,21 @@ impl ModLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, Copy)]
|
||||||
|
pub enum SetFullscreen {
|
||||||
|
#[serde(rename = "Leave unset")]
|
||||||
|
LeaveUnset,
|
||||||
|
#[serde(rename = "Set windowed")]
|
||||||
|
SetWindowed,
|
||||||
|
#[serde(rename = "Set fullscreen")]
|
||||||
|
SetFullscreen,
|
||||||
|
}
|
||||||
|
impl Default for SetFullscreen {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::LeaveUnset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct JavaSettings {
|
pub struct JavaSettings {
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
@ -268,6 +285,7 @@ impl Profile {
|
|||||||
java: None,
|
java: None,
|
||||||
memory: None,
|
memory: None,
|
||||||
resolution: None,
|
resolution: None,
|
||||||
|
force_fullscreen: SetFullscreen::LeaveUnset,
|
||||||
hooks: None,
|
hooks: None,
|
||||||
modrinth_update_version: None,
|
modrinth_update_version: None,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -46,13 +46,11 @@ pub async fn get_all_jre() -> Result<Vec<JavaVersion>, JREError> {
|
|||||||
];
|
];
|
||||||
for java_path in java_paths {
|
for java_path in java_paths {
|
||||||
let Ok(java_subpaths) = std::fs::read_dir(java_path) else {continue };
|
let Ok(java_subpaths) = std::fs::read_dir(java_path) else {continue };
|
||||||
for java_subpath in java_subpaths {
|
for java_subpath in java_subpaths.flatten() {
|
||||||
if let Ok(java_subpath) = java_subpath {
|
|
||||||
let path = java_subpath.path();
|
let path = java_subpath.path();
|
||||||
jre_paths.insert(path.join("bin"));
|
jre_paths.insert(path.join("bin"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Windows Registry Keys
|
// Windows Registry Keys
|
||||||
let key_paths = [
|
let key_paths = [
|
||||||
@ -93,8 +91,7 @@ pub async fn get_all_jre() -> Result<Vec<JavaVersion>, JREError> {
|
|||||||
pub fn get_paths_from_jre_winregkey(jre_key: RegKey) -> HashSet<PathBuf> {
|
pub fn get_paths_from_jre_winregkey(jre_key: RegKey) -> HashSet<PathBuf> {
|
||||||
let mut jre_paths = HashSet::new();
|
let mut jre_paths = HashSet::new();
|
||||||
|
|
||||||
for subkey in jre_key.enum_keys() {
|
for subkey in jre_key.enum_keys().flatten() {
|
||||||
if let Ok(subkey) = subkey {
|
|
||||||
if let Ok(subkey) = jre_key.open_subkey(subkey) {
|
if let Ok(subkey) = jre_key.open_subkey(subkey) {
|
||||||
let subkey_value_names =
|
let subkey_value_names =
|
||||||
[r"JavaHome", r"InstallationPath", r"\\hotspot\\MSI"];
|
[r"JavaHome", r"InstallationPath", r"\\hotspot\\MSI"];
|
||||||
@ -108,7 +105,6 @@ pub fn get_paths_from_jre_winregkey(jre_key: RegKey) -> HashSet<PathBuf> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
jre_paths
|
jre_paths
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -29,6 +29,7 @@ pub async fn import_get_importable_instances(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Import an instance from a launcher type and base path
|
/// Import an instance from a launcher type and base path
|
||||||
|
/// profile_path should be a blank profile for this purpose- if the function fails, it will be deleted
|
||||||
/// eg: import_instance(ImportLauncherType::MultiMC, PathBuf::from("C:/MultiMC"), "Instance 1")
|
/// eg: import_instance(ImportLauncherType::MultiMC, PathBuf::from("C:/MultiMC"), "Instance 1")
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn import_import_instance(
|
pub async fn import_import_instance(
|
||||||
|
|||||||
@ -276,6 +276,7 @@ pub struct EditProfile {
|
|||||||
pub memory: Option<MemorySettings>,
|
pub memory: Option<MemorySettings>,
|
||||||
pub resolution: Option<WindowSize>,
|
pub resolution: Option<WindowSize>,
|
||||||
pub hooks: Option<Hooks>,
|
pub hooks: Option<Hooks>,
|
||||||
|
pub force_fullscreen: Option<SetFullscreen>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
@ -315,6 +316,9 @@ pub async fn profile_edit(
|
|||||||
prof.java = edit_profile.java.clone();
|
prof.java = edit_profile.java.clone();
|
||||||
prof.memory = edit_profile.memory;
|
prof.memory = edit_profile.memory;
|
||||||
prof.resolution = edit_profile.resolution;
|
prof.resolution = edit_profile.resolution;
|
||||||
|
if let Some(force_fullscreen) = edit_profile.force_fullscreen {
|
||||||
|
prof.force_fullscreen = force_fullscreen;
|
||||||
|
}
|
||||||
prof.hooks = edit_profile.hooks.clone();
|
prof.hooks = edit_profile.hooks.clone();
|
||||||
|
|
||||||
prof.metadata.date_modified = chrono::Utc::now();
|
prof.metadata.date_modified = chrono::Utc::now();
|
||||||
|
|||||||
@ -182,6 +182,9 @@
|
|||||||
<span class="label__title size-card-header">Window</span>
|
<span class="label__title size-card-header">Window</span>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="adjacent-input">
|
||||||
|
<DropdownSelect v-model="forceFullscreen" :options="fullscreenOptions" />
|
||||||
|
</div>
|
||||||
<div class="adjacent-input">
|
<div class="adjacent-input">
|
||||||
<Checkbox v-model="overrideWindowSettings" label="Override global window settings" />
|
<Checkbox v-model="overrideWindowSettings" label="Override global window settings" />
|
||||||
</div>
|
</div>
|
||||||
@ -439,10 +442,12 @@ const maxMemory = Math.floor((await get_max_memory().catch(handleError)) / 1024)
|
|||||||
|
|
||||||
const overrideWindowSettings = ref(!!props.instance.resolution)
|
const overrideWindowSettings = ref(!!props.instance.resolution)
|
||||||
const resolution = ref(props.instance.resolution ?? globalSettings.game_resolution)
|
const resolution = ref(props.instance.resolution ?? globalSettings.game_resolution)
|
||||||
|
|
||||||
const overrideHooks = ref(!!props.instance.hooks)
|
const overrideHooks = ref(!!props.instance.hooks)
|
||||||
const hooks = ref(props.instance.hooks ?? globalSettings.hooks)
|
const hooks = ref(props.instance.hooks ?? globalSettings.hooks)
|
||||||
|
|
||||||
|
const fullscreenOptions = ref(['Leave unchanged', 'Set windowed', 'Set fullscreen'])
|
||||||
|
const forceFullscreen = ref(props.instance.force_fullscreen)
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
[
|
[
|
||||||
title,
|
title,
|
||||||
@ -458,6 +463,7 @@ watch(
|
|||||||
memory,
|
memory,
|
||||||
overrideWindowSettings,
|
overrideWindowSettings,
|
||||||
resolution,
|
resolution,
|
||||||
|
forceFullscreen,
|
||||||
overrideHooks,
|
overrideHooks,
|
||||||
hooks,
|
hooks,
|
||||||
],
|
],
|
||||||
@ -505,6 +511,10 @@ watch(
|
|||||||
editProfile.resolution = resolution.value
|
editProfile.resolution = resolution.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (forceFullscreen.value) {
|
||||||
|
editProfile.force_fullscreen = forceFullscreen.value
|
||||||
|
}
|
||||||
|
|
||||||
if (overrideHooks.value) {
|
if (overrideHooks.value) {
|
||||||
editProfile.hooks = hooks.value
|
editProfile.hooks = hooks.value
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user