karaoke-dev / karaoke

Will be the best karaoke system.
http://blog.karaoke.dev
GNU General Public License v3.0
198 stars 15 forks source link

Fix split text cause the exception. #1733

Open andy840119 opened 1 year ago

andy840119 commented 1 year ago

Here's the callstack:

System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
   at System.Collections.Generic.List`1.Enumerator.MoveNext()
   at osu.Framework.Extensions.IEnumerableExtensions.EnumerableExtensions.ForEach[T](IEnumerable`1 collection, Action`1 action)
   at osu.Game.Rulesets.Karaoke.Edit.ChangeHandlers.HitObjectChangeHandler`1.PerformOnSelection[T](Action`1 action) in D:\Github\karaoke-dev\karaoke\osu.Game.Rulesets.Karaoke\Edit\ChangeHandlers\HitObjectChangeHandler.cs:line 71
   at osu.Game.Rulesets.Karaoke.Edit.ChangeHandlers.HitObjectChangeHandler`1.PerformOnSelection(Action`1 action) in D:\Github\karaoke-dev\karaoke\osu.Game.Rulesets.Karaoke\Edit\ChangeHandlers\HitObjectChangeHandler.cs:line 43
   at osu.Game.Rulesets.Karaoke.Edit.ChangeHandlers.Lyrics.LyricsChangeHandler.Split(Int32 index) in D:\Github\karaoke-dev\karaoke\osu.Game.Rulesets.Karaoke\Edit\ChangeHandlers\Lyrics\LyricsChangeHandler.cs:line 19
   at osu.Game.Rulesets.Karaoke.Edit.Lyrics.Components.Lyrics.EditableLyric.OnDoubleClick(DoubleClickEvent e) in D:\Github\karaoke-dev\karaoke\osu.Game.Rulesets.Karaoke\Edit\Lyrics\Components\Lyrics\EditableLyric.cs:line 56
   at osu.Framework.Graphics.Drawable.TriggerEvent(UIEvent e)
   at osu.Framework.Input.ButtonEventManager`1.<>c__DisplayClass14_0.<PropagateButtonEvent>b__0(Drawable target)
   at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Func`2 predicate, Boolean& found)
   at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
   at osu.Framework.Input.ButtonEventManager`1.PropagateButtonEvent(IEnumerable`1 drawables, UIEvent e)
   at osu.Framework.Input.MouseButtonEventManager.handleDoubleClick(InputState state, List`1 targets)
   at osu.Framework.Input.MouseButtonEventManager.HandleButtonDown(InputState state, List`1 targets)
   at osu.Framework.Input.ButtonEventManager`1.handleButtonDown(InputState state)
   at osu.Framework.Input.ButtonEventManager`1.HandleButtonStateChange(InputState state, ButtonStateChangeKind kind)
   at osu.Framework.Input.InputManager.HandleMouseButtonStateChange(ButtonStateChangeEvent`1 e)
   at osu.Framework.Input.InputManager.HandleInputStateChange(InputStateChangeEvent inputStateChange)
   at osu.Framework.Input.StateChanges.ButtonInput`1.Apply(InputState state, IInputStateChangeHandler handler)
   at osu.Framework.Input.StateChanges.MouseButtonInput.Apply(InputState state, IInputStateChangeHandler handler)
   at osu.Framework.Input.PassThroughInputManager.Handle(UIEvent e)
   at osu.Framework.Graphics.Drawable.OnMouseDown(MouseDownEvent e)
   at osu.Framework.Graphics.Drawable.TriggerEvent(UIEvent e)
   at osu.Framework.Input.ButtonEventManager`1.<>c__DisplayClass14_0.<PropagateButtonEvent>b__0(Drawable target)
   at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Func`2 predicate, Boolean& found)
   at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
   at osu.Framework.Input.ButtonEventManager`1.PropagateButtonEvent(IEnumerable`1 drawables, UIEvent e)
   at osu.Framework.Input.MouseButtonEventManager.HandleButtonDown(InputState state, List`1 targets)
   at osu.Framework.Input.ButtonEventManager`1.handleButtonDown(InputState state)
   at osu.Framework.Input.ButtonEventManager`1.HandleButtonStateChange(InputState state, ButtonStateChangeKind kind)
   at osu.Framework.Input.InputManager.HandleMouseButtonStateChange(ButtonStateChangeEvent`1 e)
   at osu.Framework.Input.InputManager.HandleInputStateChange(InputStateChangeEvent inputStateChange)
   at osu.Framework.Input.UserInputManager.HandleInputStateChange(InputStateChangeEvent inputStateChange)
   at osu.Framework.Input.StateChanges.ButtonInput`1.Apply(InputState state, IInputStateChangeHandler handler)
   at osu.Framework.Input.StateChanges.MouseButtonInput.Apply(InputState state, IInputStateChangeHandler handler)
   at osu.Framework.Input.InputManager.Update()
   at osu.Framework.Input.PassThroughInputManager.Update()
   at osu.Framework.Graphics.Drawable.UpdateSubTree()
   at osu.Framework.Graphics.Containers.CompositeDrawable.UpdateSubTree()
   at osu.Framework.Platform.GameHost.UpdateFrame()
   at osu.Framework.Threading.GameThread.processFrame()
andy840119 commented 1 year ago

Just need to add the ToArray() method to the PerformOnSelection in the HitObjectChangeHandler<T> can fix this problem.

But need to think more about it.

if (changeHandler is TransactionalCommitComponent transactionalCommitComponent && !transactionalCommitComponent.TransactionActive)
{
    // not really sure how to fix that.
    beatmap.PerformOnSelection(h =>
    {
        if (h is T tHitObject)
            action(tHitObject);
    });
}
else
{
    // easy to fix.
    beatmap.SelectedHitObjects.ToArray().ForEach(h =>
    {
        if (h is T tHitObject)
            action(tHitObject);
    });
}