mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-24 06:34:09 +00:00
Refactor encoding logic in ChangeBatcher and enhance duplicate handling
- Updated `encodeInstance` and `encodeService` to utilize helper functions for encoding attributes and tags, improving code organization and readability. - Changed duplicate handling to allow encoding of instances with duplicate-named siblings, with the server managing name collisions through a dedup suffix system. - Added debug logging for duplicate detection to aid in diagnostics. - Streamlined property encoding by implementing a helper function to iterate over encodable properties, enhancing maintainability and consistency. These changes improve the clarity and efficiency of the encoding process while ensuring accurate handling of duplicate instances.
This commit is contained in:
parent
24dec4073d
commit
9d8762bbaf
5 changed files with 274 additions and 188 deletions
|
|
@ -10,10 +10,10 @@
|
|||
Children are recursively encoded and included.
|
||||
|
||||
DUPLICATE HANDLING:
|
||||
- Instances with duplicate-named siblings at ANY level of the path are skipped
|
||||
- This matches the behavior in ref_properties.rs::is_path_unique
|
||||
- A path is ambiguous if duplicates exist anywhere from the instance to the root
|
||||
- A warning is logged when duplicates are encountered
|
||||
- Instances with duplicate-named siblings are still encoded and sent to the
|
||||
server. The server handles filesystem name collisions via the dedup suffix
|
||||
system (~2, ~3, etc.) in syncback/file_names.rs.
|
||||
- A debug message is logged when duplicates are detected (for diagnostics only).
|
||||
]]
|
||||
|
||||
local Packages = script.Parent.Parent.Parent.Packages
|
||||
|
|
@ -21,7 +21,7 @@ local Log = require(Packages.Log)
|
|||
local RbxDom = require(Packages.RbxDom)
|
||||
|
||||
local encodeProperty = require(script.Parent.encodeProperty)
|
||||
local UNENCODABLE_DATA_TYPES = require(script.Parent.propertyFilter)
|
||||
local Helpers = require(script.Parent.encodeHelpers)
|
||||
|
||||
-- Script class names that need Source property
|
||||
local SCRIPT_CLASS_NAMES = {
|
||||
|
|
@ -30,13 +30,6 @@ local SCRIPT_CLASS_NAMES = {
|
|||
ModuleScript = true,
|
||||
}
|
||||
|
||||
-- Properties to always skip when encoding (internal Roblox properties that never serialize)
|
||||
local SKIP_PROPERTIES = {
|
||||
Parent = true,
|
||||
Name = true, -- We handle this separately
|
||||
Archivable = true,
|
||||
}
|
||||
|
||||
-- Properties to skip per class (matches filter_out_property in Rust syncback)
|
||||
-- These are properties whose values are encoded in the file itself, not metadata
|
||||
local SKIP_PROPERTIES_BY_CLASS = {
|
||||
|
|
@ -117,45 +110,6 @@ end
|
|||
-- Forward declaration for recursion
|
||||
local encodeInstance
|
||||
|
||||
-- Encode Attributes if present on any instance
|
||||
local function encodeAttributes(instance, properties)
|
||||
-- Try to get Attributes - this works for all instance types
|
||||
local success, attributes = pcall(function()
|
||||
return instance:GetAttributes()
|
||||
end)
|
||||
|
||||
if success and attributes and next(attributes) then
|
||||
-- Attributes need to be encoded specially
|
||||
-- The server expects them in the properties map under "Attributes"
|
||||
local attributeDescriptor = RbxDom.findCanonicalPropertyDescriptor(instance.ClassName, "Attributes")
|
||||
if attributeDescriptor then
|
||||
local encodeSuccess, encodeResult = encodeProperty(instance, "Attributes", attributeDescriptor)
|
||||
if encodeSuccess and encodeResult ~= nil then
|
||||
properties.Attributes = encodeResult
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Encode Tags if present on any instance
|
||||
local function encodeTags(instance, properties)
|
||||
-- Try to get Tags - this works for all instance types
|
||||
local success, tags = pcall(function()
|
||||
return instance:GetTags()
|
||||
end)
|
||||
|
||||
if success and tags and #tags > 0 then
|
||||
-- Tags need to be encoded specially
|
||||
local tagDescriptor = RbxDom.findCanonicalPropertyDescriptor(instance.ClassName, "Tags")
|
||||
if tagDescriptor then
|
||||
local encodeSuccess, encodeResult = encodeProperty(instance, "Tags", tagDescriptor)
|
||||
if encodeSuccess and encodeResult ~= nil then
|
||||
properties.Tags = encodeResult
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
encodeInstance = function(instance, parentId, _skipPathCheck)
|
||||
-- Log duplicate-named siblings as a debug message (server handles them via
|
||||
-- dedup suffixes) but do NOT skip encoding.
|
||||
|
|
@ -174,9 +128,8 @@ encodeInstance = function(instance, parentId, _skipPathCheck)
|
|||
|
||||
local properties = {}
|
||||
|
||||
-- Always try to encode Attributes and Tags for any instance type
|
||||
encodeAttributes(instance, properties)
|
||||
encodeTags(instance, properties)
|
||||
Helpers.encodeAttributes(instance, properties)
|
||||
Helpers.encodeTags(instance, properties)
|
||||
|
||||
-- For scripts, encode the Source property first (required for script files)
|
||||
if SCRIPT_CLASS_NAMES[instance.ClassName] then
|
||||
|
|
@ -195,61 +148,22 @@ encodeInstance = function(instance, parentId, _skipPathCheck)
|
|||
end
|
||||
end
|
||||
|
||||
-- Encode ALL properties for ANY instance type (matching Rust syncback behavior)
|
||||
-- The server handles filtering defaults and serialization
|
||||
local classDescriptor = RbxDom.findClassDescriptor(instance.ClassName)
|
||||
if classDescriptor then
|
||||
-- Get class-specific properties to skip
|
||||
local classSkipProps = SKIP_PROPERTIES_BY_CLASS[instance.ClassName] or {}
|
||||
|
||||
for propertyName, propertyMeta in pairs(classDescriptor.properties) do
|
||||
-- Skip universally skipped properties
|
||||
if SKIP_PROPERTIES[propertyName] then
|
||||
continue
|
||||
end
|
||||
|
||||
-- Skip class-specific properties (Source, RunContext for scripts, etc.)
|
||||
if classSkipProps[propertyName] then
|
||||
continue
|
||||
end
|
||||
|
||||
-- Skip Attributes and Tags since we handle them separately above
|
||||
if propertyName == "Attributes" or propertyName == "Tags" then
|
||||
continue
|
||||
end
|
||||
|
||||
-- Only encode properties that:
|
||||
-- 1. Are readable (ReadWrite or Read scriptability)
|
||||
-- 2. Actually serialize (not "DoesNotSerialize")
|
||||
local isReadable = propertyMeta.scriptability == "ReadWrite"
|
||||
or propertyMeta.scriptability == "Read"
|
||||
or propertyMeta.scriptability == "Custom"
|
||||
local doesSerialize = propertyMeta.serialization ~= "DoesNotSerialize"
|
||||
|
||||
if isReadable and doesSerialize then
|
||||
-- Get the full PropertyDescriptor for encoding
|
||||
local descriptor = RbxDom.findCanonicalPropertyDescriptor(instance.ClassName, propertyName)
|
||||
if descriptor then
|
||||
if UNENCODABLE_DATA_TYPES[descriptor.dataType] then
|
||||
continue
|
||||
end
|
||||
|
||||
-- Ref properties cannot be encoded during instance addition
|
||||
-- because the target instance has no server ID yet. They are
|
||||
-- handled separately by encodePatchUpdate.lua during property
|
||||
-- update encoding via the InstanceMap.
|
||||
if descriptor.dataType == "Ref" then
|
||||
continue
|
||||
end
|
||||
|
||||
local encodeSuccess, encodeResult = encodeProperty(instance, propertyName, descriptor)
|
||||
if encodeSuccess and encodeResult ~= nil then
|
||||
properties[propertyName] = encodeResult
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Encode ALL properties for ANY instance type (matching Rust syncback behavior).
|
||||
-- The server handles filtering defaults and serialization.
|
||||
local classSkipProps = SKIP_PROPERTIES_BY_CLASS[instance.ClassName]
|
||||
Helpers.forEachEncodableProperty(instance.ClassName, classSkipProps, function(propertyName, descriptor)
|
||||
-- Ref properties cannot be encoded during instance addition because the
|
||||
-- target instance has no server ID yet. They are handled separately by
|
||||
-- encodePatchUpdate.lua via the InstanceMap.
|
||||
if descriptor.dataType == "Ref" then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local encodeSuccess, encodeResult = encodeProperty(instance, propertyName, descriptor)
|
||||
if encodeSuccess and encodeResult ~= nil then
|
||||
properties[propertyName] = encodeResult
|
||||
end
|
||||
end)
|
||||
|
||||
-- Recursively encode children (including those with duplicate names;
|
||||
-- the server handles duplicates via dedup suffix system)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue