use super::session::Mailbox;
use crate::avatar::HandleNewAvatar;
use crate::initialize::create_sub_agent_dir;
use crate::initialize::create_sub_object_dir;
use crate::session::OutgoingPacket;
use crate::session::SendUIMessage;
use crate::transport::http_handler::download_renderable_mesh;
use crate::transport::http_handler::download_texture;
use actix::AsyncContext;
use actix::ResponseFuture;
use actix::WrapFuture;
use actix::{Handler, Message};
use glam::Quat;
use glam::Vec3;
use log::info;
use log::{error, warn};
use metaverse_agent::avatar::Avatar;
use metaverse_inventory::object_update::get_object_scale_rotation_position;
use metaverse_inventory::object_update::get_object_update;
use metaverse_mesh::generate::generate_object_mesh;
use metaverse_messages::http::capabilities::Capability;
use metaverse_messages::packet::message::UIMessage;
use metaverse_messages::packet::packet::Packet;
use metaverse_messages::udp::object::improved_terse_object_update::ImprovedTerseObjectUpdate;
use metaverse_messages::udp::object::object_update::AttachItem;
use metaverse_messages::udp::object::object_update::ExtraParams;
use metaverse_messages::udp::object::object_update_cached::ObjectUpdateCached;
use metaverse_messages::udp::object::request_multiple_objects::CacheMissType;
use metaverse_messages::udp::object::request_multiple_objects::RequestMultipleObjects;
use metaverse_messages::ui::mesh_update::MeshType;
use metaverse_messages::ui::mesh_update::MeshUpdate;
use metaverse_messages::utils::object_types::ObjectType;
use metaverse_messages::utils::texture_entry::TextureEntry;
use serde::Serialize;
use std::fs::File;
use std::io;
use std::io::Write;
use std::path::PathBuf;
use uuid::Uuid;
use metaverse_inventory::object_update::insert_object_update_minimal;
#[derive(Debug, Message, Clone)]
#[rtype(result = "()")]
pub struct HandleObjectUpdate {
pub object_type: ObjectType,
pub full_id: Uuid,
pub local_id: u32,
pub position: Vec3,
pub rotation: Quat,
pub scale: Vec3,
pub parent: Option<u32>,
pub parent_id: Option<u32>,
pub name_value: Option<String>,
pub extra_params: Option<Vec<ExtraParams>>,
pub texture: TextureEntry,
}
#[derive(Debug, Message)]
#[rtype(result = "()")]
pub struct HandlePrim {
pub object: HandleObjectUpdate,
}
#[derive(Debug, Message)]
#[rtype(result = "()")]
pub struct HandleAttachment {
pub object: HandleObjectUpdate,
pub item: AttachItem,
}
#[derive(Debug, Message)]
#[rtype(result = "()")]
pub struct DownloadObject {
pub object: HandleObjectUpdate,
pub asset_id: Uuid,
pub texture_id: Uuid,
pub position: Vec3,
}
#[derive(Debug, Message)]
#[rtype(result = "()")]
pub struct HandleImprovedTerseObjectUpdate {
pub improved_terse_object_update: ImprovedTerseObjectUpdate,
}
#[derive(Debug, Message)]
#[rtype(result = "()")]
pub struct HandleObjectUpdateCached {
pub object_update_cached: ObjectUpdateCached,
}
impl Handler<HandleImprovedTerseObjectUpdate> for Mailbox {
type Result = ();
fn handle(
&mut self,
_msg: HandleImprovedTerseObjectUpdate,
_ctx: &mut Self::Context,
) -> Self::Result {
warn!("ImprovedTerseObjectUpdate packet received. Currently unimplemented.")
}
}
impl Handler<HandleObjectUpdateCached> for Mailbox {
type Result = ();
fn handle(&mut self, msg: HandleObjectUpdateCached, ctx: &mut Self::Context) -> Self::Result {
if let Some(session) = &self.session {
let mut requests = Vec::new();
for object in &msg.object_update_cached.objects {
requests.push((CacheMissType::Normal, object.id));
}
let request = RequestMultipleObjects {
session_id: session.session_id,
agent_id: session.agent_id,
requests,
};
ctx.address().do_send(OutgoingPacket {
packet: Packet::new_request_multiple_objects(request),
});
}
}
}
impl Handler<HandleObjectUpdate> for Mailbox {
type Result = ResponseFuture<()>;
fn handle(&mut self, msg: HandleObjectUpdate, ctx: &mut Self::Context) -> Self::Result {
let db_pool = self.inventory_db_connection.clone();
let addr = ctx.address();
let msg_cloned = msg.clone();
if self.session.is_none() {
return Box::pin(async {});
};
Box::pin(async move {
insert_object_update_minimal(
&db_pool,
msg.local_id,
msg.full_id,
msg.object_type.clone(),
msg.parent_id,
msg.position,
msg.rotation,
msg.scale,
)
.await
.unwrap_or_else(|e| {
error!("Object Update Error: {:?}, {:?}", e, msg.full_id);
return;
});
match msg.object_type {
ObjectType::Prim => {
if let Some(name_value) = msg.name_value.clone() {
match AttachItem::parse_attach_item(name_value) {
Ok(item) => {
addr.do_send(HandleAttachment { object: msg, item });
}
Err(_) => {
addr.do_send(HandlePrim { object: msg });
}
}
} else {
addr.do_send(HandlePrim { object: msg });
}
}
ObjectType::Tree
| ObjectType::Grass
| ObjectType::Unknown
| ObjectType::ParticleSystem
| ObjectType::NewTree => {
warn!("Received unhandled ObjectUpdate type");
}
ObjectType::Avatar => {
if let Err(e) = create_sub_agent_dir(&msg.full_id.to_string()) {
warn!("Failed to create agent dir for {:?}: {:?}", msg.full_id, e);
}
addr.do_send(HandleNewAvatar {
avatar: Avatar::new(msg_cloned.full_id, msg_cloned.position),
});
}
_ => {
warn!("Unknown object type");
}
}
})
}
}
impl Handler<HandlePrim> for Mailbox {
type Result = ();
fn handle(&mut self, msg: HandlePrim, ctx: &mut Self::Context) -> Self::Result {
if let Some(extra_params) = &msg.object.extra_params {
for param in extra_params {
match param {
ExtraParams::Sculpt(sculpt) => {
ctx.address().do_send(DownloadObject {
asset_id: sculpt.texture_id,
texture_id: Uuid::nil(),
object: msg.object.clone(),
position: msg.object.position,
});
}
_ => {
warn!("Recieved a non sculpt objectupdate. Currently unimplemented")
}
}
}
};
}
}
impl Handler<HandleAttachment> for Mailbox {
type Result = ResponseFuture<()>;
fn handle(&mut self, msg: HandleAttachment, _ctx: &mut Self::Context) -> Self::Result {
let db_pool = self.inventory_db_connection.clone();
Box::pin(async move {
let mut current_id = match msg.object.parent_id {
Some(id) => id,
None => return,
};
let mut visited = std::collections::HashSet::new();
loop {
if !visited.insert(current_id) {
break;
}
let obj = match get_object_update(&db_pool, current_id).await {
Ok(obj) => obj,
Err(_) => return,
};
if obj.parent_id == 0 {
break;
}
current_id = obj.parent_id;
}
})
}
}
impl Handler<DownloadObject> for Mailbox {
type Result = ();
fn handle(&mut self, mut msg: DownloadObject, ctx: &mut Self::Context) -> Self::Result {
if let Some(session) = self.session.as_mut() {
let server_endpoint = session
.capability_urls
.get(&Capability::ViewerAsset)
.unwrap()
.to_string();
let addr = ctx.address();
let inventory_db = self.inventory_db_connection.clone();
ctx.spawn(
async move {
let base_dir = match create_sub_object_dir(&msg.asset_id.to_string()) {
Ok(base_dir) => base_dir,
Err(e) => {
error!("failed to create base dir: {:?}", e);
return;
}
};
let texture_id = msg.object.texture.texture_id;
let texture_path = base_dir.join(format!("{:?}.png", texture_id));
let texture_path = match download_texture(
ObjectType::Texture.to_string(),
texture_id,
&server_endpoint,
&texture_path,
)
.await
{
Ok(_) => texture_path,
Err(e) => {
error!("Failed to download prim texture: {:?} {:?}", e, texture_id);
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("assets")
.join("benthic_default_texture.png")
}
};
match download_renderable_mesh(
msg.asset_id,
"name".to_string(),
&server_endpoint,
&texture_path,
)
.await
{
Ok(render_object) => {
let json_path = match write_json(
&render_object,
msg.asset_id,
msg.asset_id.to_string(),
) {
Ok(json) => json,
Err(e) => {
error!("Failed to write json: {:?}", e);
return;
}
};
let glb_path = base_dir.join(format!("{:?}_high.glb", msg.asset_id));
match generate_object_mesh(json_path, glb_path.clone()) {
Ok(_) => {
info!("Rendering object at: {:?}", msg.asset_id)
}
Err(e) => warn!("{:?}", e),
};
if let Some(parent_id) = msg.object.parent_id {
match get_object_scale_rotation_position(&inventory_db, parent_id)
.await
{
Ok((_parent_scale, parent_rotation, parent_position)) => {
let rotated_offset =
parent_rotation.mul_vec3(msg.object.position);
msg.object.position = parent_position + rotated_offset;
msg.object.rotation = parent_rotation * msg.object.rotation;
}
Err(e) => {
error!("{:?}", e);
return;
}
};
}
addr.do_send(SendUIMessage {
ui_message: UIMessage::new_mesh_update(MeshUpdate {
position: msg.object.position,
scale: msg.object.scale,
rotation: msg.object.rotation,
parent: msg.object.parent,
scene_id: Some(msg.object.local_id),
path: glb_path,
mesh_type: MeshType::Avatar,
id: None,
}),
});
}
Err(e) => {
error!("{:?}, {:?}", e, msg);
}
}
}
.into_actor(self),
);
}
}
}
pub fn write_json<T: Serialize>(data: &T, asset_id: Uuid, filename: String) -> io::Result<PathBuf> {
match create_sub_object_dir(&asset_id.to_string()) {
Ok(mut object_dir) => match serde_json::to_string(&data) {
Ok(json) => {
object_dir.push(format!("{}.json", filename));
let mut file = File::create(&object_dir).unwrap();
file.write_all(json.as_bytes()).unwrap();
Ok(object_dir)
}
Err(e) => {
error!("Failed to serialize scene group {:?}, {:?}", filename, e);
Err(io::Error::other(e))
}
},
Err(e) => {
error!(
"Failed to create agent dir for {:?}. Unable to cache downloaded items.",
e
);
Err(io::Error::other(e))
}
}
}