Skip to content

Commit 9636be7

Browse files
committed
Make "Always" confirmation behavior setting work by handling it for ongoing sync patches
- Add "Always" option to confirmationBehavior setting - Route WebSocket patches through the confirmation callback - Fix UI not transitioning back to Connected after confirming ongoing patches - Fix DomLabel Flipper motor cleanup on unmount to prevent errors
1 parent 4965165 commit 9636be7

File tree

4 files changed

+77
-28
lines changed

4 files changed

+77
-28
lines changed

plugin/src/App/Components/PatchVisualizer/DomLabel.lua

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ function DomLabel:init()
7272
end)
7373
end
7474

75+
function DomLabel:willUnmount()
76+
-- Stop the motor to prevent onStep callbacks after unmount
77+
self.motor:stop()
78+
end
79+
7580
function DomLabel:didUpdate(prevProps)
7681
if
7782
prevProps.instance ~= self.props.instance

plugin/src/App/init.lua

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -751,36 +751,38 @@ function App:startSession()
751751
end
752752

753753
local confirmationBehavior = Settings:get("confirmationBehavior")
754-
if confirmationBehavior == "Initial" then
755-
-- Only confirm if we haven't synced this project yet this session
756-
if self.knownProjects[serverInfo.projectName] then
757-
Log.trace(
758-
"Accepting patch without confirmation because project has already been connected and behavior is set to Initial"
759-
)
760-
return "Accept"
761-
end
762-
elseif confirmationBehavior == "Large Changes" then
763-
-- Only confirm if the patch impacts many instances
764-
if PatchSet.countInstances(patch) < Settings:get("largeChangesConfirmationThreshold") then
765-
Log.trace(
766-
"Accepting patch without confirmation because patch is small and behavior is set to Large Changes"
767-
)
768-
return "Accept"
769-
end
770-
elseif confirmationBehavior == "Unlisted PlaceId" then
771-
-- Only confirm if the current placeId is not in the servePlaceIds allowlist
772-
if serverInfo.expectedPlaceIds then
773-
local isListed = table.find(serverInfo.expectedPlaceIds, game.PlaceId) ~= nil
774-
if isListed then
754+
if confirmationBehavior ~= "Always" then
755+
if confirmationBehavior == "Initial" then
756+
-- Only confirm if we haven't synced this project yet this session
757+
if self.knownProjects[serverInfo.projectName] then
775758
Log.trace(
776-
"Accepting patch without confirmation because placeId is listed and behavior is set to Unlisted PlaceId"
759+
"Accepting patch without confirmation because project has already been connected and behavior is set to Initial"
777760
)
778761
return "Accept"
779762
end
763+
elseif confirmationBehavior == "Large Changes" then
764+
-- Only confirm if the patch impacts many instances
765+
if PatchSet.countInstances(patch) < Settings:get("largeChangesConfirmationThreshold") then
766+
Log.trace(
767+
"Accepting patch without confirmation because patch is small and behavior is set to Large Changes"
768+
)
769+
return "Accept"
770+
end
771+
elseif confirmationBehavior == "Unlisted PlaceId" then
772+
-- Only confirm if the current placeId is not in the servePlaceIds allowlist
773+
if serverInfo.expectedPlaceIds then
774+
local isListed = table.find(serverInfo.expectedPlaceIds, game.PlaceId) ~= nil
775+
if isListed then
776+
Log.trace(
777+
"Accepting patch without confirmation because placeId is listed and behavior is set to Unlisted PlaceId"
778+
)
779+
return "Accept"
780+
end
781+
end
782+
elseif confirmationBehavior == "Never" then
783+
Log.trace("Accepting patch without confirmation because behavior is set to Never")
784+
return "Accept"
780785
end
781-
elseif confirmationBehavior == "Never" then
782-
Log.trace("Accepting patch without confirmation because behavior is set to Never")
783-
return "Accept"
784786
end
785787

786788
-- The datamodel name gets overwritten by Studio, making confirmation of it intrusive
@@ -821,7 +823,22 @@ function App:startSession()
821823
timeout = 7,
822824
})
823825

824-
return self.confirmationEvent:Wait()
826+
local result = self.confirmationEvent:Wait()
827+
828+
-- Reset UI state back to Connected after confirmation
829+
-- This is needed for ongoing WebSocket patches where the session
830+
-- is already connected and won't trigger a status change
831+
if self.serveSession and self.serveSession:getStatus() == ServeSession.Status.Connected then
832+
self:setState({
833+
appStatus = AppStatus.Connected,
834+
toolbarIcon = Assets.Images.PluginButtonConnected,
835+
-- Clear patchTree to avoid animation issues when the
836+
-- PatchVisualizer unmounts while Flipper motors are running
837+
patchTree = nil,
838+
})
839+
end
840+
841+
return result
825842
end)
826843

827844
serveSession:start()

plugin/src/ServeSession.lua

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ function ServeSession.new(options)
104104
__instanceMap = instanceMap,
105105
__changeBatcher = changeBatcher,
106106
__statusChangedCallback = nil,
107+
__userConfirmCallback = nil,
108+
__serverInfo = nil,
107109
__connections = connections,
108110
__precommitCallbacks = {},
109111
__postcommitCallbacks = {},
@@ -196,6 +198,7 @@ function ServeSession:start()
196198
:connect()
197199
:andThen(function(serverInfo)
198200
self:setLoadingText("Loading initial data from server...")
201+
self.__serverInfo = serverInfo
199202
return self:__initialSync(serverInfo):andThen(function()
200203
self:setLoadingText("Starting sync loop...")
201204
self:__setStatus(Status.Connected, serverInfo.projectName)
@@ -209,9 +212,33 @@ function ServeSession:start()
209212

210213
Log.debug("Received {} messages from Rojo server", #messagesPacket.messages)
211214

215+
-- Combine all messages into a single patch
216+
local combinedPatch = PatchSet.newEmpty()
212217
for _, message in messagesPacket.messages do
213-
self:__applyPatch(message)
218+
PatchSet.assign(combinedPatch, message)
214219
end
220+
221+
-- Spawn a new thread to handle potentially yielding confirmation
222+
task.spawn(function()
223+
Log.trace("Processing WebSocket patch, callback exists: {}", self.__userConfirmCallback ~= nil)
224+
Log.trace("ServerInfo exists: {}", self.__serverInfo ~= nil)
225+
226+
local userDecision = "Accept"
227+
if self.__userConfirmCallback ~= nil then
228+
userDecision = self.__userConfirmCallback(self.__instanceMap, combinedPatch, self.__serverInfo)
229+
end
230+
231+
Log.trace("WebSocket patch decision: {}", userDecision)
232+
233+
if userDecision == "Abort" then
234+
self:__stopInternal("Aborted Rojo sync operation")
235+
return
236+
elseif userDecision == "Accept" then
237+
self:__applyPatch(combinedPatch)
238+
end
239+
-- Note: "Reject" is not handled for ongoing patches, only for initial sync
240+
end)
241+
215242
self.__apiContext:setMessageCursor(messagesPacket.messageCursor)
216243
end,
217244
})

plugin/src/Settings.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ local defaultSettings = {
2020
checkForUpdates = true,
2121
checkForPrereleases = false,
2222
autoConnectPlaytestServer = false,
23-
confirmationBehavior = "Initial" :: "Never" | "Initial" | "Large Changes" | "Unlisted PlaceId",
23+
confirmationBehavior = "Initial" :: "Never" | "Initial" | "Always" | "Large Changes" | "Unlisted PlaceId",
2424
largeChangesConfirmationThreshold = 5,
2525
playSounds = true,
2626
typecheckingEnabled = false,

0 commit comments

Comments
 (0)