mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
fix(wasm-plugins): fix the panic on hot-reloading (#3036)
* fix(wasm-plugins): fix the panic on hot-reloading * fix(wasm-plugins): do not cache permission denials from non-main threads
This commit is contained in:
@@ -8,6 +8,7 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
pin::Pin,
|
||||
sync::{Arc, atomic::AtomicBool},
|
||||
thread::ThreadId,
|
||||
time::Duration,
|
||||
};
|
||||
use thiserror::Error;
|
||||
@@ -183,6 +184,9 @@ pub struct PluginManager {
|
||||
// Background task for hot reloading
|
||||
hot_reload_task: RwLock<Option<JoinHandle<()>>>,
|
||||
hot_reload_enabled: AtomicBool,
|
||||
// Thread ID of the thread that created the plugin manager.
|
||||
// Permission prompts use rustyline, which is only safe on this thread.
|
||||
main_thread_id: ThreadId,
|
||||
}
|
||||
|
||||
/// Represents a successfully loaded plugin
|
||||
@@ -235,6 +239,7 @@ impl PluginManager {
|
||||
state_notify: Arc::new(Notify::new()),
|
||||
hot_reload_task: RwLock::new(None),
|
||||
hot_reload_enabled: AtomicBool::new(false),
|
||||
main_thread_id: std::thread::current().id(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,13 +443,32 @@ impl PluginManager {
|
||||
}
|
||||
|
||||
/// Ask the server owner if they allow the permissions requested by a plugin
|
||||
///
|
||||
/// This must only be called from the main thread, because the underlying
|
||||
/// `rustyline` console handle is neither `Send` nor `Sync`. Calling it from a
|
||||
/// different thread (e.g. the hot-reload watcher task) will panic.
|
||||
///
|
||||
/// Returns `(allowed, wait_time, cacheable)`. Non-main-thread denials are not
|
||||
/// cacheable so the user gets prompted again on the next cold load.
|
||||
#[expect(clippy::print_stdout)]
|
||||
fn ask_permission_confirmation(metadata: &PluginMetadata) -> (bool, std::time::Duration) {
|
||||
fn ask_permission_confirmation(
|
||||
&self,
|
||||
metadata: &PluginMetadata,
|
||||
) -> (bool, std::time::Duration, bool) {
|
||||
use colored::Colorize;
|
||||
use rustyline::DefaultEditor;
|
||||
|
||||
if metadata.permissions.is_empty() {
|
||||
return (true, std::time::Duration::ZERO);
|
||||
return (true, std::time::Duration::ZERO, true);
|
||||
}
|
||||
|
||||
if std::thread::current().id() != self.main_thread_id {
|
||||
warn!(
|
||||
"Plugin \"{}\" ({}) requests permissions from a non-main thread. \
|
||||
Permission prompts are only supported on the main thread. \
|
||||
Denying permissions. To load this plugin interactively, restart the server.",
|
||||
metadata.name, metadata.version
|
||||
);
|
||||
return (false, Duration::ZERO, false);
|
||||
}
|
||||
|
||||
let start_time = std::time::Instant::now();
|
||||
@@ -484,12 +508,11 @@ impl PluginManager {
|
||||
let input = line.trim().to_lowercase();
|
||||
input == "y" || input == "yes"
|
||||
})
|
||||
} else if let Ok(mut rl) = DefaultEditor::new() {
|
||||
rl.readline(&prompt).is_ok_and(|line| {
|
||||
let input = line.trim().to_lowercase();
|
||||
input == "y" || input == "yes"
|
||||
})
|
||||
} else {
|
||||
warn!(
|
||||
"Console readline is not available; cannot prompt for plugin \"{}\" permissions",
|
||||
metadata.name
|
||||
);
|
||||
false
|
||||
};
|
||||
|
||||
@@ -497,7 +520,7 @@ impl PluginManager {
|
||||
wrapper.return_readline(rl);
|
||||
}
|
||||
|
||||
(result, start_time.elapsed())
|
||||
(result, start_time.elapsed(), true)
|
||||
}
|
||||
|
||||
/// Spawn initialization for a single plugin
|
||||
@@ -842,15 +865,17 @@ impl PluginManager {
|
||||
return (true, std::time::Duration::ZERO);
|
||||
}
|
||||
|
||||
let (allowed, wait_time) = Self::ask_permission_confirmation(metadata);
|
||||
cache.entries.insert(
|
||||
hash,
|
||||
cache::PermissionCacheEntry {
|
||||
permissions_requested: metadata.permissions.clone(),
|
||||
approved: allowed,
|
||||
},
|
||||
);
|
||||
let _ = cache.save(cache_path).await;
|
||||
let (allowed, wait_time, cacheable) = self.ask_permission_confirmation(metadata);
|
||||
if cacheable {
|
||||
cache.entries.insert(
|
||||
hash,
|
||||
cache::PermissionCacheEntry {
|
||||
permissions_requested: metadata.permissions.clone(),
|
||||
approved: allowed,
|
||||
},
|
||||
);
|
||||
let _ = cache.save(cache_path).await;
|
||||
}
|
||||
(allowed, wait_time)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user