godot: UndoRedo doesn't undo in the reverse order
Godot version
3.4.4.stable
System information
Windows 10, GLES3
Issue description
When using UndoRedo
to record and commit actions they are executed in the order that they are added (good, correct) but they are not undone in the reverse order (problematic).
This is a problem if you persist an UndoRedo
while you accumulate actions as part of a single gesture (stroking a line). If the line self-intersects the undo corrupts the data and does not restore it to the state that it was in previously.
UndoRedo.undo()
should execute everything in reverse because it’s not always possible to guarantee the ordering of the code that adds methods/data to be undone.
Steps to reproduce
tool
extends EditorPlugin
func _echo(what:String, n:int) -> void:
prints(what, n)
func somewhere() -> void:
var test := get_undo_redo()
test.create_action("test")
for n in 3:
test.add_do_method(self, "_echo", "do", n)
test.add_undo_method(self, "_echo", "undo", n)
test.commit_action()
test.undo()
do 0
do 1
do 2
test
undo 0
undo 1
undo 2
Minimal reproduction project
No response
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Comments: 17 (17 by maintainers)
Merging only works for actions that have the same name. If your
3 2 1
and5 4
actions are different then they should have different name and never be merged.UndoRedo was designed specifically for editor, not for games. If you have a complex use-case where you have multiple different actions merged together and their order matters and all, it’s just out-of-scope for this class.
The main use of merging is actually in the editor. Often when changing something in the editor you might see message spam about setting some property (e.g. when using ColorPicker). These 30+ actions are merged into 1, otherwise it would make undo unusable.
This is issue was recently brought up on chat. Reverse order could be an optional argument of
create_action()
method.IMHO, the way you structure your code assumes undo methods will be called in reverse order.
The
add_*()
methods just add the command to the corresponding action.It’s strange to write something like: