mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-26 10:34:07 +00:00
Refactor syncback process and improve code readability
- Reformatted the `SYNCBACK_SERVICES` list for better readability by aligning entries vertically. - Simplified the `performSyncback` function by removing unnecessary Promise wrapping, enhancing clarity and maintainability. - Updated the `NotConnectedPage` component to improve the structure of button elements and their associated tooltips. - Refactored the `hydrate` and `matching` modules to streamline function calls and improve code organization. These changes enhance the overall readability and maintainability of the syncback process and related components.
This commit is contained in:
parent
372a422b3d
commit
93ea0bdef6
16 changed files with 546 additions and 375 deletions
|
|
@ -5,10 +5,20 @@ local ServerStorage = game:GetService("ServerStorage")
|
|||
local RunService = game:GetService("RunService")
|
||||
|
||||
local SYNCBACK_SERVICES = {
|
||||
"Lighting", "MaterialService", "ReplicatedFirst", "ReplicatedStorage",
|
||||
"ServerScriptService", "ServerStorage", "SoundService", "StarterGui",
|
||||
"StarterPack", "StarterPlayer", "Teams", "TextChatService",
|
||||
"VoiceChatService", "Workspace",
|
||||
"Lighting",
|
||||
"MaterialService",
|
||||
"ReplicatedFirst",
|
||||
"ReplicatedStorage",
|
||||
"ServerScriptService",
|
||||
"ServerStorage",
|
||||
"SoundService",
|
||||
"StarterGui",
|
||||
"StarterPack",
|
||||
"StarterPlayer",
|
||||
"Teams",
|
||||
"TextChatService",
|
||||
"VoiceChatService",
|
||||
"Workspace",
|
||||
}
|
||||
|
||||
local Rojo = script:FindFirstAncestor("Rojo")
|
||||
|
|
@ -628,7 +638,6 @@ function App:performSyncback()
|
|||
port = Config.defaultPort
|
||||
end
|
||||
|
||||
Promise.new(function(resolve, reject)
|
||||
local services = {}
|
||||
for _, className in SYNCBACK_SERVICES do
|
||||
local ok, service = pcall(game.FindService, game, className)
|
||||
|
|
@ -650,17 +659,6 @@ function App:performSyncback()
|
|||
})
|
||||
|
||||
Http.post(url, body)
|
||||
:andThen(function(response)
|
||||
if response.StatusCode >= 200 and response.StatusCode < 300 then
|
||||
resolve()
|
||||
else
|
||||
reject(
|
||||
`Syncback request failed ({response.StatusCode}): {response.Body or "unknown error"}`
|
||||
)
|
||||
end
|
||||
end)
|
||||
:catch(reject)
|
||||
end)
|
||||
:andThen(function()
|
||||
Log.info("Syncback data sent to server.")
|
||||
self:addNotification({
|
||||
|
|
|
|||
|
|
@ -40,10 +40,8 @@ local function hydrate(instanceMap, virtualInstances, rootId, rootInstance, sess
|
|||
end
|
||||
end
|
||||
|
||||
local result = Matching.matchChildren(
|
||||
session, validVirtualIds, existingChildren, virtualInstances,
|
||||
rootId, rootInstance
|
||||
)
|
||||
local result =
|
||||
Matching.matchChildren(session, validVirtualIds, existingChildren, virtualInstances, rootId, rootInstance)
|
||||
|
||||
-- Recursively hydrate matched pairs
|
||||
for _, pair in result.matched do
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ local RbxDom = require(Packages.RbxDom)
|
|||
|
||||
local trueEquals = require(script.Parent.trueEquals)
|
||||
|
||||
local UNMATCHED_PENALTY: number = 10000
|
||||
local UNMATCHED_PENALTY = 10000
|
||||
|
||||
-- ================================================================
|
||||
-- Types
|
||||
|
|
@ -49,21 +49,30 @@ type MatchingSession = {
|
|||
|
||||
type VCache = {
|
||||
props: { [string]: any },
|
||||
extraProps: { string }?,
|
||||
tags: { [string]: boolean }?,
|
||||
attrs: { [string]: any }?,
|
||||
extraProps: { string },
|
||||
tags: { [string]: boolean },
|
||||
attrs: { [string]: any },
|
||||
childCount: number,
|
||||
}
|
||||
|
||||
type SCache = {
|
||||
instance: Instance,
|
||||
props: { [string]: any },
|
||||
tags: { [string]: boolean }?,
|
||||
attrs: { [string]: any }?,
|
||||
tags: { [string]: boolean },
|
||||
attrs: { [string]: any },
|
||||
children: { Instance },
|
||||
childCount: number,
|
||||
}
|
||||
|
||||
type VirtualInstance = {
|
||||
Name: string,
|
||||
ClassName: string,
|
||||
Children: { string },
|
||||
Properties: { [string]: any }?,
|
||||
}
|
||||
|
||||
type VirtualInstances = { [string]: VirtualInstance }
|
||||
|
||||
type ScoredPair = {
|
||||
vi: number,
|
||||
si: number,
|
||||
|
|
@ -82,17 +91,29 @@ local function newSession(): MatchingSession
|
|||
}
|
||||
end
|
||||
|
||||
-- ================================================================
|
||||
-- Utilities
|
||||
-- ================================================================
|
||||
|
||||
local function rawIndex(inst: any, key: string): any
|
||||
return inst[key]
|
||||
end
|
||||
|
||||
local function safeGet(inst: Instance, key: string): (boolean, any)
|
||||
return pcall(rawIndex, inst :: any, key)
|
||||
end
|
||||
|
||||
-- ================================================================
|
||||
-- Pre-computation helpers (called once per instance per group)
|
||||
-- ================================================================
|
||||
|
||||
local function cacheVirtual(vInst: any, classKeys: any): VCache
|
||||
local function cacheVirtual(vInst: VirtualInstance, classKeys: any): VCache
|
||||
local decoded: { [string]: any } = {}
|
||||
local extraProps: { string }? = nil
|
||||
local extraProps: { string } = {}
|
||||
local vProps = vInst.Properties
|
||||
|
||||
if vProps then
|
||||
for propName, encodedValue in vProps do
|
||||
for propName, encodedValue in pairs(vProps) do
|
||||
if propName == "Tags" or propName == "Attributes" then
|
||||
continue
|
||||
end
|
||||
|
|
@ -106,27 +127,23 @@ local function cacheVirtual(vInst: any, classKeys: any): VCache
|
|||
if pcallOk and decodeOk and value ~= nil then
|
||||
decoded[propName] = value
|
||||
if not classKeys.propNameSet[propName] then
|
||||
if not extraProps then
|
||||
extraProps = {}
|
||||
end
|
||||
table.insert(extraProps, propName)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local decodedTags: { [string]: boolean }? = nil
|
||||
local decodedTags: { [string]: boolean } = {}
|
||||
if vProps and vProps.Tags then
|
||||
local ok, tags = RbxDom.EncodedValue.decode(vProps.Tags)
|
||||
if ok and type(tags) == "table" then
|
||||
decodedTags = {}
|
||||
for _, tag in tags do
|
||||
for _, tag in ipairs(tags) do
|
||||
decodedTags[tag] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local decodedAttrs: { [string]: any }? = nil
|
||||
local decodedAttrs: { [string]: any } = {}
|
||||
if vProps and vProps.Attributes then
|
||||
local ok, attrs = RbxDom.EncodedValue.decode(vProps.Attributes)
|
||||
if ok and type(attrs) == "table" then
|
||||
|
|
@ -143,68 +160,40 @@ local function cacheVirtual(vInst: any, classKeys: any): VCache
|
|||
}
|
||||
end
|
||||
|
||||
local function cacheStudio(studioInstance: Instance, classKeys: any, extraPropNames: { string }?): SCache
|
||||
local function cacheStudio(studioInstance: Instance, classKeys: any, extraPropNames: { string }): SCache
|
||||
local props: { [string]: any } = {}
|
||||
|
||||
for _, propName in classKeys.propNames do
|
||||
local ok, value = pcall(function()
|
||||
return (studioInstance :: any)[propName]
|
||||
end)
|
||||
if ok and value ~= nil then
|
||||
for _, propName in ipairs(classKeys.propNames) do
|
||||
local ok, value = safeGet(studioInstance, propName)
|
||||
if ok then
|
||||
props[propName] = value
|
||||
end
|
||||
end
|
||||
|
||||
if extraPropNames then
|
||||
for _, propName in extraPropNames do
|
||||
for _, propName in ipairs(extraPropNames) do
|
||||
if props[propName] ~= nil then
|
||||
continue
|
||||
end
|
||||
local ok, value = pcall(function()
|
||||
return (studioInstance :: any)[propName]
|
||||
end)
|
||||
if ok and value ~= nil then
|
||||
local ok, value = safeGet(studioInstance, propName)
|
||||
if ok then
|
||||
props[propName] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local tags: { [string]: boolean }? = nil
|
||||
local tagsOk, studioTags = pcall(function()
|
||||
return studioInstance:GetTags()
|
||||
end)
|
||||
if tagsOk then
|
||||
tags = {}
|
||||
for _, tag in studioTags do
|
||||
local tags: { [string]: boolean } = {}
|
||||
for _, tag in ipairs(studioInstance:GetTags()) do
|
||||
tags[tag] = true
|
||||
end
|
||||
end
|
||||
|
||||
local attrs: { [string]: any }? = nil
|
||||
local attrsOk, studioAttrs = pcall(function()
|
||||
return studioInstance:GetAttributes()
|
||||
end)
|
||||
if attrsOk then
|
||||
attrs = studioAttrs
|
||||
end
|
||||
|
||||
local children: { Instance } = {}
|
||||
local childCount: number = 0
|
||||
local childOk, studioKids = pcall(function()
|
||||
return studioInstance:GetChildren()
|
||||
end)
|
||||
if childOk then
|
||||
children = studioKids
|
||||
childCount = #studioKids
|
||||
end
|
||||
local children = studioInstance:GetChildren()
|
||||
|
||||
return {
|
||||
instance = studioInstance,
|
||||
props = props,
|
||||
tags = tags,
|
||||
attrs = attrs,
|
||||
attrs = studioInstance:GetAttributes(),
|
||||
children = children,
|
||||
childCount = childCount,
|
||||
childCount = #children,
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -213,12 +202,12 @@ end
|
|||
-- ================================================================
|
||||
|
||||
local function countOwnDiffs(vCache: VCache, sCache: SCache, classKeys: any): number
|
||||
local cost: number = 0
|
||||
local cost = 0
|
||||
local vProps = vCache.props
|
||||
local sProps = sCache.props
|
||||
local defaults = classKeys.defaults
|
||||
|
||||
for _, propName in classKeys.propNames do
|
||||
for _, propName in ipairs(classKeys.propNames) do
|
||||
local vVal = vProps[propName]
|
||||
if vVal == nil then
|
||||
vVal = defaults[propName]
|
||||
|
|
@ -229,43 +218,37 @@ local function countOwnDiffs(vCache: VCache, sCache: SCache, classKeys: any): nu
|
|||
end
|
||||
end
|
||||
|
||||
if vCache.extraProps then
|
||||
for _, propName in vCache.extraProps do
|
||||
for _, propName in ipairs(vCache.extraProps) do
|
||||
if not trueEquals(vProps[propName], sProps[propName]) then
|
||||
cost += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local vTags = vCache.tags
|
||||
local sTags = sCache.tags
|
||||
if vTags and sTags then
|
||||
for tag in vTags do
|
||||
for tag, _ in pairs(vTags) do
|
||||
if not sTags[tag] then
|
||||
cost += 1
|
||||
end
|
||||
end
|
||||
for tag in sTags do
|
||||
for tag, _ in pairs(sTags) do
|
||||
if not vTags[tag] then
|
||||
cost += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local vAttrs = vCache.attrs
|
||||
local sAttrs = sCache.attrs
|
||||
if vAttrs and sAttrs then
|
||||
for key, vVal in vAttrs do
|
||||
for key, vVal in pairs(vAttrs) do
|
||||
if not trueEquals(vVal, sAttrs[key]) then
|
||||
cost += 1
|
||||
end
|
||||
end
|
||||
for key in sAttrs do
|
||||
for key, _ in pairs(sAttrs) do
|
||||
if vAttrs[key] == nil then
|
||||
cost += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if vCache.childCount ~= sCache.childCount then
|
||||
cost += 1
|
||||
|
|
@ -279,7 +262,7 @@ end
|
|||
-- ================================================================
|
||||
|
||||
local function removeMatched(arr: { any }, matchedIndices: { [number]: boolean }): ()
|
||||
local write: number = 1
|
||||
local write = 1
|
||||
for read = 1, #arr do
|
||||
if not matchedIndices[read] then
|
||||
arr[write] = arr[read]
|
||||
|
|
@ -301,7 +284,7 @@ local function computePairCost(
|
|||
session: MatchingSession,
|
||||
virtualId: string,
|
||||
studioInstance: Instance,
|
||||
virtualInstances: { [string]: any },
|
||||
virtualInstances: VirtualInstances,
|
||||
bestSoFar: number
|
||||
): number
|
||||
local vc = session.costCache[virtualId]
|
||||
|
|
@ -321,7 +304,7 @@ local function computePairCost(
|
|||
local vCache = cacheVirtual(vInst, classKeys)
|
||||
local sCache = cacheStudio(studioInstance, classKeys, vCache.extraProps)
|
||||
|
||||
local cost: number = countOwnDiffs(vCache, sCache, classKeys)
|
||||
local cost = countOwnDiffs(vCache, sCache, classKeys)
|
||||
if cost >= bestSoFar then
|
||||
return cost
|
||||
end
|
||||
|
|
@ -334,23 +317,21 @@ local function computePairCost(
|
|||
elseif not vChildren or #vChildren == 0 then
|
||||
cost += #studioKids * UNMATCHED_PENALTY
|
||||
elseif #studioKids == 0 then
|
||||
for _, childId in vChildren do
|
||||
for _, childId in ipairs(vChildren) do
|
||||
if virtualInstances[childId] then
|
||||
cost += UNMATCHED_PENALTY
|
||||
end
|
||||
end
|
||||
else
|
||||
local validVChildren: { string } = {}
|
||||
for _, childId in vChildren do
|
||||
for _, childId in ipairs(vChildren) do
|
||||
if virtualInstances[childId] then
|
||||
table.insert(validVChildren, childId)
|
||||
end
|
||||
end
|
||||
if #validVChildren > 0 then
|
||||
local childResult: MatchResult = matchChildren(
|
||||
session, validVChildren, studioKids, virtualInstances,
|
||||
virtualId, studioInstance
|
||||
)
|
||||
local childResult =
|
||||
matchChildren(session, validVChildren, studioKids, virtualInstances, virtualId, studioInstance)
|
||||
cost += childResult.totalCost
|
||||
else
|
||||
cost += #studioKids * UNMATCHED_PENALTY
|
||||
|
|
@ -371,7 +352,7 @@ matchChildren = function(
|
|||
session: MatchingSession,
|
||||
virtualChildren: { string },
|
||||
studioChildren: { Instance },
|
||||
virtualInstances: { [string]: any },
|
||||
virtualInstances: VirtualInstances,
|
||||
parentVirtualId: string?,
|
||||
parentStudioInstance: Instance?
|
||||
): MatchResult
|
||||
|
|
@ -394,10 +375,10 @@ matchChildren = function(
|
|||
-- Group by (Name, ClassName) -- direct property access, no pcall
|
||||
-- ============================================================
|
||||
local vByKey: { [string]: { number } } = {}
|
||||
for i, id in remainingVirtual do
|
||||
for i, id in ipairs(remainingVirtual) do
|
||||
local vInst = virtualInstances[id]
|
||||
if vInst then
|
||||
local key: string = vInst.Name .. "\0" .. vInst.ClassName
|
||||
local key = vInst.Name .. "\0" .. vInst.ClassName
|
||||
local group = vByKey[key]
|
||||
if not group then
|
||||
group = {}
|
||||
|
|
@ -408,8 +389,8 @@ matchChildren = function(
|
|||
end
|
||||
|
||||
local sByKey: { [string]: { number } } = {}
|
||||
for i, inst in remainingStudio do
|
||||
local key: string = inst.Name .. "\0" .. inst.ClassName
|
||||
for i, inst in ipairs(remainingStudio) do
|
||||
local key = inst.Name .. "\0" .. inst.ClassName
|
||||
local group = sByKey[key]
|
||||
if not group then
|
||||
group = {}
|
||||
|
|
@ -424,7 +405,7 @@ matchChildren = function(
|
|||
local matchedV: { [number]: boolean } = {}
|
||||
local matchedS: { [number]: boolean } = {}
|
||||
|
||||
for key, vIndices in vByKey do
|
||||
for key, vIndices in pairs(vByKey) do
|
||||
local sIndices = sByKey[key]
|
||||
if sIndices and #vIndices == 1 and #sIndices == 1 then
|
||||
local vi, si = vIndices[1], sIndices[1]
|
||||
|
|
@ -447,10 +428,10 @@ matchChildren = function(
|
|||
-- ============================================================
|
||||
if #remainingVirtual > 0 and #remainingStudio > 0 then
|
||||
local vByKey2: { [string]: { number } } = {}
|
||||
for i, id in remainingVirtual do
|
||||
for i, id in ipairs(remainingVirtual) do
|
||||
local vInst = virtualInstances[id]
|
||||
if vInst then
|
||||
local key: string = vInst.Name .. "\0" .. vInst.ClassName
|
||||
local key = vInst.Name .. "\0" .. vInst.ClassName
|
||||
local group = vByKey2[key]
|
||||
if not group then
|
||||
group = {}
|
||||
|
|
@ -461,8 +442,8 @@ matchChildren = function(
|
|||
end
|
||||
|
||||
local sByKey2: { [string]: { number } } = {}
|
||||
for i, inst in remainingStudio do
|
||||
local key: string = inst.Name .. "\0" .. inst.ClassName
|
||||
for i, inst in ipairs(remainingStudio) do
|
||||
local key = inst.Name .. "\0" .. inst.ClassName
|
||||
local group = sByKey2[key]
|
||||
if not group then
|
||||
group = {}
|
||||
|
|
@ -474,7 +455,7 @@ matchChildren = function(
|
|||
local matchedV2: { [number]: boolean } = {}
|
||||
local matchedS2: { [number]: boolean } = {}
|
||||
|
||||
for key, vIndices in vByKey2 do
|
||||
for key, vIndices in pairs(vByKey2) do
|
||||
local sIndices = sByKey2[key]
|
||||
if not sIndices then
|
||||
continue
|
||||
|
|
@ -487,8 +468,8 @@ matchChildren = function(
|
|||
local classKeys = RbxDom.getClassComparisonKeys(firstVInst.ClassName)
|
||||
|
||||
local vCaches: { [number]: VCache } = {}
|
||||
local allExtraProps: { [string]: boolean }? = nil
|
||||
for _, vi in vIndices do
|
||||
local allExtraProps: { [string]: boolean } = {}
|
||||
for _, vi in ipairs(vIndices) do
|
||||
if matchedV2[vi] then
|
||||
continue
|
||||
end
|
||||
|
|
@ -498,26 +479,18 @@ matchChildren = function(
|
|||
end
|
||||
local vCache = cacheVirtual(vInst, classKeys)
|
||||
vCaches[vi] = vCache
|
||||
if vCache.extraProps then
|
||||
if not allExtraProps then
|
||||
allExtraProps = {}
|
||||
end
|
||||
for _, propName in vCache.extraProps do
|
||||
for _, propName in ipairs(vCache.extraProps) do
|
||||
allExtraProps[propName] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local extraPropNamesArray: { string }? = nil
|
||||
if allExtraProps then
|
||||
extraPropNamesArray = {}
|
||||
for propName in allExtraProps do
|
||||
local extraPropNamesArray: { string } = {}
|
||||
for propName, _ in pairs(allExtraProps) do
|
||||
table.insert(extraPropNamesArray, propName)
|
||||
end
|
||||
end
|
||||
|
||||
local sCaches: { [number]: SCache } = {}
|
||||
for _, si in sIndices do
|
||||
for _, si in ipairs(sIndices) do
|
||||
if not matchedS2[si] then
|
||||
sCaches[si] = cacheStudio(remainingStudio[si], classKeys, extraPropNamesArray)
|
||||
end
|
||||
|
|
@ -525,10 +498,10 @@ matchChildren = function(
|
|||
|
||||
-- Score all (A, B) pairs
|
||||
local scoredPairs: { ScoredPair } = {}
|
||||
local pairIdx: number = 0
|
||||
local bestSoFar: number = math.huge
|
||||
local pairIdx = 0
|
||||
local bestSoFar = math.huge
|
||||
|
||||
for _, vi in vIndices do
|
||||
for _, vi in ipairs(vIndices) do
|
||||
if matchedV2[vi] then
|
||||
continue
|
||||
end
|
||||
|
|
@ -537,7 +510,7 @@ matchChildren = function(
|
|||
continue
|
||||
end
|
||||
|
||||
for _, si in sIndices do
|
||||
for _, si in ipairs(sIndices) do
|
||||
if matchedS2[si] then
|
||||
continue
|
||||
end
|
||||
|
|
@ -548,7 +521,7 @@ matchChildren = function(
|
|||
|
||||
pairIdx += 1
|
||||
|
||||
local cost: number = countOwnDiffs(vCache, sCache, classKeys)
|
||||
local cost = countOwnDiffs(vCache, sCache, classKeys)
|
||||
|
||||
if cost < bestSoFar then
|
||||
local vInst = virtualInstances[remainingVirtual[vi]]
|
||||
|
|
@ -561,22 +534,26 @@ matchChildren = function(
|
|||
elseif not vChildren or #vChildren == 0 then
|
||||
cost += #studioKids * UNMATCHED_PENALTY
|
||||
elseif #studioKids == 0 then
|
||||
for _, childId in vChildren do
|
||||
for _, childId in ipairs(vChildren) do
|
||||
if virtualInstances[childId] then
|
||||
cost += UNMATCHED_PENALTY
|
||||
end
|
||||
end
|
||||
else
|
||||
local validVChildren: { string } = {}
|
||||
for _, childId in vChildren do
|
||||
for _, childId in ipairs(vChildren) do
|
||||
if virtualInstances[childId] then
|
||||
table.insert(validVChildren, childId)
|
||||
end
|
||||
end
|
||||
if #validVChildren > 0 then
|
||||
local childResult: MatchResult = matchChildren(
|
||||
session, validVChildren, studioKids, virtualInstances,
|
||||
remainingVirtual[vi], sCache.instance
|
||||
local childResult = matchChildren(
|
||||
session,
|
||||
validVChildren,
|
||||
studioKids,
|
||||
virtualInstances,
|
||||
remainingVirtual[vi],
|
||||
sCache.instance
|
||||
)
|
||||
cost += childResult.totalCost
|
||||
else
|
||||
|
|
@ -600,7 +577,7 @@ matchChildren = function(
|
|||
return a.idx < b.idx
|
||||
end)
|
||||
|
||||
for _, pair in scoredPairs do
|
||||
for _, pair in ipairs(scoredPairs) do
|
||||
if matchedV2[pair.vi] or matchedS2[pair.si] then
|
||||
continue
|
||||
end
|
||||
|
|
@ -620,15 +597,13 @@ matchChildren = function(
|
|||
-- ============================================================
|
||||
-- Compute totalCost for ALL matched pairs (session cache helps)
|
||||
-- ============================================================
|
||||
local totalCost: number = 0
|
||||
for _, pair in matched do
|
||||
totalCost += computePairCost(
|
||||
session, pair.virtualId, pair.studioInstance, virtualInstances, math.huge
|
||||
)
|
||||
local totalCost = 0
|
||||
for _, pair in ipairs(matched) do
|
||||
totalCost += computePairCost(session, pair.virtualId, pair.studioInstance, virtualInstances, math.huge)
|
||||
end
|
||||
totalCost += (#remainingVirtual + #remainingStudio) * UNMATCHED_PENALTY
|
||||
|
||||
local result: MatchResult = {
|
||||
local result = {
|
||||
matched = matched,
|
||||
unmatchedVirtual = remainingVirtual,
|
||||
unmatchedStudio = remainingStudio,
|
||||
|
|
|
|||
|
|
@ -19,9 +19,7 @@ local function fuzzyEq(a: number, b: number): boolean
|
|||
end
|
||||
|
||||
local function color3Eq(a: Color3, b: Color3): boolean
|
||||
return math.floor(a.R * 255) == math.floor(b.R * 255)
|
||||
and math.floor(a.G * 255) == math.floor(b.G * 255)
|
||||
and math.floor(a.B * 255) == math.floor(b.B * 255)
|
||||
return fuzzyEq(a.R, b.R) and fuzzyEq(a.G, b.G) and fuzzyEq(a.B, b.B)
|
||||
end
|
||||
|
||||
local function vector2Eq(a: Vector2, b: Vector2): boolean
|
||||
|
|
@ -35,10 +33,18 @@ end
|
|||
local function cframeEq(a: CFrame, b: CFrame): boolean
|
||||
local ax, ay, az, aR00, aR01, aR02, aR10, aR11, aR12, aR20, aR21, aR22 = a:GetComponents()
|
||||
local bx, by, bz, bR00, bR01, bR02, bR10, bR11, bR12, bR20, bR21, bR22 = b:GetComponents()
|
||||
return fuzzyEq(ax, bx) and fuzzyEq(ay, by) and fuzzyEq(az, bz)
|
||||
and fuzzyEq(aR00, bR00) and fuzzyEq(aR01, bR01) and fuzzyEq(aR02, bR02)
|
||||
and fuzzyEq(aR10, bR10) and fuzzyEq(aR11, bR11) and fuzzyEq(aR12, bR12)
|
||||
and fuzzyEq(aR20, bR20) and fuzzyEq(aR21, bR21) and fuzzyEq(aR22, bR22)
|
||||
return fuzzyEq(ax, bx)
|
||||
and fuzzyEq(ay, by)
|
||||
and fuzzyEq(az, bz)
|
||||
and fuzzyEq(aR00, bR00)
|
||||
and fuzzyEq(aR01, bR01)
|
||||
and fuzzyEq(aR02, bR02)
|
||||
and fuzzyEq(aR10, bR10)
|
||||
and fuzzyEq(aR11, bR11)
|
||||
and fuzzyEq(aR12, bR12)
|
||||
and fuzzyEq(aR20, bR20)
|
||||
and fuzzyEq(aR21, bR21)
|
||||
and fuzzyEq(aR22, bR22)
|
||||
end
|
||||
|
||||
local function trueEquals(a: any, b: any): boolean
|
||||
|
|
@ -54,11 +60,12 @@ local function trueEquals(a: any, b: any): boolean
|
|||
end
|
||||
|
||||
local t = typeof(a)
|
||||
if t ~= typeof(b) then
|
||||
if t == "number" and typeof(b) == "EnumItem" then
|
||||
local tb = typeof(b)
|
||||
if t ~= tb then
|
||||
if t == "number" and tb == "EnumItem" then
|
||||
return a == (b :: EnumItem).Value
|
||||
end
|
||||
if t == "EnumItem" and typeof(b) == "number" then
|
||||
if t == "EnumItem" and tb == "number" then
|
||||
return (a :: EnumItem).Value == b
|
||||
end
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -1350,6 +1350,13 @@ impl JobThreadContext {
|
|||
Some(new_dir_path.join(file_name));
|
||||
new_ref_segment =
|
||||
Some(slugified_new_name.clone());
|
||||
if patch_set.stage_ids.contains(&id) {
|
||||
let old_init = dir_path.join(file_name);
|
||||
pending_stage_paths
|
||||
.retain(|p| *p != old_init);
|
||||
pending_stage_paths
|
||||
.push(new_dir_path.join(file_name));
|
||||
}
|
||||
let old_meta = grandparent.join(format!(
|
||||
"{}.meta.json5",
|
||||
dir_name
|
||||
|
|
@ -1511,6 +1518,10 @@ impl JobThreadContext {
|
|||
} else {
|
||||
overridden_source_path = Some(new_path.clone());
|
||||
new_ref_segment = Some(new_file_name.clone());
|
||||
if patch_set.stage_ids.contains(&id) {
|
||||
pending_stage_paths.retain(|p| *p != *path);
|
||||
pending_stage_paths.push(new_path.clone());
|
||||
}
|
||||
let old_meta = parent
|
||||
.join(format!("{}.meta.json5", old_base));
|
||||
let new_meta = parent.join(format!(
|
||||
|
|
@ -1689,6 +1700,11 @@ impl JobThreadContext {
|
|||
);
|
||||
} else {
|
||||
overridden_source_path = Some(new_path.clone());
|
||||
if patch_set.stage_ids.contains(&id) {
|
||||
pending_stage_paths
|
||||
.retain(|p| *p != actual_file);
|
||||
pending_stage_paths.push(new_path.clone());
|
||||
}
|
||||
// For standalone files, the ref path
|
||||
// segment changes with the extension.
|
||||
// For init files, the directory name
|
||||
|
|
|
|||
|
|
@ -72,8 +72,11 @@ impl ServeCommand {
|
|||
|
||||
loop {
|
||||
let (vfs, critical_errors) = Vfs::new_default_with_errors();
|
||||
let session =
|
||||
Arc::new(ServeSession::new(vfs, project_path.clone(), Some(critical_errors))?);
|
||||
let session = Arc::new(ServeSession::new(
|
||||
vfs,
|
||||
project_path.clone(),
|
||||
Some(critical_errors),
|
||||
)?);
|
||||
let server = LiveServer::new(session);
|
||||
|
||||
log::info!("Listening: http://{}:{}", host, port);
|
||||
|
|
@ -94,7 +97,7 @@ fn run_live_syncback(project_path: &Path, payload: SyncbackPayload) -> anyhow::R
|
|||
let new_dom = build_dom_from_chunks(payload)?;
|
||||
|
||||
let vfs = Vfs::new_oneshot();
|
||||
let session_old = ServeSession::new_oneshot(vfs, project_path.to_path_buf())?;
|
||||
let session_old = ServeSession::new_oneshot(vfs, project_path)?;
|
||||
|
||||
let mut dom_old = session_old.tree();
|
||||
|
||||
|
|
@ -149,8 +152,8 @@ fn build_dom_from_chunks(payload: SyncbackPayload) -> anyhow::Result<WeakDom> {
|
|||
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(|| {
|
||||
let chunk_dom =
|
||||
rbx_binary::from_reader(Cursor::new(&chunk.data)).with_context(|| {
|
||||
format!("Failed to parse rbxm for service {}", chunk.class_name)
|
||||
})?;
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,12 @@ pub struct MatchingSession {
|
|||
cost_cache: RefCell<HashMap<(Ref, Ref), u32>>,
|
||||
}
|
||||
|
||||
impl Default for MatchingSession {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl MatchingSession {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
|
|
@ -116,7 +122,14 @@ pub fn match_forward(
|
|||
let snap_remaining = snap_matched.iter().filter(|&&m| !m).count();
|
||||
let tree_remaining = tree_available.iter().filter(|&&a| a).count();
|
||||
if snap_remaining == 0 || tree_remaining == 0 {
|
||||
return build_result(snap_available, tree_children, &tree_available, matched, tree, session);
|
||||
return build_result(
|
||||
snap_available,
|
||||
tree_children,
|
||||
&tree_available,
|
||||
matched,
|
||||
tree,
|
||||
session,
|
||||
);
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
|
|
@ -186,7 +199,14 @@ pub fn match_forward(
|
|||
}
|
||||
}
|
||||
|
||||
build_result(snap_available, tree_children, &tree_available, matched, tree, session)
|
||||
build_result(
|
||||
snap_available,
|
||||
tree_children,
|
||||
&tree_available,
|
||||
matched,
|
||||
tree,
|
||||
session,
|
||||
)
|
||||
}
|
||||
|
||||
/// Build the final result, consuming the snapshot children.
|
||||
|
|
@ -1139,7 +1159,12 @@ mod tests {
|
|||
make_snapshot_with_props("Wall", "Part", tree_props_b),
|
||||
make_snapshot_with_props("Wall", "Part", tree_props_a),
|
||||
]);
|
||||
let result = match_forward(vec![snap_a, snap_b], &tree_refs, &tree, &MatchingSession::new());
|
||||
let result = match_forward(
|
||||
vec![snap_a, snap_b],
|
||||
&tree_refs,
|
||||
&tree,
|
||||
&MatchingSession::new(),
|
||||
);
|
||||
|
||||
assert_eq!(result.matched.len(), 2, "Both should match");
|
||||
|
||||
|
|
|
|||
|
|
@ -235,7 +235,14 @@ fn compute_children_patches(
|
|||
|
||||
// Matched pairs: recursively compute patches.
|
||||
for (snapshot_child, tree_child_id) in match_result.matched {
|
||||
compute_patch_set_internal(context, snapshot_child, tree, tree_child_id, patch_set, session);
|
||||
compute_patch_set_internal(
|
||||
context,
|
||||
snapshot_child,
|
||||
tree,
|
||||
tree_child_id,
|
||||
patch_set,
|
||||
session,
|
||||
);
|
||||
}
|
||||
|
||||
// Unmatched snapshots: new instances to be added.
|
||||
|
|
|
|||
|
|
@ -30,6 +30,12 @@ pub struct MatchingSession {
|
|||
cost_cache: RefCell<HashMap<(Ref, Ref), u32>>,
|
||||
}
|
||||
|
||||
impl Default for MatchingSession {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl MatchingSession {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
|
|
@ -122,12 +128,19 @@ pub fn match_children(
|
|||
let mut total_cost: u32 = 0;
|
||||
for &(new_ref, old_ref) in &matched {
|
||||
total_cost = total_cost.saturating_add(compute_change_count(
|
||||
new_ref, old_ref, new_dom, old_dom, new_hashes, old_hashes, u32::MAX, 0, session,
|
||||
new_ref,
|
||||
old_ref,
|
||||
new_dom,
|
||||
old_dom,
|
||||
new_hashes,
|
||||
old_hashes,
|
||||
u32::MAX,
|
||||
0,
|
||||
session,
|
||||
));
|
||||
}
|
||||
total_cost = total_cost.saturating_add(
|
||||
(remaining_new.len() + remaining_old.len()) as u32 * UNMATCHED_PENALTY,
|
||||
);
|
||||
total_cost = total_cost
|
||||
.saturating_add((remaining_new.len() + remaining_old.len()) as u32 * UNMATCHED_PENALTY);
|
||||
return MatchResult {
|
||||
matched,
|
||||
unmatched_new: remaining_new,
|
||||
|
|
@ -178,7 +191,14 @@ pub fn match_children(
|
|||
for &new_ref in &avail_new {
|
||||
for &old_ref in &avail_old {
|
||||
let cost = compute_change_count(
|
||||
new_ref, old_ref, new_dom, old_dom, new_hashes, old_hashes, best_so_far, 0,
|
||||
new_ref,
|
||||
old_ref,
|
||||
new_dom,
|
||||
old_dom,
|
||||
new_hashes,
|
||||
old_hashes,
|
||||
best_so_far,
|
||||
0,
|
||||
session,
|
||||
);
|
||||
pairs.push((cost, new_ref, old_ref));
|
||||
|
|
@ -208,12 +228,19 @@ pub fn match_children(
|
|||
let mut total_cost: u32 = 0;
|
||||
for &(new_ref, old_ref) in &matched {
|
||||
total_cost = total_cost.saturating_add(compute_change_count(
|
||||
new_ref, old_ref, new_dom, old_dom, new_hashes, old_hashes, u32::MAX, 0, session,
|
||||
new_ref,
|
||||
old_ref,
|
||||
new_dom,
|
||||
old_dom,
|
||||
new_hashes,
|
||||
old_hashes,
|
||||
u32::MAX,
|
||||
0,
|
||||
session,
|
||||
));
|
||||
}
|
||||
total_cost = total_cost.saturating_add(
|
||||
(remaining_new.len() + remaining_old.len()) as u32 * UNMATCHED_PENALTY,
|
||||
);
|
||||
total_cost = total_cost
|
||||
.saturating_add((remaining_new.len() + remaining_old.len()) as u32 * UNMATCHED_PENALTY);
|
||||
|
||||
MatchResult {
|
||||
matched,
|
||||
|
|
@ -545,7 +572,15 @@ mod tests {
|
|||
let new_children: Vec<Ref> = new_dom.get_by_ref(new_root).unwrap().children().to_vec();
|
||||
let old_children: Vec<Ref> = old_dom.get_by_ref(old_root).unwrap().children().to_vec();
|
||||
|
||||
let result = match_children(&new_children, &old_children, &new_dom, &old_dom, None, None, &MatchingSession::new());
|
||||
let result = match_children(
|
||||
&new_children,
|
||||
&old_children,
|
||||
&new_dom,
|
||||
&old_dom,
|
||||
None,
|
||||
None,
|
||||
&MatchingSession::new(),
|
||||
);
|
||||
|
||||
assert_eq!(result.matched.len(), 2);
|
||||
assert!(result.unmatched_new.is_empty());
|
||||
|
|
@ -585,7 +620,15 @@ mod tests {
|
|||
let new_children: Vec<Ref> = new_dom.get_by_ref(new_root).unwrap().children().to_vec();
|
||||
let old_children: Vec<Ref> = old_dom.get_by_ref(old_root).unwrap().children().to_vec();
|
||||
|
||||
let result = match_children(&new_children, &old_children, &new_dom, &old_dom, None, None, &MatchingSession::new());
|
||||
let result = match_children(
|
||||
&new_children,
|
||||
&old_children,
|
||||
&new_dom,
|
||||
&old_dom,
|
||||
None,
|
||||
None,
|
||||
&MatchingSession::new(),
|
||||
);
|
||||
|
||||
assert_eq!(result.matched.len(), 2);
|
||||
for (new_ref, old_ref) in &result.matched {
|
||||
|
|
@ -616,7 +659,15 @@ mod tests {
|
|||
let new_children: Vec<Ref> = new_dom.get_by_ref(new_root).unwrap().children().to_vec();
|
||||
let old_children: Vec<Ref> = old_dom.get_by_ref(old_root).unwrap().children().to_vec();
|
||||
|
||||
let result = match_children(&new_children, &old_children, &new_dom, &old_dom, None, None, &MatchingSession::new());
|
||||
let result = match_children(
|
||||
&new_children,
|
||||
&old_children,
|
||||
&new_dom,
|
||||
&old_dom,
|
||||
None,
|
||||
None,
|
||||
&MatchingSession::new(),
|
||||
);
|
||||
|
||||
assert_eq!(result.matched.len(), 1);
|
||||
assert_eq!(result.unmatched_new.len(), 1);
|
||||
|
|
@ -638,7 +689,15 @@ mod tests {
|
|||
let new_children: Vec<Ref> = new_dom.get_by_ref(new_root).unwrap().children().to_vec();
|
||||
let old_children: Vec<Ref> = old_dom.get_by_ref(old_root).unwrap().children().to_vec();
|
||||
|
||||
let result = match_children(&new_children, &old_children, &new_dom, &old_dom, None, None, &MatchingSession::new());
|
||||
let result = match_children(
|
||||
&new_children,
|
||||
&old_children,
|
||||
&new_dom,
|
||||
&old_dom,
|
||||
None,
|
||||
None,
|
||||
&MatchingSession::new(),
|
||||
);
|
||||
assert_eq!(result.matched.len(), 2);
|
||||
assert!(result.unmatched_new.is_empty());
|
||||
assert!(result.unmatched_old.is_empty());
|
||||
|
|
@ -648,7 +707,15 @@ mod tests {
|
|||
fn empty_children() {
|
||||
let new_dom = WeakDom::new(InstanceBuilder::new("DataModel"));
|
||||
let old_dom = WeakDom::new(InstanceBuilder::new("DataModel"));
|
||||
let result = match_children(&[], &[], &new_dom, &old_dom, None, None, &MatchingSession::new());
|
||||
let result = match_children(
|
||||
&[],
|
||||
&[],
|
||||
&new_dom,
|
||||
&old_dom,
|
||||
None,
|
||||
None,
|
||||
&MatchingSession::new(),
|
||||
);
|
||||
assert!(result.matched.is_empty());
|
||||
}
|
||||
|
||||
|
|
@ -667,8 +734,24 @@ mod tests {
|
|||
let new_children: Vec<Ref> = new_dom.get_by_ref(new_root).unwrap().children().to_vec();
|
||||
let old_children: Vec<Ref> = old_dom.get_by_ref(old_root).unwrap().children().to_vec();
|
||||
|
||||
let r1 = match_children(&new_children, &old_children, &new_dom, &old_dom, None, None, &MatchingSession::new());
|
||||
let r2 = match_children(&new_children, &old_children, &new_dom, &old_dom, None, None, &MatchingSession::new());
|
||||
let r1 = match_children(
|
||||
&new_children,
|
||||
&old_children,
|
||||
&new_dom,
|
||||
&old_dom,
|
||||
None,
|
||||
None,
|
||||
&MatchingSession::new(),
|
||||
);
|
||||
let r2 = match_children(
|
||||
&new_children,
|
||||
&old_children,
|
||||
&new_dom,
|
||||
&old_dom,
|
||||
None,
|
||||
None,
|
||||
&MatchingSession::new(),
|
||||
);
|
||||
|
||||
assert_eq!(r1.matched.len(), r2.matched.len());
|
||||
for (a, b) in r1.matched.iter().zip(r2.matched.iter()) {
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@ use crate::{
|
|||
web::{
|
||||
interface::{
|
||||
ErrorResponse, Instance, InstanceMetadata, MessagesPacket, OpenResponse, ReadResponse,
|
||||
ServerInfoResponse, SocketPacket, SocketPacketBody, SocketPacketType,
|
||||
SubscribeMessage, SyncbackPayload, SyncbackRequest, WriteRequest, WriteResponse,
|
||||
PROTOCOL_VERSION, SERVER_VERSION,
|
||||
ServerInfoResponse, SocketPacket, SocketPacketBody, SocketPacketType, SubscribeMessage,
|
||||
SyncbackPayload, SyncbackRequest, WriteRequest, WriteResponse, PROTOCOL_VERSION,
|
||||
SERVER_VERSION,
|
||||
},
|
||||
util::{deserialize_msgpack, msgpack, msgpack_ok, serialize_msgpack},
|
||||
},
|
||||
|
|
@ -256,7 +256,7 @@ async fn handle_api_syncback(
|
|||
};
|
||||
syncback_signal.fire(payload);
|
||||
|
||||
msgpack_ok(&serde_json::json!({"status": "syncback_initiated"}))
|
||||
msgpack_ok(serde_json::json!({"status": "syncback_initiated"}))
|
||||
}
|
||||
|
||||
pub struct ApiService {
|
||||
|
|
|
|||
|
|
@ -41,12 +41,15 @@ impl SyncbackSignal {
|
|||
}
|
||||
|
||||
pub fn fire(&self, payload: SyncbackPayload) {
|
||||
*self.payload.lock().unwrap() = Some(payload);
|
||||
*self.payload.lock().unwrap_or_else(|e| e.into_inner()) = Some(payload);
|
||||
self.notify.notify_one();
|
||||
}
|
||||
|
||||
pub fn take_payload(&self) -> Option<SyncbackPayload> {
|
||||
self.payload.lock().unwrap().take()
|
||||
self.payload
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner())
|
||||
.take()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -505,8 +505,10 @@ pub fn make_rbxl_from_chunks(chunks: &[librojo::web_api::ServiceChunk]) -> Vec<u
|
|||
let chunk_dom = rbx_binary::from_reader(Cursor::new(&chunk.data))
|
||||
.unwrap_or_else(|e| panic!("Failed to parse rbxm for {}: {e}", chunk.class_name));
|
||||
|
||||
let service_ref =
|
||||
dom.insert(root_ref, rbx_dom_weak::InstanceBuilder::new(&chunk.class_name));
|
||||
let service_ref = dom.insert(
|
||||
root_ref,
|
||||
rbx_dom_weak::InstanceBuilder::new(&chunk.class_name),
|
||||
);
|
||||
|
||||
let mut ref_map: HashMap<Ref, Ref> = HashMap::new();
|
||||
for &child_ref in chunk_dom.root().children() {
|
||||
|
|
@ -554,10 +556,8 @@ pub fn make_rbxl_from_chunks(chunks: &[librojo::web_api::ServiceChunk]) -> Vec<u
|
|||
"VoiceChatService",
|
||||
"Workspace",
|
||||
];
|
||||
let existing: std::collections::HashSet<String> = chunks
|
||||
.iter()
|
||||
.map(|c| c.class_name.clone())
|
||||
.collect();
|
||||
let existing: std::collections::HashSet<String> =
|
||||
chunks.iter().map(|c| c.class_name.clone()).collect();
|
||||
for &svc in &visible_services {
|
||||
if !existing.contains(svc) {
|
||||
dom.insert(root_ref, rbx_dom_weak::InstanceBuilder::new(svc));
|
||||
|
|
|
|||
|
|
@ -899,3 +899,53 @@ fn batch_mixed_stage_and_source_write() {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// stage_ids survives rename (Fix 2 from audit)
|
||||
// ===========================================================================
|
||||
|
||||
#[test]
|
||||
fn stage_ids_stages_new_path_after_rename() {
|
||||
let mut session = TestServeSession::new_with_git("git_sync_defaults", |path| {
|
||||
git_commit_all(path, "initial commit");
|
||||
});
|
||||
let info = session.wait_to_come_online();
|
||||
let read = session.get_api_read(info.root_instance_id).unwrap();
|
||||
|
||||
let (module_id, _) =
|
||||
find_instance_by_name(&read.instances, "ModuleA").expect("ModuleA should exist");
|
||||
|
||||
let old_path = session.path().join("src/ModuleA.luau");
|
||||
let new_path = session.path().join("src/RenamedModule.luau");
|
||||
assert!(old_path.exists(), "ModuleA.luau should exist before rename");
|
||||
|
||||
let write_request = WriteRequest {
|
||||
session_id: info.session_id,
|
||||
removed: vec![],
|
||||
added: HashMap::new(),
|
||||
updated: vec![InstanceUpdate {
|
||||
id: module_id,
|
||||
changed_name: Some("RenamedModule".to_string()),
|
||||
changed_class_name: None,
|
||||
changed_properties: UstrMap::default(),
|
||||
changed_metadata: None,
|
||||
}],
|
||||
stage_ids: vec![module_id],
|
||||
};
|
||||
session.post_api_write(&write_request).unwrap();
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
|
||||
assert!(
|
||||
new_path.exists(),
|
||||
"RenamedModule.luau should exist after rename"
|
||||
);
|
||||
assert!(
|
||||
!old_path.exists(),
|
||||
"ModuleA.luau should be gone after rename"
|
||||
);
|
||||
|
||||
assert!(
|
||||
git_is_staged(session.path(), "src/RenamedModule.luau"),
|
||||
"New file (RenamedModule.luau) should be staged after rename + stage_ids"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,7 @@ use crate::rojo_test::{
|
|||
|
||||
use librojo::web_api::{ServiceChunk, SocketPacketType, PROTOCOL_VERSION};
|
||||
|
||||
fn assert_live_matches_cli(
|
||||
fixture_name: &str,
|
||||
chunks: &[ServiceChunk],
|
||||
place_id: Option<u64>,
|
||||
) {
|
||||
fn assert_live_matches_cli(fixture_name: &str, chunks: &[ServiceChunk], place_id: Option<u64>) {
|
||||
run_serve_test(fixture_name, |session, _| {
|
||||
session.post_api_syncback(place_id, chunks.to_vec());
|
||||
session.wait_to_come_back_online();
|
||||
|
|
@ -32,10 +28,16 @@ fn parity_scripts() {
|
|||
vec![
|
||||
InstanceBuilder::new("ModuleScript")
|
||||
.with_name("Utils")
|
||||
.with_property("Source", rbx_dom_weak::types::Variant::String("return {}".into())),
|
||||
.with_property(
|
||||
"Source",
|
||||
rbx_dom_weak::types::Variant::String("return {}".into()),
|
||||
),
|
||||
InstanceBuilder::new("Script")
|
||||
.with_name("Main")
|
||||
.with_property("Source", rbx_dom_weak::types::Variant::String("print('hi')".into()))
|
||||
.with_property(
|
||||
"Source",
|
||||
rbx_dom_weak::types::Variant::String("print('hi')".into()),
|
||||
)
|
||||
.with_property(
|
||||
"RunContext",
|
||||
rbx_dom_weak::types::Variant::Enum(rbx_dom_weak::types::Enum::from_u32(1)),
|
||||
|
|
@ -117,7 +119,10 @@ fn parity_mixed_file_types() {
|
|||
vec![
|
||||
InstanceBuilder::new("StringValue")
|
||||
.with_name("MyString")
|
||||
.with_property("Value", rbx_dom_weak::types::Variant::String("hello world".into())),
|
||||
.with_property(
|
||||
"Value",
|
||||
rbx_dom_weak::types::Variant::String("hello world".into()),
|
||||
),
|
||||
InstanceBuilder::new("Folder").with_name("Container"),
|
||||
InstanceBuilder::new("ModuleScript")
|
||||
.with_name("DataModule")
|
||||
|
|
|
|||
|
|
@ -309,7 +309,8 @@ fn forward_matching_ufowave_textures() {
|
|||
|
||||
// Run forward sync matching
|
||||
let session = librojo::snapshot::matching::MatchingSession::new();
|
||||
let result = librojo::snapshot::matching::match_forward(snap_textures, &tree_textures, &tree, &session);
|
||||
let result =
|
||||
librojo::snapshot::matching::match_forward(snap_textures, &tree_textures, &tree, &session);
|
||||
|
||||
assert_eq!(
|
||||
result.matched.len(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue