mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-04-20 20:55:50 +00:00
273 lines
5.6 KiB
Lua
273 lines
5.6 KiB
Lua
local Promise = require(script.Parent.Parent.modules.Promise)
|
|
|
|
local Config = require(script.Parent.Config)
|
|
local Http = require(script.Parent.Http)
|
|
local Api = require(script.Parent.Api)
|
|
local Reconciler = require(script.Parent.Reconciler)
|
|
|
|
local function collectMatch(source, pattern)
|
|
local result = {}
|
|
|
|
for match in source:gmatch(pattern) do
|
|
table.insert(result, match)
|
|
end
|
|
|
|
return result
|
|
end
|
|
|
|
local Plugin = {}
|
|
Plugin.__index = Plugin
|
|
|
|
function Plugin.new()
|
|
local address = "localhost"
|
|
local port = Config.dev and 8001 or 8000
|
|
|
|
local remote = ("http://%s:%d"):format(address, port)
|
|
|
|
local self = {
|
|
_http = Http.new(remote),
|
|
_reconciler = Reconciler.new(),
|
|
_api = nil,
|
|
_polling = false,
|
|
_syncInProgress = false,
|
|
}
|
|
|
|
setmetatable(self, Plugin)
|
|
|
|
do
|
|
local screenGui = Instance.new("ScreenGui")
|
|
screenGui.Name = "Rojo UI"
|
|
screenGui.Parent = game.CoreGui
|
|
screenGui.DisplayOrder = -1
|
|
screenGui.Enabled = false
|
|
|
|
local label = Instance.new("TextLabel")
|
|
label.Font = Enum.Font.SourceSans
|
|
label.TextSize = 20
|
|
label.Text = "Rojo polling..."
|
|
label.BackgroundColor3 = Color3.fromRGB(31, 31, 31)
|
|
label.BackgroundTransparency = 0.5
|
|
label.BorderSizePixel = 0
|
|
label.TextColor3 = Color3.new(1, 1, 1)
|
|
label.Size = UDim2.new(0, 120, 0, 28)
|
|
label.Position = UDim2.new(0, 0, 0, 0)
|
|
label.Parent = screenGui
|
|
|
|
self._label = screenGui
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--[[
|
|
Clears all state and issues a notice to the user that the plugin has
|
|
restarted.
|
|
]]
|
|
function Plugin:restart()
|
|
warn("Rojo: The server has changed since the last request, reloading plugin...")
|
|
|
|
self._reconciler:destruct()
|
|
self._reconciler = Reconciler.new()
|
|
|
|
self._api = nil
|
|
self._polling = false
|
|
self._syncInProgress = false
|
|
end
|
|
|
|
function Plugin:api()
|
|
if not self._api then
|
|
self._api = Api.connect(self._http)
|
|
:catch(function(err)
|
|
self._api = nil
|
|
return Promise.reject(err)
|
|
end)
|
|
end
|
|
|
|
return self._api
|
|
end
|
|
|
|
function Plugin:connect()
|
|
print("Rojo: Testing connection...")
|
|
|
|
return self:api()
|
|
:andThen(function(api)
|
|
local ok, info = api:getInfo():await()
|
|
|
|
if not ok then
|
|
return Promise.reject(info)
|
|
end
|
|
|
|
print("Rojo: Server found!")
|
|
print("Rojo: Protocol version:", info.protocolVersion)
|
|
print("Rojo: Server version:", info.serverVersion)
|
|
end)
|
|
:catch(function(err)
|
|
if err == Api.Error.ServerIdMismatch then
|
|
self:restart()
|
|
return self:connect()
|
|
else
|
|
return Promise.reject(err)
|
|
end
|
|
end)
|
|
end
|
|
|
|
function Plugin:togglePolling()
|
|
if self._polling then
|
|
return self:stopPolling()
|
|
else
|
|
return self:startPolling()
|
|
end
|
|
end
|
|
|
|
function Plugin:stopPolling()
|
|
if not self._polling then
|
|
return Promise.resolve(false)
|
|
end
|
|
|
|
print("Rojo: Stopped polling server for changes.")
|
|
|
|
self._polling = false
|
|
self._label.Enabled = false
|
|
|
|
return Promise.resolve(true)
|
|
end
|
|
|
|
function Plugin:_pull(api, project, routes)
|
|
return api:read(routes)
|
|
:andThen(function(items)
|
|
for index = 1, #routes do
|
|
local itemRoute = routes[index]
|
|
local partitionName = itemRoute[1]
|
|
local partition = project.partitions[partitionName]
|
|
local item = items[index]
|
|
|
|
local partitionRoute = collectMatch(partition.target, "[^.]+")
|
|
|
|
-- If the item route's length was 1, we need to rename the instance to
|
|
-- line up with the partition's root object name.
|
|
--
|
|
-- This is a HACK!
|
|
if #itemRoute == 1 then
|
|
if item then
|
|
local objectName = partition.target:match("[^.]+$")
|
|
item.Name = objectName
|
|
end
|
|
end
|
|
|
|
local fullRoute = {}
|
|
for _, piece in ipairs(partitionRoute) do
|
|
table.insert(fullRoute, piece)
|
|
end
|
|
|
|
for i = 2, #itemRoute do
|
|
table.insert(fullRoute, itemRoute[i])
|
|
end
|
|
|
|
self._reconciler:reconcileRoute(fullRoute, item, itemRoute)
|
|
end
|
|
end)
|
|
end
|
|
|
|
function Plugin:startPolling()
|
|
if self._polling then
|
|
return
|
|
end
|
|
|
|
print("Rojo: Polling server for changes...")
|
|
|
|
self._polling = true
|
|
self._label.Enabled = true
|
|
|
|
return self:api()
|
|
:andThen(function(api)
|
|
local syncOk, result = self:syncIn():await()
|
|
|
|
if not syncOk then
|
|
return Promise.reject(result)
|
|
end
|
|
|
|
local infoOk, info = api:getInfo():await()
|
|
|
|
if not infoOk then
|
|
return Promise.reject(info)
|
|
end
|
|
|
|
while self._polling do
|
|
local changesOk, changes = api:getChanges():await()
|
|
|
|
if not changesOk then
|
|
return Promise.reject(changes)
|
|
end
|
|
|
|
if #changes > 0 then
|
|
local routes = {}
|
|
|
|
for _, change in ipairs(changes) do
|
|
table.insert(routes, change.route)
|
|
end
|
|
|
|
local pullOk, pullResult = self:_pull(api, info.project, routes):await()
|
|
|
|
if not pullOk then
|
|
return Promise.reject(pullResult)
|
|
end
|
|
end
|
|
|
|
wait(Config.pollingRate)
|
|
end
|
|
end)
|
|
:catch(function(err)
|
|
self:stopPolling()
|
|
|
|
if err == Api.Error.ServerIdMismatch then
|
|
self:restart()
|
|
return self:startPolling()
|
|
else
|
|
return Promise.reject(err)
|
|
end
|
|
end)
|
|
end
|
|
|
|
function Plugin:syncIn()
|
|
if self._syncInProgress then
|
|
warn("Rojo: Can't sync right now, because a sync is already in progress.")
|
|
|
|
return Promise.resolve()
|
|
end
|
|
|
|
self._syncInProgress = true
|
|
print("Rojo: Syncing from server...")
|
|
|
|
return self:api()
|
|
:andThen(function(api)
|
|
local ok, info = api:getInfo():await()
|
|
|
|
if not ok then
|
|
return Promise.reject(info)
|
|
end
|
|
|
|
local routes = {}
|
|
|
|
for name in pairs(info.project.partitions) do
|
|
table.insert(routes, {name})
|
|
end
|
|
|
|
self:_pull(api, info.project, routes)
|
|
|
|
self._syncInProgress = false
|
|
print("Rojo: Sync successful!")
|
|
end)
|
|
:catch(function(err)
|
|
self._syncInProgress = false
|
|
|
|
if err == Api.Error.ServerIdMismatch then
|
|
self:restart()
|
|
return self:syncIn()
|
|
else
|
|
return Promise.reject(err)
|
|
end
|
|
end)
|
|
end
|
|
|
|
return Plugin
|