Skip to content

Commit 533a806

Browse files
authored
fix: Ban/Mute ObjectDisposedException and collection modification crashes in UI disposal (#2787)
- Use DelayedDelete() in BanMuteBox event handlers instead of direct Dispose() - Canvas: Fix ProcessDelayedDeletes() to iterate over a copy of the disposal queue, preventing "Collection was modified" exceptions when nested DelayedDelete() calls occur during disposal - Add error handling to catch and log disposal exceptions instead of crashing the entire client - User Ban working and tested
1 parent 7e68596 commit 533a806

File tree

2 files changed

+30
-9
lines changed

2 files changed

+30
-9
lines changed

Intersect.Client.Core/Interface/Game/Admin/BanMuteBox.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,14 @@ public BanMuteBox(string title, string prompt, EventHandler okayHandler) : base(
103103
buttonOkay.Clicked += (s, e) =>
104104
{
105105
okayHandler?.Invoke(this, EventArgs.Empty);
106-
Dispose();
106+
DelayedDelete();
107107
};
108108

109109
var buttonCancel = new Button(this, "ButtonCancel")
110110
{
111111
Text = Strings.BanMute.Cancel,
112112
};
113-
buttonCancel.Clicked += (s, e) => Dispose();
113+
buttonCancel.Clicked += (s, e) => DelayedDelete();
114114

115115
LoadJsonUi(UI.InGame, Graphics.Renderer?.GetResolutionString(), true);
116116

Intersect.Client.Framework/Gwen/Control/Canvas.cs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -262,21 +262,42 @@ public void AddDelayedDelete(Base control)
262262

263263
private void ProcessDelayedDeletes()
264264
{
265+
List<IDisposable> controlsToDispose;
266+
265267
lock (_disposeQueue)
266268
{
267-
foreach (var control in _disposeQueue)
269+
if (_disposeQueue.Count == 0)
268270
{
271+
return;
272+
}
273+
274+
controlsToDispose = new List<IDisposable>(_disposeQueue);
275+
_disposeQueue.Clear();
276+
269277
#if DEBUG
270-
if (control is Base node)
271-
{
272-
_delayedDeleteStackTraces.Remove(node);
273-
}
278+
// Remove all the stuff from debug tracking in one go
279+
_delayedDeleteStackTraces.Clear();
274280
#endif
281+
}
275282

283+
// Dispose outside the lock
284+
foreach (var control in controlsToDispose)
285+
{
286+
try
287+
{
276288
control.Dispose();
277289
}
278-
279-
_disposeQueue.Clear();
290+
catch (ObjectDisposedException)
291+
{
292+
// Already disposed, ignore
293+
}
294+
catch (Exception ex)
295+
{
296+
ApplicationContext.Context.Value?.Logger.LogError(
297+
ex,
298+
"Error disposing control during ProcessDelayedDeletes"
299+
);
300+
}
280301
}
281302
}
282303

0 commit comments

Comments
 (0)