From 4cb49c78259d3009d150dffef69fefbfedd0f55c Mon Sep 17 00:00:00 2001 From: boatbomber Date: Mon, 8 Aug 2022 01:08:55 -0700 Subject: [PATCH] Add sync locking for Team Create (#590) * Add sync locking * Steal lock from users who left without releasing * Do not remove lock as unknown instance * Don't delete non Archivable instance --- plugin/src/App/init.lua | 62 ++++++++++++++++++++++++++++++++++ plugin/src/Reconciler/diff.lua | 6 ++++ 2 files changed, 68 insertions(+) diff --git a/plugin/src/App/init.lua b/plugin/src/App/init.lua index 5de9ffdd..d239c52c 100644 --- a/plugin/src/App/init.lua +++ b/plugin/src/App/init.lua @@ -1,3 +1,6 @@ +local Players = game:GetService("Players") +local ServerStorage = game:GetService("ServerStorage") + local Rojo = script:FindFirstAncestor("Rojo") local Plugin = Rojo.Plugin local Packages = Rojo.Packages @@ -92,7 +95,65 @@ function App:getHostAndPort() return host, port end +function App:claimSyncLock() + if #Players:GetPlayers() == 0 then + Log.trace("Skipping sync lock because this isn't in Team Create") + return true + end + + local lock = ServerStorage:FindFirstChild("__Rojo_SessionLock") + if not lock then + lock = Instance.new("ObjectValue") + lock.Name = "__Rojo_SessionLock" + lock.Archivable = false + lock.Value = Players.LocalPlayer + lock.Parent = ServerStorage + Log.trace("Created and claimed sync lock") + return true + end + + if lock.Value and lock.Value ~= Players.LocalPlayer and lock.Value.Parent then + Log.trace("Found existing sync lock owned by {}", lock.Value) + return false, lock.Value + end + + lock.Value = Players.LocalPlayer + Log.trace("Claimed existing sync lock") + return true +end + +function App:releaseSyncLock() + local lock = ServerStorage:FindFirstChild("__Rojo_SessionLock") + if not lock then + Log.trace("No sync lock found, assumed released") + return + end + + if lock.Value == Players.LocalPlayer then + lock.Value = nil + Log.trace("Released sync lock") + return + end + + Log.trace("Could not relase sync lock because it is owned by {}", lock.Value) +end + function App:startSession() + local claimedLock, priorOwner = self:claimSyncLock() + if not claimedLock then + local msg = string.format("Could not sync because user '%s' is already syncing", tostring(priorOwner)) + + Log.warn(msg) + self:addNotification(msg, 10) + self:setState({ + appStatus = AppStatus.Error, + errorMessage = msg, + toolbarIcon = Assets.Images.PluginButtonWarning, + }) + + return + end + local host, port = self:getHostAndPort() local sessionOptions = { @@ -155,6 +216,7 @@ function App:startSession() self:addNotification(string.format("Connected to session '%s' at %s.", details, address), 5) elseif status == ServeSession.Status.Disconnected then self.serveSession = nil + self:releaseSyncLock() -- Details being present indicates that this -- disconnection was from an error. diff --git a/plugin/src/Reconciler/diff.lua b/plugin/src/Reconciler/diff.lua index 995d06d8..97819e32 100644 --- a/plugin/src/Reconciler/diff.lua +++ b/plugin/src/Reconciler/diff.lua @@ -115,6 +115,12 @@ local function diff(instanceMap, virtualInstances, rootId) local childId = instanceMap.fromInstances[childInstance] if childId == nil then + if childInstance.Archivable == false then + -- We don't remove instances that aren't going to be saved anyway, + -- such as the Rojo session lock value. + continue + end + -- This is an existing instance not present in the virtual DOM. -- We can mark it for deletion unless the user has asked us not -- to delete unknown stuff.