diff --git a/crates/dtmm/src/engine.rs b/crates/dtmm/src/engine.rs index 0ec0566..3526cb5 100644 --- a/crates/dtmm/src/engine.rs +++ b/crates/dtmm/src/engine.rs @@ -14,9 +14,9 @@ use sdk::filetype::lua; use sdk::filetype::package::Package; use sdk::murmur::Murmur64; use sdk::{ - Bundle, BundleDatabase, BundleFile, BundleFileType, BundleFileVariant, FromBinary, ToBinary, + Bundle, BundleDatabase, BundleFile, BundleFileType, BundleFileVariant, FromBinary, ModConfig, + ToBinary, }; -use serde::Deserialize; use tokio::io::AsyncWriteExt; use tokio::{fs, try_join}; use tracing::Instrument; @@ -176,7 +176,10 @@ async fn build_bundles(state: Arc) -> Result<()> { bundle.add_file(file); - let src = mod_dir.join(pkg_info.get_name()); + let bundle_name = Murmur64::hash(pkg_info.get_name()) + .to_string() + .to_ascii_lowercase(); + let src = mod_dir.join(&bundle_name); let dest = bundle_dir.clone(); let pkg_name = pkg_info.get_name().clone(); let mod_name = mod_info.get_name().clone(); @@ -200,7 +203,7 @@ async fn build_bundles(state: Arc) -> Result<()> { dest.display() ); fs::hard_link(&src, dest.as_ref()).await.wrap_err_with(|| { - format!("failed to hard link bundle {pkg_name} for mod {mod_name}") + format!("failed to hard link bundle {pkg_name} for mod {mod_name}. src: {}, dest: {}", src.display(), dest.display()) }) } .instrument(span); @@ -376,13 +379,6 @@ pub(crate) async fn deploy_mods(state: State) -> Result<()> { Ok(()) } -#[derive(Debug, Default, Deserialize)] -struct ModConfig { - name: String, - #[serde(default)] - description: String, -} - #[tracing::instrument(skip(state))] pub(crate) async fn import_mod(state: State, info: FileInfo) -> Result { let data = fs::read(&info.path) @@ -462,7 +458,7 @@ pub(crate) async fn import_mod(state: State, info: FileInfo) -> Result .into_iter() .map(|(name, files)| PackageInfo::new(name, files.into_iter().collect())) .collect(); - let info = ModInfo::new(mod_cfg.name, mod_cfg.description, packages); + let info = ModInfo::new(mod_cfg, packages); Ok(info) } diff --git a/crates/dtmm/src/state.rs b/crates/dtmm/src/state.rs index ec6530d..3241585 100644 --- a/crates/dtmm/src/state.rs +++ b/crates/dtmm/src/state.rs @@ -7,6 +7,7 @@ use druid::{ AppDelegate, Command, Data, DelegateCtx, Env, FileInfo, Handled, Lens, Selector, SingleUse, Target, }; +use sdk::ModConfig; use tokio::sync::mpsc::UnboundedSender; use crate::Config; @@ -58,6 +59,27 @@ impl PackageInfo { } } +#[derive(Clone, Data, Debug)] +pub(crate) struct ModResourceInfo { + init: String, + data: String, + localization: String, +} + +impl ModResourceInfo { + pub(crate) fn get_init(&self) -> &String { + &self.init + } + + pub(crate) fn get_data(&self) -> &String { + &self.data + } + + pub(crate) fn get_localization(&self) -> &String { + &self.localization + } +} + #[derive(Clone, Data, Debug, Lens)] pub(crate) struct ModInfo { name: String, @@ -65,15 +87,22 @@ pub(crate) struct ModInfo { enabled: bool, #[lens(ignore)] packages: Vector, + #[lens(ignore)] + resources: ModResourceInfo, } impl ModInfo { - pub fn new(name: String, description: String, packages: Vector) -> Self { + pub fn new(cfg: ModConfig, packages: Vector) -> Self { Self { - name, - description: Arc::new(description), - packages, + name: cfg.name, + description: Arc::new(cfg.description), enabled: false, + packages, + resources: ModResourceInfo { + init: cfg.resources.init, + data: cfg.resources.data, + localization: cfg.resources.localization, + }, } } @@ -84,6 +113,14 @@ impl ModInfo { pub(crate) fn get_name(&self) -> &String { &self.name } + + pub(crate) fn get_enabled(&self) -> bool { + self.enabled + } + + pub(crate) fn get_resources(&self) -> &ModResourceInfo { + &self.resources + } } impl PartialEq for ModInfo { diff --git a/crates/dtmt/src/cmd/build.rs b/crates/dtmt/src/cmd/build.rs index c2a2045..0c34a11 100644 --- a/crates/dtmt/src/cmd/build.rs +++ b/crates/dtmt/src/cmd/build.rs @@ -7,8 +7,7 @@ use color_eyre::{Help, Report}; use futures::future::try_join_all; use futures::StreamExt; use sdk::filetype::package::Package; -use sdk::{Bundle, BundleFile}; -use serde::Deserialize; +use sdk::{Bundle, BundleFile, ModConfig}; use tokio::fs::{self, File}; use tokio::io::AsyncReadExt; @@ -36,16 +35,8 @@ pub(crate) fn command_definition() -> Command { )) } -#[derive(Debug, Default, Deserialize)] -struct ProjectConfig { - #[serde(skip)] - dir: PathBuf, - name: String, - packages: Vec, -} - #[tracing::instrument] -async fn find_project_config(dir: Option) -> Result { +async fn find_project_config(dir: Option) -> Result { let (path, mut file) = if let Some(path) = dir { let file = File::open(&path.join(PROJECT_CONFIG_NAME)) .await @@ -83,7 +74,7 @@ async fn find_project_config(dir: Option) -> Result { let mut buf = String::new(); file.read_to_string(&mut buf).await?; - let mut cfg: ProjectConfig = serde_sjson::from_str(&buf)?; + let mut cfg: ModConfig = serde_sjson::from_str(&buf)?; cfg.dir = path; Ok(cfg) } @@ -210,23 +201,23 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> }) }); - let bundles = try_join_all(tasks).await?; + let bundles = try_join_all(tasks) + .await + .wrap_err("failed to build mod bundles")?; - let mod_file = { - let mut path = cfg.dir.join(&cfg.name); - path.set_extension("mod"); - fs::read(path).await? + let config_file = { + let path = cfg.dir.join("dtmt.cfg"); + fs::read(&path) + .await + .wrap_err_with(|| format!("failed to read mod config at {}", path.display()))? }; - let config_file = fs::read(cfg.dir.join("dtmt.cfg")).await?; - { let dest = dest.clone(); let name = cfg.name.clone(); tokio::task::spawn_blocking(move || { let mut archive = Archive::new(name); - archive.add_mod_file(mod_file); archive.add_config(config_file); for bundle in bundles { diff --git a/crates/dtmt/src/cmd/new.rs b/crates/dtmt/src/cmd/new.rs index a7a66ca..e757f49 100644 --- a/crates/dtmt/src/cmd/new.rs +++ b/crates/dtmt/src/cmd/new.rs @@ -8,13 +8,19 @@ use futures::{StreamExt, TryStreamExt}; use string_template::Template; use tokio::fs::{self, DirBuilder}; -const TEMPLATES: [(&str, &str); 6] = [ +const TEMPLATES: [(&str, &str); 5] = [ ( "dtmt.cfg", r#"name = "{{name}}" description = "An elaborate description of my cool game mod!" version = "0.1.0" +resources = { + script = "scripts/mods/{{name}}/init" + data = "scripts/mods/{{name}}/data" + localization = "scripts/mods/{{name}}/locationzation" +} + packages = [ "packages/{{name}}" ] @@ -23,21 +29,6 @@ depends = [ "dmf" ] "#, - ), - ( - "{{name}}.mod", - r#"return { - run = function() - fassert(rawget(_G, "new_mod"), "`{{title}}` encountered an error loading the Darktide Mod Framework.") - - new_mod("{{name}}", { - mod_script = "scripts/mods/{{name}}/{{name}}", - mod_data = "scripts/mods/{{name}}/{{name}}_data", - mod_localization = "scripts/mods/{{name}}/{{name}}_localization", - }) - end, - packages = {}, -}"#, ), ( "packages/{{name}}.package", @@ -47,7 +38,7 @@ depends = [ "#, ), ( - "scripts/mods/{{name}}/{{name}}.lua", + "scripts/mods/{{name}}/init.lua", r#"local mod = get_mod("{{name}}") -- Your mod code goes here. @@ -55,7 +46,7 @@ depends = [ "#, ), ( - "scripts/mods/{{name}}/{{name}}_data.lua", + "scripts/mods/{{name}}/data.lua", r#"local mod = get_mod("{{name}}") return { @@ -65,7 +56,7 @@ return { }"#, ), ( - "scripts/mods/{{name}}/{{name}}_localization.lua", + "scripts/mods/{{name}}/localization.lua", r#"return { mod_description = { en = "An elaborate description of my cool game mod!", @@ -127,8 +118,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> promptly::prompt_default("The mod identifier name", default)? }; - tracing::debug!(root = %root.display()); - tracing::debug!(title, name); + tracing::debug!(root = %root.display(), title, name); let mut data = HashMap::new(); data.insert("name", name.as_str()); diff --git a/crates/dtmt/src/mods/archive.rs b/crates/dtmt/src/mods/archive.rs index 683314e..9f9eaa1 100644 --- a/crates/dtmt/src/mods/archive.rs +++ b/crates/dtmt/src/mods/archive.rs @@ -12,7 +12,6 @@ use zip::ZipWriter; pub struct Archive { name: String, bundles: Vec, - mod_file: Option>, config_file: Option>, } @@ -21,7 +20,6 @@ impl Archive { Self { name, bundles: Vec::new(), - mod_file: None, config_file: None, } } @@ -30,10 +28,6 @@ impl Archive { self.bundles.push(bundle) } - pub fn add_mod_file(&mut self, content: Vec) { - self.mod_file = Some(content); - } - pub fn add_config(&mut self, content: Vec) { self.config_file = Some(content); } @@ -42,11 +36,6 @@ impl Archive { where P: AsRef, { - let mod_file = self - .mod_file - .as_ref() - .ok_or_else(|| eyre::eyre!("Mod file is missing in mod archive"))?; - let config_file = self .config_file .as_ref() @@ -64,13 +53,6 @@ impl Archive { let base_path = PathBuf::from(&self.name); - { - let mut name = base_path.join(&self.name); - name.set_extension("mod"); - zip.start_file(name.to_string_lossy(), Default::default())?; - zip.write_all(mod_file)?; - } - { let name = base_path.join("dtmt.cfg"); zip.start_file(name.to_string_lossy(), Default::default())?; diff --git a/lib/sdk/src/lib.rs b/lib/sdk/src/lib.rs index e229e28..1ce68d6 100644 --- a/lib/sdk/src/lib.rs +++ b/lib/sdk/src/lib.rs @@ -9,3 +9,23 @@ pub use bundle::database::BundleDatabase; pub use bundle::decompress; pub use bundle::{Bundle, BundleFile, BundleFileType, BundleFileVariant}; pub use context::Context; + +#[derive(Clone, Debug, Default, serde::Deserialize)] +pub struct ModConfigResources { + pub init: String, + pub data: String, + pub localization: String, +} + +#[derive(Clone, Debug, Default, serde::Deserialize)] +pub struct ModConfig { + #[serde(skip)] + pub dir: std::path::PathBuf, + pub name: String, + pub description: String, + pub version: String, + pub packages: Vec, + pub resources: ModConfigResources, + #[serde(default)] + pub depends: Vec, +}