nightshade 0.13.3

A cross-platform data-oriented game engine.
Documentation
use crate::ecs::world::World;
use crate::mcp::McpResponse;

pub(crate) fn mcp_load_asset(
    world: &mut World,
    request: crate::mcp::LoadAssetRequest,
) -> McpResponse {
    use crate::ecs::prefab::resources::{CachedPrefab, mesh_cache_insert, prefab_cache_insert};
    use crate::ecs::world::commands::WorldCommand;

    let crate::mcp::LoadAssetRequest { path, asset_name } = request;

    let extension = std::path::Path::new(&path)
        .extension()
        .and_then(|e| e.to_str())
        .map(|e| e.to_lowercase());

    let process_result =
        |world: &mut World,
         prefabs: Vec<crate::ecs::prefab::Prefab>,
         meshes: std::collections::HashMap<String, crate::ecs::mesh::components::Mesh>,
         textures: std::collections::HashMap<String, (Vec<u8>, u32, u32)>,
         animations: Vec<crate::ecs::animation::components::AnimationClip>,
         skins: Vec<crate::ecs::prefab::GltfSkin>,
         asset_name: &str,
         path: &str| {
            for (name, (rgba_data, width, height)) in textures {
                world.queue_command(WorldCommand::LoadTexture {
                    name,
                    rgba_data,
                    width,
                    height,
                });
            }

            for (name, mesh) in meshes {
                mesh_cache_insert(&mut world.resources.mesh_cache, name, mesh);
            }

            let prefab_count = prefabs.len();
            for (index, prefab) in prefabs.into_iter().enumerate() {
                let name = if prefab_count == 1 {
                    asset_name.to_string()
                } else {
                    format!("{}_{}", asset_name, index)
                };
                prefab_cache_insert(
                    &mut world.resources.prefab_cache,
                    name,
                    CachedPrefab {
                        prefab,
                        animations: animations.clone(),
                        skins: skins.clone(),
                        source_path: Some(path.to_string()),
                    },
                );
            }

            McpResponse::Success(format!(
                "Loaded asset '{}' from '{}' ({} prefab(s))",
                asset_name, path, prefab_count
            ))
        };

    match extension.as_deref() {
        Some("glb") | Some("gltf") => match std::fs::read(&path) {
            Ok(bytes) => match crate::ecs::prefab::import_gltf_from_bytes(&bytes) {
                Ok(result) => process_result(
                    world,
                    result.prefabs,
                    result.meshes,
                    result.textures,
                    result.animations,
                    result.skins,
                    &asset_name,
                    &path,
                ),
                Err(e) => McpResponse::Error(format!("Failed to load '{}': {}", path, e)),
            },
            Err(e) => McpResponse::Error(format!("Failed to read '{}': {}", path, e)),
        },
        #[cfg(feature = "fbx")]
        Some("fbx") => {
            match crate::ecs::prefab::import_fbx_from_path(std::path::Path::new(&path)) {
                Ok(result) => process_result(
                    world,
                    result.prefabs,
                    result.meshes,
                    result.textures,
                    result.animations,
                    result.skins,
                    &asset_name,
                    &path,
                ),
                Err(e) => McpResponse::Error(format!("Failed to load '{}': {}", path, e)),
            }
        }
        _ => McpResponse::Error(
            "Unsupported file extension. Supported: .glb, .gltf, .fbx".to_string(),
        ),
    }
}

pub(crate) fn mcp_spawn_prefab(
    world: &mut World,
    request: crate::mcp::SpawnPrefabRequest,
) -> McpResponse {
    use crate::ecs::prefab::resources::prefab_cache_get;
    use crate::ecs::prefab::spawn_prefab_with_skins;
    use crate::ecs::world::NAME;

    let crate::mcp::SpawnPrefabRequest {
        asset_name,
        entity_name,
        position,
        scale,
    } = request;
    let scale = scale.unwrap_or([1.0, 1.0, 1.0]);

    if world.resources.entity_names.contains_key(&entity_name) {
        return McpResponse::Error(format!("Entity '{}' already exists", entity_name));
    }

    let Some(cached) = prefab_cache_get(&world.resources.prefab_cache, &asset_name) else {
        return McpResponse::Error(format!(
            "Asset '{}' not found. Use load_asset first.",
            asset_name
        ));
    };

    let prefab = cached.prefab.clone();
    let animations = cached.animations.clone();
    let skins = cached.skins.clone();
    let pos = nalgebra_glm::Vec3::new(position[0], position[1], position[2]);

    let entity = spawn_prefab_with_skins(world, &prefab, &animations, &skins, pos);

    if scale != [1.0, 1.0, 1.0]
        && let Some(transform) = world.core.get_local_transform_mut(entity)
    {
        transform.scale = nalgebra_glm::Vec3::new(scale[0], scale[1], scale[2]);
        crate::ecs::transform::systems::mark_local_transform_dirty(world, entity);
    }

    world.core.add_components(entity, NAME);
    world.core.set_name(
        entity,
        crate::ecs::name::components::Name(entity_name.clone()),
    );
    world
        .resources
        .entity_names
        .insert(entity_name.clone(), entity);

    McpResponse::Success(format!(
        "Spawned prefab '{}' as entity '{}' at {:?}",
        asset_name, entity_name, position
    ))
}

pub(crate) fn mcp_list_loaded_assets(world: &World) -> McpResponse {
    use crate::ecs::prefab::resources::prefab_cache_names;

    let names: Vec<String> = prefab_cache_names(&world.resources.prefab_cache)
        .cloned()
        .collect();
    McpResponse::AssetList(names)
}