Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: multiplayer undo / redo #7348

Merged
merged 10 commits into from
Apr 17, 2024
Merged

feat: multiplayer undo / redo #7348

merged 10 commits into from
Apr 17, 2024

Conversation

Mrazator
Copy link
Collaborator

@Mrazator Mrazator commented Nov 27, 2023

TLDR; Added support for multiplayer undo/redo, by calculating invertible increments and storing them inside the local-only undo/redo stacks.

Other smaller changes include:

  • Disabled footer buttons, when undo/redo stack is empty or zoom in/out reaches min/max value
  • Added support for undo when collab is started ~ no cleanup and inability to undo
  • Added recording on de-selection, while improving overall undo/redo of selections
  • Fixed bug with colour picker not being recorded
  • Fixed bugs with editing linear element editor not being recorded
  • Fixed bugs with editing group not being recorded
  • Set potential stepping stones for

For more details check the high-level description of the introduced and modified components below.

image

Scene: https://link.excalidraw.com/readonly/jaraG0I63Qj6pPxqHVGn

History (modified)

  • Saves emitted changes by the store into the local-only undo and redo stacks.
  • Iterates history stack on undo/redo, when the recorded changes have no visible impact - i.e. when elements/appState changes relate to deleted elements or a remote client modified it in the meantime to the same resulting state-specific cases covered in here.
  • Updates redo entry with the latest conflicting properties when applying undo entry (and vice versa), for details check this.

Store (introduced)

  • Represents a component which abstracts away management of incremental changes.
  • Captures all committed actions previously recorded by the history into an internal always up-to-date snapshot.
    • Remote actions filter out yet uncommitted elements (as a result of local async action, such as dragging, editing, resizing etc.) and only update the snapshot, without calculating the increment.
  • Calculates and emits the store increment as a set of changes based on the previously captured snapshot.
  • Exposes event listener for other components interested in the store increments - for now only history is tied to it, but in the future, we could also tie other components, such as collab, storage or versioning.

Change (introduced)

  • Represents a point in the state history as a result of a user action (event).
  • Encapsulates a payload in the form of delta/s and contains methods for calculating/applying/inverting the change.
  • Contains additional logic which is specific to each change implementation, i.e. conflict resolution logic for ElementsChange, which mostly relates to the text container bindings in the multiplayer scenarios (specific cases covered in here.
  • For now, two specific implementations were introduced, AppStateChange (purely local) and ElementsChange (local, but could conflict with remote changes).
    • Potentially, each conflicting change could hold additional metadata, such as id, type, source, and logical timestamp, which would extend it with CRDT-like characteristics.

Delta (introduced)

  • Represents a delta between two objects on the properties level in the form of a pure object (side-effects free).
  • Encapsulates deleted and inserted partials, where deleted is a set of all the previous (removed) values and inserted is a set of all the next (added, updated) values.
  • Can be stored inside the history stacks, inversed when applying undo/redo or used for time travel purposes.
  • Inverse operation simply replaces deleted with inserted and vice versa - as visible here.
  • To sneak peek the Delta format take a look at the following gist.

Copy link

vercel bot commented Nov 27, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
excalidraw ✅ Ready (Inspect) Visit Preview Apr 16, 2024 10:13pm
excalidraw-package-example ✅ Ready (Inspect) Visit Preview Apr 16, 2024 10:13pm
excalidraw-package-example-with-nextjs ✅ Ready (Inspect) Visit Preview Apr 16, 2024 10:13pm
1 Ignored Deployment
Name Status Preview Updated (UTC)
docs ⬜️ Ignored (Inspect) Visit Preview Apr 16, 2024 10:13pm

@Mrazator Mrazator changed the title Delta base undo redo Delta based undo redo Nov 27, 2023
Copy link
Member

@dwelle dwelle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants