From e6d3ebdf6d56e6f40171eb18ff303b84339e9b31 Mon Sep 17 00:00:00 2001 From: Truman Kilen Date: Sun, 17 Dec 2023 15:32:43 -0600 Subject: [PATCH] Show mint mods in public server list --- Cargo.lock | 15 ++++ Cargo.toml | 7 +- assets/integration.pak | Bin 149165 -> 197983 bytes hook/Cargo.toml | 3 + hook/src/lib.rs | 55 +++++++++++++-- hook_resolvers/src/lib.rs | 54 +++++++++++++++ mint_lib/Cargo.toml | 3 + mint_lib/src/lib.rs | 2 + mint_lib/src/mod_info.rs | 141 ++++++++++++++++++++++++++++++++++++++ src/integrate.rs | 139 +++++++++++++++++++++++++++++++++---- src/providers/mod.rs | 105 +--------------------------- 11 files changed, 402 insertions(+), 122 deletions(-) create mode 100644 mint_lib/src/mod_info.rs diff --git a/Cargo.lock b/Cargo.lock index 2f8954d3..b3dc08be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2104,7 +2104,10 @@ dependencies = [ "hook_resolvers", "mint_lib", "patternsleuth", + "repak", "retour", + "serde", + "serde_json", "windows 0.52.0", ] @@ -2346,6 +2349,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" @@ -2672,7 +2684,10 @@ name = "mint_lib" version = "0.2.10" dependencies = [ "anyhow", + "itertools 0.12.0", "repak", + "serde", + "serde_json", "steamlocate", ] diff --git a/Cargo.toml b/Cargo.toml index 8493cfd3..3694a786 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,9 @@ anyhow = { version = "1.0.75", features = ["backtrace"] } patternsleuth = { git = "https://github.com/trumank/patternsleuth" } steamlocate = "2.0.0-alpha.0" repak = { git = "https://github.com/trumank/repak" } +serde = { version = "1.0.193", features = ["derive"] } +serde_json = "1.0.108" +itertools = "0.12.0" [package] name = "mint" @@ -60,8 +63,8 @@ rfd = "0.12.1" rust-ini = "0.20.0" self_update = { version = "0.39.0", default-features = false, features = ["archive-zip", "rustls"] } semver = "1.0.20" -serde = { version = "1.0.193", features = ["derive"] } -serde_json = "1.0.108" +serde.workspace = true +serde_json.workspace = true sha2 = "0.10.8" steamlocate.workspace = true task-local-extensions = "0.1.4" diff --git a/assets/integration.pak b/assets/integration.pak index ea81a85bfaa7cc01a12cc61c118b592485bcc252..b5e465be0a8a87eb3dc4c7e41c5c1894a915e54a 100644 GIT binary patch literal 197983 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

