Implement fallback error handling and enhance scripts-only mode in ServeSession

- Added a fallback mechanism in the `__applyPatch` method to handle errors during patch application, logging warnings for any encountered issues.
- Improved the transitive closure logic in the `__connectScriptsOnlyWatchers` method to ensure all necessary temporary IDs are included as skeleton containers, enhancing the integrity of the synchronization process.

These changes aim to improve error resilience and ensure proper handling of script references in scripts-only mode.
This commit is contained in:
Joe 2026-02-28 12:00:01 -06:00
parent d4b390a3ec
commit 22e437a055

View file

@ -574,6 +574,7 @@ function ServeSession:__applyPatch(patch)
error(unappliedPatch) error(unappliedPatch)
end end
local fallbackOk, fallbackErr = pcall(function()
if Settings:get("enableSyncFallback") and not PatchSet.isEmpty(unappliedPatch) then if Settings:get("enableSyncFallback") and not PatchSet.isEmpty(unappliedPatch) then
-- Some changes did not apply, let's try replacing them instead -- Some changes did not apply, let's try replacing them instead
local addedIdList = PatchSet.addedIdList(unappliedPatch) local addedIdList = PatchSet.addedIdList(unappliedPatch)
@ -599,8 +600,13 @@ function ServeSession:__applyPatch(patch)
PatchSet.assign(unappliedPatch, unappliedUpdateRefs) PatchSet.assign(unappliedPatch, unappliedUpdateRefs)
end end
end end
end)
self.__applyingPatch = false self.__applyingPatch = false
if not fallbackOk then
Log.warn("Sync fallback errored: {}", fallbackErr)
end
if not PatchSet.isEmpty(unappliedPatch) then if not PatchSet.isEmpty(unappliedPatch) then
Log.info( Log.info(
"Could not apply all changes requested by the Rojo server:\n{}", "Could not apply all changes requested by the Rojo server:\n{}",
@ -1014,6 +1020,46 @@ function ServeSession:__connectScriptsOnlyWatchers()
self.__instanceMap:insert(scriptTempId, descendant) self.__instanceMap:insert(scriptTempId, descendant)
self.__instanceMap.scriptsOnlyTempIds[scriptTempId] = true self.__instanceMap.scriptsOnlyTempIds[scriptTempId] = true
-- Transitive closure: ensure every temp-ID parent referenced by
-- a patch entry is itself included as a skeleton container. When
-- multiple scripts are created in the same pruned subtree (e.g.,
-- paste), later handlers find intermediates already in instanceMap
-- with temp IDs from earlier handlers. Without re-including those
-- skeletons, the server's Phase 0 can't resolve the temp parents.
local closureNeeded = true
while closureNeeded do
closureNeeded = false
local toAdd = {}
for _, entry in pairs(patch.added) do
local pid = entry.parent
if
pid
and self.__instanceMap.scriptsOnlyTempIds[pid]
and not patch.added[pid]
and not toAdd[pid]
then
local inst = self.__instanceMap.fromIds[pid]
if inst then
local pp = inst.Parent
local ppId = pp and self.__instanceMap.fromInstances[pp]
if ppId then
toAdd[pid] = {
parent = ppId,
name = inst.Name,
className = inst.ClassName,
properties = {},
children = {},
}
closureNeeded = true
end
end
end
end
for id, entry in toAdd do
patch.added[id] = entry
end
end
Log.info( Log.info(
"Scripts-only: detected new script {} outside mapped tree, sending to server", "Scripts-only: detected new script {} outside mapped tree, sending to server",
descendant:GetFullName() descendant:GetFullName()