fix: Fixes GetAllSharedAccounts and other list conversions in AdminGump (#1422)

---------

Co-authored-by: Kamron Batman <3953314+kamronbatman@users.noreply.github.com>
This commit is contained in:
Mink80 2023-07-09 00:20:00 +02:00 committed by GitHub
parent 27e00cc73f
commit 3825b16132
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 100 additions and 110 deletions

View file

@ -806,6 +806,27 @@ public static class Utility
return outputList; return outputList;
} }
// ToArray returns an array containing the contents of the List.
// This requires copying the List, which is an O(n) operation.
public static List<R> ToList<T, R>(this PooledRefList<T> poolList) where T : R
{
var size = poolList._size;
var items = poolList._items;
var list = new List<R>(size);
if (size == 0)
{
return list;
}
for (var i = 0; i < items.Length; i++)
{
list.Add(items[i]);
}
return list;
}
public static bool ToBoolean(string value) => public static bool ToBoolean(string value) =>
bool.TryParse(value, out var b) bool.TryParse(value, out var b)
? b ? b

View file

@ -2,9 +2,11 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using Server.Accounting; using Server.Accounting;
using Server.Buffers; using Server.Buffers;
using Server.Collections;
using Server.Commands; using Server.Commands;
using Server.Misc; using Server.Misc;
using Server.Multis; using Server.Multis;
@ -597,18 +599,7 @@ namespace Server.Gumps
} }
case AdminGumpPage.Accounts_Shared: case AdminGumpPage.Accounts_Shared:
{ {
List<KeyValuePair<IPAddress, List<Account>>> sharedAccounts; m_List ??= GetAllSharedAccounts();
if (m_List == null)
{
sharedAccounts = GetAllSharedAccounts();
// TODO: Find a better way, don't use KVPs?
m_List = sharedAccounts.ConvertAll(kvp => (object)kvp);
}
else
{
sharedAccounts = m_List.SafeConvertList<object, KeyValuePair<IPAddress, List<Account>>>();
}
AddLabelCropped(12, 120, 60, 20, LabelHue, "Count"); AddLabelCropped(12, 120, 60, 20, LabelHue, "Count");
AddLabelCropped(72, 120, 120, 20, LabelHue, "Address"); AddLabelCropped(72, 120, 120, 20, LabelHue, "Address");
@ -623,7 +614,7 @@ namespace Server.Gumps
AddImage(375, 122, 0x25EA); AddImage(375, 122, 0x25EA);
} }
if ((listPage + 1) * 12 < sharedAccounts.Count) if ((listPage + 1) * 12 < m_List.Count)
{ {
AddButton(392, 122, 0x15E1, 0x15E5, GetButtonID(1, 1)); AddButton(392, 122, 0x15E1, 0x15E5, GetButtonID(1, 1));
} }
@ -632,21 +623,16 @@ namespace Server.Gumps
AddImage(392, 122, 0x25E6); AddImage(392, 122, 0x25E6);
} }
if (sharedAccounts.Count == 0) if (m_List.Count == 0)
{ {
AddLabel(12, 140, LabelHue, "There are no accounts to display."); AddLabel(12, 140, LabelHue, "There are no accounts to display.");
} }
using var sb = ValueStringBuilder.Create(); using var sb = ValueStringBuilder.Create();
for (int i = 0, index = listPage * 12; for (int i = 0, index = listPage * 12; i < 12 && index >= 0 && index < m_List.Count; ++i, ++index)
i < 12 && index >= 0 && index < sharedAccounts.Count;
++i, ++index)
{ {
var kvp = sharedAccounts[index]; var (ipAddr, accts) = (KeyValuePair<IPAddress, List<Account>>)m_List[index];
var ipAddr = kvp.Key;
var accts = kvp.Value;
var offset = 140 + i * 20; var offset = 140 + i * 20;
@ -977,17 +963,7 @@ namespace Server.Gumps
break; break;
} }
List<IPAddress> ipAddresses; m_List ??= a.LoginIPs.ToList<object>();
if (m_List == null)
{
ipAddresses = a.LoginIPs.ToList();
m_List = ipAddresses.ToList<object>();
}
else
{
ipAddresses = m_List.SafeConvertList<object, IPAddress>();
}
AddHtml(10, 195, 400, 20, Color(Center("Client Addresses"), LabelColor32)); AddHtml(10, 195, 400, 20, Color(Center("Client Addresses"), LabelColor32));
@ -1018,7 +994,7 @@ namespace Server.Gumps
AddImage(184, 223, 0x25EA); AddImage(184, 223, 0x25EA);
} }
if ((listPage + 1) * 6 < ipAddresses.Count) if ((listPage + 1) * 6 < m_List.Count)
{ {
AddButton(201, 223, 0x15E1, 0x15E5, GetButtonID(1, 1)); AddButton(201, 223, 0x15E1, 0x15E5, GetButtonID(1, 1));
} }
@ -1027,14 +1003,14 @@ namespace Server.Gumps
AddImage(201, 223, 0x25E6); AddImage(201, 223, 0x25E6);
} }
if (ipAddresses.Count == 0) if (m_List.Count == 0)
{ {
AddHtml(18, 243, 200, 60, Color("This account has not yet been accessed.", LabelColor32)); AddHtml(18, 243, 200, 60, Color("This account has not yet been accessed.", LabelColor32));
} }
for (int i = 0, index = listPage * 6; i < 6 && index >= 0 && index < ipAddresses.Count; ++i, ++index) for (int i = 0, index = listPage * 6; i < 6 && index >= 0 && index < m_List.Count; ++i, ++index)
{ {
AddHtml(18, 243 + i * 22, 114, 20, Color(ipAddresses[index].ToString(), LabelColor32)); AddHtml(18, 243 + i * 22, 114, 20, Color(m_List[index].ToString(), LabelColor32));
AddButton(130, 242 + i * 22, 0xFA2, 0xFA4, GetButtonID(8, index)); AddButton(130, 242 + i * 22, 0xFA2, 0xFA4, GetButtonID(8, index));
AddButton(160, 242 + i * 22, 0xFA8, 0xFAA, GetButtonID(9, index)); AddButton(160, 242 + i * 22, 0xFA8, 0xFAA, GetButtonID(9, index));
AddButton(190, 242 + i * 22, 0xFB1, 0xFB3, GetButtonID(10, index)); AddButton(190, 242 + i * 22, 0xFB1, 0xFB3, GetButtonID(10, index));
@ -1049,17 +1025,7 @@ namespace Server.Gumps
break; break;
} }
List<string> ipRestrictions; m_List ??= a.IpRestrictions.ToList<object>();
if (m_List == null)
{
ipRestrictions = a.IpRestrictions.ToList();
m_List = ipRestrictions.ToList<object>();
}
else
{
ipRestrictions = m_List.SafeConvertList<object, string>();
}
AddHtml(10, 195, 400, 20, Color(Center("Address Restrictions"), LabelColor32)); AddHtml(10, 195, 400, 20, Color(Center("Address Restrictions"), LabelColor32));
@ -1092,7 +1058,7 @@ namespace Server.Gumps
AddImage(184, 223, 0x25EA); AddImage(184, 223, 0x25EA);
} }
if ((listPage + 1) * 6 < ipRestrictions.Count) if ((listPage + 1) * 6 < m_List.Count)
{ {
AddButton(201, 223, 0x15E1, 0x15E5, GetButtonID(1, 1)); AddButton(201, 223, 0x15E1, 0x15E5, GetButtonID(1, 1));
} }
@ -1101,16 +1067,14 @@ namespace Server.Gumps
AddImage(201, 223, 0x25E6); AddImage(201, 223, 0x25E6);
} }
if (ipRestrictions.Count == 0) if (m_List.Count == 0)
{ {
AddHtml(18, 243, 200, 60, Color("There are no addresses in this list.", LabelColor32)); AddHtml(18, 243, 200, 60, Color("There are no addresses in this list.", LabelColor32));
} }
for (int i = 0, index = listPage * 6; for (int i = 0, index = listPage * 6; i < 6 && index >= 0 && index < m_List.Count; ++i, ++index)
i < 6 && index >= 0 && index < ipRestrictions.Count;
++i, ++index)
{ {
AddHtml(18, 243 + i * 22, 114, 20, Color(ipRestrictions[index], LabelColor32)); AddHtml(18, 243 + i * 22, 114, 20, Color((string)m_List[index], LabelColor32));
AddButton(190, 242 + i * 22, 0xFB1, 0xFB3, GetButtonID(8, index)); AddButton(190, 242 + i * 22, 0xFB1, 0xFB3, GetButtonID(8, index));
} }
@ -1246,17 +1210,7 @@ namespace Server.Gumps
{ {
AddFirewallHeader(); AddFirewallHeader();
HashSet<Firewall.IFirewallEntry> firewallEntries; m_List ??= Firewall.Set.ToList<object>();
if (m_List == null)
{
firewallEntries = Firewall.Set;
m_List = firewallEntries.ToList<object>();
}
else
{
firewallEntries = m_List.SafeConvertSet<object, Firewall.IFirewallEntry>();
}
AddLabelCropped(12, 120, 358, 20, LabelHue, "IP Address"); AddLabelCropped(12, 120, 358, 20, LabelHue, "IP Address");
@ -1269,7 +1223,7 @@ namespace Server.Gumps
AddImage(375, 122, 0x25EA); AddImage(375, 122, 0x25EA);
} }
if ((listPage + 1) * 12 < firewallEntries.Count) if ((listPage + 1) * 12 < m_List.Count)
{ {
AddButton(392, 122, 0x15E1, 0x15E5, GetButtonID(1, 1)); AddButton(392, 122, 0x15E1, 0x15E5, GetButtonID(1, 1));
} }
@ -1278,7 +1232,7 @@ namespace Server.Gumps
AddImage(392, 122, 0x25E6); AddImage(392, 122, 0x25E6);
} }
if (firewallEntries.Count == 0) if (m_List.Count == 0)
{ {
AddLabel(12, 140, LabelHue, "The firewall list is empty."); AddLabel(12, 140, LabelHue, "The firewall list is empty.");
} }
@ -1286,7 +1240,7 @@ namespace Server.Gumps
{ {
var i = 0; var i = 0;
var index = listPage * 12; var index = listPage * 12;
foreach (var firewallEntry in firewallEntries) foreach (var firewallEntry in m_List)
{ {
if (i >= 12) if (i >= 12)
{ {
@ -1317,11 +1271,9 @@ namespace Server.Gumps
AddHtml(10, 175, 400, 20, Color(Center("Potentially Affected Accounts"), LabelColor32)); AddHtml(10, 175, 400, 20, Color(Center("Potentially Affected Accounts"), LabelColor32));
List<Account> blockedAccts;
if (m_List == null) if (m_List == null)
{ {
blockedAccts = new List<Account>(); using var blockedEntriesList = PooledRefList<Account>.Create();
foreach (var ia in Accounts.GetAccounts()) foreach (var ia in Accounts.GetAccounts())
{ {
@ -1333,18 +1285,15 @@ namespace Server.Gumps
{ {
if (firewallEntry.IsBlocked(loginList[i])) if (firewallEntry.IsBlocked(loginList[i]))
{ {
blockedAccts.Add(acct); blockedEntriesList.Add(acct);
break; break;
} }
} }
} }
blockedAccts.Sort(AccountComparer.Instance); blockedEntriesList.Sort(AccountComparer.Instance);
m_List = blockedAccts.ToList<object>();
} m_List = blockedEntriesList.ToList<Account, object>();
else
{
blockedAccts = m_List.SafeConvertList<object, Account>();
} }
if (listPage > 0) if (listPage > 0)
@ -1356,7 +1305,7 @@ namespace Server.Gumps
AddImage(375, 177, 0x25EA); AddImage(375, 177, 0x25EA);
} }
if ((listPage + 1) * 12 < blockedAccts.Count) if ((listPage + 1) * 12 < m_List.Count)
{ {
AddButton(392, 177, 0x15E1, 0x15E5, GetButtonID(1, 1)); AddButton(392, 177, 0x15E1, 0x15E5, GetButtonID(1, 1));
} }
@ -1365,16 +1314,16 @@ namespace Server.Gumps
AddImage(392, 177, 0x25E6); AddImage(392, 177, 0x25E6);
} }
if (blockedAccts.Count == 0) if (m_List.Count == 0)
{ {
AddLabelCropped(12, 200, 398, 20, LabelHue, "No accounts found."); AddLabelCropped(12, 200, 398, 20, LabelHue, "No accounts found.");
} }
for (int i = 0, index = listPage * 9; for (int i = 0, index = listPage * 9;
i < 9 && index >= 0 && index < blockedAccts.Count; i < 9 && index >= 0 && index < m_List.Count;
++i, ++index) ++i, ++index)
{ {
var a = blockedAccts[index]; var a = (Account)m_List[index];
var offset = 200 + i * 20; var offset = 200 + i * 20;
@ -1569,7 +1518,7 @@ namespace Server.Gumps
AddButtonLabeled(200, 80, GetButtonID(6, 2), "Add (Target)"); AddButtonLabeled(200, 80, GetButtonID(6, 2), "Add (Target)");
} }
private static List<KeyValuePair<IPAddress, List<Account>>> GetAllSharedAccounts() private static List<object> GetAllSharedAccounts()
{ {
var table = new Dictionary<IPAddress, List<Account>>(); var table = new Dictionary<IPAddress, List<Account>>();
@ -1579,30 +1528,40 @@ namespace Server.Gumps
for (var i = 0; i < theirAddresses.Length; ++i) for (var i = 0; i < theirAddresses.Length; ++i)
{ {
table.TryAdd(theirAddresses[i], new List<Account> { acct }); var theirAddress = theirAddresses[i];
// This path is heavy for larger shards, so use optimized code
ref var accts = ref CollectionsMarshal.GetValueRefOrAddDefault(table, theirAddress, out var acctExists);
// If we don't have a list, create one
if (!acctExists)
{
accts = new List<Account>();
}
accts.Add(acct);
} }
} }
var tableEntries = table.ToList(); var list = new List<object>();
for (var i = 0; i < tableEntries.Count; ++i) // Lets find all the entries that have only one account
foreach (var kvp in table)
{ {
var kvp = tableEntries[i]; if (kvp.Value.Count > 1)
var list = kvp.Value; {
// Sort the accounts alphabetically
kvp.Value.Sort(AccountComparer.Instance);
if (kvp.Value.Count == 1) // Can't avoid boxing because `m_List` in AdminGump is expecting List<object>
{ list.Add(kvp);
list.RemoveAt(i--);
}
else
{
list.Sort(AccountComparer.Instance);
} }
} }
tableEntries.Sort(SharedAccountComparer.Instance); // Sort by highest accounts per IP first
list.Sort(SharedAccountDescendingComparer.Instance);
return tableEntries; return list;
} }
private static List<Account> GetSharedAccounts(IPAddress ipAddress) private static List<Account> GetSharedAccounts(IPAddress ipAddress)
@ -1946,24 +1905,22 @@ namespace Server.Gumps
if (m_PageType == AdminGumpPage.Accounts) if (m_PageType == AdminGumpPage.Accounts)
{ {
var list = m_List.SafeConvertList<object, Account>(); if (m_State is List<Account> rads)
if (list != null && m_State is List<Account> rads)
{ {
for (int i = 0, v = m_ListPage * 12; i < 12 && v < list.Count; ++i, ++v) for (int i = 0, v = m_ListPage * 12; i < 12 && v < m_List.Count; ++i, ++v)
{ {
var obj = list[v]; var acct = (Account)m_List[v];
if (info.IsSwitched(v)) if (info.IsSwitched(v))
{ {
if (!rads.Contains(obj)) if (!rads.Contains(acct))
{ {
rads.Add(obj); rads.Add(acct);
} }
} }
else if (rads.Contains(obj)) else if (rads.Contains(acct))
{ {
rads.Remove(obj); rads.Remove(acct);
} }
} }
} }
@ -4166,12 +4123,24 @@ namespace Server.Gumps
} }
} }
private class SharedAccountComparer : IComparer<KeyValuePair<IPAddress, List<Account>>> private class SharedAccountDescendingComparer : IComparer<object>
{ {
public static readonly IComparer<KeyValuePair<IPAddress, List<Account>>> Instance = new SharedAccountComparer(); public static readonly IComparer<object> Instance = new SharedAccountDescendingComparer();
public int Compare(KeyValuePair<IPAddress, List<Account>> x, KeyValuePair<IPAddress, List<Account>> y) => public int Compare(object x, object y)
x.Value.Count - y.Value.Count; {
if (x is not KeyValuePair<IPAddress, List<Account>> a)
{
return -1;
}
if (y is not KeyValuePair<IPAddress, List<Account>> b)
{
return 1;
}
return a.Value.Count - b.Value.Count;
}
} }
private class AddCommentPrompt : Prompt private class AddCommentPrompt : Prompt