Clear MIDI masters properly to avoid replay freezes (#36809)

While trying to play a replay I noticed that the replay would freeze
when seeking in some cases. After some debugging, I discovered that two
MIDI renderers had each other as master, which caused an infinite loop
processing MIDI events.

I'm not entirely sure of the sequence of events that leads to this
during replay playback, but I did notice that MIDI render masters are
never set to null. This is in the best case just a memory leak, in the
worst case probably the source of the bug, so... I fixed that.
This commit is contained in:
Pieter-Jan Briers
2025-04-26 08:42:16 +02:00
committed by GitHub
parent f4322c5bb7
commit e7be57d85f

View File

@@ -167,11 +167,14 @@ public sealed class InstrumentSystem : SharedInstrumentSystem
private void UpdateRendererMaster(InstrumentComponent instrument)
{
if (instrument.Renderer == null || instrument.Master == null)
if (instrument.Renderer == null)
return;
if (!TryComp(instrument.Master, out InstrumentComponent? masterInstrument) || masterInstrument.Renderer == null)
if (instrument.Master == null || !TryComp(instrument.Master, out InstrumentComponent? masterInstrument) || masterInstrument.Renderer == null)
{
instrument.Renderer.Master = null;
return;
}
instrument.Renderer.Master = masterInstrument.Renderer;
}
@@ -196,15 +199,16 @@ public sealed class InstrumentSystem : SharedInstrumentSystem
return;
}
instrument.Renderer?.SystemReset();
instrument.Renderer?.ClearAllEvents();
if (instrument.Renderer is { } renderer)
{
renderer.Master = null;
renderer.SystemReset();
renderer.ClearAllEvents();
var renderer = instrument.Renderer;
// We dispose of the synth two seconds from now to allow the last notes to stop from playing.
// Don't use timers bound to the entity in case it is getting deleted.
if (renderer != null)
// We dispose of the synth two seconds from now to allow the last notes to stop from playing.
// Don't use timers bound to the entity in case it is getting deleted.
Timer.Spawn(2000, () => { renderer.Dispose(); });
}
instrument.Renderer = null;
instrument.MidiEventBuffer.Clear();