Command Bar Undo Does An Undo Twice

If you run a command with command bar after making another change (ex: moving a part 10 studs to the left) and pressing control Z, it undoes the command and also moves the part back from 10 studs to the left.

Expected behavior

The running of the command should save as another point in the game, so undoing after a command should only undo the command.

4 Likes

This is an unfortunate part of how Roblox tracks changes to the data model. It’d be impossible for Roblox to know how to track edits in a way that doesn’t interfere with what you’re doing.

In order for your edits to be properly logged to the undo stack, take a look at ChangeHistoryService | Documentation - Roblox Creator Hub. Slapping this at the end of most command bar scripts will suffice:

game:GetService("ChangeHistoryService"):SetWaypoint("Did something")
Examples as to why this isn't particularly a bug

Let’s pretend Roblox created an undo waypoint every time a new instance was parented.
This code is now impossible to undo:

for i = 1, 1000 do
    local part = Instance.new("Part")
    -- Pretend this does something important with the parts
    part.Parent = workspace
end

“Okay, create the waypoint after the script is done running.”
Then this code will not undo properly, creating a waypoint before it’s actually done editing things:

workspace:SetAttribute('SomeProperty', false)

-- Spawn a thread that will finish after main thread
task.delay(5, function()
    workspace:SetAttribute('SomeProperty', true)
end)

“Okay, create the waypoint after EVERY thread is done running.”
Then this code would never create a waypoint at all:

-- Create a monitoring thread that never ends
-- This could also be something like an event connection
-- that listens to RenderStepped or property changes
task.spawn(function()
    while true do
        print("Still monitoring...")
        task.wait(1)
    end
end)

-- Create 100 parts that should be undoable as a single operation
for i = 1, 100 do
    local part = Instance.new("Part")
    part.Position = Vector3.new(i, 0, 0)
    part.Parent = workspace
end

These parts end up being impossible to undo, and get batched with some other edit, and we’re back to square one.


Alternatively, I’d suggest opening a feature request for an automatic waypoint system, with some examples of how it’d make your life easier.

Does Roblox just track actions instead of saving the data model every edit? Because if they saved the data model then they could just save a version before each command is ran and then restore that. (If this is heavy on performance then they could do it only for commands)

But I am assuming that they do save the data model, just not when a command is ran, since commands are able to be undone, just only in “batches”