metaverse_core 0.3.2

core program for server and client IO
use ::bevy::ecs::resource::Resource;
use benthic_protocol::{
    render_data::{AvatarObject, RenderObject},
    session::{EnvironmentCache, InventoryData, RegionData, Session, set_cache_enabled},
};
use default_asset_converter::generated::DEFAULT_SKELETON;
use glam::{Vec2, Vec3};
use metaverse_avatar::{
    avatar::{Avatar, OutfitObject},
    avatar_object_handler::{AvatarState, add_object_to_avatar, finalize_avatar, init_avatar},
};
use metaverse_messages::http::{mesh::Mesh, scene::SceneGroup};
use metaverse_objects::object_handler::create_render_object;
use std::{
    collections::{BTreeSet, HashMap},
    fs::{self, File},
    io::Write,
    net::{IpAddr, Ipv4Addr},
    path::PathBuf,
};
use uuid::{Uuid, uuid};
pub mod bevy;

#[derive(Debug, Resource)]
pub struct CreationArtifacts {
    pub avatar_list: HashMap<Uuid, Avatar>,
    pub avatar_object: AvatarObject,
    pub gltf_object: PathBuf,
}
impl CreationArtifacts {
    fn new() -> CreationArtifacts {
        CreationArtifacts {
            avatar_list: HashMap::new(),
            avatar_object: AvatarObject {
                objects: Vec::new(),
                global_skeleton: DEFAULT_SKELETON.clone(),
                used_joints: BTreeSet::new(),
            },
            gltf_object: PathBuf::new(),
        }
    }
}

pub const AGENT_ID: Uuid = uuid!("96ce3a85-273b-4a1a-b300-78bce703c325");
pub async fn mock_avatar_load() -> CreationArtifacts {
    set_cache_enabled(false);
    let mut artifacts = CreationArtifacts::new();
    let inventory_db_connection = sqlx::SqlitePool::connect(":memory:").await.unwrap();

    let mut session: Session<(), Avatar, (), ()> = Session {
        inventory_db_connection,
        agent_id: AGENT_ID,
        session_id: Uuid::nil(),
        address: String::new(),
        seed_capability_url: String::new(),
        sequence_number: 0,
        local_ip: IpAddr::V4(Ipv4Addr::UNSPECIFIED),
        capability_urls: HashMap::new(),

        region_data: RegionData {
            region_coordinates: Vec2::ZERO,
            region_id: String::new(),
            ..Default::default()
        },

        environment_cache: EnvironmentCache {
            patch_queue: HashMap::new(),
            patch_cache: HashMap::new(),
        },

        inventory_data: InventoryData {
            inventory_root: Uuid::nil(),
            inventory_lib_owner: Uuid::nil(),
            inventory_init: true,
        },

        socket: None,

        #[cfg(feature = "avatar")]
        avatars: HashMap::new(),
    };
    session.agent_id = AGENT_ID;
    session.inventory_data.inventory_init = true;

    let mut avatar = Avatar::new(AGENT_ID, Vec3::ZERO);
    avatar.outfit_size = 4;

    init_avatar(&mut session, &avatar).unwrap();

    let generated_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/generated");
    fs::create_dir_all(&generated_dir).unwrap();

    let generated_agent_dir = generated_dir.join(AGENT_ID.to_string());
    fs::create_dir_all(&generated_agent_dir).unwrap();

    let scene_groups_path =
        PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/data/scene_groups");

    for entry in fs::read_dir(&scene_groups_path).unwrap() {
        let entry = entry.unwrap();
        let path = entry.path();

        if path.extension().is_some_and(|ext| ext != "json") {
            continue;
        }

        let file = fs::File::open(&path).unwrap();

        let scene_group: SceneGroup = serde_json::from_reader(file).unwrap();

        let mut render_objects: Vec<RenderObject> = Vec::new();

        for part in &scene_group.parts {
            println!("generating: {:?}", part.metadata.name);

            let mesh_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
                .join(format!("tests/data/meshes/{}.json", part.sculpt.texture));

            let file = fs::File::open(&mesh_path).unwrap();

            let texture_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(format!(
                "tests/data/textures/{}.png",
                part.shape.texture.texture_id
            ));

            let mesh: Mesh = serde_json::from_reader(file).unwrap();

            let render_object = create_render_object(
                mesh,
                part.metadata.name.clone(),
                &texture_path,
                part.sculpt.texture,
            )
            .unwrap();

            render_objects.push(render_object);
        }

        let json_name = format!(
            "{:?}_{}.json",
            scene_group.parts[0].sculpt.texture, scene_group.parts[0].metadata.name
        );

        let json_path = generated_agent_dir.join(json_name);

        let json = serde_json::to_string(&render_objects).unwrap();

        let mut file = File::create(&json_path).unwrap();
        file.write_all(json.as_bytes()).unwrap();

        let state =
            add_object_to_avatar(&mut session, AGENT_ID, OutfitObject::MeshObject(json_path))
                .unwrap();

        if matches!(state, AvatarState::FullyLoaded) {
            let avatar = session.avatars.get_mut(&AGENT_ID).unwrap();

            let (glb_path, skeleton) = finalize_avatar(
                AGENT_ID,
                avatar.skeleton.clone(),
                avatar.used_joints.clone(),
                avatar.items.clone(),
            )
            .await
            .unwrap();
            avatar.path = Some(glb_path.clone());
            avatar.skeleton = skeleton;

            artifacts.gltf_object = glb_path;
        }
    }

    let avatar = session.avatars.get(&AGENT_ID).unwrap();

    artifacts.avatar_object = AvatarObject {
        objects: avatar
            .items
            .iter()
            .filter_map(|item| match item {
                OutfitObject::MeshObject(path) => Some(path.clone()),
                _ => None,
            })
            .collect(),
        global_skeleton: avatar.skeleton.clone(),
        used_joints: avatar.used_joints.clone(),
    };

    artifacts
}