From 698589806abeb50e53c39570a5230b0c92665a20 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Fri, 5 Dec 2025 16:57:41 +0100 Subject: [PATCH] fix: don't crash when failed to lock session.lock fix: https://github.com/Pumpkin-MC/Pumpkin/issues/1212 fix: https://github.com/Pumpkin-MC/Pumpkin/issues/1194 --- pumpkin-world/src/lock/anvil.rs | 14 +++++++++++--- pumpkin-world/src/lock/mod.rs | 2 ++ pumpkin/src/server/mod.rs | 15 ++++++++++----- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/pumpkin-world/src/lock/anvil.rs b/pumpkin-world/src/lock/anvil.rs index f314c6bab..029a7d75d 100644 --- a/pumpkin-world/src/lock/anvil.rs +++ b/pumpkin-world/src/lock/anvil.rs @@ -1,6 +1,10 @@ use super::{LevelLocker, LockError}; -use std::{fs::File, io::Write, path::Path}; +use std::{ + fs::{File, TryLockError}, + io::Write, + path::Path, +}; pub struct AnvilLevelLocker { _locked_file: File, @@ -23,8 +27,12 @@ impl LevelLocker for AnvilLevelLocker { // im not joking, mojang writes a snowman into the lock file file.write_all(SNOWMAN) .map_err(|_| LockError::FailedWrite)?; - file.try_lock() - .map_err(|_| LockError::AlreadyLocked(SESSION_LOCK_FILE_NAME.to_string()))?; + file.try_lock().map_err(|e| match e { + TryLockError::WouldBlock => { + LockError::AlreadyLocked(SESSION_LOCK_FILE_NAME.to_string()) + } + TryLockError::Error(io_err) => LockError::Error(io_err), + })?; Ok(Self { _locked_file: file }) } } diff --git a/pumpkin-world/src/lock/mod.rs b/pumpkin-world/src/lock/mod.rs index de43a3339..608ba855b 100644 --- a/pumpkin-world/src/lock/mod.rs +++ b/pumpkin-world/src/lock/mod.rs @@ -13,6 +13,8 @@ pub trait LevelLocker: Send + Sync { pub enum LockError { #[error("Oh no, Level is already locked by {0}")] AlreadyLocked(String), + #[error("{0}")] + Error(std::io::Error), #[error("Failed to write into lock file")] FailedWrite, } diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 0b3ba57da..1f1ed53fe 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -102,7 +102,7 @@ pub struct Server { world_info_writer: Arc, // Gets unlocked when dropped // TODO: Make this a trait - _locker: Arc, + _locker: Arc>, } impl Server { @@ -138,10 +138,15 @@ impl Server { fs::copy(dat_path, backup_path).unwrap(); } } - // if we fail to lock, lets crash ???. maybe not the best solution when we have a large server with many worlds and one is locked. - // So TODO - let locker = AnvilLevelLocker::lock(&world_path).expect("Failed to lock level"); - + let locker = match AnvilLevelLocker::lock(&world_path) { + Ok(l) => Some(l), + Err(err) => { + log::warn!( + "Could not lock the level file. Data corruption is possible if the world is accessed by multiple processes simultaneously. Error: {err}" + ); + None + } + }; let player_data_path = world_path.join("playerdata"); let level_info = level_info.unwrap_or_else(|err| {