implement auto-saving under reticles

This commit is contained in:
りき萌 2024-10-23 20:46:42 +02:00
parent 4ba7e25510
commit e0e64f7e24
2 changed files with 75 additions and 55 deletions

View file

@ -324,34 +324,49 @@ impl SessionLoop {
wall::EventKind::Interact { interactions } => { wall::EventKind::Interact { interactions } => {
let (done_tx, done_rx) = oneshot::channel(); let (done_tx, done_rx) = oneshot::channel();
if interactions let chunks_to_modify: Vec<_> =
.iter() chunks_to_modify(self.wall.settings(), interactions)
.any(|i| matches!(i, Interaction::SetBrush { .. })) .into_iter()
{ .collect();
// SetBrush is an important event, so we wait for the render thread match self.chunk_images.load(chunks_to_modify.clone()).await {
// to unload. Ok(_) => {
_ = self if interactions
.render_commands_tx .iter()
.send(RenderCommand::Interact { .any(|i| matches!(i, Interaction::SetBrush { .. }))
interactions: interactions.clone(), {
done: done_tx, // SetBrush is an important event, so we wait for the render thread
}) // to unload.
.await; _ = self
} else { .render_commands_tx
// If there is no SetBrush, there's no need to wait, so we fire events .send(RenderCommand::Interact {
// blindly. If the thread's not okay with that... well, whatever. interactions: interactions.clone(),
// That's your issue for making a really slow brush. done: done_tx,
let send_result = })
self.render_commands_tx.try_send(RenderCommand::Interact { .await;
interactions: interactions.clone(), } else {
done: done_tx, // If there is no SetBrush, there's no need to wait, so we fire events
// blindly. If the thread's not okay with that... well, whatever.
// That's your issue for making a really slow brush.
let send_result =
self.render_commands_tx.try_send(RenderCommand::Interact {
interactions: interactions.clone(),
done: done_tx,
});
if send_result.is_err() {
info!(
?interactions,
"render thread is overloaded, dropping interaction request"
);
}
}
let auto_save = Arc::clone(&self.auto_save);
tokio::spawn(async move {
_ = done_rx.await;
auto_save.request(chunks_to_modify).await;
}); });
if send_result.is_err() {
info!(
?interactions,
"render thread is overloaded, dropping interaction request"
);
} }
Err(err) => error!(?err, "while loading chunks for render command"),
} }
// TODO: Auto save. This'll need us to compute which chunks will be affected // TODO: Auto save. This'll need us to compute which chunks will be affected
@ -501,12 +516,10 @@ impl SessionLoop {
Interaction::Dotter { from, to, num } => { Interaction::Dotter { from, to, num } => {
if brush_ok { if brush_ok {
if let Some(trampoline) = if let Some(tramp) = jumpstart_trampoline(&mut haku, &mut trampoline) {
jumpstart_trampoline(&mut haku, &mut trampoline) let cont = haku.cont(tramp);
{
let cont = haku.cont(trampoline);
if cont == Cont::Dotter { if cont == Cont::Dotter {
_ = haku.dotter(trampoline, from, to, num); _ = haku.dotter(tramp, from, to, num);
} else { } else {
error!("received Dotter interaction when a {cont:?} continuation was next"); error!("received Dotter interaction when a {cont:?} continuation was next");
} }
@ -562,6 +575,29 @@ fn render_area(wall_settings: &wall::Settings, interaction: &Interaction) -> Opt
} }
} }
fn chunks_to_modify(
wall_settings: &wall::Settings,
interactions: &[Interaction],
) -> HashSet<ChunkPosition> {
let mut chunks = HashSet::new();
for interaction in interactions {
// NOTE: This is mostly a tentative overestimation, and can result in more chunks being
// marked as needing autosave than will be touched in reality.
// It's better to play safe in this case than lose data.
if let Some(render_area) = render_area(wall_settings, interaction) {
let top_left_chunk = wall_settings.chunk_at(render_area.top_left);
let bottom_right_chunk = wall_settings.chunk_at_ceil(render_area.bottom_right);
for chunk_y in top_left_chunk.y..bottom_right_chunk.y {
for chunk_x in top_left_chunk.x..bottom_right_chunk.x {
chunks.insert(ChunkPosition::new(chunk_x, chunk_y));
}
}
}
}
chunks
}
fn jumpstart_trampoline<'a>( fn jumpstart_trampoline<'a>(
haku: &mut Haku, haku: &mut Haku,
trampoline: &'a mut Option<Trampoline>, trampoline: &'a mut Option<Trampoline>,
@ -572,25 +608,6 @@ fn jumpstart_trampoline<'a>(
trampoline.as_mut() trampoline.as_mut()
} }
fn chunks_to_modify(wall: &Wall, points: &[Vec2]) -> HashSet<ChunkPosition> {
let mut chunks = HashSet::new();
for point in points {
let paint_area = wall.settings().paint_area as f32;
let left = point.x - paint_area / 2.0;
let top = point.y - paint_area / 2.0;
let top_left_chunk = wall.settings().chunk_at(Vec2::new(left, top));
let bottom_right_chunk = wall
.settings()
.chunk_at_ceil(Vec2::new(left + paint_area, top + paint_area));
for chunk_y in top_left_chunk.y..bottom_right_chunk.y {
for chunk_x in top_left_chunk.x..bottom_right_chunk.x {
chunks.insert(ChunkPosition::new(chunk_x, chunk_y));
}
}
}
chunks
}
#[instrument(skip(wall, haku, value))] #[instrument(skip(wall, haku, value))]
fn draw_to_chunks( fn draw_to_chunks(
wall: &Wall, wall: &Wall,

View file

@ -5,7 +5,7 @@ use tokio::{
sync::mpsc, sync::mpsc,
time::{interval, MissedTickBehavior}, time::{interval, MissedTickBehavior},
}; };
use tracing::instrument; use tracing::{info, instrument};
use super::{chunk_images::ChunkImages, ChunkPosition}; use super::{chunk_images::ChunkImages, ChunkPosition};
@ -77,9 +77,12 @@ impl AutoSaveLoop {
// NOTE: We don't care about actually using the images here - // NOTE: We don't care about actually using the images here -
// the ChunkImages service writes them to the database by itself, and that's all our // the ChunkImages service writes them to the database by itself, and that's all our
// request is for. // request is for.
_ = self if !self.unsaved_chunks.is_empty() {
.chunk_images info!("saving chunks");
.encoded(self.unsaved_chunks.drain().collect()) _ = self
.await; .chunk_images
.encoded(self.unsaved_chunks.drain().collect())
.await;
}
} }
} }