All checks were successful
lint/clippy Checking for common mistakes and opportunities for code improvement
build/msvc Build for the target platform: msvc
build/linux Build for the target platform: linux
Libraries like `io`, `os` and `ffi` allow practically unrestricted access to the system's files and running arbitrary operations. The base game removes them for this reason, and while we don't want to disable them permanently, very few mods should ever have a need for them. So we hide them behind a setting, worded so that people only enable it when absolutely needed. Closes #112.
227 lines
6.1 KiB
Rust
227 lines
6.1 KiB
Rust
use std::path::PathBuf;
|
|
use std::sync::Arc;
|
|
|
|
use druid::im::{HashMap, Vector};
|
|
use druid::text::RichText;
|
|
use druid::{Data, ImageBuf, Lens, WindowHandle, WindowId};
|
|
use dtmt_shared::ModConfig;
|
|
use nexusmods::Mod as NexusMod;
|
|
|
|
use super::SelectedModLens;
|
|
|
|
#[derive(Copy, Clone, Data, Debug, PartialEq)]
|
|
pub(crate) enum View {
|
|
Mods,
|
|
Settings,
|
|
}
|
|
|
|
impl Default for View {
|
|
fn default() -> Self {
|
|
Self::Mods
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Data, Debug, PartialEq)]
|
|
pub struct PackageInfo {
|
|
pub name: String,
|
|
pub files: Vector<String>,
|
|
}
|
|
|
|
impl PackageInfo {
|
|
pub fn new(name: String, files: Vector<String>) -> Self {
|
|
Self { name, files }
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub(crate) struct ModResourceInfo {
|
|
pub init: PathBuf,
|
|
pub data: Option<PathBuf>,
|
|
pub localization: Option<PathBuf>,
|
|
}
|
|
|
|
#[derive(Clone, Data, Debug, PartialEq)]
|
|
pub(crate) enum ModOrder {
|
|
Before,
|
|
After,
|
|
}
|
|
|
|
#[derive(Clone, Data, Debug, PartialEq)]
|
|
pub(crate) struct ModDependency {
|
|
pub id: String,
|
|
pub order: ModOrder,
|
|
}
|
|
|
|
impl From<dtmt_shared::ModDependency> for ModDependency {
|
|
fn from(value: dtmt_shared::ModDependency) -> Self {
|
|
match value {
|
|
dtmt_shared::ModDependency::ID(id) => ModDependency {
|
|
id,
|
|
order: ModOrder::Before,
|
|
},
|
|
dtmt_shared::ModDependency::Config { id, order } => ModDependency {
|
|
id,
|
|
order: match order {
|
|
dtmt_shared::ModOrder::Before => ModOrder::Before,
|
|
dtmt_shared::ModOrder::After => ModOrder::After,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Data, Debug, Lens, serde::Serialize, serde::Deserialize)]
|
|
pub(crate) struct NexusInfo {
|
|
pub id: u64,
|
|
pub version: String,
|
|
pub author: String,
|
|
pub summary: Arc<String>,
|
|
pub description: Arc<String>,
|
|
}
|
|
|
|
impl From<NexusMod> for NexusInfo {
|
|
fn from(value: NexusMod) -> Self {
|
|
Self {
|
|
id: value.mod_id,
|
|
version: value.version,
|
|
author: value.author,
|
|
summary: Arc::new(value.summary),
|
|
description: Arc::new(value.description),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Data, Debug, Lens)]
|
|
pub(crate) struct ModInfo {
|
|
pub id: String,
|
|
pub name: String,
|
|
pub summary: Arc<String>,
|
|
pub description: Option<Arc<String>>,
|
|
pub categories: Vector<String>,
|
|
pub author: Option<String>,
|
|
pub image: Option<ImageBuf>,
|
|
pub version: String,
|
|
pub enabled: bool,
|
|
#[lens(ignore)]
|
|
#[data(ignore)]
|
|
pub packages: Vector<Arc<PackageInfo>>,
|
|
#[lens(ignore)]
|
|
#[data(ignore)]
|
|
pub resources: ModResourceInfo,
|
|
pub depends: Vector<ModDependency>,
|
|
#[data(ignore)]
|
|
pub nexus: Option<NexusInfo>,
|
|
}
|
|
|
|
impl ModInfo {
|
|
pub fn new(
|
|
cfg: ModConfig,
|
|
packages: Vector<Arc<PackageInfo>>,
|
|
image: Option<ImageBuf>,
|
|
nexus: Option<NexusInfo>,
|
|
) -> Self {
|
|
Self {
|
|
id: cfg.id,
|
|
name: cfg.name,
|
|
summary: Arc::new(cfg.summary),
|
|
description: cfg.description.map(Arc::new),
|
|
author: cfg.author,
|
|
version: cfg.version,
|
|
enabled: false,
|
|
packages,
|
|
image,
|
|
categories: cfg.categories.into_iter().collect(),
|
|
resources: ModResourceInfo {
|
|
init: cfg.resources.init,
|
|
data: cfg.resources.data,
|
|
localization: cfg.resources.localization,
|
|
},
|
|
depends: cfg.depends.into_iter().map(ModDependency::from).collect(),
|
|
nexus,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Data, Lens)]
|
|
pub(crate) struct State {
|
|
pub current_view: View,
|
|
pub mods: Vector<Arc<ModInfo>>,
|
|
pub selected_mod_index: Option<usize>,
|
|
pub dirty: bool,
|
|
pub is_deployment_in_progress: bool,
|
|
pub is_reset_in_progress: bool,
|
|
pub is_save_in_progress: bool,
|
|
pub is_next_save_pending: bool,
|
|
pub is_update_in_progress: bool,
|
|
pub is_io_enabled: bool,
|
|
pub game_dir: Arc<PathBuf>,
|
|
pub data_dir: Arc<PathBuf>,
|
|
pub nexus_api_key: Arc<String>,
|
|
pub log: Vector<RichText>,
|
|
// True, when the initial loading of configuration and mods is still in progress
|
|
pub loading: bool,
|
|
|
|
#[lens(ignore)]
|
|
#[data(ignore)]
|
|
pub config_path: Arc<PathBuf>,
|
|
#[lens(ignore)]
|
|
#[data(ignore)]
|
|
pub windows: HashMap<WindowId, WindowHandle>,
|
|
#[lens(ignore)]
|
|
#[data(ignore)]
|
|
pub ctx: Arc<sdk::Context>,
|
|
}
|
|
|
|
impl State {
|
|
#[allow(non_upper_case_globals)]
|
|
pub const selected_mod: SelectedModLens = SelectedModLens;
|
|
|
|
pub fn new() -> Self {
|
|
let ctx = sdk::Context::new();
|
|
|
|
Self {
|
|
ctx: Arc::new(ctx),
|
|
current_view: View::default(),
|
|
mods: Vector::new(),
|
|
selected_mod_index: None,
|
|
dirty: false,
|
|
is_deployment_in_progress: false,
|
|
is_reset_in_progress: false,
|
|
is_save_in_progress: false,
|
|
is_next_save_pending: false,
|
|
is_update_in_progress: false,
|
|
is_io_enabled: false,
|
|
config_path: Arc::new(PathBuf::new()),
|
|
game_dir: Arc::new(PathBuf::new()),
|
|
data_dir: Arc::new(PathBuf::new()),
|
|
nexus_api_key: Arc::new(String::new()),
|
|
log: Vector::new(),
|
|
windows: HashMap::new(),
|
|
loading: true,
|
|
}
|
|
}
|
|
|
|
pub fn select_mod(&mut self, index: usize) {
|
|
self.selected_mod_index = Some(index);
|
|
}
|
|
|
|
pub fn add_mod(&mut self, info: Arc<ModInfo>) {
|
|
if let Some(pos) = self.mods.iter().position(|i| i.id == info.id) {
|
|
self.mods.set(pos, info);
|
|
self.selected_mod_index = Some(pos);
|
|
} else {
|
|
self.mods.push_back(info);
|
|
self.selected_mod_index = Some(self.mods.len() - 1);
|
|
}
|
|
}
|
|
|
|
pub fn can_move_mod_down(&self) -> bool {
|
|
self.selected_mod_index
|
|
.map(|i| i < (self.mods.len().saturating_sub(1)))
|
|
.unwrap_or(false)
|
|
}
|
|
|
|
pub fn can_move_mod_up(&self) -> bool {
|
|
self.selected_mod_index.map(|i| i > 0).unwrap_or(false)
|
|
}
|
|
}
|