From d7f57a21767015b311fabbb2b0d5b29eae0ae593 Mon Sep 17 00:00:00 2001 From: crumblingstatue Date: Sat, 15 Apr 2023 12:36:47 +0200 Subject: [PATCH] Store worlds in user data dir --- Cargo.lock | 52 ++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/app.rs | 8 ++++- src/game.rs | 6 ++-- src/player.rs | 6 ++-- src/world.rs | 41 ++++++++++++++----------- src/world/reg_chunk_existence.rs | 6 ++-- src/world/serialization.rs | 8 ++--- 8 files changed, 98 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7470717..43f883b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -281,6 +281,26 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "directories" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74be3be809c18e089de43bdc504652bb2bc473fca8756131f8689db8cf079ba9" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04414300db88f70d74c5ff54e50f9e1d1737d9a5b90f53fcf2e95ca2a9ab554b" +dependencies = [ + "libc", + "redox_users", + "windows-sys 0.45.0", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -724,6 +744,7 @@ dependencies = [ "anyhow", "clap", "derivative", + "directories", "egui", "egui-inspect", "egui-sfml", @@ -959,6 +980,17 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + [[package]] name = "regex" version = "1.7.3" @@ -1183,6 +1215,26 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + [[package]] name = "toml" version = "0.7.3" diff --git a/Cargo.toml b/Cargo.toml index 11bf836..acdbd5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ log = "0.4.17" env_logger = "0.10.0" zstd = "0.12.3" clap = { version = "4.2.2", features = ["derive"] } +directories = "5.0.0" [dependencies.s2dc] #git = "https://github.com/crumblingstatue/s2dc.git" diff --git a/src/app.rs b/src/app.rs index f18a16d..701db21 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,4 +1,5 @@ use anyhow::Context; +use directories::ProjectDirs; use egui_sfml::SfEgui; use gamedebug_core::{imm, imm_dbg}; use sfml::{ @@ -36,6 +37,7 @@ pub struct App { pub rt: RenderTexture, /// Light map overlay, blended together with the non-lighted version of the scene pub light_map: RenderTexture, + pub project_dirs: ProjectDirs, } impl App { @@ -51,10 +53,13 @@ impl App { RenderTexture::new(rw_size.x, rw_size.y).context("Failed to create render texture")?; let light_map = RenderTexture::new(rw_size.x, rw_size.y) .context("Failed to create lightmap texture")?; + let project_dirs = ProjectDirs::from("", "", "mantle-diver").unwrap(); + let worlds_dir = project_dirs.data_dir().join("worlds"); + let path = worlds_dir.join(&args.world_name); Ok(Self { rw, should_quit: false, - game: GameState::new(args.world_name), + game: GameState::new(args.world_name, path), res, sf_egui, input: Input::default(), @@ -62,6 +67,7 @@ impl App { scale: 1, rt, light_map, + project_dirs, }) } diff --git a/src/game.rs b/src/game.rs index 7f264ac..81c2288 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use derivative::Derivative; use egui_inspect::derive::Inspect; use sfml::{ @@ -114,12 +116,12 @@ impl GameState { } } - pub(crate) fn new(world_name: String) -> GameState { + pub(crate) fn new(world_name: String, path: PathBuf) -> GameState { let mut spawn_point = WorldPos::SURFACE_CENTER; spawn_point.y -= 1104; Self { camera_offset: spawn_point, - world: World::new(spawn_point, &world_name), + world: World::new(spawn_point, &world_name, path), gravity: 0.55, tile_to_place: 1, current_biome: Biome::Surface, diff --git a/src/player.rs b/src/player.rs index aef0bd4..8d8fca1 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,3 +1,5 @@ +use std::path::Path; + use egui_inspect::{derive::Inspect, inspect}; use s2dc::{vec2, MobileEntity}; use serde::{Deserialize, Serialize}; @@ -52,8 +54,8 @@ impl Player { self.col_en.en.pos.y + self.col_en.en.bb.y } - pub(crate) fn save(&self) { - let result = std::fs::write("player.dat", rmp_serde::to_vec(self).unwrap()); + pub(crate) fn save(&self, path: &Path) { + let result = std::fs::write(path.join("player.dat"), rmp_serde::to_vec(self).unwrap()); log::info!("{result:?}"); } } diff --git a/src/world.rs b/src/world.rs index 567451f..2fe90ed 100644 --- a/src/world.rs +++ b/src/world.rs @@ -1,7 +1,12 @@ mod reg_chunk_existence; mod serialization; -use std::{fmt::Debug, fs::File, io::Seek, path::Path}; +use std::{ + fmt::Debug, + fs::File, + io::Seek, + path::{Path, PathBuf}, +}; use egui_inspect::derive::Inspect; use fnv::FnvHashMap; @@ -47,15 +52,18 @@ pub struct World { pub ticks: u64, pub player: Player, pub name: String, + #[opaque] + pub path: PathBuf, } impl World { - pub fn new(spawn_point: WorldPos, name: &str) -> Self { + pub fn new(spawn_point: WorldPos, name: &str, path: PathBuf) -> Self { Self { chunks: Default::default(), ticks: Default::default(), player: Player::new_at(spawn_point), name: name.to_string(), + path, } } /// Get mutable access to the tile at `pos`. @@ -66,15 +74,14 @@ impl World { let chk = self .chunks .entry(chk) - .or_insert_with(|| Chunk::load_or_gen(chk, worldgen, &self.name)); + .or_insert_with(|| Chunk::load_or_gen(chk, worldgen, &self.path)); chk.at_mut(local) } pub fn save(&self) { - let result = std::fs::create_dir(&self.name); + let result = std::fs::create_dir(&self.path); log::info!("{result:?}"); - std::env::set_current_dir(&self.name).unwrap(); self.save_meta(); - self.player.save(); + self.player.save(&self.path); self.save_chunks(); } pub fn save_meta(&self) { @@ -82,12 +89,15 @@ impl World { name: self.name.clone(), ticks: self.ticks, }; - let result = std::fs::write("world.dat", rmp_serde::to_vec(&meta).unwrap()); + let result = std::fs::write( + self.path.join("world.dat"), + rmp_serde::to_vec(&meta).unwrap(), + ); log::info!("{result:?}"); } pub fn save_chunks(&self) { for (pos, chk) in self.chunks.iter() { - save_chunk(pos, chk); + save_chunk(pos, chk, &self.path); } } } @@ -225,12 +235,9 @@ impl Chunk { Self { tiles } } - pub fn load_or_gen(chk: ChunkPos, worldgen: &Worldgen, world_name: &str) -> Chunk { + pub fn load_or_gen(chk: ChunkPos, worldgen: &Worldgen, world_path: &Path) -> Chunk { log::info!("Loading chunk {chk:?} (reg: {:?})", chk.region()); - let prev_dir = std::env::current_dir().unwrap(); - let result = std::env::set_current_dir(world_name); - log::info!("{result:?}"); - let reg_filename = format_reg_file_name(chk.region()); + let reg_filename = world_path.join(format_reg_file_name(chk.region())); let chunk = if chunk_exists(®_filename, chk) { log::info!("Chunk exists, loading"); let mut f = File::open(®_filename).unwrap(); @@ -245,8 +252,6 @@ impl Chunk { log::warn!("Chunk at {:?} doesn't exist, generating.", chk); Chunk::gen(chk, worldgen) }; - let result = std::env::set_current_dir(prev_dir); - log::info!("{result:?}"); chunk } @@ -255,11 +260,11 @@ impl Chunk { } } -fn chunk_exists(reg_filename: &str, pos: ChunkPos) -> bool { - if !Path::new(®_filename).exists() { +fn chunk_exists(reg_path: &Path, pos: ChunkPos) -> bool { + if !Path::new(®_path).exists() { return false; } - let bitset = ExistenceBitset::read_from_fs(reg_filename); + let bitset = ExistenceBitset::read_from_fs(reg_path); let local = pos.local(); let idx = loc_idx(local.1, local.0); crate::bitmanip::nth_bit_set(bitset.0, idx as usize) diff --git a/src/world/reg_chunk_existence.rs b/src/world/reg_chunk_existence.rs index ff4f9ce..20cab70 100644 --- a/src/world/reg_chunk_existence.rs +++ b/src/world/reg_chunk_existence.rs @@ -1,4 +1,4 @@ -use std::{fs::File, io::Read}; +use std::{fs::File, io::Read, path::Path}; #[derive(Clone, Copy)] pub struct ExistenceBitset(pub u64); @@ -12,8 +12,8 @@ impl ExistenceBitset { ExistenceBitset(u64::from_le_bytes(buf)) } - pub fn read_from_fs(reg_filename: &str) -> ExistenceBitset { - let mut f = File::open(reg_filename).unwrap(); + pub fn read_from_fs(path: &Path) -> ExistenceBitset { + let mut f = File::open(path).unwrap(); Self::read_from_file(&mut f) } } diff --git a/src/world/serialization.rs b/src/world/serialization.rs index 26345d1..8bf4257 100644 --- a/src/world/serialization.rs +++ b/src/world/serialization.rs @@ -11,8 +11,8 @@ use crate::world::{ use super::{default_chunk_tiles, loc_byte_idx_xy, Chunk, ChunkPos}; -pub(super) fn save_chunk(pos: &ChunkPos, chk: &Chunk) { - let reg_file_name = format_reg_file_name(pos.region()); +pub(super) fn save_chunk(pos: &ChunkPos, chk: &Chunk, world_dir: &Path) { + let reg_file_name = world_dir.join(format_reg_file_name(pos.region())); let reg_file_exists = Path::new(®_file_name).exists(); if !reg_file_exists { log::warn!("Region file doesn't exist. Going to create one."); @@ -86,8 +86,8 @@ fn test_chunk_seri() { for t in &mut chk.tiles { t.bg = 1; } - save_chunk(&ChunkPos { x: 2, y: 0 }, &chk); - save_chunk(&ChunkPos { x: 3, y: 0 }, &chk); + save_chunk(&ChunkPos { x: 2, y: 0 }, &chk, "testworld".as_ref()); + save_chunk(&ChunkPos { x: 3, y: 0 }, &chk, "testworld".as_ref()); let raw = std::fs::read("0.0.rgn").unwrap(); zstd::decode_all(&raw[8..]).unwrap(); std::fs::remove_file("0.0.rgn").unwrap();