Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instances support #73

Merged
merged 15 commits into from
Aug 20, 2024
Merged
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ logs/
log/
nomi_logs/

/assets/
/instances/
/libraries/

debug/

node_modules
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

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

62 changes: 62 additions & 0 deletions crates/client/src/cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use eframe::egui::Ui;
use itertools::Itertools;
use std::{
collections::HashMap,
path::PathBuf,
sync::{Arc, LazyLock},
};
use tracing::error;

use nomi_core::{fs::read_toml_config_sync, instance::InstanceProfileId};
use parking_lot::RwLock;

use crate::{errors_pool::ErrorPoolExt, views::ModdedProfile};

pub static GLOBAL_CACHE: LazyLock<Arc<RwLock<GlobalCache>>> = LazyLock::new(|| Arc::new(RwLock::new(GlobalCache::new())));

pub struct GlobalCache {
profiles: HashMap<InstanceProfileId, Arc<RwLock<ModdedProfile>>>,
}

impl Default for GlobalCache {
fn default() -> Self {
Self::new()
}
}

impl GlobalCache {
pub fn new() -> Self {
Self { profiles: HashMap::new() }
}

pub fn get_profile(&self, id: InstanceProfileId) -> Option<Arc<RwLock<ModdedProfile>>> {
self.profiles.get(&id).cloned()
}

pub fn load_profile(&mut self, id: InstanceProfileId, path: PathBuf) -> Option<Arc<RwLock<ModdedProfile>>> {
read_toml_config_sync(path)
.inspect_err(|error| error!(%error, "Cannot read profile config"))
.report_error()
.and_then(|profile| {
self.profiles.insert(id, profile);
self.profiles.get(&id).cloned()
})
}

fn loaded_profiles(&self) -> Vec<Arc<RwLock<ModdedProfile>>> {
self.profiles.values().cloned().collect_vec()
}
}

pub fn ui_for_loaded_profiles(ui: &mut Ui) {
ui.vertical(|ui| {
for profile in GLOBAL_CACHE.read().loaded_profiles() {
let profile = profile.read();
ui.horizontal(|ui| {
ui.label(&profile.profile.name);
ui.label(profile.profile.version());
});
ui.separator();
}
});
}
85 changes: 66 additions & 19 deletions crates/client/src/collections.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
use std::{collections::HashSet, sync::Arc};

use egui_task_manager::*;
use nomi_core::repository::fabric_meta::FabricVersions;
use nomi_core::{instance::InstanceProfileId, repository::fabric_meta::FabricVersions};
use nomi_modding::modrinth::{
project::{Project, ProjectId},
version::Version,
};

use crate::{
errors_pool::ErrorPoolExt,
views::{ProfilesConfig, SimpleDependency},
toasts,
views::{InstancesConfig, SimpleDependency},
};

pub struct FabricDataCollection;
Expand Down Expand Up @@ -73,9 +74,9 @@ impl<'c> TasksCollection<'c> for JavaCollection {
pub struct GameDownloadingCollection;

impl<'c> TasksCollection<'c> for GameDownloadingCollection {
type Context = &'c ProfilesConfig;
type Context = &'c InstancesConfig;

type Target = Option<()>;
type Target = Option<InstanceProfileId>;

type Executor = executors::Linear;

Expand All @@ -84,27 +85,71 @@ impl<'c> TasksCollection<'c> for GameDownloadingCollection {
}

fn handle(context: Self::Context) -> Handler<'c, Self::Target> {
Handler::new(|_| {
context.update_config().report_error();
Handler::new(|id| {
if let Some(id) = id {
context.update_profile_config(id).report_error();
if let Some(instance) = context.find_instance(id.instance()) {
if let Some(profile) = instance.write().find_profile_mut(id) {
profile.is_downloaded = true
};

instance.read().write_blocking().report_error();
}
}
})
}
}

pub struct GameDeletionCollection;

impl<'c> TasksCollection<'c> for GameDeletionCollection {
type Context = ();
type Context = &'c InstancesConfig;

type Target = ();
type Target = InstanceProfileId;

type Executor = executors::Linear;

fn name() -> &'static str {
"Game deletion collection"
}

fn handle(_context: Self::Context) -> Handler<'c, Self::Target> {
Handler::new(|()| ())
fn handle(context: Self::Context) -> Handler<'c, Self::Target> {
Handler::new(|id: InstanceProfileId| {
if let Some(instance) = context.find_instance(id.instance()) {
instance.write().remove_profile(id);
if context.update_instance_config(id.instance()).report_error().is_some() {
toasts::add(|toasts| toasts.success("Successfully removed the profile"))
}
}
})
}
}

pub struct InstanceDeletionCollection;

impl<'c> TasksCollection<'c> for InstanceDeletionCollection {
type Context = &'c mut InstancesConfig;

type Target = Option<usize>;

type Executor = executors::Linear;

fn name() -> &'static str {
"Instance deletion collection"
}

fn handle(context: Self::Context) -> Handler<'c, Self::Target> {
Handler::new(|id: Option<usize>| {
let Some(id) = id else {
return;
};

if context.remove_instance(id).is_none() {
return;
}

toasts::add(|toasts| toasts.success("Successfully removed the instance"))
})
}
}

