fix(core): Fixes threading issue and adds threadguard (#565)

Setting `m.NetState = null` from a different thread caused `Mobile.Delta` and `NetState.Send` to run from a separate thread which cannot happen.
- [X] Fixes this issue
- [X] Adds THREADGUARD compiler constant as an option to trace this in the future.
This commit is contained in:
Kamron Batman 2021-04-08 21:56:31 -07:00 • committed by GitHub
parent 47bbd72e55
commit 529abb4283
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 118 additions and 81 deletions

View file

@ -1447,93 +1447,96 @@ namespace Server
}
set
{
if (m_NetState != value)
if (m_NetState == value)
{
m_Map?.OnClientChange(m_NetState, value, this);
return;
}
m_Target?.Cancel(this, TargetCancelType.Disconnected);
#if THREADGUARD
if (Thread.CurrentThread != Core.Thread)
{
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine("Attempting to set Mobile.NetState value from an invalid thread!");
Console.WriteLine(new StackTrace());
Utility.PopColor();
return;
}
#endif
m_Spell?.OnConnectionChanged();
m_Map?.OnClientChange(m_NetState, value, this);
m_Target?.Cancel(this, TargetCancelType.Disconnected);
m_Spell?.OnConnectionChanged();
m_NetState?.CancelAllTrades();
// if (m_Spell != null)
// m_Spell.FinishSequence();
var box = FindBankNoCreate();
m_NetState?.CancelAllTrades();
if (box?.Opened == true)
{
box.Close();
}
var box = FindBankNoCreate();
m_NetState = value;
if (box?.Opened == true)
if (m_NetState == null)
{
OnDisconnected();
EventSink.InvokeDisconnected(this);
// Disconnected, start the logout timer
if (m_LogoutTimer == null)
{
box.Close();
}
// REMOVED:
// m_Actions.Clear();
m_NetState = value;
if (m_NetState == null)
{
OnDisconnected();
EventSink.InvokeDisconnected(this);
// Disconnected, start the logout timer
if (m_LogoutTimer == null)
{
m_LogoutTimer = Timer.DelayCall(GetLogoutDelay(), Logout);
}
else
{
m_LogoutTimer.Stop();
m_LogoutTimer.Delay = GetLogoutDelay();
m_LogoutTimer.Start();
}
m_LogoutTimer = Timer.DelayCall(GetLogoutDelay(), Logout);
}
else
{
OnConnected();
EventSink.InvokeConnected(this);
// Connected, stop the logout timer and if needed, move to the world
m_LogoutTimer?.Stop();
m_LogoutTimer = null;
if (m_Map == Map.Internal && LogoutMap != null)
{
Map = LogoutMap;
Location = LogoutLocation;
}
m_LogoutTimer.Stop();
m_LogoutTimer.Delay = GetLogoutDelay();
m_LogoutTimer.Start();
}
for (var i = Items.Count - 1; i >= 0; --i)
{
if (i >= Items.Count)
{
continue;
}
var item = Items[i];
if (item is SecureTradeContainer)
{
for (var j = item.Items.Count - 1; j >= 0; --j)
{
if (j < item.Items.Count)
{
item.Items[j].OnSecureTrade(this, this, this, false);
AddToBackpack(item.Items[j]);
}
}
Timer.DelayCall(item.Delete);
}
}
DropHolding();
OnNetStateChanged();
}
else
{
OnConnected();
EventSink.InvokeConnected(this);
// Connected, stop the logout timer and if needed, move to the world
m_LogoutTimer?.Stop();
m_LogoutTimer = null;
if (m_Map == Map.Internal && LogoutMap != null)
{
Map = LogoutMap;
Location = LogoutLocation;
}
}
for (var i = Items.Count - 1; i >= 0; --i)
{
if (i >= Items.Count)
{
continue;
}
var item = Items[i];
if (item is SecureTradeContainer)
{
for (var j = item.Items.Count - 1; j >= 0; --j)
{
if (j < item.Items.Count)
{
item.Items[j].OnSecureTrade(this, this, this, false);
AddToBackpack(item.Items[j]);
}
}
Timer.DelayCall(item.Delete);
}
}
DropHolding();
OnNetStateChanged();
}
}
@ -7922,6 +7925,17 @@ namespace Server
public virtual void Delta(MobileDelta flag)
{
#if THREADGUARD
if (Thread.CurrentThread != Core.Thread)
{
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine("Attempting to queue a delta change from an invalid thread!");
Console.WriteLine(new StackTrace());
Utility.PopColor();
return;
}
#endif
if (m_Map == null || m_Map == Map.Internal || Deleted)
{
return;