Intense robustness pass

This commit is contained in:
Lucien Greathouse
2018-05-21 12:48:25 -07:00
parent 6fbe1daf8e
commit 8b17d3b7d9
3 changed files with 57 additions and 37 deletions

View File

@@ -66,7 +66,9 @@ end
function Plugin:restart()
warn("Rojo: The server has changed since the last request, reloading plugin...")
self._reconciler:clear()
self._reconciler:destruct()
self._reconciler = Reconciler.new()
self._api = nil
self._polling = false
self._syncInProgress = false

View File

@@ -1,13 +1,26 @@
local RouteMap = require(script.Parent.RouteMap)
local function classEqual(rbx, className)
if className == "*" then
local function classEqual(a, b)
if a == "*" or b == "*" then
return true
end
return rbx.ClassName == className
return a == b
end
local function applyProperties(target, properties)
for key, property in pairs(properties) do
-- TODO: Transform property value based on property.Type
-- Right now, we assume that 'value' is primitive!
target[key] = property.Value
end
end
--[[
Attempt to parent `rbx` to `parent`, doing nothing if:
* parent is already `parent`
* Changing parent threw an error
]]
local function reparent(rbx, parent)
if rbx then
if rbx.Parent == parent then
@@ -42,7 +55,7 @@ local function findNextChildPair(primaryChildren, secondaryChildren, visited)
visited[primaryChild] = true
for _, secondaryChild in ipairs(secondaryChildren) do
if primaryChild.ClassName == secondaryChild.ClassName and primaryChild.Name == secondaryChild.Name then
if classEqual(primaryChild.ClassName, secondaryChild.ClassName) and primaryChild.Name == secondaryChild.Name then
visited[secondaryChild] = true
return primaryChild, secondaryChild
@@ -114,12 +127,11 @@ function Reconciler:_reify(item)
local rbx = Instance.new(className)
rbx.Name = item.Name
for key, property in pairs(item.Properties) do
-- TODO: Check for compound types, like Vector3!
rbx[key] = property.Value
end
applyProperties(rbx, item.Properties)
self:_reconcileChildren(rbx, item)
for _, child in ipairs(item.Children) do
reparent(self:_reify(child), rbx)
end
if item.Route then
self._routeMap:insert(item.Route, rbx)
@@ -129,10 +141,10 @@ function Reconciler:_reify(item)
end
--[[
Clears any state that the Reconciler has, effectively restarting it.
Clears any state that the Reconciler has, stopping it completely.
]]
function Reconciler:clear()
self._routeMap:clear()
function Reconciler:destruct()
self._routeMap:destruct()
end
--[[
@@ -156,28 +168,16 @@ function Reconciler:reconcile(rbx, item)
end
-- Item changed type!
if not classEqual(rbx, item.ClassName) then
if not classEqual(rbx.ClassName, item.ClassName) then
self._routeMap:removeByRbx(rbx)
rbx:Destroy()
return self:_reify(item)
end
-- Apply all properties, Roblox will de-duplicate changes
for key, property in pairs(item.Properties) do
-- TODO: Transform property value based on property.Type
-- Right now, we assume that 'value' is primitive!
rbx[key] = property.Value
end
-- Use a dumb algorithm for reconciling children
applyProperties(rbx, item.Properties)
self:_reconcileChildren(rbx, item)
if item.Route then
self._routeMap:insert(item.Route, rbx)
end
return rbx
end

View File

@@ -25,6 +25,10 @@ end
function RouteMap:insert(route, rbx)
local hashed = hashRoute(route)
-- Make sure that each route and instance are only present in RouteMap once.
self:removeByRoute(route)
self:removeByRbx(rbx)
self._map[hashed] = rbx
self._reverseMap[rbx] = hashed
self._connectionsByRbx[rbx] = rbx.AncestryChanged:Connect(function(_, parent)
@@ -42,26 +46,36 @@ function RouteMap:removeByRoute(route)
local hashedRoute = hashRoute(route)
local rbx = self._map[hashedRoute]
if rbx then
self._map[hashedRoute] = nil
self._reverseMap[rbx] = nil
self._connectionsByRbx[rbx] = nil
if rbx ~= nil then
self:_removeInternal(hashedRoute, rbx)
end
end
function RouteMap:removeByRbx(rbx)
local hashedRoute = self._reverseMap[rbx]
if hashedRoute then
self._map[hashedRoute] = nil
self._reverseMap[rbx] = nil
self._connectionsByRbx[rbx] = nil
if hashedRoute ~= nil then
self:_removeInternal(hashedRoute, rbx)
end
end
--[[
Correcly removes the given Roblox Instance/Route pair from the RouteMap.
]]
function RouteMap:_removeInternal(rbx, hashedRoute)
self._map[hashedRoute] = nil
self._reverseMap[rbx] = nil
self._connectionsByRbx[rbx]:Disconnect()
self._connectionsByRbx[rbx] = nil
self:removeRbxDescendants(rbx)
end
function RouteMap:removeRbxDescendants(parentRbx)
--[[
Ensure that there are no descendants of the given Roblox Instance still
present in the map, guaranteeing that it has been cleaned out.
]]
function RouteMap:_removeRbxDescendants(parentRbx)
for rbx in pairs(self._reverseMap) do
if rbx:IsDescendantOf(parentRbx) then
self:removeByRbx(rbx)
@@ -69,7 +83,11 @@ function RouteMap:removeRbxDescendants(parentRbx)
end
end
function RouteMap:clear()
--[[
Remove all items from the map and disconnect all connections, cleaning up
the RouteMap.
]]
function RouteMap:destruct()
self._map = {}
self._reverseMap = {}