Remove float-cmp dependency and implement custom float comparison logic

- Eliminated the `float-cmp` crate from the project, replacing its functionality with custom float comparison functions that match Lua's epsilon-based equality checks.
- Updated the `variant_eq` function to utilize the new fuzzy equality methods for `Float32` and `Float64` variants, ensuring consistent behavior across Rust and Lua implementations.
- Enhanced the matching algorithms in both forward sync and syncback processes to account for refined scoring of `Ref`, `Tags`, and `Attributes`, improving scoring accuracy and parity between implementations.
- Introduced a new plan document detailing the changes made to the matching algorithms, including the rationale for the updates and the expected impact on performance and correctness.

These changes aim to improve the reliability and maintainability of the matching algorithms while ensuring parity with Lua's behavior.
This commit is contained in:
Joe 2026-02-18 23:57:12 -06:00
parent a283fd1dc9
commit 7e0538b9f5
10 changed files with 1276 additions and 87 deletions

View file

@ -46,6 +46,7 @@ type MatchResult = {
type MatchingSession = {
matchCache: { [string]: { [Instance]: MatchResult } },
costCache: { [string]: { [Instance]: number } },
vCacheStore: { [string]: VCache },
}
type RefIdentity = {
@ -103,6 +104,7 @@ local function newSession(): MatchingSession
return {
matchCache = {},
costCache = {},
vCacheStore = {},
}
end
@ -359,7 +361,11 @@ local function computePairCost(
end
local classKeys = RbxDom.getClassComparisonKeys(vInst.ClassName)
local vCache = cacheVirtual(vInst, classKeys, virtualInstances)
local vCache = session.vCacheStore[virtualId]
if not vCache then
vCache = cacheVirtual(vInst, classKeys, virtualInstances)
session.vCacheStore[virtualId] = vCache
end
local refPropNames: { string } = {}
for propName, _ in pairs(vCache.refs) do
table.insert(refPropNames, propName)
@ -485,11 +491,16 @@ matchChildren = function(
local allExtraProps: { [string]: boolean } = {}
local allRefProps: { [string]: boolean } = {}
for _, vi in ipairs(vIndices) do
local vInst = virtualInstances[virtualChildren[vi]]
local vid = virtualChildren[vi]
local vInst = virtualInstances[vid]
if not vInst then
continue
end
local vCache = cacheVirtual(vInst, classKeys, virtualInstances)
local vCache = session.vCacheStore[vid]
if not vCache then
vCache = cacheVirtual(vInst, classKeys, virtualInstances)
session.vCacheStore[vid] = vCache
end
vCaches[vi] = vCache
for _, propName in ipairs(vCache.extraProps) do
allExtraProps[propName] = true

View file

@ -544,4 +544,44 @@ return function()
expect(result.totalCost).to.equal(0)
end)
end)
describe("parity with Rust fixture", function()
it("4 Parts with different Transparency values in reversed order", function()
local session = Matching.newSession()
local transparencies = { 0, 0.3, 0.6, 0.9 }
local virtualInstances = {}
local virtualChildren = {}
for i, t in ipairs(transparencies) do
local id = "V" .. tostring(i)
virtualInstances[id] = makeVirtualInstance("Line", "Part", {
Transparency = { Float32 = t },
}, {})
table.insert(virtualChildren, id)
end
local studioChildren = {}
for i = #transparencies, 1, -1 do
local inst = Instance.new("Part")
inst.Name = "Line"
inst.Transparency = transparencies[i]
inst.Parent = container
table.insert(studioChildren, inst)
end
local result =
Matching.matchChildren(session, virtualChildren, studioChildren, virtualInstances, "ROOT", container)
expect(#result.matched).to.equal(4)
expect(#result.unmatchedVirtual).to.equal(0)
expect(#result.unmatchedStudio).to.equal(0)
for _, pair in ipairs(result.matched) do
local vInst = virtualInstances[pair.virtualId]
local vT = vInst.Properties.Transparency.Float32
local sT = pair.studioInstance.Transparency
expect(math.abs(vT - sT) < 0.001).to.equal(true)
end
end)
end)
end