Add plugin half of script-only, existing-instance-only, two way sync

This commit is contained in:
Lucien Greathouse
2019-12-18 17:39:04 -08:00
parent e83437c193
commit cfff08cdfd
4 changed files with 95 additions and 3 deletions

View File

@@ -1,4 +1,5 @@
local Http = require(script.Parent.Parent.Http)
local Log = require(script.Parent.Parent.Log)
local Promise = require(script.Parent.Parent.Promise)
local Config = require(script.Parent.Config)
@@ -153,6 +154,23 @@ function ApiContext:read(ids)
end)
end
function ApiContext:write(patch)
local url = ("%s/write"):format(self.__baseUrl)
local body = Http.jsonEncode({
sessionId = self.__sessionId,
patch = patch,
})
return Http.post(url, body)
:andThen(rejectFailedRequests)
:andThen(Http.Response.json)
:andThen(function(body)
Log.info("Write response: {:?}", body)
return body
end)
end
function ApiContext:retrieveMessages()
local url = ("%s/api/subscribe/%s"):format(self.__baseUrl, self.__messageCursor)

View File

@@ -25,6 +25,14 @@ local VALUES = {
[Environment.Test] = true,
},
},
ExperimentalTwoWaySync = {
type = "BoolValue",
values = {
[Environment.User] = false,
[Environment.Dev] = false,
[Environment.Test] = false,
},
},
}
local CONTAINER_NAME = "RojoDevSettings" .. Config.codename
@@ -132,6 +140,10 @@ function DevSettings:shouldTypecheck()
return getValue("TypecheckingEnabled")
end
function DevSettings:twoWaySyncEnabled()
return getValue("ExperimentalTwoWaySync")
end
function _G.ROJO_DEV_CREATE()
DevSettings:createDevSettings()
end

View File

@@ -176,6 +176,23 @@ function Reconciler:applyPatch(patch)
end
end
--[[
Transforms a value into one that can be sent over the network back to the
Rojo server.
This operation can fail, and so it returns bool, value.
]]
function Reconciler:encodeApiValue(value)
if typeof(value) == "string" then
return true, {
Type = "String",
Value = value,
}
end
return false
end
--[[
Transforms a value encoded by rbx_dom_weak on the server side into a value
usable by Rojo's reconciler, potentially using RbxDom.
@@ -302,7 +319,6 @@ function Reconciler:__hydrateInternal(apiInstances, id, instance, hydratePatch)
local decodedValue = self:__decodeApiValue(virtualValue)
if existingValue ~= decodedValue then
Log.warn("Diff! {:?} vs {:?}", existingValue, decodedValue)
changedProperties[propertyName] = virtualValue
end
end

View File

@@ -2,6 +2,7 @@ local Log = require(script.Parent.Parent.Log)
local Fmt = require(script.Parent.Parent.Fmt)
local t = require(script.Parent.Parent.t)
local DevSettings = require(script.Parent.DevSettings)
local InstanceMap = require(script.Parent.InstanceMap)
local Reconciler = require(script.Parent.Reconciler)
local strict = require(script.Parent.strict)
@@ -47,10 +48,16 @@ local validateServeOptions = t.strictInterface({
function ServeSession.new(options)
assert(validateServeOptions(options))
local instanceMap = InstanceMap.new()
-- Declare self ahead of time to capture it in a closure
local self
local function onInstanceChanged(instance, propertyName)
self:__onInstanceChanged(instance, propertyName)
end
local instanceMap = InstanceMap.new(onInstanceChanged)
local reconciler = Reconciler.new(instanceMap)
local self = {
self = {
__status = Status.NotStarted,
__apiContext = options.apiContext,
__reconciler = reconciler,
@@ -101,6 +108,45 @@ function ServeSession:stop()
self:__stopInternal()
end
function ServeSession:__onInstanceChanged(instance, propertyName)
if not DevSettings:twoWaySyncEnabled() then
return
end
local instanceId = self.__instanceMap.fromInstances[instance]
if instanceId == nil then
Log.warn("Ignoring change for instance {:?} as it is unknown to Rojo", instance)
return
end
local update = {
id = instanceId,
changedProperties = {},
}
if propertyName == "Name" then
update.changedName = instance.Name
else
local success, encoded = self.__reconciler:encodeApiValue(instance[propertyName])
if not success then
Log.warn("Could not sync back property {:?}.{}", instance, propertyName)
return
end
update.changedProperties[propertyName] = encoded
end
local patch = {
removed = {},
added = {},
updated = {update},
}
self.__apiContext:write(patch)
end
function ServeSession:__initialSync(rootInstanceId)
return self.__apiContext:read({ rootInstanceId })
:andThen(function(readResponseBody)