mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-04-20 20:55:50 +00:00
Add plugin half of script-only, existing-instance-only, two way sync
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user