Enhance syncback process by integrating serialization of service children

- Introduced `SerializationService` to serialize child instances during syncback, improving data handling.
- Updated `performSyncback` to include serialized children data in the syncback request.
- Refactored `ServiceChunk` structure to store child count instead of serialized data, optimizing memory usage.
- Adjusted related functions and tests to accommodate the new data structure, ensuring consistency and accuracy in syncback operations.

These changes significantly enhance the efficiency and clarity of the syncback process, allowing for better management of service data.
This commit is contained in:
Joe 2026-02-18 08:21:25 -06:00
parent a823db0297
commit a75a1443c0
7 changed files with 259 additions and 161 deletions

View file

@ -142,43 +142,47 @@ fn build_dom_from_chunks(payload: SyncbackPayload) -> anyhow::Result<WeakDom> {
let mut dom = WeakDom::new(InstanceBuilder::new("DataModel"));
let root_ref = dom.root_ref();
let mut global_ref_map: HashMap<Ref, Ref> = HashMap::new();
let mut created_services: std::collections::HashSet<String> = std::collections::HashSet::new();
let cloned_children: Vec<Ref> = if !payload.data.is_empty() {
let chunk_dom = rbx_binary::from_reader(Cursor::new(&payload.data))
.context("Failed to parse rbxm data blob")?;
let mut cloned = Vec::new();
for &child_ref in chunk_dom.root().children() {
deep_clone_into(&chunk_dom, &mut dom, child_ref, root_ref, &mut global_ref_map);
let new_ref = *global_ref_map.get(&child_ref).unwrap();
cloned.push(new_ref);
}
cloned
} else {
Vec::new()
};
let mut cursor = 0usize;
for chunk in &payload.services {
let mut builder = InstanceBuilder::new(&chunk.class_name);
for (key, value) in &chunk.properties {
builder = builder.with_property(key.as_str(), value.clone());
}
let service_ref = dom.insert(root_ref, builder);
created_services.insert(chunk.class_name.clone());
if !chunk.data.is_empty() {
let chunk_dom =
rbx_binary::from_reader(Cursor::new(&chunk.data)).with_context(|| {
format!("Failed to parse rbxm for service {}", chunk.class_name)
})?;
for &child_ref in chunk_dom.root().children() {
deep_clone_into(
&chunk_dom,
&mut dom,
child_ref,
service_ref,
&mut global_ref_map,
);
}
let count = chunk.child_count as usize;
let end = (cursor + count).min(cloned_children.len());
for &child_ref in &cloned_children[cursor..end] {
dom.transfer_within(child_ref, service_ref);
}
cursor = end;
if !chunk.refs.is_empty() {
let children: Vec<Ref> = dom.get_by_ref(service_ref).unwrap().children().to_vec();
for (prop_name, target) in &chunk.refs {
let found = children.iter().find(|&&child_ref| {
let child = dom.get_by_ref(child_ref).unwrap();
child.name.as_str() == target.name && child.class.as_str() == target.class_name
child.name.as_str() == target.name
&& child.class.as_str() == target.class_name
});
if let Some(&child_ref) = found {
let service = dom.get_by_ref_mut(service_ref).unwrap();

View file

@ -247,11 +247,13 @@ async fn handle_api_syncback(
}
log::info!(
"Live syncback requested with {} service chunks",
syncback_request.services.len()
"Live syncback requested with {} service chunks, {} bytes of rbxm data",
syncback_request.services.len(),
syncback_request.data.len()
);
let payload = SyncbackPayload {
data: syncback_request.data,
services: syncback_request.services,
};
syncback_signal.fire(payload);

View file

@ -27,18 +27,21 @@ pub(crate) const SERVER_VERSION: &str = env!("CARGO_PKG_VERSION");
/// Current protocol version, which is required to match.
pub const PROTOCOL_VERSION: u64 = 6;
/// A single service's children serialized as rbxm binary data, plus the
/// service's own properties and Ref property hints.
/// Metadata for a single service in the live syncback request.
///
/// Attributes and Tags are included in the `properties` map as
/// `Variant::Attributes(...)` and `Variant::Tags(...)` respectively,
/// matching the encoding used by the existing `/api/write` path.
/// Children are NOT stored here — they live in the top-level `data` blob
/// (a single rbxm containing all children from all services). `child_count`
/// tells the server how many consecutive children in that blob belong to
/// this service.
///
/// Attributes and Tags are included in `properties` as
/// `Variant::Attributes(...)` / `Variant::Tags(...)`.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ServiceChunk {
pub class_name: String,
#[serde(with = "serde_bytes")]
pub data: Vec<u8>,
#[serde(default)]
pub child_count: u32,
#[serde(default)]
pub properties: HashMap<String, Variant>,
#[serde(default)]
@ -64,12 +67,15 @@ pub struct SyncbackRequest {
pub protocol_version: f64,
pub server_version: String,
pub place_id: Option<f64>,
#[serde(with = "serde_bytes", default)]
pub data: Vec<u8>,
pub services: Vec<ServiceChunk>,
}
/// Payload passed from the API handler to the serve loop after validation.
#[derive(Debug)]
pub struct SyncbackPayload {
pub data: Vec<u8>,
pub services: Vec<ServiceChunk>,
}