Skip to content

Commit

Permalink
Implement session.lock
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Snowiiii committed Dec 27, 2024
1 parent 3737298 commit 98d1406
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 0 deletions.
2 changes: 2 additions & 0 deletions pumpkin-world/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
9 changes: 9 additions & 0 deletions pumpkin-world/src/level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};

Expand All @@ -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)]
Expand All @@ -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)
Expand All @@ -71,6 +79,7 @@ impl Level {
loaded_chunks: Arc::new(DashMap::new()),
chunk_watchers: Arc::new(DashMap::new()),
level_info,
_locker: Arc::new(locker),
}
}

Expand Down
1 change: 1 addition & 0 deletions pumpkin-world/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
32 changes: 32 additions & 0 deletions pumpkin-world/src/lock/anvil.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 18 additions & 0 deletions pumpkin-world/src/lock/mod.rs
Original file line number Diff line number Diff line change
@@ -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,
}

0 comments on commit 98d1406

Please sign in to comment.