use crate::context::ResourceContext;
use crate::engine::ModBridgeSystem;
use crate::event::EventBus;
use crate::modding::events::*;
use crate::modding::{ModEventSystem, ModHandle, ModLoader, PluginAction};
use crate::plugin::{Plugin, PluginBuilder, PluginBuilderExt};
use crate::system::System;
use async_trait::async_trait;
use std::any::Any;
#[derive(Default)]
pub struct ModSystemPlugin {
loader: Option<Box<dyn ModLoader>>,
}
impl ModSystemPlugin {
pub fn new() -> Self {
Self::default()
}
pub fn with_loader(mut self, loader: impl ModLoader + 'static) -> Self {
self.loader = Some(Box::new(loader));
self
}
}
#[async_trait]
impl Plugin for ModSystemPlugin {
fn name(&self) -> &'static str {
"mod_system"
}
fn build(&self, builder: &mut dyn PluginBuilder) {
builder.register_resource(ModSystemConfig::default());
if let Some(loader) = &self.loader {
builder.register_runtime_state(ModLoaderState {
loader: loader.clone_box(),
loaded_mods: Vec::new(),
});
}
builder.register_system(Box::new(ModLoadSystem));
builder.register_system(Box::new(PluginControlSystem));
builder.register_system(Box::new(ModEventSystem::new()));
builder.register_system(Box::new(ModBridgeSystem::new()));
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ModSystemConfig {
pub mod_dir: String,
pub hot_reload: bool,
pub auto_load: bool,
}
impl Default for ModSystemConfig {
fn default() -> Self {
Self {
mod_dir: "mods".to_string(),
hot_reload: false,
auto_load: true,
}
}
}
impl crate::resources::Resource for ModSystemConfig {}
pub struct ModLoaderState {
pub loader: Box<dyn ModLoader>,
pub loaded_mods: Vec<ModHandle>,
}
struct ModLoadSystem;
impl ModLoadSystem {
#[allow(dead_code)]
pub async fn update_resources(&mut self, resources: &mut ResourceContext) {
let load_requests: Vec<ModLoadRequested> = {
if let Some(mut event_bus) = resources.get_mut::<EventBus>().await {
event_bus
.reader::<ModLoadRequested>()
.iter()
.cloned()
.collect()
} else {
Vec::new()
}
};
let unload_requests: Vec<ModUnloadRequested> = {
if let Some(mut event_bus) = resources.get_mut::<EventBus>().await {
event_bus
.reader::<ModUnloadRequested>()
.iter()
.cloned()
.collect()
} else {
Vec::new()
}
};
let mut load_results = Vec::new();
if !load_requests.is_empty() {
if let Some(mut loader_state) = resources.get_mut::<ModLoaderState>().await {
for request in load_requests {
match loader_state.loader.load(&request.path) {
Ok(handle) => {
println!(
"[MOD System] Loaded MOD: {} v{}",
handle.metadata.name, handle.metadata.version
);
loader_state.loaded_mods.push(handle.clone());
load_results.push(Ok(handle));
}
Err(e) => {
eprintln!("[MOD System] Failed to load MOD {:?}: {}", request.path, e);
load_results.push(Err((request.path, e.to_string())));
}
}
}
}
}
if let Some(mut event_bus) = resources.get_mut::<EventBus>().await {
for result in load_results {
match result {
Ok(handle) => {
event_bus.publish(ModLoadedEvent { handle });
}
Err((path, error)) => {
event_bus.publish(ModLoadFailedEvent { path, error });
}
}
}
}
let mut unload_results: Vec<Result<String, ()>> = Vec::new();
if !unload_requests.is_empty() {
if let Some(mut loader_state) = resources.get_mut::<ModLoaderState>().await {
for request in unload_requests {
if let Some(pos) = loader_state
.loaded_mods
.iter()
.position(|h| h.id == request.mod_id)
{
let handle = loader_state.loaded_mods.remove(pos);
match loader_state.loader.unload(&handle) {
Ok(_) => {
println!("[MOD System] Unloaded MOD: {}", request.mod_id);
unload_results.push(Ok(request.mod_id));
}
Err(e) => {
eprintln!(
"[MOD System] Failed to unload MOD {}: {}",
request.mod_id, e
);
loader_state.loaded_mods.push(handle);
}
}
} else {
eprintln!("[MOD System] MOD '{}' not found", request.mod_id);
}
}
}
}
if let Some(mut event_bus) = resources.get_mut::<EventBus>().await {
for mod_id in unload_results.into_iter().flatten() {
event_bus.publish(ModUnloadedEvent { mod_id });
}
}
}
}
#[async_trait]
impl System for ModLoadSystem {
fn name(&self) -> &'static str {
"mod_load_system"
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
struct PluginControlSystem;
impl PluginControlSystem {
#[allow(dead_code)]
pub async fn update_resources(&mut self, resources: &mut ResourceContext) {
let commands = {
if let Some(mut loader_state) = resources.get_mut::<ModLoaderState>().await {
loader_state.loader.drain_commands()
} else {
Vec::new()
}
};
if commands.is_empty() {
return;
}
if let Some(mut event_bus) = resources.get_mut::<EventBus>().await {
for command in &commands {
event_bus.publish(PluginControlRequested {
control: command.clone(),
source_mod: None, });
}
for command in commands {
match &command.action {
PluginAction::Enable => {
println!("[MOD System] Enabling plugin: {}", command.plugin_name);
event_bus.publish(PluginEnabledEvent {
plugin_name: command.plugin_name.clone(),
});
}
PluginAction::Disable => {
println!("[MOD System] Disabling plugin: {}", command.plugin_name);
event_bus.publish(PluginDisabledEvent {
plugin_name: command.plugin_name.clone(),
});
}
PluginAction::SetParameter { key, value } => {
println!(
"[MOD System] Setting {}.{} = {:?}",
command.plugin_name, key, value
);
event_bus.publish(PluginParameterChangedEvent {
plugin_name: command.plugin_name.clone(),
key: key.clone(),
value: value.clone(),
});
}
PluginAction::TriggerHook { hook_name, data } => {
println!(
"[MOD System] Triggering hook: {}.{}",
command.plugin_name, hook_name
);
event_bus.publish(PluginHookTriggeredEvent {
plugin_name: command.plugin_name.clone(),
hook_name: hook_name.clone(),
data: data.clone(),
});
}
}
}
}
}
}
#[async_trait]
impl System for PluginControlSystem {
fn name(&self) -> &'static str {
"plugin_control_system"
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}