Use rbx_dom_weak

This commit is contained in:
Kampfkarren 2019-04-24 19:00:43 -07:00
parent fb5245e2af
commit ca813aede0
10 changed files with 125 additions and 154 deletions

2
.gitignore vendored
View file

@ -1,5 +1,5 @@
/site /site
/target **/target
/scratch-project /scratch-project
**/*.rs.bk **/*.rs.bk
/server/failed-snapshots/ /server/failed-snapshots/

5
.gitmodules vendored
View file

@ -12,4 +12,7 @@
url = https://github.com/LPGhatguy/roblox-lua-promise.git url = https://github.com/LPGhatguy/roblox-lua-promise.git
[submodule "plugin/modules/t"] [submodule "plugin/modules/t"]
path = plugin/modules/t path = plugin/modules/t
url = https://github.com/osyrisrblx/t.git url = https://github.com/osyrisrblx/t.git
[submodule "plugin/modules/rbx-dom"]
path = plugin/modules/rbx-dom
url = https://github.com/LPGhatguy/rbx-dom

View file

@ -8,6 +8,9 @@
"Roact": { "Roact": {
"$path": "modules/roact/lib" "$path": "modules/roact/lib"
}, },
"RbxDomLua": {
"$path": "modules/rbx-dom/rbx_dom_lua/src"
},
"Promise": { "Promise": {
"$path": "modules/promise/lib" "$path": "modules/promise/lib"
}, },

@ -0,0 +1 @@
Subproject commit 42c620612ce03c4026d3836d9f4c0446410929a7

View file

@ -15,6 +15,9 @@
"Roact": { "Roact": {
"$path": "modules/roact/lib" "$path": "modules/roact/lib"
}, },
"RbxDomLua": {
"$path": "modules/rbx-dom/rbx_dom_lua/src"
},
"Promise": { "Promise": {
"$path": "modules/promise/lib" "$path": "modules/promise/lib"
}, },

View file

