2020-09-17 23:02:14 +00:00
|
|
|
use crate::comp;
|
|
|
|
use common::{character::CharacterId, comp::item::ItemId};
|
|
|
|
|
|
|
|
use crate::persistence::{establish_connection, VelorenConnection};
|
2020-10-05 07:41:58 +00:00
|
|
|
use std::{path::Path, sync::Arc};
|
2020-09-20 04:57:26 +00:00
|
|
|
use tracing::{error, trace};
|
2020-09-17 23:02:14 +00:00
|
|
|
|
2021-01-08 19:12:09 +00:00
|
|
|
pub type CharacterUpdateData = (comp::Stats, comp::Inventory, Option<comp::Waypoint>);
|
2020-09-17 23:02:14 +00:00
|
|
|
|
|
|
|
/// A unidirectional messaging resource for saving characters in a
|
|
|
|
/// background thread.
|
|
|
|
///
|
|
|
|
/// This is used to make updates to a character and their persisted components,
|
|
|
|
/// such as inventory, loadout, etc...
|
|
|
|
pub struct CharacterUpdater {
|
2020-12-15 23:51:07 +00:00
|
|
|
update_tx: Option<crossbeam_channel::Sender<Vec<(CharacterId, CharacterUpdateData)>>>,
|
2020-09-17 23:02:14 +00:00
|
|
|
handle: Option<std::thread::JoinHandle<()>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CharacterUpdater {
|
2020-10-05 07:41:58 +00:00
|
|
|
pub fn new(db_dir: &Path) -> diesel::QueryResult<Self> {
|
2020-09-17 23:02:14 +00:00
|
|
|
let (update_tx, update_rx) =
|
2020-12-15 23:51:07 +00:00
|
|
|
crossbeam_channel::unbounded::<Vec<(CharacterId, CharacterUpdateData)>>();
|
2020-09-17 23:02:14 +00:00
|
|
|
|
2020-10-05 07:41:58 +00:00
|
|
|
let mut conn = establish_connection(db_dir)?;
|
2020-09-17 23:02:14 +00:00
|
|
|
|
|
|
|
let handle = std::thread::spawn(move || {
|
|
|
|
while let Ok(updates) = update_rx.recv() {
|
2020-09-20 04:57:26 +00:00
|
|
|
trace!("Persistence batch update starting");
|
2020-09-17 23:02:14 +00:00
|
|
|
execute_batch_update(updates, &mut conn);
|
2020-09-20 04:57:26 +00:00
|
|
|
trace!("Persistence batch update finished");
|
2020-09-17 23:02:14 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
Ok(Self {
|
|
|
|
update_tx: Some(update_tx),
|
|
|
|
handle: Some(handle),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Updates a collection of characters based on their id and components
|
|
|
|
pub fn batch_update<'a>(
|
|
|
|
&self,
|
|
|
|
updates: impl Iterator<
|
|
|
|
Item = (
|
|
|
|
CharacterId,
|
|
|
|
&'a comp::Stats,
|
|
|
|
&'a comp::Inventory,
|
2020-11-03 00:12:49 +00:00
|
|
|
Option<&'a comp::Waypoint>,
|
2020-09-17 23:02:14 +00:00
|
|
|
),
|
|
|
|
>,
|
|
|
|
) {
|
|
|
|
let updates = updates
|
2021-01-08 19:12:09 +00:00
|
|
|
.map(|(character_id, stats, inventory, waypoint)| {
|
2020-09-17 23:02:14 +00:00
|
|
|
(
|
|
|
|
character_id,
|
2021-01-08 19:12:09 +00:00
|
|
|
(stats.clone(), inventory.clone(), waypoint.cloned()),
|
2020-09-17 23:02:14 +00:00
|
|
|
)
|
|
|
|
})
|
2020-11-03 00:12:49 +00:00
|
|
|
.collect::<Vec<_>>();
|
2020-09-17 23:02:14 +00:00
|
|
|
|
|
|
|
if let Err(e) = self.update_tx.as_ref().unwrap().send(updates) {
|
|
|
|
error!(?e, "Could not send stats updates");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Updates a single character based on their id and components
|
|
|
|
pub fn update(
|
|
|
|
&self,
|
|
|
|
character_id: CharacterId,
|
|
|
|
stats: &comp::Stats,
|
|
|
|
inventory: &comp::Inventory,
|
2020-11-03 00:12:49 +00:00
|
|
|
waypoint: Option<&comp::Waypoint>,
|
2020-09-17 23:02:14 +00:00
|
|
|
) {
|
2021-01-08 19:12:09 +00:00
|
|
|
self.batch_update(std::iter::once((character_id, stats, inventory, waypoint)));
|
2020-09-17 23:02:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn execute_batch_update(
|
|
|
|
updates: Vec<(CharacterId, CharacterUpdateData)>,
|
|
|
|
connection: &mut VelorenConnection,
|
|
|
|
) {
|
|
|
|
let mut inserted_items = Vec::<Arc<ItemId>>::new();
|
|
|
|
|
|
|
|
if let Err(e) = connection.transaction::<_, super::error::Error, _>(|txn| {
|
2021-01-08 19:12:09 +00:00
|
|
|
for (character_id, (stats, inventory, waypoint)) in updates {
|
2020-09-17 23:02:14 +00:00
|
|
|
inserted_items.append(&mut super::character::update(
|
|
|
|
character_id,
|
|
|
|
stats,
|
|
|
|
inventory,
|
2020-11-03 00:12:49 +00:00
|
|
|
waypoint,
|
2020-09-17 23:02:14 +00:00
|
|
|
txn,
|
|
|
|
)?);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}) {
|
|
|
|
error!(?e, "Error during character batch update transaction");
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: On success, updating thee atomics is already taken care of
|
|
|
|
// internally.
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for CharacterUpdater {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
drop(self.update_tx.take());
|
|
|
|
if let Err(e) = self.handle.take().unwrap().join() {
|
|
|
|
error!(?e, "Error from joining character update thread");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|