parent
f021e507b8
commit
61dbbcf2d9
3 changed files with 99 additions and 8 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -680,6 +680,7 @@ dependencies = [
|
||||||
"sdk",
|
"sdk",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_sjson",
|
"serde_sjson",
|
||||||
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
|
|
@ -24,3 +24,4 @@ tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
||||||
zip = "0.6.4"
|
zip = "0.6.4"
|
||||||
tokio-stream = { version = "0.1.12", features = ["fs"] }
|
tokio-stream = { version = "0.1.12", features = ["fs"] }
|
||||||
path-slash = "0.2.1"
|
path-slash = "0.2.1"
|
||||||
|
time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-offset"] }
|
||||||
|
|
|
@ -15,6 +15,8 @@ use sdk::murmur::Murmur64;
|
||||||
use sdk::{
|
use sdk::{
|
||||||
Bundle, BundleDatabase, BundleFile, BundleFileType, BundleFileVariant, FromBinary, ToBinary,
|
Bundle, BundleDatabase, BundleFile, BundleFileType, BundleFileVariant, FromBinary, ToBinary,
|
||||||
};
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use time::OffsetDateTime;
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
use tracing::Instrument;
|
use tracing::Instrument;
|
||||||
|
@ -28,6 +30,13 @@ const BUNDLE_DATABASE_NAME: &str = "bundle_database.data";
|
||||||
const MOD_BOOT_SCRIPT: &str = "scripts/mod_main";
|
const MOD_BOOT_SCRIPT: &str = "scripts/mod_main";
|
||||||
const MOD_DATA_SCRIPT: &str = "scripts/mods/mod_data";
|
const MOD_DATA_SCRIPT: &str = "scripts/mods/mod_data";
|
||||||
const SETTINGS_FILE_PATH: &str = "application_settings/settings_common.ini";
|
const SETTINGS_FILE_PATH: &str = "application_settings/settings_common.ini";
|
||||||
|
const DEPLOYMENT_DATA_PATH: &str = "dtmm-deployment.sjson";
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct DeploymentData {
|
||||||
|
bundles: Vec<String>,
|
||||||
|
timestamp: OffsetDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
async fn read_file_with_backup<P>(path: P) -> Result<Vec<u8>>
|
async fn read_file_with_backup<P>(path: P) -> Result<Vec<u8>>
|
||||||
|
@ -449,8 +458,11 @@ async fn patch_boot_bundle(state: Arc<State>) -> Result<Vec<Bundle>> {
|
||||||
Ok(bundles)
|
Ok(bundles)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, fields(bundles = bundles.len()))]
|
#[tracing::instrument(skip_all, fields(bundles = bundles.as_ref().len()))]
|
||||||
async fn patch_bundle_database(state: Arc<State>, bundles: Vec<Bundle>) -> Result<()> {
|
async fn patch_bundle_database<B>(state: Arc<State>, bundles: B) -> Result<()>
|
||||||
|
where
|
||||||
|
B: AsRef<[Bundle]>,
|
||||||
|
{
|
||||||
let bundle_dir = Arc::new(state.game_dir.join("bundle"));
|
let bundle_dir = Arc::new(state.game_dir.join("bundle"));
|
||||||
let database_path = bundle_dir.join(BUNDLE_DATABASE_NAME);
|
let database_path = bundle_dir.join(BUNDLE_DATABASE_NAME);
|
||||||
|
|
||||||
|
@ -464,9 +476,9 @@ async fn patch_bundle_database(state: Arc<State>, bundles: Vec<Bundle>) -> Resul
|
||||||
db
|
db
|
||||||
};
|
};
|
||||||
|
|
||||||
for bundle in bundles {
|
for bundle in bundles.as_ref() {
|
||||||
tracing::trace!("Adding '{}' to bundle database", bundle.name().display());
|
tracing::trace!("Adding '{}' to bundle database", bundle.name().display());
|
||||||
db.add_bundle(&bundle);
|
db.add_bundle(bundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -484,6 +496,29 @@ async fn patch_bundle_database(state: Arc<State>, bundles: Vec<Bundle>) -> Resul
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(bundles = bundles.as_ref().len()))]
|
||||||
|
async fn write_deployment_data<B>(state: Arc<State>, bundles: B) -> Result<()>
|
||||||
|
where
|
||||||
|
B: AsRef<[Bundle]>,
|
||||||
|
{
|
||||||
|
let info = DeploymentData {
|
||||||
|
timestamp: OffsetDateTime::now_utc(),
|
||||||
|
bundles: bundles
|
||||||
|
.as_ref()
|
||||||
|
.iter()
|
||||||
|
.map(|bundle| format!("{:x}", bundle.name().to_murmur64()))
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
let path = state.game_dir.join(DEPLOYMENT_DATA_PATH);
|
||||||
|
let data = serde_sjson::to_string(&info).wrap_err("failed to serizalie deployment data")?;
|
||||||
|
|
||||||
|
fs::write(&path, &data)
|
||||||
|
.await
|
||||||
|
.wrap_err_with(|| format!("failed to write deployment data to '{}'", path.display()))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, fields(
|
#[tracing::instrument(skip_all, fields(
|
||||||
game_dir = %state.game_dir.display(),
|
game_dir = %state.game_dir.display(),
|
||||||
mods = state.mods.len()
|
mods = state.mods.len()
|
||||||
|
@ -522,10 +557,15 @@ pub(crate) async fn deploy_mods(state: State) -> Result<()> {
|
||||||
.wrap_err("failed to patch game settings")?;
|
.wrap_err("failed to patch game settings")?;
|
||||||
|
|
||||||
tracing::info!("Patching bundle database");
|
tracing::info!("Patching bundle database");
|
||||||
patch_bundle_database(state.clone(), bundles)
|
patch_bundle_database(state.clone(), &bundles)
|
||||||
.await
|
.await
|
||||||
.wrap_err("failed to patch bundle database")?;
|
.wrap_err("failed to patch bundle database")?;
|
||||||
|
|
||||||
|
tracing::info!("Writing deployment data");
|
||||||
|
write_deployment_data(state.clone(), &bundles)
|
||||||
|
.await
|
||||||
|
.wrap_err("failed to write deployment data")?;
|
||||||
|
|
||||||
tracing::info!("Finished deploying mods");
|
tracing::info!("Finished deploying mods");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -538,6 +578,40 @@ pub(crate) async fn reset_mod_deployment(state: State) -> Result<()> {
|
||||||
|
|
||||||
tracing::info!("Resetting mod deployment in {}", bundle_dir.display());
|
tracing::info!("Resetting mod deployment in {}", bundle_dir.display());
|
||||||
|
|
||||||
|
tracing::debug!("Reading mod deployment");
|
||||||
|
|
||||||
|
let info: DeploymentData = {
|
||||||
|
let path = state.game_dir.join(DEPLOYMENT_DATA_PATH);
|
||||||
|
let data = match fs::read(&path).await {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(err) if err.kind() == ErrorKind::NotFound => {
|
||||||
|
tracing::info!("No deployment to reset");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
return Err(err).wrap_err_with(|| {
|
||||||
|
format!("failed to read deployment info at '{}'", path.display())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let data = String::from_utf8(data).wrap_err("invalid UTF8 in deployment data")?;
|
||||||
|
|
||||||
|
serde_sjson::from_str(&data).wrap_err("invalid SJSON in deployment data")?
|
||||||
|
};
|
||||||
|
|
||||||
|
for name in info.bundles {
|
||||||
|
let path = bundle_dir.join(name);
|
||||||
|
|
||||||
|
match fs::remove_file(&path).await {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) if err.kind() == ErrorKind::NotFound => {}
|
||||||
|
Err(err) => {
|
||||||
|
tracing::error!("Failed to remove '{}': {:?}", path.display(), err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
for p in paths {
|
for p in paths {
|
||||||
let path = bundle_dir.join(p);
|
let path = bundle_dir.join(p);
|
||||||
let backup = bundle_dir.join(&format!("{}.bak", p));
|
let backup = bundle_dir.join(&format!("{}.bak", p));
|
||||||
|
@ -555,9 +629,13 @@ pub(crate) async fn reset_mod_deployment(state: State) -> Result<()> {
|
||||||
|
|
||||||
tracing::debug!("Deleting backup: {}", backup.display());
|
tracing::debug!("Deleting backup: {}", backup.display());
|
||||||
|
|
||||||
fs::remove_file(&backup)
|
match fs::remove_file(&backup).await {
|
||||||
.await
|
Ok(_) => Ok(()),
|
||||||
.wrap_err_with(|| format!("failed to remove '{}'", backup.display()))
|
Err(err) if err.kind() == ErrorKind::NotFound => Ok(()),
|
||||||
|
Err(err) => {
|
||||||
|
Err(err).wrap_err_with(|| format!("failed to remove '{}'", backup.display()))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
@ -570,6 +648,17 @@ pub(crate) async fn reset_mod_deployment(state: State) -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let path = state.game_dir.join(DEPLOYMENT_DATA_PATH);
|
||||||
|
if let Err(err) = fs::remove_file(&path).await {
|
||||||
|
tracing::error!(
|
||||||
|
"Failed to remove deployment data '{}': {:?}",
|
||||||
|
path.display(),
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tracing::info!("Reset finished");
|
tracing::info!("Reset finished");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Loading…
Add table
Reference in a new issue