use super::*; use tauri::{Emitter, Manager, State}; pub(crate) const AGC_PLUGIN_STATE_CHANGED_EVENT: &str = "game-creator-plugin-state-changed"; /// 解析插件工作区目录:环境变量优先,其次随包资源目录 `plugins/`, /// 开发构建再回退仓库里的 `plugins/` 工作区。 pub(crate) fn resolve_plugin_workspace(app: &tauri::AppHandle) -> Option { if let Some(workspace) = std::env::var_os("AGC_PLUGIN_WORKSPACE") { let workspace = PathBuf::from(workspace); if workspace.is_dir() { return Some(workspace); } } if let Ok(resource_dir) = app.path().resource_dir() { let bundled = resource_dir.join("plugins"); if bundled.is_dir() { return Some(bundled); } } #[cfg(debug_assertions)] { let repo_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../.."); let workspace = repo_root.join("plugins"); if workspace.is_dir() { return Some(workspace); } } None } impl PluginHost { pub(crate) fn reload(&self, id: &str) -> Result { let _ = self.stop(id)?; self.refresh()?; self.start(id) } } #[tauri::command] pub(crate) fn list_agc_plugins(host: State<'_, PluginHost>) -> Result, String> { host.list() } #[tauri::command] pub(crate) fn list_agc_extensions( host: State<'_, PluginHost>, ) -> Result, String> { host.list_extensions() } #[tauri::command] pub(crate) fn refresh_agc_plugins( host: State<'_, PluginHost>, ) -> Result, String> { host.refresh() } #[tauri::command] pub(crate) fn start_agc_plugin( id: String, host: State<'_, PluginHost>, app: tauri::AppHandle, ) -> Result { let result = host.start(id.trim()); if result.is_ok() { emit_plugin_state_changed(&app, &host); } result } #[tauri::command] pub(crate) fn stop_agc_plugin( id: String, host: State<'_, PluginHost>, app: tauri::AppHandle, ) -> Result { let result = host.stop(id.trim()); if result.is_ok() { emit_plugin_state_changed(&app, &host); } result } #[tauri::command] pub(crate) fn reload_agc_plugin( id: String, host: State<'_, PluginHost>, app: tauri::AppHandle, ) -> Result { let result = host.reload(id.trim()); if result.is_ok() { emit_plugin_state_changed(&app, &host); } result } #[tauri::command] pub(crate) fn set_agc_plugin_enabled( id: String, enabled: bool, host: State<'_, PluginHost>, app: tauri::AppHandle, ) -> Result, String> { let result = host.set_enabled(id.trim(), enabled); if result.is_ok() { emit_plugin_state_changed(&app, &host); } result } #[tauri::command] pub(crate) async fn call_agc_plugin( id: String, method: String, params: Value, app: tauri::AppHandle, ) -> Result { if !valid_text(&method, 120) || method.chars().any(char::is_control) { return Err("插件 RPC 方法无效".to_string()); } tauri::async_runtime::spawn_blocking(move || { app.state::().call(id.trim(), method, params) }) .await .map_err(|_| "插件 RPC 任务失败".to_string())? } #[tauri::command] pub(crate) fn read_agc_plugin_panel( id: String, panel_id: String, host: State<'_, PluginHost>, ) -> Result { host.read_panel(&id, &panel_id) } #[tauri::command] pub(crate) async fn set_agc_plugin_project_path( project_path: Option, app: tauri::AppHandle, ) -> Result<(), String> { tauri::async_runtime::spawn_blocking(move || { let host = app.state::(); let result = host.set_active_project(project_path); if result.is_ok() { emit_plugin_state_changed(&app, &host); } result }) .await .map_err(|_| "切换插件项目上下文任务失败".to_string())? } fn emit_plugin_state_changed(app: &tauri::AppHandle, host: &PluginHost) { if let Ok(extensions) = host.list_extensions() { let _ = app.emit(AGC_PLUGIN_STATE_CHANGED_EVENT, extensions); } }