mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-23 05:51:39 +00:00
Enhance live syncback process with improved reference handling and float precision
- Implemented ObjectValue carriers for service-level references, allowing for accurate serialization of non-direct child references during syncback. - Updated `encodeService` to include explicit handling of attributes and tags, ensuring they are properly encoded and preventing data loss. - Refactored `build_dom_from_chunks` to manage ObjectValue carriers and resolve references correctly, enhancing data integrity. - Truncated float values to f32 precision in `UnresolvedValue::from_variant`, aligning with rbxl storage requirements and improving consistency. These changes address critical issues in the live syncback process, enhancing reliability and data accuracy.
This commit is contained in:
parent
7dc4f52793
commit
39c8bd41f4
8 changed files with 342 additions and 121 deletions
|
|
@ -161,6 +161,9 @@ fn build_dom_from_chunks(payload: SyncbackPayload) -> anyhow::Result<WeakDom> {
|
|||
};
|
||||
|
||||
let mut cursor = 0usize;
|
||||
let mut all_carriers: Vec<Ref> = Vec::new();
|
||||
let mut deferred_refs: Vec<(Ref, Vec<(String, Ref)>)> = Vec::new();
|
||||
|
||||
for chunk in &payload.services {
|
||||
let mut builder = InstanceBuilder::new(&chunk.class_name);
|
||||
for (key, value) in &chunk.properties {
|
||||
|
|
@ -172,29 +175,33 @@ fn build_dom_from_chunks(payload: SyncbackPayload) -> anyhow::Result<WeakDom> {
|
|||
let service_ref = dom.insert(root_ref, builder);
|
||||
created_services.insert(chunk.class_name.clone());
|
||||
|
||||
let count = chunk.child_count as usize;
|
||||
let end = (cursor + count).min(cloned_children.len());
|
||||
for &child_ref in &cloned_children[cursor..end] {
|
||||
let child_count = chunk.child_count as usize;
|
||||
let ref_count = chunk.ref_target_count as usize;
|
||||
let total = child_count + ref_count;
|
||||
let end = (cursor + total).min(cloned_children.len());
|
||||
let service_range: Vec<Ref> = cloned_children[cursor..end].to_vec();
|
||||
|
||||
for &child_ref in &service_range[..child_count.min(service_range.len())] {
|
||||
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
|
||||
});
|
||||
if let Some(&child_ref) = found {
|
||||
let service = dom.get_by_ref_mut(service_ref).unwrap();
|
||||
service
|
||||
.properties
|
||||
.insert(prop_name.as_str().into(), Variant::Ref(child_ref));
|
||||
}
|
||||
let carrier_start = child_count;
|
||||
let carrier_end = service_range.len();
|
||||
let carriers = &service_range[carrier_start..carrier_end];
|
||||
|
||||
let mut ref_entries: Vec<(String, Ref)> = Vec::new();
|
||||
for (prop_name, &idx) in &chunk.refs {
|
||||
if idx == 0 || (idx as usize) > carriers.len() {
|
||||
continue;
|
||||
}
|
||||
ref_entries.push((prop_name.clone(), carriers[idx as usize - 1]));
|
||||
}
|
||||
if !ref_entries.is_empty() {
|
||||
deferred_refs.push((service_ref, ref_entries));
|
||||
}
|
||||
|
||||
all_carriers.extend_from_slice(carriers);
|
||||
cursor = end;
|
||||
}
|
||||
|
||||
for &service_name in VISIBLE_SERVICES {
|
||||
|
|
@ -205,6 +212,25 @@ fn build_dom_from_chunks(payload: SyncbackPayload) -> anyhow::Result<WeakDom> {
|
|||
|
||||
fixup_ref_properties(&mut dom, &global_ref_map);
|
||||
|
||||
for (service_ref, ref_entries) in deferred_refs {
|
||||
for (prop_name, carrier_ref) in ref_entries {
|
||||
if let Some(Variant::Ref(actual_target)) =
|
||||
dom.get_by_ref(carrier_ref).and_then(|inst| {
|
||||
inst.properties.get(&rbx_dom_weak::ustr("Value")).cloned()
|
||||
})
|
||||
{
|
||||
let service = dom.get_by_ref_mut(service_ref).unwrap();
|
||||
service
|
||||
.properties
|
||||
.insert(prop_name.as_str().into(), Variant::Ref(actual_target));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for carrier_ref in all_carriers {
|
||||
dom.destroy(carrier_ref);
|
||||
}
|
||||
|
||||
Ok(dom)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,12 +10,11 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use crate::REF_PATH_ATTRIBUTE_PREFIX;
|
||||
|
||||
/// Truncates float components of Variant types to f32 precision, matching the
|
||||
/// rbxl binary format which stores most float properties as Float32.
|
||||
/// Rounds float components of Variant types to 6 decimal places.
|
||||
/// Uses format-and-reparse for exact decimal representation in json5 output.
|
||||
fn truncate_variant(v: Variant) -> Variant {
|
||||
#[inline]
|
||||
fn t(v: f32) -> f32 {
|
||||
v as f64 as f32
|
||||
format!("{:.6}", v).parse::<f32>().unwrap_or(v)
|
||||
}
|
||||
|
||||
match v {
|
||||
|
|
@ -66,8 +65,6 @@ fn truncate_variant(v: Variant) -> Variant {
|
|||
rbx_dom_weak::types::UDim::new(t(u.x.scale), u.x.offset),
|
||||
rbx_dom_weak::types::UDim::new(t(u.y.scale), u.y.offset),
|
||||
)),
|
||||
Variant::Float32(n) => Variant::Float32(n as f64 as f32),
|
||||
Variant::Float64(n) => Variant::Float64(n as f32 as f64),
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,12 +30,16 @@ pub const PROTOCOL_VERSION: u64 = 6;
|
|||
/// Metadata for a single service in the live syncback request.
|
||||
///
|
||||
/// 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.
|
||||
/// (a single rbxm containing all children from all services).
|
||||
///
|
||||
/// Attributes and Tags are included in `properties` as
|
||||
/// `Variant::Attributes(...)` / `Variant::Tags(...)`.
|
||||
/// Each service's range in the blob is `child_count + ref_target_count`
|
||||
/// consecutive entries. The first `child_count` are real children (parented
|
||||
/// to the service). The next `ref_target_count` are ObjectValue carriers
|
||||
/// whose `Value` property holds a Ref to the actual target.
|
||||
///
|
||||
/// `refs` maps property names to 1-based indices into this combined range
|
||||
/// (0 = nil). Indices <= child_count point directly to a child instance.
|
||||
/// Indices > child_count point to an ObjectValue carrier.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ServiceChunk {
|
||||
|
|
@ -43,18 +47,11 @@ pub struct ServiceChunk {
|
|||
#[serde(default)]
|
||||
pub child_count: u32,
|
||||
#[serde(default)]
|
||||
pub ref_target_count: u32,
|
||||
#[serde(default)]
|
||||
pub properties: HashMap<String, Variant>,
|
||||
#[serde(default)]
|
||||
pub refs: HashMap<String, ServiceRef>,
|
||||
}
|
||||
|
||||
/// Describes a Ref property target by name and class so the server can resolve
|
||||
/// it among the service's cloned children.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ServiceRef {
|
||||
pub name: String,
|
||||
pub class_name: String,
|
||||
pub refs: HashMap<String, u32>,
|
||||
}
|
||||
|
||||
/// Incoming request from the plugin for live syncback.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue