55375b1406
Project CI / AI game creator shell Rust crates (push) Successful in 1m18s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m30s
Project CI / Backend tests (push) Successful in 3m58s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 10m12s
Project CI / Native shell tests (push) Successful in 5m49s
Project CI / Frontend tests (push) Successful in 2m18s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 9m50s
Project CI / AI game creator shell web tests (push) Failing after 2m11s
Project CI / Repository checks (push) Successful in 2m55s
Reviewed-on: https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/509 Co-authored-by: Linghong <ink29535@proton.me> Co-committed-by: Linghong <ink29535@proton.me>
160 lines
4.2 KiB
Rust
160 lines
4.2 KiB
Rust
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<PathBuf> {
|
||
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<PluginSummary, String> {
|
||
let _ = self.stop(id)?;
|
||
self.refresh()?;
|
||
self.start(id)
|
||
}
|
||
}
|
||
|
||
#[tauri::command]
|
||
pub(crate) fn list_agc_plugins(host: State<'_, PluginHost>) -> Result<Vec<PluginSummary>, String> {
|
||
host.list()
|
||
}
|
||
|
||
#[tauri::command]
|
||
pub(crate) fn list_agc_extensions(
|
||
host: State<'_, PluginHost>,
|
||
) -> Result<Vec<AgcExtensionSummary>, String> {
|
||
host.list_extensions()
|
||
}
|
||
|
||
#[tauri::command]
|
||
pub(crate) fn refresh_agc_plugins(
|
||
host: State<'_, PluginHost>,
|
||
) -> Result<Vec<PluginSummary>, String> {
|
||
host.refresh()
|
||
}
|
||
|
||
#[tauri::command]
|
||
pub(crate) fn start_agc_plugin(
|
||
id: String,
|
||
host: State<'_, PluginHost>,
|
||
app: tauri::AppHandle,
|
||
) -> Result<PluginSummary, String> {
|
||
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<PluginSummary, String> {
|
||
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<PluginSummary, String> {
|
||
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<Vec<PluginSummary>, 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<Value, String> {
|
||
if !valid_text(&method, 120) || method.chars().any(char::is_control) {
|
||
return Err("插件 RPC 方法无效".to_string());
|
||
}
|
||
tauri::async_runtime::spawn_blocking(move || {
|
||
app.state::<PluginHost>().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<PluginPanelContent, String> {
|
||
host.read_panel(&id, &panel_id)
|
||
}
|
||
|
||
#[tauri::command]
|
||
pub(crate) async fn set_agc_plugin_project_path(
|
||
project_path: Option<String>,
|
||
app: tauri::AppHandle,
|
||
) -> Result<(), String> {
|
||
tauri::async_runtime::spawn_blocking(move || {
|
||
let host = app.state::<PluginHost>();
|
||
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);
|
||
}
|
||
}
|