diff --git a/src/snapshot_middleware/lua.rs b/src/snapshot_middleware/lua.rs index d11e72c6..02bbc51d 100644 --- a/src/snapshot_middleware/lua.rs +++ b/src/snapshot_middleware/lua.rs @@ -72,8 +72,8 @@ pub fn snapshot_lua_init( anyhow::bail!( "init.lua, init.server.lua, and init.client.lua can \ only be used if the instance produced by the containing \ - directory would be a Folder.\n\n\ - + directory would be a Folder.\n\ + \n\ The directory {} turned into an instance of class {}.", folder_path.display(), dir_snapshot.class_name diff --git a/src/web/api.rs b/src/web/api.rs index 97624c7e..93b1ccb0 100644 --- a/src/web/api.rs +++ b/src/web/api.rs @@ -11,9 +11,9 @@ use crate::{ snapshot::{InstanceWithMeta, PatchSet, PatchUpdate}, web::{ interface::{ - ErrorResponse, Instance, InstanceMetadata as WebInstanceMetadata, InstanceUpdate, - OpenResponse, ReadResponse, ServerInfoResponse, SubscribeMessage, SubscribeResponse, - WriteRequest, WriteResponse, PROTOCOL_VERSION, SERVER_VERSION, + ErrorResponse, Instance, OpenResponse, ReadResponse, ServerInfoResponse, + SubscribeMessage, SubscribeResponse, WriteRequest, WriteResponse, PROTOCOL_VERSION, + SERVER_VERSION, }, util::{json, json_ok}, }, @@ -99,44 +99,7 @@ impl ApiService { let api_messages = messages .into_iter() - .map(|message| { - let removed = message.removed; - - let mut added = HashMap::new(); - for id in message.added { - let instance = tree.get_instance(id).unwrap(); - added.insert(id, Instance::from_rojo_instance(instance)); - - for instance in tree.descendants(id) { - added.insert(instance.id(), Instance::from_rojo_instance(instance)); - } - } - - let updated = message - .updated - .into_iter() - .map(|update| { - let changed_metadata = update - .changed_metadata - .as_ref() - .map(WebInstanceMetadata::from_rojo_metadata); - - InstanceUpdate { - id: update.id, - changed_name: update.changed_name, - changed_class_name: update.changed_class_name, - changed_properties: update.changed_properties, - changed_metadata, - } - }) - .collect(); - - SubscribeMessage { - removed, - added, - updated, - } - }) + .map(|patch| SubscribeMessage::from_patch_update(&tree, patch)) .collect(); json_ok(SubscribeResponse { diff --git a/src/web/interface.rs b/src/web/interface.rs index f5bda237..28005057 100644 --- a/src/web/interface.rs +++ b/src/web/interface.rs @@ -7,12 +7,14 @@ use std::{ collections::{HashMap, HashSet}, }; -use rbx_dom_weak::types::{Ref, Variant}; +use rbx_dom_weak::types::{Ref, Variant, VariantType}; use serde::{Deserialize, Serialize}; use crate::{ session_id::SessionId, - snapshot::{InstanceMetadata as RojoInstanceMetadata, InstanceWithMeta}, + snapshot::{ + AppliedPatchSet, InstanceMetadata as RojoInstanceMetadata, InstanceWithMeta, RojoTree, + }, }; /// Server version to report over the API, not exposed outside this crate. @@ -30,6 +32,53 @@ pub struct SubscribeMessage<'a> { pub updated: Vec, } +impl<'a> SubscribeMessage<'a> { + pub(crate) fn from_patch_update(tree: &'a RojoTree, patch: AppliedPatchSet) -> Self { + let removed = patch.removed; + + let mut added = HashMap::new(); + for id in patch.added { + let instance = tree.get_instance(id).unwrap(); + added.insert(id, Instance::from_rojo_instance(instance)); + + for instance in tree.descendants(id) { + added.insert(instance.id(), Instance::from_rojo_instance(instance)); + } + } + + let updated = patch + .updated + .into_iter() + .map(|update| { + let changed_metadata = update + .changed_metadata + .as_ref() + .map(InstanceMetadata::from_rojo_metadata); + + let changed_properties = update + .changed_properties + .into_iter() + .filter(|(_key, value)| property_filter(value.as_ref())) + .collect(); + + InstanceUpdate { + id: update.id, + changed_name: update.changed_name, + changed_class_name: update.changed_class_name, + changed_properties, + changed_metadata, + } + }) + .collect(); + + Self { + removed, + added, + updated, + } + } +} + #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct InstanceUpdate { @@ -75,14 +124,8 @@ impl<'a> Instance<'a> { let properties = source .properties() .iter() - .filter_map(|(key, value)| { - // SharedString values can't be serialized via Serde - if matches!(value, Variant::SharedString(_)) { - return None; - } - - Some((key.clone(), Cow::Borrowed(value))) - }) + .filter(|(_key, value)| property_filter(Some(value))) + .map(|(key, value)| (key.clone(), Cow::Borrowed(value))) .collect(); Instance { @@ -97,6 +140,18 @@ impl<'a> Instance<'a> { } } +fn property_filter(value: Option<&Variant>) -> bool { + let ty = value.map(|value| value.ty()); + + // Lua can't do anything with SharedString values. They also can't be + // serialized directly by Serde! + if ty == Some(VariantType::SharedString) { + return false; + } + + return true; +} + /// Response body from /api/rojo #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")]