Darktide Mod Manager #39
4 changed files with 79 additions and 4 deletions
|
@ -547,6 +547,37 @@ pub(crate) async fn deploy_mods(state: State) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip(state))]
|
||||||
|
pub(crate) async fn reset_mod_deployment(state: State) -> Result<()> {
|
||||||
|
let paths = [BUNDLE_DATABASE_NAME, BOOT_BUNDLE_NAME];
|
||||||
|
let bundle_dir = state.get_game_dir().join("bundle");
|
||||||
|
|
||||||
|
tracing::info!("Resetting mod deployment in {}", bundle_dir.display());
|
||||||
|
|
||||||
|
for p in paths {
|
||||||
|
let path = bundle_dir.join(p);
|
||||||
|
let backup = bundle_dir.join(&format!("{}.bak", p));
|
||||||
|
|
||||||
|
tracing::debug!(
|
||||||
|
"Copying from backup: {} -> {}",
|
||||||
|
backup.display(),
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
|
|
||||||
|
fs::copy(&backup, &path)
|
||||||
|
.await
|
||||||
|
.wrap_err_with(|| format!("failed to '{}' restore from backup", p))?;
|
||||||
|
|
||||||
|
tracing::debug!("Deleting backup: {}", backup.display(),);
|
||||||
|
|
||||||
|
fs::remove_file(&backup)
|
||||||
|
.await
|
||||||
|
.wrap_err_with(|| format!("failed to remove backup '{}'", p))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(state))]
|
#[tracing::instrument(skip(state))]
|
||||||
pub(crate) async fn import_mod(state: State, info: FileInfo) -> Result<ModInfo> {
|
pub(crate) async fn import_mod(state: State, info: FileInfo) -> Result<ModInfo> {
|
||||||
let data = fs::read(&info.path)
|
let data = fs::read(&info.path)
|
||||||
|
|
|
@ -19,10 +19,12 @@ use druid::SingleUse;
|
||||||
use druid::Target;
|
use druid::Target;
|
||||||
use engine::delete_mod;
|
use engine::delete_mod;
|
||||||
use engine::import_mod;
|
use engine::import_mod;
|
||||||
|
use engine::reset_mod_deployment;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use state::ACTION_FINISH_ADD_MOD;
|
use state::ACTION_FINISH_ADD_MOD;
|
||||||
use state::ACTION_FINISH_DELETE_SELECTED_MOD;
|
use state::ACTION_FINISH_DELETE_SELECTED_MOD;
|
||||||
|
use state::ACTION_FINISH_RESET_DEPLOYMENT;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
use tokio::sync::mpsc::UnboundedReceiver;
|
use tokio::sync::mpsc::UnboundedReceiver;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
@ -102,6 +104,17 @@ fn work_thread(
|
||||||
)
|
)
|
||||||
.expect("failed to send command");
|
.expect("failed to send command");
|
||||||
}),
|
}),
|
||||||
|
AsyncAction::ResetDeployment(state) => tokio::spawn(async move {
|
||||||
|
if let Err(err) = reset_mod_deployment(state).await {
|
||||||
|
tracing::error!("Failed to reset mod deployment: {:?}", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
event_sink
|
||||||
|
.write()
|
||||||
|
.await
|
||||||
|
.submit_command(ACTION_FINISH_RESET_DEPLOYMENT, (), Target::Auto)
|
||||||
|
.expect("failed to send command");
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,7 @@ use druid::{
|
||||||
lens, FileDialogOptions, FileSpec, Insets, LensExt, SingleUse, Widget, WidgetExt, WindowDesc,
|
lens, FileDialogOptions, FileSpec, Insets, LensExt, SingleUse, Widget, WidgetExt, WindowDesc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::state::{ModInfo, PathBufFormatter, State, View};
|
use crate::state::{ModInfo, PathBufFormatter, State, View, ACTION_START_RESET_DEPLOYMENT};
|
||||||
use crate::state::{
|
use crate::state::{
|
||||||
ACTION_ADD_MOD, ACTION_SELECTED_MOD_DOWN, ACTION_SELECTED_MOD_UP, ACTION_SELECT_MOD,
|
ACTION_ADD_MOD, ACTION_SELECTED_MOD_DOWN, ACTION_SELECTED_MOD_UP, ACTION_SELECT_MOD,
|
||||||
ACTION_START_DELETE_SELECTED_MOD, ACTION_START_DEPLOY,
|
ACTION_START_DELETE_SELECTED_MOD, ACTION_START_DEPLOY,
|
||||||
|
@ -60,9 +60,11 @@ fn build_top_bar() -> impl Widget<State> {
|
||||||
)
|
)
|
||||||
.with_default_spacer()
|
.with_default_spacer()
|
||||||
.with_child(
|
.with_child(
|
||||||
Button::new("Run Game").on_click(|_ctx, _state: &mut State, _env| {
|
Button::new("Reset Mods")
|
||||||
todo!();
|
.on_click(|ctx, _state: &mut State, _env| {
|
||||||
}),
|
ctx.submit_command(ACTION_START_RESET_DEPLOYMENT);
|
||||||
|
})
|
||||||
|
.disabled_if(|data, _| !data.can_reset_deployment()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.padding(theme::TOP_BAR_INSETS)
|
.padding(theme::TOP_BAR_INSETS)
|
||||||
|
|
|
@ -24,6 +24,11 @@ pub(crate) const ACTION_FINISH_DELETE_SELECTED_MOD: Selector<SingleUse<ModInfo>>
|
||||||
pub(crate) const ACTION_START_DEPLOY: Selector = Selector::new("dtmm.action.start-deploy");
|
pub(crate) const ACTION_START_DEPLOY: Selector = Selector::new("dtmm.action.start-deploy");
|
||||||
pub(crate) const ACTION_FINISH_DEPLOY: Selector = Selector::new("dtmm.action.finish-deploy");
|
pub(crate) const ACTION_FINISH_DEPLOY: Selector = Selector::new("dtmm.action.finish-deploy");
|
||||||
|
|
||||||
|
pub(crate) const ACTION_START_RESET_DEPLOYMENT: Selector =
|
||||||
|
Selector::new("dtmm.action.start-reset-deployment");
|
||||||
|
pub(crate) const ACTION_FINISH_RESET_DEPLOYMENT: Selector =
|
||||||
|
Selector::new("dtmm.action.finish-reset-deployment");
|
||||||
|
|
||||||
pub(crate) const ACTION_ADD_MOD: Selector<FileInfo> = Selector::new("dtmm.action.add-mod");
|
pub(crate) const ACTION_ADD_MOD: Selector<FileInfo> = Selector::new("dtmm.action.add-mod");
|
||||||
pub(crate) const ACTION_FINISH_ADD_MOD: Selector<SingleUse<ModInfo>> =
|
pub(crate) const ACTION_FINISH_ADD_MOD: Selector<SingleUse<ModInfo>> =
|
||||||
Selector::new("dtmm.action.finish-add-mod");
|
Selector::new("dtmm.action.finish-add-mod");
|
||||||
|
@ -145,6 +150,7 @@ pub(crate) struct State {
|
||||||
mods: Vector<ModInfo>,
|
mods: Vector<ModInfo>,
|
||||||
selected_mod_index: Option<usize>,
|
selected_mod_index: Option<usize>,
|
||||||
is_deployment_in_progress: bool,
|
is_deployment_in_progress: bool,
|
||||||
|
is_reset_in_progress: bool,
|
||||||
game_dir: Arc<PathBuf>,
|
game_dir: Arc<PathBuf>,
|
||||||
data_dir: Arc<PathBuf>,
|
data_dir: Arc<PathBuf>,
|
||||||
ctx: Arc<sdk::Context>,
|
ctx: Arc<sdk::Context>,
|
||||||
|
@ -163,6 +169,7 @@ impl State {
|
||||||
mods: Vector::new(),
|
mods: Vector::new(),
|
||||||
selected_mod_index: None,
|
selected_mod_index: None,
|
||||||
is_deployment_in_progress: false,
|
is_deployment_in_progress: false,
|
||||||
|
is_reset_in_progress: false,
|
||||||
game_dir: Arc::new(config.game_dir.unwrap_or_default()),
|
game_dir: Arc::new(config.game_dir.unwrap_or_default()),
|
||||||
data_dir: Arc::new(config.data_dir.unwrap_or_default()),
|
data_dir: Arc::new(config.data_dir.unwrap_or_default()),
|
||||||
}
|
}
|
||||||
|
@ -208,6 +215,10 @@ impl State {
|
||||||
!self.is_deployment_in_progress
|
!self.is_deployment_in_progress
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn can_reset_deployment(&self) -> bool {
|
||||||
|
!self.is_reset_in_progress
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn get_game_dir(&self) -> &PathBuf {
|
pub(crate) fn get_game_dir(&self) -> &PathBuf {
|
||||||
&self.game_dir
|
&self.game_dir
|
||||||
}
|
}
|
||||||
|
@ -292,6 +303,7 @@ impl<T: Data> Lens<Vector<T>, Vector<(usize, T)>> for IndexedVectorLens {
|
||||||
|
|
||||||
pub(crate) enum AsyncAction {
|
pub(crate) enum AsyncAction {
|
||||||
DeployMods(State),
|
DeployMods(State),
|
||||||
|
ResetDeployment(State),
|
||||||
AddMod((State, FileInfo)),
|
AddMod((State, FileInfo)),
|
||||||
DeleteMod((State, ModInfo)),
|
DeleteMod((State, ModInfo)),
|
||||||
}
|
}
|
||||||
|
@ -334,6 +346,23 @@ impl AppDelegate<State> for Delegate {
|
||||||
state.is_deployment_in_progress = false;
|
state.is_deployment_in_progress = false;
|
||||||
Handled::Yes
|
Handled::Yes
|
||||||
}
|
}
|
||||||
|
cmd if cmd.is(ACTION_START_RESET_DEPLOYMENT) => {
|
||||||
|
if self
|
||||||
|
.sender
|
||||||
|
.send(AsyncAction::ResetDeployment(state.clone()))
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
state.is_reset_in_progress = true;
|
||||||
|
} else {
|
||||||
|
tracing::error!("Failed to queue action to reset mod deployment");
|
||||||
|
}
|
||||||
|
|
||||||
|
Handled::Yes
|
||||||
|
}
|
||||||
|
cmd if cmd.is(ACTION_FINISH_RESET_DEPLOYMENT) => {
|
||||||
|
state.is_reset_in_progress = false;
|
||||||
|
Handled::Yes
|
||||||
|
}
|
||||||
cmd if cmd.is(ACTION_SELECT_MOD) => {
|
cmd if cmd.is(ACTION_SELECT_MOD) => {
|
||||||
let index = cmd
|
let index = cmd
|
||||||
.get(ACTION_SELECT_MOD)
|
.get(ACTION_SELECT_MOD)
|
||||||
|
|
Loading…
Add table
Reference in a new issue