From aa69046055343887459b25aa05f4d46c8f51b09e Mon Sep 17 00:00:00 2001 From: Mckol Date: Wed, 13 May 2020 01:58:15 +0200 Subject: [PATCH] Changed the default path of persistence database Now the default is `./saves/`, it's also configurable. --- server/src/events/player.rs | 3 ++- server/src/lib.rs | 7 ++++++- server/src/persistence/character.rs | 25 +++++++++++++------------ server/src/persistence/mod.rs | 27 ++++++--------------------- server/src/persistence/stats.rs | 15 ++++++++++----- server/src/settings.rs | 4 ++++ server/src/state_ext.rs | 5 ++++- server/src/sys/message.rs | 12 +++++++++++- server/src/sys/persistence/stats.rs | 10 ++++++++-- 9 files changed, 64 insertions(+), 44 deletions(-) diff --git a/server/src/events/player.rs b/server/src/events/player.rs index 770666b246..4d42df8aea 100644 --- a/server/src/events/player.rs +++ b/server/src/events/player.rs @@ -42,6 +42,7 @@ pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) { } pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event { + let db_dir = &server.server_settings.persistence_db_dir.clone(); let state = server.state_mut(); // Tell other clients to remove from player list @@ -77,7 +78,7 @@ pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event state.read_storage::().get(entity), ) { if let Some(character_id) = player.character_id { - persistence::stats::update(character_id, stats, None); + persistence::stats::update(character_id, stats, None, db_dir); } } diff --git a/server/src/lib.rs b/server/src/lib.rs index f5deddb577..ebe55fa292 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -117,6 +117,9 @@ impl Server { // Server-only components state.ecs_mut().register::(); state.ecs_mut().register::(); + state.ecs_mut().insert(crate::settings::PersistenceDBDir( + settings.persistence_db_dir.clone(), + )); #[cfg(feature = "worldgen")] let world = World::generate(settings.world_seed, WorldOpts { @@ -236,7 +239,9 @@ impl Server { // Run pending DB migrations (if any) debug!("Running DB migrations..."); - if let Some(error) = persistence::run_migrations().err() { + if let Some(error) = + persistence::run_migrations(&this.server_settings.persistence_db_dir).err() + { log::info!("Migration error: {}", format!("{:#?}", error)); } diff --git a/server/src/persistence/character.rs b/server/src/persistence/character.rs index a927d4239b..b1b7159601 100644 --- a/server/src/persistence/character.rs +++ b/server/src/persistence/character.rs @@ -16,12 +16,12 @@ type CharacterListResult = Result, Error>; /// /// After first logging in, and after a character is selected, we fetch this /// data for the purpose of inserting their persisted data for the entity. -pub fn load_character_data(character_id: i32) -> Result { +pub fn load_character_data(character_id: i32, db_dir: &str) -> Result { let (character_data, body_data, stats_data) = schema::character::dsl::character .filter(schema::character::id.eq(character_id)) .inner_join(schema::body::table) .inner_join(schema::stats::table) - .first::<(Character, Body, Stats)>(&establish_connection())?; + .first::<(Character, Body, Stats)>(&establish_connection(db_dir))?; Ok(comp::Stats::from(StatsJoinData { alias: &character_data.alias, @@ -37,13 +37,13 @@ pub fn load_character_data(character_id: i32) -> Result { /// In the event that a join fails, for a character (i.e. they lack an entry for /// stats, body, etc...) the character is skipped, and no entry will be /// returned. -pub fn load_character_list(player_uuid: &str) -> CharacterListResult { +pub fn load_character_list(player_uuid: &str, db_dir: &str) -> CharacterListResult { let data: Vec<(Character, Body, Stats)> = schema::character::dsl::character .filter(schema::character::player_uuid.eq(player_uuid)) .order(schema::character::id.desc()) .inner_join(schema::body::table) .inner_join(schema::stats::table) - .load::<(Character, Body, Stats)>(&establish_connection())?; + .load::<(Character, Body, Stats)>(&establish_connection(db_dir))?; Ok(data .iter() @@ -72,10 +72,11 @@ pub fn create_character( character_alias: String, character_tool: Option, body: &comp::Body, + db_dir: &str, ) -> CharacterListResult { - check_character_limit(uuid)?; + check_character_limit(uuid, db_dir)?; - let connection = establish_connection(); + let connection = establish_connection(db_dir); connection.transaction::<_, diesel::result::Error, _>(|| { use schema::{body, character, character::dsl::*, stats}; @@ -136,26 +137,26 @@ pub fn create_character( Ok(()) })?; - load_character_list(uuid) + load_character_list(uuid, db_dir) } /// Delete a character. Returns the updated character list. -pub fn delete_character(uuid: &str, character_id: i32) -> CharacterListResult { +pub fn delete_character(uuid: &str, character_id: i32, db_dir: &str) -> CharacterListResult { use schema::character::dsl::*; - diesel::delete(character.filter(id.eq(character_id))).execute(&establish_connection())?; + diesel::delete(character.filter(id.eq(character_id))).execute(&establish_connection(db_dir))?; - load_character_list(uuid) + load_character_list(uuid, db_dir) } -fn check_character_limit(uuid: &str) -> Result<(), Error> { +fn check_character_limit(uuid: &str, db_dir: &str) -> Result<(), Error> { use diesel::dsl::count_star; use schema::character::dsl::*; let character_count = character .select(count_star()) .filter(player_uuid.eq(uuid)) - .load::(&establish_connection())?; + .load::(&establish_connection(db_dir))?; match character_count.first() { Some(count) => { diff --git a/server/src/persistence/mod.rs b/server/src/persistence/mod.rs index e7d20d4415..c0662b2b13 100644 --- a/server/src/persistence/mod.rs +++ b/server/src/persistence/mod.rs @@ -9,35 +9,20 @@ extern crate diesel; use diesel::prelude::*; use diesel_migrations::embed_migrations; -use std::{env, fs, path::Path}; +use std::fs; // See: https://docs.rs/diesel_migrations/1.4.0/diesel_migrations/macro.embed_migrations.html // This macro is called at build-time, and produces the necessary migration info // for the `embedded_migrations` call below. embed_migrations!(); -pub fn run_migrations() -> Result<(), diesel_migrations::RunMigrationsError> { - let _ = fs::create_dir(format!("{}/saves/", binary_absolute_path())); - embedded_migrations::run_with_output(&establish_connection(), &mut std::io::stdout()) +pub fn run_migrations(db_dir: &str) -> Result<(), diesel_migrations::RunMigrationsError> { + let _ = fs::create_dir(format!("{}/", db_dir)); + embedded_migrations::run_with_output(&establish_connection(db_dir), &mut std::io::stdout()) } -fn establish_connection() -> SqliteConnection { - let database_url = format!("{}/saves/db.sqlite", binary_absolute_path()); +fn establish_connection(db_dir: &str) -> SqliteConnection { + let database_url = format!("{}/db.sqlite", db_dir); SqliteConnection::establish(&database_url) .unwrap_or_else(|_| panic!("Error connecting to {}", database_url)) } - -// Get the absolute path of the binary so that the database is always stored -// beside it, no matter where the binary is run from -fn binary_absolute_path() -> String { - let binary_path; - match env::current_exe() { - Ok(exe_path) => binary_path = exe_path, - Err(e) => panic!("Failed to get current exe path: {}", e), - }; - - match Path::new(&binary_path.display().to_string()).parent() { - Some(path) => return path.display().to_string(), - None => panic!("Failed to get current exe parent path"), - }; -} diff --git a/server/src/persistence/stats.rs b/server/src/persistence/stats.rs index 3faa77b7f5..ee0355ee1b 100644 --- a/server/src/persistence/stats.rs +++ b/server/src/persistence/stats.rs @@ -4,13 +4,18 @@ use super::{establish_connection, models::StatsUpdate, schema}; use crate::comp; use diesel::prelude::*; -pub fn update(character_id: i32, stats: &comp::Stats, conn: Option<&SqliteConnection>) { +pub fn update( + character_id: i32, + stats: &comp::Stats, + conn: Option<&SqliteConnection>, + db_dir: &str, +) { log::warn!("stats persisting..."); if let Err(error) = diesel::update(schema::stats::table.filter(schema::stats::character_id.eq(character_id))) .set(&StatsUpdate::from(stats)) - .execute(conn.unwrap_or(&establish_connection())) + .execute(conn.unwrap_or(&establish_connection(db_dir))) { log::warn!( "Failed to update stats for character: {:?}: {:?}", @@ -20,8 +25,8 @@ pub fn update(character_id: i32, stats: &comp::Stats, conn: Option<&SqliteConnec } } -pub fn batch_update<'a>(updates: impl Iterator) { - let connection = &establish_connection(); +pub fn batch_update<'a>(updates: impl Iterator, db_dir: &str) { + let connection = &establish_connection(db_dir); - updates.for_each(|(character_id, stats)| update(character_id, stats, Some(connection))); + updates.for_each(|(character_id, stats)| update(character_id, stats, Some(connection), db_dir)); } diff --git a/server/src/settings.rs b/server/src/settings.rs index 0a04f8225a..1bbebc748a 100644 --- a/server/src/settings.rs +++ b/server/src/settings.rs @@ -21,6 +21,7 @@ pub struct ServerSettings { /// When set to None, loads the default map file (if available); otherwise, /// uses the value of the file options to decide how to proceed. pub map_file: Option, + pub persistence_db_dir: String, } impl Default for ServerSettings { @@ -56,6 +57,7 @@ impl Default for ServerSettings { .iter() .map(|n| n.to_string()) .collect(), + persistence_db_dir: "saves".to_owned(), } } } @@ -127,3 +129,5 @@ impl ServerSettings { fn get_settings_path() -> PathBuf { PathBuf::from(r"server_settings.ron") } } + +pub struct PersistenceDBDir(pub String); diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 952770dd95..b908e3a7c4 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -163,7 +163,10 @@ impl StateExt for State { // Grab persisted character data from the db and insert their associated // components. If for some reason the data can't be returned (missing // data, DB error), kick the client back to the character select screen. - match persistence::character::load_character_data(character_id) { + match persistence::character::load_character_data( + character_id, + &server_settings.persistence_db_dir, + ) { Ok(stats) => self.write_component(entity, stats), Err(error) => { log::warn!( diff --git a/server/src/sys/message.rs b/server/src/sys/message.rs index add63e2e96..8307c97d41 100644 --- a/server/src/sys/message.rs +++ b/server/src/sys/message.rs @@ -1,5 +1,8 @@ use super::SysTimer; -use crate::{auth_provider::AuthProvider, client::Client, persistence, CLIENT_TIMEOUT}; +use crate::{ + auth_provider::AuthProvider, client::Client, persistence, settings::PersistenceDBDir, + CLIENT_TIMEOUT, +}; use common::{ comp::{Admin, CanBuild, ControlEvent, Controller, ForceUpdate, Ori, Player, Pos, Stats, Vel}, event::{EventBus, ServerEvent}, @@ -24,6 +27,7 @@ impl<'a> System<'a> for Sys { Entities<'a>, Read<'a, EventBus>, Read<'a, Time>, + ReadExpect<'a, PersistenceDBDir>, ReadExpect<'a, TerrainGrid>, Write<'a, SysTimer>, ReadStorage<'a, Uid>, @@ -47,6 +51,7 @@ impl<'a> System<'a> for Sys { entities, server_event_bus, time, + persistence_db_dir, terrain, mut timer, uids, @@ -68,6 +73,8 @@ impl<'a> System<'a> for Sys { let time = time.0; + let persistence_db_dir = &persistence_db_dir.0; + let mut server_emitter = server_event_bus.emitter(); let mut new_chat_msgs = Vec::new(); @@ -317,6 +324,7 @@ impl<'a> System<'a> for Sys { if let Some(player) = players.get(entity) { match persistence::character::load_character_list( &player.uuid().to_string(), + persistence_db_dir, ) { Ok(character_list) => { client.notify(ServerMsg::CharacterListUpdate(character_list)); @@ -335,6 +343,7 @@ impl<'a> System<'a> for Sys { alias, tool, &body, + persistence_db_dir, ) { Ok(character_list) => { client.notify(ServerMsg::CharacterListUpdate(character_list)); @@ -351,6 +360,7 @@ impl<'a> System<'a> for Sys { match persistence::character::delete_character( &player.uuid().to_string(), character_id, + persistence_db_dir, ) { Ok(character_list) => { client.notify(ServerMsg::CharacterListUpdate(character_list)); diff --git a/server/src/sys/persistence/stats.rs b/server/src/sys/persistence/stats.rs index 0c633bedfd..88d6d3f542 100644 --- a/server/src/sys/persistence/stats.rs +++ b/server/src/sys/persistence/stats.rs @@ -1,9 +1,10 @@ use crate::{ persistence::stats, + settings::PersistenceDBDir, sys::{SysScheduler, SysTimer}, }; use common::comp::{Player, Stats}; -use specs::{Join, ReadStorage, System, Write}; +use specs::{Join, ReadExpect, ReadStorage, System, Write}; pub struct Sys; @@ -11,11 +12,15 @@ impl<'a> System<'a> for Sys { type SystemData = ( ReadStorage<'a, Player>, ReadStorage<'a, Stats>, + ReadExpect<'a, PersistenceDBDir>, Write<'a, SysScheduler>, Write<'a, SysTimer>, ); - fn run(&mut self, (players, player_stats, mut scheduler, mut timer): Self::SystemData) { + fn run( + &mut self, + (players, player_stats, persistence_db_dir, mut scheduler, mut timer): Self::SystemData, + ) { if scheduler.should_run() { timer.start(); @@ -23,6 +28,7 @@ impl<'a> System<'a> for Sys { (&players, &player_stats) .join() .filter_map(|(player, stats)| player.character_id.map(|id| (id, stats))), + &persistence_db_dir.0, ); timer.end();