Files
rojo/plugin/src/ServeSession.lua

128 lines
3.0 KiB
Lua

local t = require(script.Parent.Parent.t)
local strict = require(script.Parent.strict)
local Status = strict("Session.Status", {
NotStarted = "NotStarted",
Connecting = "Connecting",
Connected = "Connected",
Disconnected = "Disconnected",
})
local function DEBUG_printPatch(patch)
local HttpService = game:GetService("HttpService")
for removed in ipairs(patch.removed) do
print("Remove:", removed)
end
for id, added in pairs(patch.added) do
print("Add:", id, HttpService:JSONEncode(added))
end
for updated in ipairs(patch.updated) do
print("Update:", HttpService:JSONEncode(updated))
end
end
local ServeSession = {}
ServeSession.__index = ServeSession
ServeSession.Status = Status
local validateServeOptions = t.strictInterface({
apiContext = t.table,
reconciler = t.table,
})
function ServeSession.new(options)
assert(validateServeOptions(options))
local self = {
__status = Status.NotStarted,
__apiContext = options.apiContext,
__reconciler = options.reconciler,
__statusChangedCallback = nil,
}
setmetatable(self, ServeSession)
return self
end
function ServeSession:onStatusChanged(callback)
self.__statusChangedCallback = callback
end
function ServeSession:start()
self:__setStatus(Status.Connecting)
self.__apiContext:connect()
:andThen(function(serverInfo)
self:__setStatus(Status.Connected)
local rootInstanceId = serverInfo.rootInstanceId
return self.__apiContext:read({ rootInstanceId })
:andThen(function(readResponseBody)
local hydratePatch = self.__reconciler:hydrate(
readResponseBody.instances,
rootInstanceId,
game
)
-- TODO: Prompt user to notify them of this patch, since
-- it's effectively a conflict between the Rojo server and
-- the client.
self.__reconciler:applyPatch(hydratePatch)
-- TODO: Applying a patch may eventually only apply part of
-- the patch and start a content negotiation process with
-- the Rojo server. We should handle that!
local function mainLoop()
return self.__apiContext:retrieveMessages()
:andThen(function(messages)
for _, message in ipairs(messages) do
-- TODO: Update server to return patches in
-- correct format so that we don't have to
-- transform them for the reconciler.
local asPatch = {
removed = message.removedInstances,
updated = message.updatedInstances,
added = message.addedInstances,
}
self.__reconciler:applyPatch(asPatch)
end
if self.__status ~= Status.Disconnected then
return mainLoop()
end
end)
end
return mainLoop()
end)
end)
:catch(function(err)
self:__setStatus(Status.Disconnected, err)
end)
end
function ServeSession:stop()
self:__setStatus(Status.Disconnected)
end
function ServeSession:__setStatus(status, detail)
self.__status = status
if self.__statusChangedCallback ~= nil then
self.__statusChangedCallback(status, detail)
end
end
return ServeSession