Use template engine to build mod_data.lua
The string-building version became too complex to maintain properly.
This commit is contained in:
parent
dfaa39cd54
commit
eee1f500b8
5 changed files with 99 additions and 68 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -906,6 +906,7 @@ dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"luajit2-sys",
|
"luajit2-sys",
|
||||||
|
"minijinja",
|
||||||
"nexusmods",
|
"nexusmods",
|
||||||
"oodle",
|
"oodle",
|
||||||
"path-slash",
|
"path-slash",
|
||||||
|
@ -2084,6 +2085,15 @@ version = "0.3.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minijinja"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "208758577ef2c86cf5dd3e85730d161413ec3284e2d73b2ef65d9a24d9971bcb"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minimal-lexical"
|
name = "minimal-lexical"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
|
|
@ -35,3 +35,4 @@ ansi-parser = "0.9.0"
|
||||||
string_template = "0.2.1"
|
string_template = "0.2.1"
|
||||||
luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" }
|
luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" }
|
||||||
async-recursion = "1.0.5"
|
async-recursion = "1.0.5"
|
||||||
|
minijinja = "1.0.10"
|
||||||
|
|
27
crates/dtmm/assets/mod_data.lua.j2
Normal file
27
crates/dtmm/assets/mod_data.lua.j2
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
return {
|
||||||
|
{% for mod in mods %}
|
||||||
|
{
|
||||||
|
id = "{{ mod.id }}",
|
||||||
|
name = "{{ mod.name }}",
|
||||||
|
bundled = {{ mod.bundled }},
|
||||||
|
packages = {
|
||||||
|
{% for pkg in mod.packages %}
|
||||||
|
"{{ pkg }}",
|
||||||
|
{% endfor %}
|
||||||
|
},
|
||||||
|
run = function()
|
||||||
|
{% if mod.data is none %}
|
||||||
|
return dofile("{{ mod.init }}")
|
||||||
|
{% else %}
|
||||||
|
new_mod("{{ mod.id }}", {
|
||||||
|
mod_script = "{{ mod.init }}",
|
||||||
|
mod_data = "{{ mod.data }}",
|
||||||
|
{% if not mod.localization is none %}
|
||||||
|
mod_localization = "{{ mod.localization }}",
|
||||||
|
{% endif %}
|
||||||
|
})
|
||||||
|
{% endif %}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{% endfor %}
|
||||||
|
}
|
|
@ -61,14 +61,14 @@ local function patch_mod_loading_state()
|
||||||
if state == "load_package" and package_manager:update() then
|
if state == "load_package" and package_manager:update() then
|
||||||
log("StateBootLoadMods", "Packages loaded, loading mods")
|
log("StateBootLoadMods", "Packages loaded, loading mods")
|
||||||
self._state = "load_mods"
|
self._state = "load_mods"
|
||||||
local ModLoader = require("scripts/mods/dml/init")
|
local DML = require("scripts/mods/dml/init")
|
||||||
|
|
||||||
local mod_data = require("scripts/mods/mod_data")
|
local mod_data = require("scripts/mods/mod_data")
|
||||||
local mod_loader = ModLoader:new(mod_data, self._parent:gui())
|
local mod_loader = DML.create_loader(mod_data, self._parent:gui())
|
||||||
|
|
||||||
self._mod_loader = mod_loader
|
self._dml = DML
|
||||||
Managers.mod = mod_loader
|
Managers.mod = mod_loader
|
||||||
elseif state == "load_mods" and self._mod_loader:update(dt) then
|
elseif state == "load_mods" and self._dml.update(Managers.mod, dt) then
|
||||||
log("StateBootLoadMods", "Mods loaded, exiting")
|
log("StateBootLoadMods", "Mods loaded, exiting")
|
||||||
return true, false
|
return true, false
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@ use color_eyre::eyre::Context;
|
||||||
use color_eyre::{eyre, Help, Report, Result};
|
use color_eyre::{eyre, Help, Report, Result};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use futures::{stream, TryStreamExt};
|
use futures::{stream, TryStreamExt};
|
||||||
use path_slash::PathBufExt;
|
use minijinja::Environment;
|
||||||
use sdk::filetype::lua;
|
use sdk::filetype::lua;
|
||||||
use sdk::filetype::package::Package;
|
use sdk::filetype::package::Package;
|
||||||
use sdk::murmur::Murmur64;
|
use sdk::murmur::Murmur64;
|
||||||
|
@ -201,10 +201,10 @@ async fn copy_recursive(
|
||||||
async move {
|
async move {
|
||||||
if is_dir {
|
if is_dir {
|
||||||
tracing::trace!("Creating directory '{}'", dest.display());
|
tracing::trace!("Creating directory '{}'", dest.display());
|
||||||
fs::create_dir(&dest)
|
// Instead of trying to filter "already exists" errors out explicitly,
|
||||||
.await
|
// we just ignore all. It'll fail eventually with the next copy operation.
|
||||||
.map(|_| ())
|
let _ = fs::create_dir(&dest).await;
|
||||||
.wrap_err_with(|| format!("Failed to create directory '{}'", dest.display()))
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
tracing::trace!("Copying file '{}' -> '{}'", path.display(), dest.display());
|
tracing::trace!("Copying file '{}' -> '{}'", path.display(), dest.display());
|
||||||
fs::copy(&path, &dest).await.map(|_| ()).wrap_err_with(|| {
|
fs::copy(&path, &dest).await.map(|_| ()).wrap_err_with(|| {
|
||||||
|
@ -262,67 +262,60 @@ async fn copy_mod_folders(state: Arc<ActionState>) -> Result<Vec<String>> {
|
||||||
Ok(ids)
|
Ok(ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_mod_data_lua(state: Arc<ActionState>) -> String {
|
fn build_mod_data_lua(state: Arc<ActionState>) -> Result<String> {
|
||||||
let mut lua = String::from("return {\n");
|
#[derive(Serialize)]
|
||||||
|
struct TemplateDataMod {
|
||||||
// DMF is handled explicitely by the loading procedures, as it actually drives most of that
|
id: String,
|
||||||
// and should therefore not show up in the load order.
|
name: String,
|
||||||
for mod_info in state.mods.iter().filter(|m| m.id != "dml" && m.enabled) {
|
bundled: bool,
|
||||||
lua.push_str(" {\n name = \"");
|
init: String,
|
||||||
lua.push_str(&mod_info.name);
|
data: Option<String>,
|
||||||
|
localization: Option<String>,
|
||||||
lua.push_str("\",\n id = \"");
|
packages: Vec<String>,
|
||||||
lua.push_str(&mod_info.id);
|
|
||||||
|
|
||||||
lua.push_str("\",\n bundled = \"");
|
|
||||||
if mod_info.bundled {
|
|
||||||
lua.push_str("true");
|
|
||||||
} else {
|
|
||||||
lua.push_str("false");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lua.push_str("\",\n run = function()\n");
|
let mut env = Environment::new();
|
||||||
|
env.add_template("mod_data.lua", include_str!("../../assets/mod_data.lua.j2"))
|
||||||
|
.wrap_err("Failed to compile template for `mod_data.lua`")?;
|
||||||
|
let tmpl = env
|
||||||
|
.get_template("mod_data.lua")
|
||||||
|
.wrap_err("Failed to get template `mod_data.lua`")?;
|
||||||
|
|
||||||
let resources = &mod_info.resources;
|
let data: Vec<TemplateDataMod> = state
|
||||||
if resources.data.is_some() || resources.localization.is_some() {
|
.mods
|
||||||
lua.push_str(" new_mod(\"");
|
.iter()
|
||||||
lua.push_str(&mod_info.id);
|
.filter_map(|m| {
|
||||||
lua.push_str("\", {\n mod_script = \"");
|
if m.id == "dml" || !m.enabled {
|
||||||
lua.push_str(&resources.init.to_slash_lossy());
|
return None;
|
||||||
|
|
||||||
if let Some(data) = resources.data.as_ref() {
|
|
||||||
lua.push_str("\",\n mod_data = \"");
|
|
||||||
lua.push_str(&data.to_slash_lossy());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(localization) = &resources.localization {
|
Some(TemplateDataMod {
|
||||||
lua.push_str("\",\n mod_localization = \"");
|
id: m.id.clone(),
|
||||||
lua.push_str(&localization.to_slash_lossy());
|
name: m.name.clone(),
|
||||||
}
|
bundled: m.bundled,
|
||||||
|
init: m.resources.init.to_string_lossy().to_string(),
|
||||||
|
data: m
|
||||||
|
.resources
|
||||||
|
.data
|
||||||
|
.as_ref()
|
||||||
|
.map(|p| p.to_string_lossy().to_string()),
|
||||||
|
localization: m
|
||||||
|
.resources
|
||||||
|
.localization
|
||||||
|
.as_ref()
|
||||||
|
.map(|p| p.to_string_lossy().to_string()),
|
||||||
|
packages: m.packages.iter().map(|p| p.name.clone()).collect(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
lua.push_str("\",\n })\n");
|
let lua = tmpl
|
||||||
} else {
|
.render(minijinja::context!(mods => data))
|
||||||
lua.push_str(" return dofile(\"");
|
.wrap_err("Failed to render template `mod_data.lua`")?;
|
||||||
lua.push_str(&resources.init.to_slash_lossy());
|
|
||||||
lua.push_str("\")\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
lua.push_str(" end,\n packages = {\n");
|
tracing::debug!("mod_data.lua:\n{}", lua);
|
||||||
|
|
||||||
for pkg_info in &mod_info.packages {
|
Ok(lua)
|
||||||
lua.push_str(" \"");
|
|
||||||
lua.push_str(&pkg_info.name);
|
|
||||||
lua.push_str("\",\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
lua.push_str(" },\n },\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
lua.push('}');
|
|
||||||
|
|
||||||
tracing::debug!("mod_data_lua:\n{}", lua);
|
|
||||||
|
|
||||||
lua
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
|
@ -340,7 +333,7 @@ async fn build_bundles(state: Arc<ActionState>) -> Result<Vec<Bundle>> {
|
||||||
let span = tracing::debug_span!("Building mod data script");
|
let span = tracing::debug_span!("Building mod data script");
|
||||||
let _enter = span.enter();
|
let _enter = span.enter();
|
||||||
|
|
||||||
let lua = build_mod_data_lua(state.clone());
|
let lua = build_mod_data_lua(state.clone()).wrap_err("Failed to build Lua mod data")?;
|
||||||
|
|
||||||
tracing::trace!("Compiling mod data script");
|
tracing::trace!("Compiling mod data script");
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue