fix: tighten resource-leak and null-deref hot spots
Three pre-existing upstream defects flagged by CodeRabbit, fixed in the v1.0.0 standalone cut where we own the codebase: - Ipc/ExtraChat.cs Dispose now unsubscribes all three IPC subscriptions (OverrideChannelGate, ChannelCommandColoursGate, ChannelNamesGate) instead of only the first; previously the latter two leaked their subscriptions on every plugin reload - GameFunctions/Types/TellTarget.cs FromTarget guards against a zero IPlayerCharacter.Address before dereferencing the unsafe Character* cast; previously a missing/destroyed target object would crash the game on /tell construction - GameFunctions/GameFunctions.cs ResolveTextCommandPlaceholderDetour null-checks the Hook reference before calling .Original instead of using the null-forgiving operator; defensive guard for teardown races
This commit is contained in:
@@ -249,9 +249,15 @@ internal unsafe class GameFunctions : IDisposable
|
|||||||
|
|
||||||
private nint ResolveTextCommandPlaceholderDetour(nint a1, byte* placeholderText, byte a3, byte a4)
|
private nint ResolveTextCommandPlaceholderDetour(nint a1, byte* placeholderText, byte a3, byte a4)
|
||||||
{
|
{
|
||||||
|
// The detour is only invoked through the hook, so the hook should
|
||||||
|
// never be null here, but the nullable field declaration forces us
|
||||||
|
// to handle the theoretical race during teardown.
|
||||||
|
if (ResolveTextCommandPlaceholderHook is null)
|
||||||
|
return nint.Zero;
|
||||||
|
|
||||||
var placeholder = MemoryHelper.ReadStringNullTerminated((nint) placeholderText);
|
var placeholder = MemoryHelper.ReadStringNullTerminated((nint) placeholderText);
|
||||||
if (ReplacementName == null || placeholder != Placeholder)
|
if (ReplacementName == null || placeholder != Placeholder)
|
||||||
return ResolveTextCommandPlaceholderHook!.Original(a1, placeholderText, a3, a4);
|
return ResolveTextCommandPlaceholderHook.Original(a1, placeholderText, a3, a4);
|
||||||
|
|
||||||
MemoryHelper.WriteString(PlaceholderNamePtr, ReplacementName);
|
MemoryHelper.WriteString(PlaceholderNamePtr, ReplacementName);
|
||||||
ReplacementName = null;
|
ReplacementName = null;
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ public class TellTarget
|
|||||||
|
|
||||||
public unsafe void FromTarget(IPlayerCharacter target)
|
public unsafe void FromTarget(IPlayerCharacter target)
|
||||||
{
|
{
|
||||||
|
if (target.Address == nint.Zero)
|
||||||
|
return;
|
||||||
|
|
||||||
Name = target.Name.TextValue;
|
Name = target.Name.TextValue;
|
||||||
World = target.HomeWorld.RowId;
|
World = target.HomeWorld.RowId;
|
||||||
ContentId = ((Character*)target.Address)->ContentId;
|
ContentId = ((Character*)target.Address)->ContentId;
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ public sealed class ExtraChat : IDisposable
|
|||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
OverrideChannelGate.Unsubscribe(OnOverrideChannel);
|
OverrideChannelGate.Unsubscribe(OnOverrideChannel);
|
||||||
|
ChannelCommandColoursGate.Unsubscribe(OnChannelCommandColours);
|
||||||
|
ChannelNamesGate.Unsubscribe(OnChannelNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnOverrideChannel(OverrideInfo info)
|
private void OnOverrideChannel(OverrideInfo info)
|
||||||
|
|||||||
Reference in New Issue
Block a user