Implement session.lock
Lock session.lock so other processes know that we currently read and write to the level. This is vanilla behavior.
Test:
Tested to start the a new server while on was locking the file, and got `Failed to lock level: AlreadyLocked("session.lock")` as expected
This commit is contained in:
parent
37372986b0
commit
98d1406646
@ -25,6 +25,8 @@ dashmap = "6.1.0"
|
||||
flate2 = "1.0"
|
||||
lz4 = "1.28.0"
|
||||
|
||||
file-guard = "0.2.0"
|
||||
|
||||
enum_dispatch = "0.3.13"
|
||||
|
||||
fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
|
||||
|
||||
@ -14,6 +14,7 @@ use crate::{
|
||||
anvil::AnvilChunkReader, ChunkData, ChunkParsingError, ChunkReader, ChunkReadingError,
|
||||
},
|
||||
generation::{get_world_gen, Seed, WorldGenerator},
|
||||
lock::{anvil::AnvilLevelLocker, LevelLocker},
|
||||
world_info::{anvil::AnvilLevelInfo, LevelData, WorldInfoReader, WorldInfoWriter},
|
||||
};
|
||||
|
||||
@ -35,6 +36,9 @@ pub struct Level {
|
||||
chunk_watchers: Arc<DashMap<Vector2<i32>, usize>>,
|
||||
chunk_reader: Arc<dyn ChunkReader>,
|
||||
world_gen: Arc<dyn WorldGenerator>,
|
||||
// Gets unlocked when dropped
|
||||
// TODO: Make this a trait
|
||||
_locker: Arc<AnvilLevelLocker>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -55,6 +59,10 @@ impl Level {
|
||||
region_folder,
|
||||
};
|
||||
|
||||
// if we fail to lock, lets crash ???. maybe not the best soultion when we have a large server with many worlds and one is locked.
|
||||
// So TODO
|
||||
let locker = AnvilLevelLocker::look(&level_folder).expect("Failed to lock level");
|
||||
|
||||
// TODO: Load info correctly based on world format type
|
||||
let level_info = AnvilLevelInfo
|
||||
.read_world_info(&level_folder)
|
||||
@ -71,6 +79,7 @@ impl Level {
|
||||
loaded_chunks: Arc::new(DashMap::new()),
|
||||
chunk_watchers: Arc::new(DashMap::new()),
|
||||
level_info,
|
||||
_locker: Arc::new(locker),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@ pub mod dimension;
|
||||
mod generation;
|
||||
pub mod item;
|
||||
pub mod level;
|
||||
mod lock;
|
||||
pub mod world_info;
|
||||
pub const WORLD_HEIGHT: usize = 384;
|
||||
pub const WORLD_LOWEST_Y: i16 = -64;
|
||||
|
||||
32
pumpkin-world/src/lock/anvil.rs
Normal file
32
pumpkin-world/src/lock/anvil.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use file_guard::{FileGuard, Lock};
|
||||
|
||||
use super::{LevelLocker, LockError};
|
||||
|
||||
use std::{fs::File, io::Write, sync::Arc};
|
||||
|
||||
pub struct AnvilLevelLocker {
|
||||
_lock: Option<FileGuard<Arc<File>>>,
|
||||
}
|
||||
|
||||
const SESSION_LOCK_FILE_NAME: &str = "session.lock";
|
||||
|
||||
const SNOWMAN: &[u8] = "☃".as_bytes();
|
||||
|
||||
impl LevelLocker<Self> for AnvilLevelLocker {
|
||||
fn look(folder: &crate::level::LevelFolder) -> Result<Self, LockError> {
|
||||
let file_path = folder.root_folder.join(SESSION_LOCK_FILE_NAME);
|
||||
let mut file = File::options()
|
||||
.create(true)
|
||||
.truncate(false)
|
||||
.write(true)
|
||||
.open(file_path)
|
||||
.unwrap();
|
||||
// im not joking, mojang writes a snowman into the lock file
|
||||
file.write_all(SNOWMAN)
|
||||
.map_err(|_| LockError::FailedWrite)?;
|
||||
let file_arc = Arc::new(file);
|
||||
let lock = file_guard::try_lock(file_arc, Lock::Exclusive, 0, 1)
|
||||
.map_err(|_| LockError::AlreadyLocked(SESSION_LOCK_FILE_NAME.to_string()))?;
|
||||
Ok(Self { _lock: Some(lock) })
|
||||
}
|
||||
}
|
||||
18
pumpkin-world/src/lock/mod.rs
Normal file
18
pumpkin-world/src/lock/mod.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::level::LevelFolder;
|
||||
|
||||
pub mod anvil;
|
||||
|
||||
// Gets unlocked when dropped
|
||||
pub trait LevelLocker<T>: Send + Sync {
|
||||
fn look(folder: &LevelFolder) -> Result<T, LockError>;
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum LockError {
|
||||
#[error("Oh no, Level is already locked by {0}")]
|
||||
AlreadyLocked(String),
|
||||
#[error("Failed to write into lock file")]
|
||||
FailedWrite,
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user