Z3z);^`-+;O{haE{BeXwhiVZ4G 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 LqUR;M z_UG3=c-b?(==sK8t+(C%@iRT5>G}B;tvmdq+1S{V>3QN`H~lyJKewGUfu0xqFl_VX z*ELB-qD>cEf9eTO9#}Dpo|oD0_~7^Ur*1%Nv3SMf-@U);)=S_4PaN{YvatK^&bo@8 zKR)G`*V3;Y*$gB5O_l$8qVH!fwYZO-YpTZXijKN}&g1la?0N6q@k!e!#;>90;_F7- zzOUca9%XGgNG`8IloSTx`#uok!ovqK#WaXc2~%dHDx&hKL`c zf`c%Z`v#Y05>W!wb<4Jx_4q7k1tr)4K5nnm%@air&hfeI=B$3c+vR7Qs` zz&oi(;6Wu6TiU;(i2(WmPb6?90QgW9IAJpy>zr zu&RIf#Y1YaH;?f7362@4{Fw)&2dg9*AVlV^e>L@m#j{HKejPKqG<^M?iRX~}YuMrc E0dehxX#fBK delta 13094 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 Result<()> { .and_then(Path::parent) .map(|p| p.join("Content/Paks/mods_P.pak")) .context("could not determine pak path")?; - if !pak_path.exists() { - return Ok(()); - } + + let mut pak_reader = BufReader::new(std::fs::File::open(pak_path)?); + let pak = repak::PakBuilder::new().reader(&mut pak_reader)?; + + let meta_buf = pak.get("meta.json", &mut pak_reader)?; + let meta: Meta = serde_json::from_slice(&meta_buf)?; let installation_type = DRGInstallationType::from_exe_path()?; @@ -85,6 +89,47 @@ unsafe fn patch() -> Result<()> { .enable()?; } + if let Ok(server_mods) = resolution.server_mods { + let mods_fname = server_mods.mods_fname.0; + let set_fstring = server_mods.set_fstring.0; + USessionHandlingFSDFillSessionSetting + .initialize( + std::mem::transmute(server_mods.fill_session_setting.0), + move |world, game_settings, full_server| { + USessionHandlingFSDFillSessionSetting.call(world, game_settings, full_server); + + #[derive(serde::Serialize)] + struct Wrapper { + name: String, + version: String, + category: i32, + } + + let s = serde_json::to_string(&vec![Wrapper { + name: meta.to_server_list_string(), + version: "mint".into(), + category: 0, + }]) + .unwrap(); + + let buf = s.encode_utf16().chain([0]).collect::>(); + let s = FString::from_slice(&buf); + + type Fn = unsafe extern "system" fn( + *const c_void, + *const c_void, + *const FString, + u32, + ); + + let f: Fn = std::mem::transmute(set_fstring); + + f(game_settings, *(mods_fname as *const *const c_void), &s, 3); + }, + )? + .enable()?; + } + match installation_type { DRGInstallationType::Steam => { if let Ok(address) = resolution.disable { @@ -217,6 +262,8 @@ static_detour! { static SaveGameToSlot: unsafe extern "system" fn(*const USaveGame, *const FString, i32) -> bool; static LoadGameFromSlot: unsafe extern "system" fn(*const FString, i32) -> *const USaveGame; static DoesSaveGameExist: unsafe extern "system" fn(*const FString, i32) -> bool; + static USessionHandlingFSDFillSessionSetting: unsafe extern "system" fn(*const c_void, *mut c_void, bool); + static FOnlineSessionSettingsSetFString: unsafe extern "system" fn(*const c_void, *const c_void, *const FString, u32); } #[allow(non_upper_case_globals)] diff --git a/hook_resolvers/src/lib.rs b/hook_resolvers/src/lib.rs index 9418136d..82adcfb5 100644 --- a/hook_resolvers/src/lib.rs +++ b/hook_resolvers/src/lib.rs @@ -2,6 +2,7 @@ use patternsleuth::resolvers::futures::future::join_all; use patternsleuth::resolvers::unreal::*; use patternsleuth::resolvers::*; use patternsleuth::scanner::Pattern; +use patternsleuth::MemoryAccessorTrait; #[derive(Debug)] pub struct GetServerName(pub usize); @@ -22,6 +23,49 @@ impl_resolver_singleton!(GetServerName, |ctx| async { )) }); +#[derive(Debug)] +pub struct FOnlineSessionSettingsSetFString(pub usize); +impl_resolver_singleton!(FOnlineSessionSettingsSetFString, |ctx| async { + let patterns = ["48 89 5C 24 ?? 48 89 54 24 ?? 55 56 57 48 83 EC 40 49 8B F8 48 8D 69"]; + let res = join_all(patterns.iter().map(|p| ctx.scan(Pattern::new(p).unwrap()))).await; + Ok(Self(ensure_one(res.into_iter().flatten())?)) +}); + +#[derive(Debug)] +pub struct USessionHandlingFSDFillSessionSettting(pub usize); +impl_resolver_singleton!(USessionHandlingFSDFillSessionSettting, |ctx| async { + let patterns = ["48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 55 41 54 41 55 41 56 41 57 48 8B EC 48 83 EC 50 48 8B B9"]; + let res = join_all(patterns.iter().map(|p| ctx.scan(Pattern::new(p).unwrap()))).await; + Ok(Self(ensure_one(res.into_iter().flatten())?)) +}); + +#[derive(Debug)] +pub struct ModsFName(pub usize); +impl_resolver_singleton!(ModsFName, |ctx| async { + let strings = ctx + .scan( + Pattern::from_bytes("Mods\0".encode_utf16().flat_map(u16::to_le_bytes).collect()) + .unwrap(), + ) + .await; + + let refs = join_all(strings.iter().map(|s| { + ctx.scan( + Pattern::new(format!( + "41 b8 01 00 00 00 48 8d 15 X0x{s:X} 48 8d 0d | ?? ?? ?? ?? e9 ?? ?? ?? ??" + )) + .unwrap(), + ) + })) + .await; + + Ok(Self(try_ensure_one( + refs.iter() + .flatten() + .map(|a| Ok(ctx.image().memory.rip4(*a)?)), + )?)) +}); + #[derive(Debug)] pub struct Disable(pub usize); impl_resolver_singleton!(Disable, |ctx| async { @@ -53,6 +97,15 @@ impl_resolver_singleton!(FMemoryFree, |ctx| async { Ok(Self(ensure_one(res.into_iter().flatten())?)) }); +impl_try_collector! { + #[derive(Debug)] + pub struct ServerModsResolution { + pub set_fstring: FOnlineSessionSettingsSetFString, + pub fill_session_setting: USessionHandlingFSDFillSessionSettting, + pub mods_fname: ModsFName, + } +} + impl_try_collector! { #[derive(Debug)] pub struct ServerNameResolution { @@ -80,6 +133,7 @@ impl_collector! { pub fmemory_free: FMemoryFree, pub disable: Disable, pub server_name: ServerNameResolution, + pub server_mods: ServerModsResolution, pub save_game: SaveGameResolution, } } diff --git a/mint_lib/Cargo.toml b/mint_lib/Cargo.toml index 4ad85ab8..7568b4f0 100644 --- a/mint_lib/Cargo.toml +++ b/mint_lib/Cargo.toml @@ -10,3 +10,6 @@ edition.workspace = true anyhow.workspace = true steamlocate.workspace = true repak.workspace = true +serde.workspace = true +serde_json.workspace = true +itertools.workspace = true diff --git a/mint_lib/src/lib.rs b/mint_lib/src/lib.rs index c47ac38d..ad72e784 100644 --- a/mint_lib/src/lib.rs +++ b/mint_lib/src/lib.rs @@ -1,3 +1,5 @@ +pub mod mod_info; + use std::path::{Path, PathBuf}; use anyhow::{bail, Context, Result}; diff --git a/mint_lib/src/mod_info.rs b/mint_lib/src/mod_info.rs new file mode 100644 index 00000000..961d1220 --- /dev/null +++ b/mint_lib/src/mod_info.rs @@ -0,0 +1,141 @@ +use std::collections::BTreeSet; + +use serde::{Deserialize, Serialize}; + +/// Tags from mod.io. +#[derive(Debug, Clone)] +pub struct ModioTags { + pub qol: bool, + pub gameplay: bool, + pub audio: bool, + pub visual: bool, + pub framework: bool, + pub versions: BTreeSet, + pub required_status: RequiredStatus, + pub approval_status: ApprovalStatus, +} + +#[derive(Debug, Copy, Clone)] +pub enum RequiredStatus { + RequiredByAll, + Optional, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub enum ApprovalStatus { + Verified, + Approved, + Sandbox, +} + +/// Whether a mod can be resolved by clients or not +#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd, Hash)] +pub enum ResolvableStatus { + Unresolvable(String), + Resolvable, +} + +/// Returned from ModStore +#[derive(Debug, Clone)] +pub struct ModInfo { + pub provider: &'static str, + pub name: String, + pub spec: ModSpecification, // unpinned version + pub versions: Vec, // pinned versions TODO make this a different type + pub resolution: ModResolution, + pub suggested_require: bool, + pub suggested_dependencies: Vec, // ModResponse + pub modio_tags: Option, // only available for mods from mod.io + pub modio_id: Option, // only available for mods from mod.io +} + +/// Returned from ModProvider +#[derive(Debug, Clone)] +pub enum ModResponse { + Redirect(ModSpecification), + Resolve(ModInfo), +} + +/// Points to a mod, optionally a specific version +#[derive( + Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize, +)] +pub struct ModSpecification { + pub url: String, +} + +impl ModSpecification { + pub fn new(url: String) -> Self { + Self { url } + } + pub fn satisfies_dependency(&self, other: &ModSpecification) -> bool { + // TODO this hack works surprisingly well but is still a complete hack and should be replaced + self.url.starts_with(&other.url) || other.url.starts_with(&self.url) + } +} + +/// Points to a specific version of a specific mod +#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd, Hash)] +pub struct ModResolution { + pub url: String, + pub status: ResolvableStatus, +} + +impl ModResolution { + pub fn resolvable(url: String) -> Self { + Self { + url, + status: ResolvableStatus::Resolvable, + } + } + pub fn unresolvable(url: String, name: String) -> Self { + Self { + url, + status: ResolvableStatus::Unresolvable(name), + } + } + /// Used to get the URL if resolvable or just return the mod name if not + pub fn get_resolvable_url_or_name(&self) -> &str { + match &self.status { + ResolvableStatus::Resolvable => &self.url, + ResolvableStatus::Unresolvable(name) => name, + } + } +} + +/// Stripped down mod info stored in the mod pak to be used in game +#[derive(Debug, Serialize, Deserialize)] +pub struct Meta { + pub version: String, + pub mods: Vec, +} +#[derive(Debug, Serialize, Deserialize)] +pub struct MetaMod { + pub name: String, + pub approval: ApprovalStatus, +} +impl Meta { + pub fn to_server_list_string(&self) -> String { + use itertools::Itertools; + + ["mint".into(), self.version.clone()] + .into_iter() + .chain( + self.mods + .iter() + .sorted_by_key(|m| (std::cmp::Reverse(m.approval), &m.name)) + .flat_map(|m| { + [ + match m.approval { + ApprovalStatus::Verified => 'V', + ApprovalStatus::Approved => 'A', + ApprovalStatus::Sandbox => 'S', + } + .into(), + m.name.replace(';', ""), + ] + }), + ) + .join(";") + } +} diff --git a/src/integrate.rs b/src/integrate.rs index 2e0edf19..2f7930bf 100644 --- a/src/integrate.rs +++ b/src/integrate.rs @@ -5,8 +5,10 @@ use std::io::{self, BufReader, BufWriter, Cursor, ErrorKind, Read, Seek}; use std::path::{Path, PathBuf}; use anyhow::{Context, Result}; +use mint_lib::mod_info::{ApprovalStatus, Meta, MetaMod}; use mint_lib::DRGInstallation; use repak::PakWriter; +use serde::Deserialize; use tracing::info; use uasset_utils::splice::{ extract_tracked_statements, inject_tracked_statements, walk, AssetVersion, TrackedStatement, @@ -71,12 +73,12 @@ pub fn uninstall>(path_pak: P, modio_mods: HashSet) -> Resul #[tracing::instrument(level = "debug")] fn uninstall_modio(installation: &DRGInstallation, modio_mods: HashSet) -> Result<()> { - #[derive(Debug, serde::Deserialize)] + #[derive(Debug, Deserialize)] struct ModioState { #[serde(rename = "Mods")] mods: Vec, } - #[derive(Debug, serde::Deserialize)] + #[derive(Debug, Deserialize)] struct ModioMod { #[serde(rename = "ID")] id: u32, @@ -268,6 +270,12 @@ pub fn integrate>( ) } + let mut other_deferred = vec![]; + let mut deferred = |path| { + other_deferred.push(path); + path + }; + let pcb_path = "FSD/Content/Game/BP_PlayerControllerBase"; let patch_paths = [ "FSD/Content/Game/BP_GameInstance", @@ -277,14 +285,15 @@ pub fn integrate>( "FSD/Content/UI/Menu_ServerList/_MENU_ServerList", "FSD/Content/UI/Menu_ServerList/WND_JoiningModded", ]; - let escape_menu_path = "FSD/Content/UI/Menu_EscapeMenu/MENU_EscapeMenu"; - let modding_tab_path = "FSD/Content/UI/Menu_EscapeMenu/Modding/MENU_Modding"; + let escape_menu_path = deferred("FSD/Content/UI/Menu_EscapeMenu/MENU_EscapeMenu"); + let modding_tab_path = deferred("FSD/Content/UI/Menu_EscapeMenu/Modding/MENU_Modding"); + let server_list_entry_path = deferred("FSD/Content/UI/Menu_ServerList/ITM_ServerList_Entry"); let mut deferred_assets: HashMap<&str, RawAsset> = HashMap::from_iter( [pcb_path] .iter() .chain(patch_paths.iter()) - .chain([escape_menu_path, modding_tab_path].iter()) + .chain(other_deferred.iter()) .map(|path| (*path, RawAsset::default())), ); @@ -469,6 +478,7 @@ pub fn integrate>( } patch_deferred(escape_menu_path, patch_modding_tab)?; patch_deferred(modding_tab_path, patch_modding_tab_item)?; + patch_deferred(server_list_entry_path, patch_server_list_entry)?; let mut int_pak_reader = Cursor::new(include_bytes!("../assets/integration.pak")); let int_pak = repak::PakBuilder::new() @@ -568,18 +578,40 @@ pub fn integrate>( kind: IntegrationErrKind::Generic(e.into()), })?; - write_file(&mut mod_pak, &mut int_out.0.into_inner(), int_path.0).map_err(|e| { - IntegrationErr { - mod_ctxt: None, - kind: IntegrationErrKind::Generic(e), - } + write_file(&mut mod_pak, &int_out.0.into_inner(), int_path.0).map_err(|e| IntegrationErr { + mod_ctxt: None, + kind: IntegrationErrKind::Generic(e), })?; - write_file(&mut mod_pak, &mut int_out.1.into_inner(), int_path.1).map_err(|e| { - IntegrationErr { + write_file(&mut mod_pak, &int_out.1.into_inner(), int_path.1).map_err(|e| IntegrationErr { + mod_ctxt: None, + kind: IntegrationErrKind::Generic(e), + })?; + + { + let meta = Meta { + version: env!("CARGO_PKG_VERSION").into(), + mods: mods + .iter() + .map(|(info, _)| MetaMod { + name: info.name.clone(), + approval: info + .modio_tags + .as_ref() + .map(|t| t.approval_status) + .unwrap_or(ApprovalStatus::Sandbox), + }) + .collect(), + }; + write_file( + &mut mod_pak, + &serde_json::to_vec(&meta).unwrap(), + "meta.json", + ) + .map_err(|e| IntegrationErr { mod_ctxt: None, kind: IntegrationErrKind::Generic(e), - } - })?; + })?; + } mod_pak.write_index().map_err(|e| IntegrationErr { mod_ctxt: None, @@ -1243,3 +1275,82 @@ fn patch_modding_tab_item(asset: &mut Asset) -> Result<()> { Ok(()) } + +fn patch_server_list_entry(asset: &mut Asset) -> Result<()> { + let get_mods_installed = asset + .imports + .iter() + .enumerate() + .find(|(_, i)| { + i.class_package.get_content(|s| s == "/Script/CoreUObject") + && i.class_name.get_content(|s| s == "Function") + && i.object_name.get_content(|s| s == "FSDGetModsInstalled") + }) + .map(|(pi, _)| PackageIndex::from_import(pi as i32).unwrap()); + + let ver = AssetVersion::new_from(asset); + let mut statements = extract_tracked_statements(asset, ver, &None); + + for (_pi, statements) in statements.iter_mut() { + for statement in statements { + walk(&mut statement.ex, &|ex| { + if let KismetExpression::ExCallMath(ex) = ex { + if Some(ex.stack_node) == get_mods_installed && ex.parameters.len() == 2 { + ex.parameters[1] = ExFalse { + token: EExprToken::ExFalse, + } + .into(); + info!("patched server list entry"); + } + } + }); + } + } + inject_tracked_statements(asset, ver, statements); + + { + // swap out tooltip with rebuilt version + let itm_tab_modding = get_import( + asset, + vec![ + Import::new( + "/Script/CoreUObject", + "Package", + "/Game/UI/Menu_ServerList/TOOLTIP_ServerEntry_Mods", + ), + Import::new( + "/Script/UMG", + "WidgetBlueprintGeneratedClass", + "TOOLTIP_ServerEntry_Mods_C", + ), + ], + ); + let itm_tab_modding_cdo = get_import( + asset, + vec![ + Import::new( + "/Script/CoreUObject", + "Package", + "/Game/UI/Menu_ServerList/TOOLTIP_ServerEntry_Mods", + ), + Import::new( + "/Game/UI/Menu_ServerList/TOOLTIP_ServerEntry_Mods", + "TOOLTIP_ServerEntry_Mods_C", + "Default__TOOLTIP_ServerEntry_Mods_C", + ), + ], + ); + let new_package = asset.add_fname( + "/Game/_AssemblyStorm/ModIntegration/RebuiltAssets/TOOLTIP_ServerEntry_Mods", + ); + asset.imports[(-itm_tab_modding_cdo.index - 1) as usize].class_package = + new_package.clone(); + let package_index = { + let obj = &mut asset.imports[(-itm_tab_modding.index - 1) as usize]; + obj.outer_index + }; + asset.imports[(-package_index.index - 1) as usize].object_name = new_package; + } + + Ok(()) +} diff --git a/src/providers/mod.rs b/src/providers/mod.rs index 4f8f63c3..2675a4e0 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -11,12 +11,14 @@ use serde::{Deserialize, Serialize}; use tokio::sync::mpsc::Sender; use tracing::info; -use std::collections::{BTreeSet, HashMap, HashSet}; +use std::collections::{HashMap, HashSet}; use std::io::{Read, Seek}; use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; use std::sync::{Arc, RwLock}; +pub use mint_lib::mod_info::*; + type Providers = RwLock>>; pub type ProviderCache = Arc>>; @@ -367,107 +369,6 @@ impl FetchProgress { } } -/// Whether a mod can be resolved by clients or not -#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd, Hash)] -pub enum ResolvableStatus { - Unresolvable(String), - Resolvable, -} - -/// Returned from ModStore -#[derive(Debug, Clone)] -pub struct ModInfo { - pub provider: &'static str, - pub name: String, - pub spec: ModSpecification, // unpinned version - pub versions: Vec, // pinned versions TODO make this a different type - pub resolution: ModResolution, - pub suggested_require: bool, - pub suggested_dependencies: Vec, // ModResponse - pub modio_tags: Option, // only available for mods from mod.io - pub modio_id: Option, // only available for mods from mod.io -} - -/// Tags from mod.io. -#[derive(Debug, Clone)] -pub struct ModioTags { - pub qol: bool, - pub gameplay: bool, - pub audio: bool, - pub visual: bool, - pub framework: bool, - pub versions: BTreeSet, - pub required_status: RequiredStatus, - pub approval_status: ApprovalStatus, -} - -#[derive(Debug, Copy, Clone)] -pub enum RequiredStatus { - RequiredByAll, - Optional, -} - -#[derive(Debug, Copy, Clone)] -pub enum ApprovalStatus { - Verified, - Approved, - Sandbox, -} - -/// Returned from ModProvider -#[derive(Debug, Clone)] -pub enum ModResponse { - Redirect(ModSpecification), - Resolve(ModInfo), -} - -/// Points to a mod, optionally a specific version -#[derive( - Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize, -)] -pub struct ModSpecification { - pub url: String, -} - -impl ModSpecification { - pub fn new(url: String) -> Self { - Self { url } - } - pub fn satisfies_dependency(&self, other: &ModSpecification) -> bool { - // TODO this hack works surprisingly well but is still a complete hack and should be replaced - self.url.starts_with(&other.url) || other.url.starts_with(&self.url) - } -} - -/// Points to a specific version of a specific mod -#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd, Hash)] -pub struct ModResolution { - pub url: String, - pub status: ResolvableStatus, -} - -impl ModResolution { - fn resolvable(url: String) -> Self { - Self { - url, - status: ResolvableStatus::Resolvable, - } - } - fn unresolvable(url: String, name: String) -> Self { - Self { - url, - status: ResolvableStatus::Unresolvable(name), - } - } - /// Used to get the URL if resolvable or just return the mod name if not - pub fn get_resolvable_url_or_name(&self) -> &str { - match &self.status { - ResolvableStatus::Resolvable => &self.url, - ResolvableStatus::Unresolvable(name) => name, - } - } -} - #[async_trait::async_trait] pub trait ModProvider: Send + Sync { async fn resolve_mod(