@ -2,13 +2,14 @@ local t = require(script.Parent.Parent.t)
local InstanceMap = require(script.Parent.InstanceMap) local InstanceMap = require(script.Parent.InstanceMap)
local Logging = require(script.Parent.Logging) local Logging = require(script.Parent.Logging)
local setCanonicalProperty = require(script.Parent.setCanonicalProperty) local RbxDomLua = require(script.Parent.Parent.RbxDomLua)
local rojoValueToRobloxValue = require(script.Parent.rojoValueToRobloxValue)
local Types = require(script.Parent.Types) local Types = require(script.Parent.Types)
local Reconciler = {} local Reconciler = {}
Reconciler.__index = Reconciler Reconciler.__index = Reconciler
local writeInstance = RbxDomLua.CanonicalInstance.writeInstance
function Reconciler.new() function Reconciler.new()
local self = { local self = {
instanceMap = InstanceMap.new(), instanceMap = InstanceMap.new(),
@ -27,6 +28,29 @@ function Reconciler:applyUpdate(requestedIds, virtualInstancesById)
end end
end end
function Reconciler:mapProperties(virtualProperties, virtualInstancesById)
-- Maps properties from the Rojo format to one rbx-dom-lua uses
local properties = {}
for propertyName, property in pairs(virtualProperties) do
if property.Type == "BinaryString" then
local binaryString = ""
for _, characterCode in pairs(property.Value) do
binaryString = binaryString .. string.char(characterCode)
end
properties[propertyName] = binaryString
elseif property.Type == "Ref" then
if property.Value ~= nil then
properties[propertyName] = self.instanceMap.fromIds[property.Value]
end
else
properties[propertyName] = property.Value
end
end
return properties
end
local reconcileSchema = Types.ifEnabled(t.tuple( local reconcileSchema = Types.ifEnabled(t.tuple(
t.map(t.string, Types.VirtualInstance), t.map(t.string, Types.VirtualInstance),
t.string, t.string,
@ -53,11 +77,9 @@ function Reconciler:reconcile(virtualInstancesById, id, instance)
self.instanceMap:insert(id, instance) self.instanceMap:insert(id, instance)
-- Some instances don't like being named, even if their name already matches -- Some instances don't like being named, even if their name already matches
setCanonicalProperty(instance, "Name", virtualInstance.Name) writeInstance(instance, {
Name = virtualInstance.Name,
for key, value in pairs(virtualInstance.Properties) do })
setCanonicalProperty(instance, key, rojoValueToRobloxValue(value))
end
local existingChildren = instance:GetChildren() local existingChildren = instance:GetChildren()
@ -105,6 +127,8 @@ function Reconciler:reconcile(virtualInstancesById, id, instance)
end end
end end
writeInstance(instance, self:mapProperties(virtualInstance.Properties))
-- The root instance of a project won't have a parent, like the DataModel, -- The root instance of a project won't have a parent, like the DataModel,
-- so we need to be careful here. -- so we need to be careful here.
if virtualInstance.Parent ~= nil then if virtualInstance.Parent ~= nil then
@ -117,7 +141,9 @@ function Reconciler:reconcile(virtualInstancesById, id, instance)
-- Some instances, like services, don't like having their Parent -- Some instances, like services, don't like having their Parent
-- property poked, even if we're setting it to the same value. -- property poked, even if we're setting it to the same value.
setCanonicalProperty(instance, "Parent", parent) writeInstance(instance, {
Parent = parent,
})
end end
return instance return instance
@ -143,18 +169,19 @@ function Reconciler:__reify(virtualInstancesById, id, parent)
local virtualInstance = virtualInstancesById[id] local virtualInstance = virtualInstancesById[id]
local instance = Instance.new(virtualInstance.ClassName) local instance = Instance.new(virtualInstance.ClassName)
writeInstance(instance, {
for key, value in pairs(virtualInstance.Properties) do Name = virtualInstance.Name,
setCanonicalProperty(instance, key, rojoValueToRobloxValue(value)) })
end
setCanonicalProperty(instance, "Name", virtualInstance.Name)
for _, childId in ipairs(virtualInstance.Children) do for _, childId in ipairs(virtualInstance.Children) do
self:__reify(virtualInstancesById, childId, instance) self:__reify(virtualInstancesById, childId, instance)
end end
setCanonicalProperty(instance, "Parent", parent) writeInstance(instance, self:mapProperties(virtualInstance.Properties))
writeInstance(instance, {
Parent = parent,
})
self.instanceMap:insert(id, instance) self.instanceMap:insert(id, instance)
return instance return instance

View file

@ -1,3 +1,5 @@
local CollectionService = game:GetService("CollectionService")
local Reconciler = require(script.Parent.Reconciler) local Reconciler = require(script.Parent.Reconciler)
return function() return function()
@ -53,8 +55,8 @@ return function()
Properties = { Properties = {
Value = { Value = {
Type = "Int32", Type = "Int32",
Value = 9 Value = 9,
} },
}, },
}, },
} }
@ -65,6 +67,73 @@ return function()
expect(instance.Value).to.equal(9) expect(instance.Value).to.equal(9)
end) end)
it("should assign properties that can't just be easily set", function()
local instance = Instance.new("Folder")
local binaryString = {}
for _, character in pairs(("Tag1\0Tag2"):split("")) do
table.insert(binaryString, character:byte())
end
local instanceId = "test-id"
local virtualInstancesById = {
[instanceId] = {
Name = "Folder",
ClassName = "Folder",
Children = {},
Properties = {
Tags = {
Type = "BinaryString",
Value = binaryString,
},
},
},
}
local reconciler = Reconciler.new()
reconciler:reconcile(virtualInstancesById, instanceId, instance)
assert(CollectionService:HasTag(instance, "Tag1"))
assert(CollectionService:HasTag(instance, "Tag2"))
end)
it("should assign ref properties", function()
local model = Instance.new("Model")
model.Name = "Parent"
local child = Instance.new("Part")
child.Name = "Child"
child.Parent = model
local childId = "child-id"
local modelId = "model-id"
local virtualInstancesById = {
[modelId] = {
Name = "Parent",
ClassName = "Model",
Children = { childId },
Properties = {
PrimaryPart = {
Type = "Ref",
Value = childId,
},
},
},
[childId] = {
Name = "Child",
ClassName = "Part",
Children = {},
Properties = {},
},
}
local reconciler = Reconciler.new()
reconciler:reconcile(virtualInstancesById, modelId, model)
expect(model.PrimaryPart).to.equal(child)
end)
it("should wipe unknown children by default", function() it("should wipe unknown children by default", function()
local parent = Instance.new("Folder") local parent = Instance.new("Folder")
parent.Name = "Parent" parent.Name = "Parent"

View file

@ -1,38 +0,0 @@
local primitiveTypes = {
Bool = true,
Enum = true,
Float32 = true,
Float64 = true,
Int32 = true,
Int64 = true,
String = true,
}
local directConstructors = {
CFrame = CFrame.new,
Color3 = Color3.new,
Color3uint8 = Color3.fromRGB,
Rect = Rect.new,
UDim = UDim.new,
UDim2 = UDim2.new,
Vector2 = Vector2.new,
Vector2int16 = Vector2int16.new,
Vector3 = Vector3.new,
Vector3int16 = Vector3int16.new,
}
local function rojoValueToRobloxValue(value)
if primitiveTypes[value.Type] then
return value.Value
end
local constructor = directConstructors[value.Type]
if constructor ~= nil then
return constructor(unpack(value.Value))
end
local errorMessage = ("The Rojo plugin doesn't know how to handle values of type %q yet!"):format(tostring(value.Type))
error(errorMessage)
end
return rojoValueToRobloxValue

View file

@ -1,40 +0,0 @@
local rojoValueToRobloxValue = require(script.Parent.rojoValueToRobloxValue)
return function()
it("should convert primitives", function()
local inputString = {
Type = "String",
Value = "Hello, world!",
}
local inputFloat32 = {
Type = "Float32",
Value = 12341.512,
}
expect(rojoValueToRobloxValue(inputString)).to.equal(inputString.Value)
expect(rojoValueToRobloxValue(inputFloat32)).to.equal(inputFloat32.Value)
end)
it("should convert properties with direct constructors", function()
local inputColor3 = {
Type = "Color3",
Value = {0, 1, 0.5},
}
local outputColor3 = Color3.new(0, 1, 0.5)
local inputCFrame = {
Type = "CFrame",
Value = {
1, 2, 3,
4, 5, 6,
7, 8, 9,
10, 11, 12,
},
}
local outputCFrame = CFrame.new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
expect(rojoValueToRobloxValue(inputColor3)).to.equal(outputColor3)
expect(rojoValueToRobloxValue(inputCFrame)).to.equal(outputCFrame)
end)
end

View file

@ -1,57 +0,0 @@
local Logging = require(script.Parent.Logging)
--[[
Attempts to set a property on the given instance.
This method deals in terms of what Rojo calls 'canonical properties', which
don't necessarily exist either in serialization or in Lua-reflected APIs,
but may be present in the API dump.
Ideally, canonical properties map 1:1 with properties we can assign, but in
some cases like LocalizationTable contents and CollectionService tags, we
have to read/write properties a little differently.
]]
local function setCanonicalProperty(instance, key, value)
-- The 'Contents' property of LocalizationTable isn't directly exposed, but
-- has corresponding (deprecated) getters and setters.
if instance.ClassName == "LocalizationTable" and key == "Contents" then
instance:SetContents(value)
return
end
-- Temporary workaround for fixing issue #141 in this specific case.
if instance.ClassName == "Lighting" and key == "Technology" then
return
end
-- If we don't have permissions to access this value at all, we can skip it.
local readSuccess, existingValue = pcall(function()
return instance[key]
end)
if not readSuccess then
-- An error will be thrown if there was a permission issue or if the
-- property doesn't exist. In the latter case, we should tell the user
-- because it's probably their fault.
if existingValue:find("lacking permission") then
Logging.trace("Permission error reading property %s on class %s", tostring(key), instance.ClassName)
return
else
error(("Invalid property %s on class %s: %s"):format(tostring(key), instance.ClassName, existingValue), 2)
end
end
local writeSuccess, err = pcall(function()
if existingValue ~= value then
instance[key] = value
end
end)
if not writeSuccess then
error(("Cannot set property %s on class %s: %s"):format(tostring(key), instance.ClassName, err), 2)
end
return true
end
return setCanonicalProperty