Expand Down Expand Up @@ -182,9 +227,9 @@ impl<'c> TasksCollection<'c> for DependenciesCollection {
pub struct ModsDownloadingCollection;

impl<'c> TasksCollection<'c> for ModsDownloadingCollection {
type Context = &'c ProfilesConfig;
type Context = &'c InstancesConfig;

type Target = Option<()>;
type Target = Option<InstanceProfileId>;

type Executor = executors::Linear;

Expand All @@ -193,8 +238,10 @@ impl<'c> TasksCollection<'c> for ModsDownloadingCollection {
}

fn handle(context: Self::Context) -> Handler<'c, Self::Target> {
Handler::new(|_| {
context.update_config().report_error();
Handler::new(|id| {
if let Some(id) = id {
context.update_profile_config(id).report_error();
}
})
}
}
Expand All @@ -220,9 +267,9 @@ impl<'c> TasksCollection<'c> for GameRunnerCollection {
pub struct DownloadAddedModsCollection;

impl<'c> TasksCollection<'c> for DownloadAddedModsCollection {
type Context = (&'c mut HashSet<ProjectId>, &'c ProfilesConfig);
type Context = (&'c mut HashSet<ProjectId>, &'c InstancesConfig);

type Target = ProjectId;
type Target = (InstanceProfileId, ProjectId);

type Executor = executors::Parallel;

Expand All @@ -231,9 +278,9 @@ impl<'c> TasksCollection<'c> for DownloadAddedModsCollection {
}

fn handle(context: Self::Context) -> Handler<'c, Self::Target> {
Handler::new(|id| {
context.0.remove(&id);
context.1.update_config().report_error();
Handler::new(|(profile_id, project_id)| {
context.0.remove(&project_id);
context.1.update_profile_config(profile_id).report_error();
})
}
}
5 changes: 2 additions & 3 deletions crates/client/src/consts.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
pub const DOT_NOMI_MODS_STASH_DIR: &str = "./.nomi/mods_stash";
pub const MINECRAFT_MODS_DIRECTORY: &str = "./minecraft/mods";
pub const NOMI_LOADED_LOCK_FILE: &str = "./minecraft/mods/Loaded.lock";
pub const DOT_NOMI_MODS_STASH_DIR: &str = ".nomi/mods_stash";
pub const NOMI_LOADED_LOCK_FILE: &str = "Loaded.lock";
pub const NOMI_LOADED_LOCK_FILE_NAME: &str = "Loaded";
28 changes: 16 additions & 12 deletions crates/client/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
errors_pool::ErrorPoolExt,
states::States,
subscriber::EguiLayer,
views::{self, profiles::ProfilesPage, settings::SettingsPage, Logs, ModManager, ModManagerState, ProfileInfo, View},
views::{self, profiles::Instances, settings::SettingsPage, Logs, ModManager, ModManagerState, ProfileInfo, View},
Tab, TabKind,
};
use eframe::egui::{self};
Expand All @@ -24,6 +24,8 @@ pub struct MyContext {

pub is_allowed_to_take_action: bool,
pub is_profile_window_open: bool,
pub is_instance_window_open: bool,
pub is_errors_window_open: bool,

pub images_clean_requested: bool,
}
Expand All @@ -44,11 +46,14 @@ impl MyContext {
egui_layer,
launcher_manifest: launcher_manifest_ref,
file_dialog: FileDialog::new(),
is_profile_window_open: false,

states: States::new(),
manager: TaskManager::new(),

is_allowed_to_take_action: true,
is_profile_window_open: false,
is_instance_window_open: false,
is_errors_window_open: false,

images_clean_requested: false,
}
}
Expand All @@ -69,30 +74,29 @@ impl TabViewer for MyContext {
match &tab.kind {
TabKind::Mods { profile } => {
let profile = profile.read();
self.states.profiles.profiles.find_profile(profile.profile.id).is_none()
self.states.instances.instances.find_profile(profile.profile.id).is_none()
}
TabKind::ProfileInfo { profile } => {
let profile = profile.read();
self.states.profiles.profiles.find_profile(profile.profile.id).is_none()
self.states.instances.instances.find_profile(profile.profile.id).is_none()
}
_ => false,
}
}

fn ui(&mut self, ui: &mut egui::Ui, tab: &mut Self::Tab) {
match &tab.kind {
TabKind::Profiles => ProfilesPage {
TabKind::Profiles => Instances {
is_allowed_to_take_action: self.is_allowed_to_take_action,
profile_info_state: &mut self.states.profile_info,
manager: &mut self.manager,
settings_state: &self.states.settings,
profiles_state: &mut self.states.profiles,
menu_state: &mut self.states.add_profile_menu_state,
profiles_state: &mut self.states.instances,
menu_state: &mut self.states.add_profile_menu,
tabs_state: &mut self.states.tabs,
logs_state: &self.states.logs_state,

launcher_manifest: self.launcher_manifest,
is_profile_window_open: &mut self.is_profile_window_open,
}
.ui(ui),
TabKind::Settings => SettingsPage {
Expand All @@ -111,19 +115,19 @@ impl TabViewer for MyContext {
TabKind::DownloadProgress => {
views::DownloadingProgress {
manager: &self.manager,
profiles_state: &mut self.states.profiles,
profiles_state: &mut self.states.instances,
}
.ui(ui);
}
TabKind::Mods { profile } => ModManager {
task_manager: &mut self.manager,
profiles_config: &mut self.states.profiles.profiles,
profiles_config: &mut self.states.instances.instances,
mod_manager_state: &mut self.states.mod_manager,
profile: profile.clone(),
}
.ui(ui),
TabKind::ProfileInfo { profile } => ProfileInfo {
profiles: &self.states.profiles.profiles,
profiles: &self.states.instances.instances,
task_manager: &mut self.manager,
profile: profile.clone(),
tabs_state: &mut self.states.tabs,
Expand Down
Loading
Loading