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() function Plugin:restart()
warn("Rojo: The server has changed since the last request, reloading plugin...") 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._api = nil
self._polling = false self._polling = false
self._syncInProgress = false self._syncInProgress = false

View File

@@ -1,13 +1,26 @@
local RouteMap = require(script.Parent.RouteMap) local RouteMap = require(script.Parent.RouteMap)
local function classEqual(rbx, className) local function classEqual(a, b)
if className == "*" then if a == "*" or b == "*" then
return true return true
end end
return rbx.ClassName == className return a == b
end 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) local function reparent(rbx, parent)
if rbx then if rbx then
if rbx.Parent == parent then if rbx.Parent == parent then
@@ -42,7 +55,7 @@ local function findNextChildPair(primaryChildren, secondaryChildren, visited)
visited[primaryChild] = true visited[primaryChild] = true
for _, secondaryChild in ipairs(secondaryChildren) do 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 visited[secondaryChild] = true
return primaryChild, secondaryChild return primaryChild, secondaryChild
@@ -114,12 +127,11 @@ function Reconciler:_reify(item)
local rbx = Instance.new(className) local rbx = Instance.new(className)
rbx.Name = item.Name rbx.Name = item.Name
for key, property in pairs(item.Properties) do applyProperties(rbx, item.Properties)
-- TODO: Check for compound types, like Vector3!
rbx[key] = property.Value
end
self:_reconcileChildren(rbx, item) for _, child in ipairs(item.Children) do
reparent(self:_reify(child), rbx)
end
if item.Route then if item.Route then
self._routeMap:insert(item.Route, rbx) self._routeMap:insert(item.Route, rbx)
@@ -129,10 +141,10 @@ function Reconciler:_reify(item)
end end
--[[ --[[
Clears any state that the Reconciler has, effectively restarting it. Clears any state that the Reconciler has, stopping it completely.
]] ]]
function Reconciler:clear() function Reconciler:destruct()
self._routeMap:clear() self._routeMap:destruct()
end end
--[[ --[[
@@ -156,28 +168,16 @@ function Reconciler:reconcile(rbx, item)
end end
-- Item changed type! -- Item changed type!
if not classEqual(rbx, item.ClassName) then if not classEqual(rbx.ClassName, item.ClassName) then
self._routeMap:removeByRbx(rbx) self._routeMap:removeByRbx(rbx)
rbx:Destroy() rbx:Destroy()
return self:_reify(item) return self:_reify(item)
end end
-- Apply all properties, Roblox will de-duplicate changes applyProperties(rbx, item.Properties)
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
self:_reconcileChildren(rbx, item) self:_reconcileChildren(rbx, item)
if item.Route then
self._routeMap:insert(item.Route, rbx)
end
return rbx return rbx
end end

View File

@@ -25,6 +25,10 @@ end
function RouteMap:insert(route, rbx) function RouteMap:insert(route, rbx)
local hashed = hashRoute(route) 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._map[hashed] = rbx
self._reverseMap[rbx] = hashed self._reverseMap[rbx] = hashed
self._connectionsByRbx[rbx] = rbx.AncestryChanged:Connect(function(_, parent) self._connectionsByRbx[rbx] = rbx.AncestryChanged:Connect(function(_, parent)
@@ -42,26 +46,36 @@ function RouteMap:removeByRoute(route)
local hashedRoute = hashRoute(route) local hashedRoute = hashRoute(route)
local rbx = self._map[hashedRoute] local rbx = self._map[hashedRoute]
if rbx then if rbx ~= nil then
self._map[hashedRoute] = nil self:_removeInternal(hashedRoute, rbx)
self._reverseMap[rbx] = nil
self._connectionsByRbx[rbx] = nil
end end
end end
function RouteMap:removeByRbx(rbx) function RouteMap:removeByRbx(rbx)
local hashedRoute = self._reverseMap[rbx] local hashedRoute = self._reverseMap[rbx]
if hashedRoute then if hashedRoute ~= nil then
self._map[hashedRoute] = nil self:_removeInternal(hashedRoute, rbx)
self._reverseMap[rbx] = nil
self._connectionsByRbx[rbx] = nil
end 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) self:removeRbxDescendants(rbx)
end 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 for rbx in pairs(self._reverseMap) do
if rbx:IsDescendantOf(parentRbx) then if rbx:IsDescendantOf(parentRbx) then
self:removeByRbx(rbx) self:removeByRbx(rbx)
@@ -69,7 +83,11 @@ function RouteMap:removeRbxDescendants(parentRbx)
end end
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._map = {}
self._reverseMap = {} self._reverseMap = {}