## Summary - Adds `public virtual bool Murderer => Kills >= 5` property to `Mobile`, replacing ~30 scattered `Kills >= 5` / `Kills < 5` magic-number checks across the codebase - `BaseCreature` overrides `Murderer` to also return `true` when `AlwaysMurderer` is set - Updates `Corpse` serialization to v15, storing `_murderer` as a `bool` field (migrated from `int Kills >= 5` in earlier versions)
4343 lines
182 KiB
C#
4343 lines
182 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Runtime.InteropServices;
|
|
using System.Threading;
|
|
using Server.Accounting;
|
|
using Server.Collections;
|
|
using Server.Commands;
|
|
using Server.Maps;
|
|
using Server.Misc;
|
|
using Server.Multis;
|
|
using Server.Network;
|
|
using Server.Prompts;
|
|
using Server.Saves;
|
|
using Server.Text;
|
|
|
|
namespace Server.Gumps
|
|
{
|
|
public enum AdminGumpPage
|
|
{
|
|
Information_General,
|
|
Information_Perf,
|
|
Administer,
|
|
Clients,
|
|
Accounts,
|
|
Accounts_Shared,
|
|
Firewall,
|
|
Administer_WorldBuilding,
|
|
Administer_Server,
|
|
Administer_Access,
|
|
Administer_Access_Lockdown,
|
|
Administer_Commands,
|
|
ClientInfo,
|
|
AccountDetails,
|
|
AccountDetails_Information,
|
|
AccountDetails_Characters,
|
|
AccountDetails_Access,
|
|
AccountDetails_Access_ClientIPs,
|
|
AccountDetails_Comments,
|
|
AccountDetails_Tags,
|
|
AccountDetails_ChangePassword,
|
|
AccountDetails_ChangeAccess,
|
|
FirewallInfo
|
|
}
|
|
|
|
public class AdminGump : Gump
|
|
{
|
|
private const int LabelColor = 0x7FFF;
|
|
private const int SelectedColor = 0x421F;
|
|
private const int DisabledColor = 0x4210;
|
|
|
|
private const int LabelColor32 = 0xFFFFFF;
|
|
private const int SelectedColor32 = 0x8080FF;
|
|
private const int DisabledColor32 = 0x808080;
|
|
|
|
private const int LabelHue = 0x480;
|
|
private const int GreenHue = 0x40;
|
|
private const int RedHue = 0x20;
|
|
|
|
private static readonly string[] m_AccessLevelStrings =
|
|
{
|
|
"Player",
|
|
"Counselor",
|
|
"Game Master",
|
|
"Seer",
|
|
"Administrator",
|
|
"Developer",
|
|
"Owner"
|
|
};
|
|
|
|
private readonly Mobile m_From;
|
|
private readonly List<object> m_List;
|
|
private readonly int m_ListPage;
|
|
private readonly AdminGumpPage m_PageType;
|
|
private readonly object m_State;
|
|
|
|
public override bool Singleton => true;
|
|
|
|
public AdminGump(
|
|
Mobile from, AdminGumpPage pageType, int listPage = 0, List<object> list = null, string notice = null,
|
|
object state = null
|
|
) : base(50, 40)
|
|
{
|
|
m_From = from;
|
|
m_PageType = pageType;
|
|
m_ListPage = listPage;
|
|
m_State = state;
|
|
m_List = list;
|
|
|
|
FilterAccess(m_List, from);
|
|
|
|
AddPage(0);
|
|
|
|
AddBackground(0, 0, 420, 480, 5054);
|
|
|
|
AddBlackAlpha(10, 10, 170, 100);
|
|
AddBlackAlpha(190, 10, 220, 100);
|
|
AddBlackAlpha(10, 120, 400, 300);
|
|
AddBlackAlpha(10, 430, 400, 40);
|
|
|
|
AddPageButton(
|
|
10,
|
|
10,
|
|
GetButtonID(0, 0),
|
|
"INFORMATION",
|
|
AdminGumpPage.Information_General,
|
|
AdminGumpPage.Information_Perf
|
|
);
|
|
AddPageButton(
|
|
10,
|
|
30,
|
|
GetButtonID(0, 1),
|
|
"ADMINISTER",
|
|
AdminGumpPage.Administer,
|
|
AdminGumpPage.Administer_Access,
|
|
AdminGumpPage.Administer_Commands,
|
|
AdminGumpPage.Administer_Server,
|
|
AdminGumpPage.Administer_WorldBuilding,
|
|
AdminGumpPage.Administer_Access_Lockdown
|
|
);
|
|
AddPageButton(10, 50, GetButtonID(0, 2), "CLIENT LIST", AdminGumpPage.Clients, AdminGumpPage.ClientInfo);
|
|
AddPageButton(
|
|
10,
|
|
70,
|
|
GetButtonID(0, 3),
|
|
"ACCOUNT LIST",
|
|
AdminGumpPage.Accounts,
|
|
AdminGumpPage.Accounts_Shared,
|
|
AdminGumpPage.AccountDetails,
|
|
AdminGumpPage.AccountDetails_Information,
|
|
AdminGumpPage.AccountDetails_Characters,
|
|
AdminGumpPage.AccountDetails_Access,
|
|
AdminGumpPage.AccountDetails_Access_ClientIPs,
|
|
AdminGumpPage.AccountDetails_Comments,
|
|
AdminGumpPage.AccountDetails_Tags,
|
|
AdminGumpPage.AccountDetails_ChangeAccess,
|
|
AdminGumpPage.AccountDetails_ChangePassword
|
|
);
|
|
AddPageButton(10, 90, GetButtonID(0, 4), "FIREWALL", AdminGumpPage.Firewall, AdminGumpPage.FirewallInfo);
|
|
|
|
if (notice != null)
|
|
{
|
|
AddHtml(20, 440, 396, 36, notice.Color(LabelColor32));
|
|
}
|
|
|
|
switch (pageType)
|
|
{
|
|
case AdminGumpPage.Information_General:
|
|
{
|
|
var banned = 0;
|
|
var active = 0;
|
|
|
|
foreach (Account acct in Accounts.GetAccounts())
|
|
{
|
|
if (acct.Banned)
|
|
{
|
|
++banned;
|
|
}
|
|
else
|
|
{
|
|
++active;
|
|
}
|
|
}
|
|
|
|
AddLabel(20, 130, LabelHue, "Active Accounts:");
|
|
AddLabel(150, 130, LabelHue, active.ToString());
|
|
|
|
AddLabel(20, 150, LabelHue, "Banned Accounts:");
|
|
AddLabel(150, 150, LabelHue, banned.ToString());
|
|
|
|
AddLabel(20, 170, LabelHue, "Firewalled:");
|
|
AddLabel(150, 170, LabelHue, Firewall.FirewallSetCount.ToString());
|
|
|
|
AddLabel(20, 190, LabelHue, "Clients:");
|
|
AddLabel(150, 190, LabelHue, NetState.Instances.Count.ToString());
|
|
|
|
AddLabel(20, 210, LabelHue, "Mobiles:");
|
|
AddLabel(150, 210, LabelHue, World.Mobiles.Count.ToString());
|
|
|
|
AddLabel(20, 230, LabelHue, "Mobile Scripts:");
|
|
AddLabel(150, 230, LabelHue, Core.ScriptMobiles.ToString());
|
|
|
|
AddLabel(20, 250, LabelHue, "Items:");
|
|
AddLabel(150, 250, LabelHue, World.Items.Count.ToString());
|
|
|
|
AddLabel(20, 270, LabelHue, "Item Scripts:");
|
|
AddLabel(150, 270, LabelHue, Core.ScriptItems.ToString());
|
|
|
|
AddLabel(20, 290, LabelHue, "Uptime:");
|
|
AddLabel(150, 290, LabelHue, FormatTimeSpan(TimeSpan.FromMilliseconds(Core.Uptime)));
|
|
|
|
AddLabel(20, 310, LabelHue, "Memory:");
|
|
AddLabel(150, 310, LabelHue, FormatByteAmount(GC.GetTotalMemory(false)));
|
|
|
|
AddLabel(20, 330, LabelHue, "Framework:");
|
|
AddLabel(150, 330, LabelHue, Environment.Version.ToString());
|
|
|
|
AddLabel(20, 350, LabelHue, "Operating System: ");
|
|
var os = Environment.OSVersion.ToString();
|
|
|
|
os = os.ReplaceOrdinal("Microsoft", "MSFT");
|
|
os = os.ReplaceOrdinal("Service Pack", "SP");
|
|
|
|
AddLabel(150, 350, LabelHue, os);
|
|
|
|
/*string str;
|
|
|
|
try{ str = FormatTimeSpan( Core.Process.TotalProcessorTime ); }
|
|
catch{ str = "(unable to retrieve)"; }
|
|
|
|
AddLabel( 20, 330, LabelHue, "Process Time:" );
|
|
AddLabel( 250, 330, LabelHue, str );*/
|
|
|
|
/*try{ str = Core.Process.PriorityClass.ToString(); }
|
|
catch{ str = "(unable to retrieve)"; }
|
|
|
|
AddLabel( 20, 350, LabelHue, "Process Priority:" );
|
|
AddLabel( 250, 350, LabelHue, str );*/
|
|
|
|
AddPageButton(200, 20, GetButtonID(0, 0), "General", AdminGumpPage.Information_General);
|
|
AddPageButton(200, 40, GetButtonID(0, 5), "Performance", AdminGumpPage.Information_Perf);
|
|
|
|
break;
|
|
}
|
|
case AdminGumpPage.Information_Perf:
|
|
{
|
|
AddLabel(20, 130, LabelHue, "Cycles Per Second:");
|
|
AddLabel(40, 150, LabelHue, $"Current: {Core.CyclesPerSecond:N2}");
|
|
AddLabel(40, 170, LabelHue, $"Average: {Core.AverageCPS:N2}");
|
|
|
|
using var sb = ValueStringBuilder.Create();
|
|
|
|
ThreadPool.GetAvailableThreads(out var curUser, out var curIOCP);
|
|
ThreadPool.GetMaxThreads(out var maxUser, out var maxIOCP);
|
|
|
|
sb.Append("Worker Threads:<br>Capacity: ");
|
|
sb.Append(maxUser);
|
|
sb.Append("<br>Available: ");
|
|
sb.Append(curUser);
|
|
sb.Append("<br>Usage: ");
|
|
sb.Append((maxUser - curUser) * 100 / maxUser);
|
|
sb.Append("%<br><br>IOCP Threads:<br>Capacity: ");
|
|
sb.Append(maxIOCP);
|
|
sb.Append("<br>Available: ");
|
|
sb.Append(curIOCP);
|
|
sb.Append("<br>Usage: ");
|
|
sb.Append((maxIOCP - curIOCP) * 100 / maxIOCP);
|
|
sb.Append('%');
|
|
|
|
AddLabel(20, 200, LabelHue, "Pooling:");
|
|
AddHtml(20, 220, 380, 150, sb.ToString(), true, true);
|
|
|
|
AddPageButton(200, 20, GetButtonID(0, 0), "General", AdminGumpPage.Information_General);
|
|
AddPageButton(200, 40, GetButtonID(0, 5), "Performance", AdminGumpPage.Information_Perf);
|
|
|
|
break;
|
|
}
|
|
case AdminGumpPage.Administer_WorldBuilding:
|
|
{
|
|
AddHtml(10, 125, 400, 20, "Generating".Center(LabelColor32));
|
|
|
|
AddButtonLabeled(20, 175, GetButtonID(3, 101), "Teleporters");
|
|
AddButtonLabeled(220, 175, GetButtonID(3, 102), "Moongates");
|
|
|
|
AddButtonLabeled(20, 200, GetButtonID(3, 103), "Spawners");
|
|
AddButtonLabeled(220, 200, GetButtonID(3, 106), "Decorations");
|
|
|
|
AddButtonLabeled(20, 225, GetButtonID(3, 104), "Doors");
|
|
AddButtonLabeled(220, 225, GetButtonID(3, 105), "Signs");
|
|
|
|
AddButtonLabeled(20, 250, GetButtonID(3, 107), "Champions");
|
|
AddButtonLabeled(220, 250, GetButtonID(3, 108), "Magincia (AOS+)");
|
|
|
|
AddButtonLabeled(20, 275, GetButtonID(3, 109), "Stealable Artifacts");
|
|
AddButtonLabeled(220, 275, GetButtonID(3, 110), "SH Teleporters");
|
|
|
|
AddButtonLabeled(20, 300, GetButtonID(3, 111), "Secret Locations");
|
|
AddButtonLabeled(220, 300, GetButtonID(3, 112), "Doom");
|
|
|
|
AddButtonLabeled(20, 325, GetButtonID(3, 113), "Khaldun Puzzles");
|
|
AddButtonLabeled(220, 325, GetButtonID(3, 114), "Do everything");
|
|
|
|
goto case AdminGumpPage.Administer;
|
|
}
|
|
case AdminGumpPage.Administer_Server:
|
|
{
|
|
AddHtml(10, 125, 400, 20, "Server".Center(LabelColor32));
|
|
|
|
AddButtonLabeled(20, 150, GetButtonID(3, 200), "Save");
|
|
|
|
/*if (!Core.Service)
|
|
{*/
|
|
AddButtonLabeled(20, 180, GetButtonID(3, 201), "Shutdown (With Save)");
|
|
AddButtonLabeled(20, 200, GetButtonID(3, 202), "Shutdown (Without Save)");
|
|
|
|
AddButtonLabeled(20, 230, GetButtonID(3, 203), "Shutdown & Restart (With Save)");
|
|
AddButtonLabeled(20, 250, GetButtonID(3, 204), "Shutdown & Restart (Without Save)");
|
|
|
|
AddButtonLabeled(20, 270, GetButtonID(3, 205), "Shutdown (With 15m Delay & Save)");
|
|
|
|
/*}
|
|
else
|
|
{
|
|
AddLabel( 20, 215, LabelHue, "Shutdown/Restart not available." );
|
|
}*/
|
|
|
|
AddHtml(10, 295, 400, 20, "Broadcast".Center(LabelColor32));
|
|
|
|
AddTextField(20, 320, 380, 20, 0);
|
|
AddButtonLabeled(20, 350, GetButtonID(3, 210), "To Everyone");
|
|
AddButtonLabeled(220, 350, GetButtonID(3, 211), "To Staff");
|
|
|
|
goto case AdminGumpPage.Administer;
|
|
}
|
|
case AdminGumpPage.Administer_Access_Lockdown:
|
|
{
|
|
AddHtml(10, 125, 400, 20, "Server Lockdown".Center(LabelColor32));
|
|
|
|
AddHtml(
|
|
20,
|
|
150,
|
|
380,
|
|
80,
|
|
"When enabled, only clients with an access level equal to or greater than the specified lockdown level may access the server. After setting a lockdown level, use the <em>Purge Invalid Clients</em> button to disconnect those clients without access.".Color(
|
|
LabelColor32
|
|
)
|
|
);
|
|
|
|
var level = AccountHandler.LockdownLevel;
|
|
var isLockedDown = level > AccessLevel.Player;
|
|
|
|
AddSelectedButton(20, 230, GetButtonID(3, 500), "Not Locked Down", !isLockedDown);
|
|
AddSelectedButton(
|
|
20,
|
|
260,
|
|
GetButtonID(3, 504),
|
|
"Administrators",
|
|
isLockedDown && level <= AccessLevel.Administrator
|
|
);
|
|
AddSelectedButton(20, 280, GetButtonID(3, 503), "Seers", isLockedDown && level <= AccessLevel.Seer);
|
|
AddSelectedButton(
|
|
20,
|
|
300,
|
|
GetButtonID(3, 502),
|
|
"Game Masters",
|
|
isLockedDown && level <= AccessLevel.GameMaster
|
|
);
|
|
AddSelectedButton(
|
|
20,
|
|
320,
|
|
GetButtonID(3, 501),
|
|
"Counselors",
|
|
isLockedDown && level <= AccessLevel.Counselor
|
|
);
|
|
|
|
AddButtonLabeled(20, 350, GetButtonID(3, 510), "Purge Invalid Clients");
|
|
|
|
goto case AdminGumpPage.Administer;
|
|
}
|
|
case AdminGumpPage.Administer_Access:
|
|
{
|
|
AddHtml(10, 125, 400, 20, "Access".Center(LabelColor32));
|
|
|
|
AddHtml(10, 155, 400, 20, "Connectivity".Center(LabelColor32));
|
|
|
|
AddButtonLabeled(20, 180, GetButtonID(3, 300), "Kick");
|
|
AddButtonLabeled(220, 180, GetButtonID(3, 301), "Ban");
|
|
|
|
AddButtonLabeled(20, 210, GetButtonID(3, 302), "Firewall");
|
|
AddButtonLabeled(220, 210, GetButtonID(3, 303), "Lockdown");
|
|
|
|
AddHtml(10, 245, 400, 20, "Staff".Center(LabelColor32));
|
|
|
|
AddButtonLabeled(20, 270, GetButtonID(3, 310), "Make Player");
|
|
AddButtonLabeled(20, 290, GetButtonID(3, 311), "Make Counselor");
|
|
AddButtonLabeled(20, 310, GetButtonID(3, 312), "Make Game Master");
|
|
AddButtonLabeled(20, 330, GetButtonID(3, 313), "Make Seer");
|
|
|
|
if (from.AccessLevel > AccessLevel.Administrator)
|
|
{
|
|
AddButtonLabeled(220, 270, GetButtonID(3, 314), "Make Administrator");
|
|
|
|
if (from.AccessLevel > AccessLevel.Developer)
|
|
{
|
|
AddButtonLabeled(220, 290, GetButtonID(3, 315), "Make Developer");
|
|
|
|
if (from.AccessLevel >= AccessLevel.Owner)
|
|
{
|
|
AddButtonLabeled(220, 310, GetButtonID(3, 316), "Make Owner");
|
|
}
|
|
}
|
|
}
|
|
|
|
goto case AdminGumpPage.Administer;
|
|
}
|
|
case AdminGumpPage.Administer_Commands:
|
|
{
|
|
AddHtml(10, 125, 400, 20, "Commands".Center(LabelColor32));
|
|
|
|
AddButtonLabeled(20, 150, GetButtonID(3, 400), "Add");
|
|
AddButtonLabeled(220, 150, GetButtonID(3, 401), "Remove");
|
|
|
|
AddButtonLabeled(20, 170, GetButtonID(3, 402), "Dupe");
|
|
AddButtonLabeled(220, 170, GetButtonID(3, 403), "Dupe in bag");
|
|
|
|
AddButtonLabeled(20, 200, GetButtonID(3, 404), "Properties");
|
|
AddButtonLabeled(220, 200, GetButtonID(3, 405), "Skills");
|
|
|
|
AddButtonLabeled(20, 230, GetButtonID(3, 406), "Mortal");
|
|
AddButtonLabeled(220, 230, GetButtonID(3, 407), "Immortal");
|
|
|
|
AddButtonLabeled(20, 250, GetButtonID(3, 408), "Squelch");
|
|
AddButtonLabeled(220, 250, GetButtonID(3, 409), "Unsquelch");
|
|
|
|
AddButtonLabeled(20, 290, GetButtonID(3, 412), "Hide");
|
|
AddButtonLabeled(220, 290, GetButtonID(3, 413), "Unhide");
|
|
|
|
AddButtonLabeled(20, 310, GetButtonID(3, 414), "Kill");
|
|
AddButtonLabeled(220, 310, GetButtonID(3, 415), "Resurrect");
|
|
|
|
AddButtonLabeled(20, 330, GetButtonID(3, 416), "Move");
|
|
AddButtonLabeled(220, 330, GetButtonID(3, 417), "Wipe");
|
|
|
|
AddButtonLabeled(20, 350, GetButtonID(3, 418), "Teleport");
|
|
AddButtonLabeled(220, 350, GetButtonID(3, 419), "Teleport (Multiple)");
|
|
|
|
goto case AdminGumpPage.Administer;
|
|
}
|
|
case AdminGumpPage.Administer:
|
|
{
|
|
AddPageButton(200, 20, GetButtonID(3, 0), "World Building", AdminGumpPage.Administer_WorldBuilding);
|
|
AddPageButton(200, 40, GetButtonID(3, 1), "Server", AdminGumpPage.Administer_Server);
|
|
AddPageButton(
|
|
200,
|
|
60,
|
|
GetButtonID(3, 2),
|
|
"Access",
|
|
AdminGumpPage.Administer_Access,
|
|
AdminGumpPage.Administer_Access_Lockdown
|
|
);
|
|
AddPageButton(200, 80, GetButtonID(3, 3), "Commands", AdminGumpPage.Administer_Commands);
|
|
|
|
break;
|
|
}
|
|
case AdminGumpPage.Clients:
|
|
{
|
|
if (m_List == null)
|
|
{
|
|
var states = NetState.Instances.ToList();
|
|
states.Sort(NetStateComparer.Instance);
|
|
|
|
m_List = states.ToList<object>();
|
|
FilterAccess(m_List, from);
|
|
}
|
|
|
|
AddClientHeader();
|
|
|
|
AddLabelCropped(12, 120, 81, 20, LabelHue, "Name");
|
|
AddLabelCropped(95, 120, 81, 20, LabelHue, "Account");
|
|
AddLabelCropped(178, 120, 81, 20, LabelHue, "Access Level");
|
|
AddLabelCropped(273, 120, 109, 20, LabelHue, "IP Address");
|
|
|
|
if (listPage > 0)
|
|
{
|
|
AddButton(375, 122, 0x15E3, 0x15E7, GetButtonID(1, 0));
|
|
}
|
|
else
|
|
{
|
|
AddImage(375, 122, 0x25EA);
|
|
}
|
|
|
|
if ((listPage + 1) * 12 < m_List.Count)
|
|
{
|
|
AddButton(392, 122, 0x15E1, 0x15E5, GetButtonID(1, 1));
|
|
}
|
|
else
|
|
{
|
|
AddImage(392, 122, 0x25E6);
|
|
}
|
|
|
|
if (m_List.Count == 0)
|
|
{
|
|
AddLabel(12, 140, LabelHue, "There are no clients to display.");
|
|
}
|
|
|
|
for (int i = 0, index = listPage * 12; i < 12 && index >= 0 && index < m_List.Count; ++i, ++index)
|
|
{
|
|
if (m_List[index] is not NetState ns)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var m = ns.Mobile;
|
|
var a = ns.Account as Account;
|
|
var offset = 140 + i * 20;
|
|
|
|
if (m == null)
|
|
{
|
|
AddLabelCropped(12, offset, 81, 20, LabelHue, "(logging in)");
|
|
}
|
|
else
|
|
{
|
|
AddLabelCropped(12, offset, 81, 20, GetHueFor(m), m.Name);
|
|
}
|
|
|
|
AddLabelCropped(95, offset, 81, 20, LabelHue, a == null ? "(no account)" : a.Username);
|
|
AddLabelCropped(
|
|
178,
|
|
offset,
|
|
81,
|
|
20,
|
|
LabelHue,
|
|
m == null
|
|
? a != null ? FormatAccessLevel(a.AccessLevel) : ""
|
|
: FormatAccessLevel(m.AccessLevel)
|
|
);
|
|
AddLabelCropped(273, offset, 109, 20, LabelHue, ns.ToString());
|
|
|
|
if (a != null || m != null)
|
|
{
|
|
AddButton(380, offset - 1, 0xFA5, 0xFA7, GetButtonID(4, index + 2));
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case AdminGumpPage.ClientInfo:
|
|
{
|
|
if (state is not Mobile m)
|
|
{
|
|
break;
|
|
}
|
|
|
|
AddClientHeader();
|
|
|
|
AddHtml(10, 125, 400, 20, "Information".Center(LabelColor32));
|
|
|
|
var y = 146;
|
|
|
|
AddLabel(20, y, LabelHue, "Name:");
|
|
AddLabel(200, y, GetHueFor(m), m.Name);
|
|
y += 20;
|
|
|
|
var a = m.Account as Account;
|
|
|
|
AddLabel(20, y, LabelHue, "Account:");
|
|
AddLabel(200, y, a?.Banned == true ? RedHue : LabelHue, a == null ? "(no account)" : a.Username);
|
|
|
|
if (m.AccessLevel > from.AccessLevel || a?.AccessLevel > from.AccessLevel)
|
|
{
|
|
AddLabel(20, y + 20, LabelHue, "You do not have permission to view this client's information.");
|
|
break;
|
|
}
|
|
|
|
AddButton(380, y, 0xFA5, 0xFA7, GetButtonID(7, 14));
|
|
y += 20;
|
|
|
|
var ns = m.NetState;
|
|
|
|
if (ns == null)
|
|
{
|
|
AddLabel(20, y, LabelHue, "Address:");
|
|
AddLabel(200, y, RedHue, "Offline");
|
|
y += 20;
|
|
|
|
AddLabel(20, y, LabelHue, "Location:");
|
|
AddLabel(200, y, LabelHue, $"{m.Location} [{m.Map}]");
|
|
y += 44;
|
|
}
|
|
else
|
|
{
|
|
AddLabel(20, y, LabelHue, "Address:");
|
|
AddLabel(200, y, GreenHue, ns.ToString());
|
|
y += 20;
|
|
|
|
var v = ns.Version;
|
|
|
|
AddLabel(20, y, LabelHue, "Version:");
|
|
if (ns.Assistant != null)
|
|
{
|
|
AddLabel(200, y, LabelHue, $"{v.SourceString ?? "(null)"} ({ns.Assistant})");
|
|
}
|
|
else
|
|
{
|
|
AddLabel(200, y, LabelHue, v.SourceString ?? "(null)");
|
|
}
|
|
|
|
y += 20;
|
|
|
|
AddLabel(20, y, LabelHue, "Location:");
|
|
AddLabel(200, y, LabelHue, $"{m.Location} [{m.Map}]");
|
|
y += 24;
|
|
}
|
|
|
|
AddButtonLabeled(20, y, GetButtonID(7, 0), "Go to");
|
|
AddButtonLabeled(200, y, GetButtonID(7, 1), "Get");
|
|
y += 20;
|
|
|
|
AddButtonLabeled(20, y, GetButtonID(7, 2), "Kick");
|
|
AddButtonLabeled(200, y, GetButtonID(7, 3), "Ban");
|
|
y += 20;
|
|
|
|
AddButtonLabeled(20, y, GetButtonID(7, 4), "Properties");
|
|
AddButtonLabeled(200, y, GetButtonID(7, 5), "Skills");
|
|
y += 20;
|
|
|
|
AddButtonLabeled(20, y, GetButtonID(7, 6), "Mortal");
|
|
AddButtonLabeled(200, y, GetButtonID(7, 7), "Immortal");
|
|
y += 20;
|
|
|
|
AddButtonLabeled(20, y, GetButtonID(7, 8), "Squelch");
|
|
AddButtonLabeled(200, y, GetButtonID(7, 9), "Unsquelch");
|
|
y += 20;
|
|
|
|
/*AddButtonLabeled( 20, y, GetButtonID( 7, 10 ), "Hide" );
|
|
AddButtonLabeled( 200, y, GetButtonID( 7, 11 ), "Unhide" );
|
|
y += 20;*/
|
|
|
|
AddButtonLabeled(20, y, GetButtonID(7, 12), "Kill");
|
|
AddButtonLabeled(200, y, GetButtonID(7, 13), "Resurrect");
|
|
y += 20;
|
|
|
|
AddButtonLabeled(20, y, GetButtonID(7, 15), "Jail");
|
|
AddButtonLabeled(200, y, GetButtonID(7, 16), "Unjail");
|
|
y += 25;
|
|
|
|
AddLabel(20, y, LabelHue, "Jail Reason:");
|
|
AddTextField(100, y, 300, 20, 1);
|
|
|
|
break;
|
|
}
|
|
case AdminGumpPage.Accounts_Shared:
|
|
{
|
|
m_List ??= GetAllSharedAccounts();
|
|
|
|
AddLabelCropped(12, 120, 60, 20, LabelHue, "Count");
|
|
AddLabelCropped(72, 120, 120, 20, LabelHue, "Address");
|
|
AddLabelCropped(192, 120, 180, 20, LabelHue, "Accounts");
|
|
|
|
if (listPage > 0)
|
|
{
|
|
AddButton(375, 122, 0x15E3, 0x15E7, GetButtonID(1, 0));
|
|
}
|
|
else
|
|
{
|
|
AddImage(375, 122, 0x25EA);
|
|
}
|
|
|
|
if ((listPage + 1) * 12 < m_List.Count)
|
|
{
|
|
AddButton(392, 122, 0x15E1, 0x15E5, GetButtonID(1, 1));
|
|
}
|
|
else
|
|
{
|
|
AddImage(392, 122, 0x25E6);
|
|
}
|
|
|
|
if (m_List.Count == 0)
|
|
{
|
|
AddLabel(12, 140, LabelHue, "There are no accounts to display.");
|
|
}
|
|
|
|
using var sb = ValueStringBuilder.Create();
|
|
|
|
for (int i = 0, index = listPage * 12; i < 12 && index >= 0 && index < m_List.Count; ++i, ++index)
|
|
{
|
|
var (ipAddr, accts) = (KeyValuePair<IPAddress, List<Account>>)m_List[index];
|
|
|
|
var offset = 140 + i * 20;
|
|
|
|
AddLabelCropped(12, offset, 60, 20, LabelHue, accts.Count.ToString());
|
|
AddLabelCropped(72, offset, 120, 20, LabelHue, ipAddr.ToString());
|
|
|
|
sb.Reset();
|
|
|
|
for (var j = 0; j < accts.Count; ++j)
|
|
{
|
|
if (j > 0)
|
|
{
|
|
sb.Append(", ");
|
|
}
|
|
|
|
if (j < 4)
|
|
{
|
|
var acct = accts[j];
|
|
|
|
sb.Append(acct.Username);
|
|
}
|
|
else
|
|
{
|
|
sb.Append("...");
|
|
break;
|
|
}
|
|
}
|
|
|
|
AddLabelCropped(192, offset, 180, 20, LabelHue, sb.ToString());
|
|
|
|
AddButton(380, offset - 1, 0xFA5, 0xFA7, GetButtonID(5, index + 56));
|
|
}
|
|
|
|
break;
|
|
}
|
|
case AdminGumpPage.Accounts:
|
|
{
|
|
m_List ??= new List<object>();
|
|
|
|
var rads = state as List<Account>;
|
|
|
|
AddAccountHeader();
|
|
|
|
if (rads == null)
|
|
{
|
|
AddLabelCropped(12, 120, 120, 20, LabelHue, "Name");
|
|
}
|
|
else
|
|
{
|
|
AddLabelCropped(32, 120, 100, 20, LabelHue, "Name");
|
|
}
|
|
|
|
AddLabelCropped(132, 120, 120, 20, LabelHue, "Access Level");
|
|
AddLabelCropped(252, 120, 120, 20, LabelHue, "Status");
|
|
|
|
if (listPage > 0)
|
|
{
|
|
AddButton(375, 122, 0x15E3, 0x15E7, GetButtonID(1, 0));
|
|
}
|
|
else
|
|
{
|
|
AddImage(375, 122, 0x25EA);
|
|
}
|
|
|
|
if ((listPage + 1) * 12 < m_List.Count)
|
|
{
|
|
AddButton(392, 122, 0x15E1, 0x15E5, GetButtonID(1, 1));
|
|
}
|
|
else
|
|
{
|
|
AddImage(392, 122, 0x25E6);
|
|
}
|
|
|
|
if (m_List.Count == 0)
|
|
{
|
|
AddLabel(12, 140, LabelHue, "There are no accounts to display.");
|
|
}
|
|
|
|
if (rads != null && notice == null)
|
|
{
|
|
AddButtonLabeled(10, 390, GetButtonID(5, 27), "Ban marked");
|
|
AddButtonLabeled(10, 410, GetButtonID(5, 28), "Delete marked");
|
|
|
|
AddButtonLabeled(210, 390, GetButtonID(5, 29), "Mark all");
|
|
AddButtonLabeled(210, 410, GetButtonID(5, 35), "Unmark house owners");
|
|
}
|
|
|
|
for (int i = 0, index = listPage * 12; i < 12 && index >= 0 && index < m_List.Count; ++i, ++index)
|
|
{
|
|
if (m_List[index] is not Account a)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var offset = 140 + i * 20;
|
|
|
|
GetAccountInfo(a, out var accessLevel, out var online);
|
|
|
|
if (rads == null)
|
|
{
|
|
AddLabelCropped(12, offset, 120, 20, LabelHue, a.Username);
|
|
}
|
|
else
|
|
{
|
|
AddCheck(10, offset, 0xD2, 0xD3, rads.Contains(a), index);
|
|
AddLabelCropped(32, offset, 100, 20, LabelHue, a.Username);
|
|
}
|
|
|
|
AddLabelCropped(132, offset, 120, 20, LabelHue, FormatAccessLevel(accessLevel));
|
|
|
|
if (online)
|
|
{
|
|
AddLabelCropped(252, offset, 120, 20, GreenHue, "Online");
|
|
}
|
|
else if (a.Banned)
|
|
{
|
|
AddLabelCropped(252, offset, 120, 20, RedHue, "Banned");
|
|
}
|
|
else
|
|
{
|
|
AddLabelCropped(252, offset, 120, 20, RedHue, "Offline");
|
|
}
|
|
|
|
AddButton(380, offset - 1, 0xFA5, 0xFA7, GetButtonID(5, index + 56));
|
|
}
|
|
|
|
break;
|
|
}
|
|
case AdminGumpPage.AccountDetails:
|
|
{
|
|
AddPageButton(
|
|
190,
|
|
10,
|
|
GetButtonID(5, 0),
|
|
"Information",
|
|
AdminGumpPage.AccountDetails_Information,
|
|
AdminGumpPage.AccountDetails_ChangeAccess,
|
|
AdminGumpPage.AccountDetails_ChangePassword
|
|
);
|
|
AddPageButton(190, 30, GetButtonID(5, 1), "Characters", AdminGumpPage.AccountDetails_Characters);
|
|
AddPageButton(
|
|
190,
|
|
50,
|
|
GetButtonID(5, 13),
|
|
"Access",
|
|
AdminGumpPage.AccountDetails_Access,
|
|
AdminGumpPage.AccountDetails_Access_ClientIPs
|
|
);
|
|
AddPageButton(190, 70, GetButtonID(5, 2), "Comments", AdminGumpPage.AccountDetails_Comments);
|
|
AddPageButton(190, 90, GetButtonID(5, 3), "Tags", AdminGumpPage.AccountDetails_Tags);
|
|
break;
|
|
}
|
|
case AdminGumpPage.AccountDetails_ChangePassword:
|
|
{
|
|
if (state is not Account a)
|
|
{
|
|
break;
|
|
}
|
|
|
|
AddHtml(10, 125, 400, 20, "Change Password".Center(LabelColor32));
|
|
|
|
AddLabel(20, 150, LabelHue, "Username:");
|
|
AddLabel(200, 150, LabelHue, a.Username);
|
|
|
|
AddLabel(20, 180, LabelHue, "Password:");
|
|
AddTextField(200, 180, 160, 20, 0);
|
|
|
|
AddLabel(20, 210, LabelHue, "Confirm:");
|
|
AddTextField(200, 210, 160, 20, 1);
|
|
|
|
AddButtonLabeled(20, 240, GetButtonID(5, 12), "Submit Change");
|
|
|
|
goto case AdminGumpPage.AccountDetails;
|
|
}
|
|
case AdminGumpPage.AccountDetails_ChangeAccess:
|
|
{
|
|
if (state is not Account a)
|
|
{
|
|
break;
|
|
}
|
|
|
|
AddHtml(10, 125, 400, 20, "Change Access Level".Center(LabelColor32));
|
|
|
|
AddLabel(20, 150, LabelHue, "Username:");
|
|
AddLabel(200, 150, LabelHue, a.Username);
|
|
|
|
AddLabel(20, 170, LabelHue, "Current Level:");
|
|
AddLabel(200, 170, LabelHue, FormatAccessLevel(a.AccessLevel));
|
|
|
|
AddButtonLabeled(20, 200, GetButtonID(5, 20), "Player");
|
|
AddButtonLabeled(20, 220, GetButtonID(5, 21), "Counselor");
|
|
AddButtonLabeled(20, 240, GetButtonID(5, 22), "Game Master");
|
|
AddButtonLabeled(20, 260, GetButtonID(5, 23), "Seer");
|
|
|
|
if (from.AccessLevel > AccessLevel.Administrator)
|
|
{
|
|
AddButtonLabeled(20, 280, GetButtonID(5, 24), "Administrator");
|
|
|
|
if (from.AccessLevel > AccessLevel.Developer)
|
|
{
|
|
AddButtonLabeled(20, 300, GetButtonID(5, 33), "Developer");
|
|
|
|
if (from.AccessLevel >= AccessLevel.Owner)
|
|
{
|
|
AddButtonLabeled(20, 320, GetButtonID(5, 34), "Owner");
|
|
}
|
|
}
|
|
}
|
|
|
|
goto case AdminGumpPage.AccountDetails;
|
|
}
|
|
case AdminGumpPage.AccountDetails_Information:
|
|
{
|
|
if (state is not Account a)
|
|
{
|
|
break;
|
|
}
|
|
|
|
var charCount = 0;
|
|
|
|
for (var i = 0; i < a.Length; ++i)
|
|
{
|
|
if (a[i] != null)
|
|
{
|
|
++charCount;
|
|
}
|
|
}
|
|
|
|
AddHtml(10, 125, 400, 20, "Information".Center(LabelColor32));
|
|
|
|
AddLabel(20, 150, LabelHue, "Username:");
|
|
AddLabel(200, 150, LabelHue, a.Username);
|
|
|
|
AddLabel(20, 170, LabelHue, "Access Level:");
|
|
AddLabel(200, 170, LabelHue, FormatAccessLevel(a.AccessLevel));
|
|
|
|
AddLabel(20, 190, LabelHue, "Status:");
|
|
AddLabel(200, 190, a.Banned ? RedHue : GreenHue, a.Banned ? "Banned" : "Active");
|
|
|
|
if (a.Banned && a.GetBanTags(out var banTime, out var banDuration))
|
|
{
|
|
if (banDuration == TimeSpan.MaxValue)
|
|
{
|
|
AddLabel(250, 190, LabelHue, "(Infinite)");
|
|
}
|
|
else if (banDuration == TimeSpan.Zero)
|
|
{
|
|
AddLabel(250, 190, LabelHue, "(Zero)");
|
|
}
|
|
else
|
|
{
|
|
var remaining = (Core.Now - banTime).Clamp(TimeSpan.Zero, banDuration);
|
|
var remMinutes = remaining.TotalMinutes;
|
|
var totMinutes = banDuration.TotalMinutes;
|
|
|
|
var perc = remMinutes / totMinutes;
|
|
|
|
AddLabel(250, 190, LabelHue, $"{FormatTimeSpan(banDuration)} [{perc * 100:F0}%]");
|
|
}
|
|
}
|
|
else if (a.Banned)
|
|
{
|
|
AddLabel(250, 190, LabelHue, "(Unspecified)");
|
|
}
|
|
|
|
AddLabel(20, 210, LabelHue, "Created:");
|
|
AddLabel(200, 210, LabelHue, a.Created.ToString());
|
|
|
|
AddLabel(20, 230, LabelHue, "Last Login:");
|
|
AddLabel(200, 230, LabelHue, a.LastLogin.ToString());
|
|
|
|
AddLabel(20, 250, LabelHue, "Character Count:");
|
|
AddLabel(200, 250, LabelHue, charCount.ToString());
|
|
|
|
AddLabel(20, 270, LabelHue, "Comment Count:");
|
|
AddLabel(200, 270, LabelHue, a.Comments.Count.ToString());
|
|
|
|
AddLabel(20, 290, LabelHue, "Tag Count:");
|
|
AddLabel(200, 290, LabelHue, a.Tags.Count.ToString());
|
|
|
|
AddButtonLabeled(20, 320, GetButtonID(5, 8), "Change Password");
|
|
AddButtonLabeled(200, 320, GetButtonID(5, 9), "Change Access Level");
|
|
|
|
if (!a.Banned)
|
|
{
|
|
AddButtonLabeled(20, 350, GetButtonID(5, 10), "Ban Account");
|
|
}
|
|
else
|
|
{
|
|
AddButtonLabeled(20, 350, GetButtonID(5, 11), "Unban Account");
|
|
}
|
|
|
|
AddButtonLabeled(200, 350, GetButtonID(5, 25), "Delete Account");
|
|
|
|
goto case AdminGumpPage.AccountDetails;
|
|
}
|
|
case AdminGumpPage.AccountDetails_Access:
|
|
{
|
|
if (state is not Account a)
|
|
{
|
|
break;
|
|
}
|
|
|
|
AddHtml(10, 125, 400, 20, "Access".Center(LabelColor32));
|
|
|
|
AddPageButton(
|
|
20,
|
|
150,
|
|
GetButtonID(5, 14),
|
|
"View client addresses",
|
|
AdminGumpPage.AccountDetails_Access_ClientIPs
|
|
);
|
|
|
|
goto case AdminGumpPage.AccountDetails;
|
|
}
|
|
case AdminGumpPage.AccountDetails_Access_ClientIPs:
|
|
{
|
|
if (state is not Account a)
|
|
{
|
|
break;
|
|
}
|
|
|
|
m_List ??= a.LoginIPs.ToList<object>();
|
|
|
|
AddHtml(10, 195, 400, 20, "Client Addresses".Center(LabelColor32));
|
|
|
|
AddButtonLabeled(227, 225, GetButtonID(5, 16), "View all shared accounts");
|
|
AddButtonLabeled(227, 245, GetButtonID(5, 17), "Ban all shared accounts");
|
|
AddButtonLabeled(227, 265, GetButtonID(5, 18), "Firewall all addresses");
|
|
AddButtonLabeled(227, 285, GetButtonID(5, 36), "Clear all addresses");
|
|
|
|
AddHtml(
|
|
225,
|
|
315,
|
|
180,
|
|
80,
|
|
"List of IP addresses which have accessed this account.".Color(LabelColor32)
|
|
);
|
|
|
|
AddImageTiled(15, 219, 206, 156, 0xBBC);
|
|
AddBlackAlpha(16, 220, 204, 154);
|
|
|
|
AddHtml(18, 221, 114, 20, "IP Address".Color(LabelColor32));
|
|
|
|
if (listPage > 0)
|
|
{
|
|
AddButton(184, 223, 0x15E3, 0x15E7, GetButtonID(1, 0));
|
|
}
|
|
else
|
|
{
|
|
AddImage(184, 223, 0x25EA);
|
|
}
|
|
|
|
if ((listPage + 1) * 6 < m_List.Count)
|
|
{
|
|
AddButton(201, 223, 0x15E1, 0x15E5, GetButtonID(1, 1));
|
|
}
|
|
else
|
|
{
|
|
AddImage(201, 223, 0x25E6);
|
|
}
|
|
|
|
if (m_List.Count == 0)
|
|
{
|
|
AddHtml(18, 243, 200, 60, "This account has not yet been accessed.".Color(LabelColor32));
|
|
}
|
|
|
|
for (int i = 0, index = listPage * 6; i < 6 && index >= 0 && index < m_List.Count; ++i, ++index)
|
|
{
|
|
AddHtml(18, 243 + i * 22, 114, 20, Html.Color($"{m_List[index]}", LabelColor32));
|
|
AddButton(130, 242 + i * 22, 0xFA2, 0xFA4, GetButtonID(8, index));
|
|
AddButton(160, 242 + i * 22, 0xFA8, 0xFAA, GetButtonID(9, index));
|
|
AddButton(190, 242 + i * 22, 0xFB1, 0xFB3, GetButtonID(10, index));
|
|
}
|
|
|
|
goto case AdminGumpPage.AccountDetails_Access;
|
|
}
|
|
case AdminGumpPage.AccountDetails_Characters:
|
|
{
|
|
if (state is not Account a)
|
|
{
|
|
break;
|
|
}
|
|
|
|
AddHtml(10, 125, 400, 20, "Characters".Center(LabelColor32));
|
|
|
|
AddLabelCropped(12, 150, 120, 20, LabelHue, "Name");
|
|
AddLabelCropped(132, 150, 120, 20, LabelHue, "Access Level");
|
|
AddLabelCropped(252, 150, 120, 20, LabelHue, "Status");
|
|
|
|
var index = 0;
|
|
|
|
for (var i = 0; i < a.Length; ++i)
|
|
{
|
|
var m = a[i];
|
|
|
|
if (m == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var offset = 170 + index * 20;
|
|
|
|
AddLabelCropped(12, offset, 120, 20, GetHueFor(m), m.Name);
|
|
AddLabelCropped(132, offset, 120, 20, LabelHue, FormatAccessLevel(m.AccessLevel));
|
|
|
|
if (m.NetState != null)
|
|
{
|
|
AddLabelCropped(252, offset, 120, 20, GreenHue, "Online");
|
|
}
|
|
else
|
|
{
|
|
AddLabelCropped(252, offset, 120, 20, RedHue, "Offline");
|
|
}
|
|
|
|
AddButton(380, offset - 1, 0xFA5, 0xFA7, GetButtonID(5, i + 50));
|
|
|
|
++index;
|
|
}
|
|
|
|
if (index == 0)
|
|
{
|
|
AddLabel(12, 170, LabelHue, "The character list is empty.");
|
|
}
|
|
|
|
goto case AdminGumpPage.AccountDetails;
|
|
}
|
|
case AdminGumpPage.AccountDetails_Comments:
|
|
{
|
|
if (state is not Account a)
|
|
{
|
|
break;
|
|
}
|
|
|
|
AddHtml(10, 125, 400, 20, "Comments".Center(LabelColor32));
|
|
|
|
AddButtonLabeled(20, 150, GetButtonID(5, 4), "Add Comment");
|
|
|
|
var sb = ValueStringBuilder.Create();
|
|
|
|
if (a.Comments.Count == 0)
|
|
{
|
|
sb.Append("There are no comments for this account.");
|
|
}
|
|
|
|
for (var i = 0; i < a.Comments.Count; ++i)
|
|
{
|
|
if (i > 0)
|
|
{
|
|
sb.Append("<BR><BR>");
|
|
}
|
|
|
|
var c = a.Comments[i];
|
|
sb.Append('[');
|
|
sb.Append(c.AddedBy);
|
|
sb.Append(" on ");
|
|
sb.Append(c.LastModified.ToString());
|
|
sb.Append("]<BR>");
|
|
sb.Append(c.Content);
|
|
}
|
|
|
|
AddHtml(20, 180, 380, 190, sb.ToString(), true, true);
|
|
|
|
sb.Dispose(); // Due to an analyzer bug, we can't use a using
|
|
goto case AdminGumpPage.AccountDetails;
|
|
}
|
|
case AdminGumpPage.AccountDetails_Tags:
|
|
{
|
|
if (state is not Account a)
|
|
{
|
|
break;
|
|
}
|
|
|
|
AddHtml(10, 125, 400, 20, "Tags".Center(LabelColor32));
|
|
|
|
AddButtonLabeled(20, 150, GetButtonID(5, 5), "Add Tag");
|
|
|
|
var sb = ValueStringBuilder.Create();
|
|
|
|
if (a.Tags.Count == 0)
|
|
{
|
|
sb.Append("There are no tags for this account.");
|
|
}
|
|
|
|
for (var i = 0; i < a.Tags.Count; ++i)
|
|
{
|
|
if (i > 0)
|
|
{
|
|
sb.Append("<BR>");
|
|
}
|
|
|
|
var tag = a.Tags[i];
|
|
|
|
sb.Append(tag.Name);
|
|
sb.Append(" = ");
|
|
sb.Append(tag.Value);
|
|
}
|
|
|
|
AddHtml(20, 180, 380, 190, sb.ToString(), true, true);
|
|
|
|
sb.Dispose();
|
|
goto case AdminGumpPage.AccountDetails;
|
|
}
|
|
case AdminGumpPage.Firewall:
|
|
{
|
|
AddFirewallHeader();
|
|
|
|
if (m_List == null)
|
|
{
|
|
Firewall.ReadFirewallSet(firewallSet =>
|
|
{
|
|
list = new List<object>(firewallSet.Count);
|
|
list.AddRange(firewallSet);
|
|
});
|
|
|
|
m_List = list;
|
|
}
|
|
|
|
AddLabelCropped(12, 120, 358, 20, LabelHue, "IP Address");
|
|
|
|
if (listPage > 0)
|
|
{
|
|
AddButton(375, 122, 0x15E3, 0x15E7, GetButtonID(1, 0));
|
|
}
|
|
else
|
|
{
|
|
AddImage(375, 122, 0x25EA);
|
|
}
|
|
|
|
if ((listPage + 1) * 12 < m_List!.Count)
|
|
{
|
|
AddButton(392, 122, 0x15E1, 0x15E5, GetButtonID(1, 1));
|
|
}
|
|
else
|
|
{
|
|
AddImage(392, 122, 0x25E6);
|
|
}
|
|
|
|
if (m_List.Count == 0)
|
|
{
|
|
AddLabel(12, 140, LabelHue, "The firewall list is empty.");
|
|
}
|
|
else
|
|
{
|
|
var i = 0;
|
|
var index = listPage * 12;
|
|
foreach (var firewallEntry in m_List)
|
|
{
|
|
if (i >= 12)
|
|
{
|
|
break;
|
|
}
|
|
|
|
var offset = 140 + i++ * 20;
|
|
|
|
AddLabelCropped(12, offset, 358, 20, LabelHue, firewallEntry.ToString());
|
|
AddButton(380, offset - 1, 0xFA5, 0xFA7, GetButtonID(6, index++ + 4));
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case AdminGumpPage.FirewallInfo:
|
|
{
|
|
AddFirewallHeader();
|
|
|
|
if (state is not IFirewallEntry firewallEntry)
|
|
{
|
|
break;
|
|
}
|
|
|
|
AddHtml(10, 125, 400, 20, Html.Center($"{firewallEntry}", LabelColor32));
|
|
|
|
AddButtonLabeled(20, 150, GetButtonID(6, 3), "Remove");
|
|
|
|
AddHtml(10, 175, 400, 20, "Potentially Affected Accounts".Center(LabelColor32));
|
|
|
|
if (m_List == null)
|
|
{
|
|
using var blockedEntriesList = PooledRefList<Account>.Create();
|
|
|
|
foreach (var ia in Accounts.GetAccounts())
|
|
{
|
|
var acct = (Account)ia;
|
|
|
|
var loginList = acct.LoginIPs;
|
|
|
|
for (var i = 0; i < loginList.Length; ++i)
|
|
{
|
|
if (firewallEntry.IsBlocked(loginList[i]))
|
|
{
|
|
blockedEntriesList.Add(acct);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
blockedEntriesList.Sort(AccountComparer.Instance);
|
|
|
|
m_List = blockedEntriesList.ToList<Account, object>();
|
|
}
|
|
|
|
if (listPage > 0)
|
|
{
|
|
AddButton(375, 177, 0x15E3, 0x15E7, GetButtonID(1, 0));
|
|
}
|
|
else
|
|
{
|
|
AddImage(375, 177, 0x25EA);
|
|
}
|
|
|
|
if ((listPage + 1) * 12 < m_List.Count)
|
|
{
|
|
AddButton(392, 177, 0x15E1, 0x15E5, GetButtonID(1, 1));
|
|
}
|
|
else
|
|
{
|
|
AddImage(392, 177, 0x25E6);
|
|
}
|
|
|
|
if (m_List.Count == 0)
|
|
{
|
|
AddLabelCropped(12, 200, 398, 20, LabelHue, "No accounts found.");
|
|
}
|
|
|
|
for (int i = 0, index = listPage * 9;
|
|
i < 9 && index >= 0 && index < m_List.Count;
|
|
++i, ++index)
|
|
{
|
|
if (m_List[index] is not Account a)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var offset = 200 + i * 20;
|
|
|
|
GetAccountInfo(a, out var accessLevel, out var online);
|
|
|
|
AddLabelCropped(12, offset, 120, 20, LabelHue, a.Username);
|
|
AddLabelCropped(132, offset, 120, 20, LabelHue, FormatAccessLevel(accessLevel));
|
|
|
|
if (online)
|
|
{
|
|
AddLabelCropped(252, offset, 120, 20, GreenHue, "Online");
|
|
}
|
|
else if (a.Banned)
|
|
{
|
|
AddLabelCropped(252, offset, 120, 20, RedHue, "Banned");
|
|
}
|
|
else
|
|
{
|
|
AddLabelCropped(252, offset, 120, 20, RedHue, "Offline");
|
|
}
|
|
|
|
AddButton(380, offset - 1, 0xFA5, 0xFA7, GetButtonID(5, index + 56));
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void AddPageButton(
|
|
int x, int y, int buttonID, string text, AdminGumpPage page,
|
|
params ReadOnlySpan<AdminGumpPage> subPages
|
|
)
|
|
{
|
|
var isSelection = m_PageType == page;
|
|
|
|
for (var i = 0; !isSelection && i < subPages.Length; ++i)
|
|
{
|
|
isSelection = m_PageType == subPages[i];
|
|
}
|
|
|
|
AddSelectedButton(x, y, buttonID, text, isSelection);
|
|
}
|
|
|
|
public void AddSelectedButton(int x, int y, int buttonID, string text, bool isSelection)
|
|
{
|
|
AddButton(x, y - 1, isSelection ? 4006 : 4005, 4007, buttonID);
|
|
AddHtml(x + 35, y, 200, 20, text.Color(isSelection ? SelectedColor32 : LabelColor32));
|
|
}
|
|
|
|
public void AddButtonLabeled(int x, int y, int buttonID, string text)
|
|
{
|
|
AddButton(x, y - 1, 4005, 4007, buttonID);
|
|
AddHtml(x + 35, y, 240, 20, text.Color(LabelColor32));
|
|
}
|
|
|
|
public void AddBlackAlpha(int x, int y, int width, int height)
|
|
{
|
|
AddImageTiled(x, y, width, height, 2624);
|
|
AddAlphaRegion(x, y, width, height);
|
|
}
|
|
|
|
public int GetButtonID(int type, int index) => 1 + index * 11 + type;
|
|
|
|
public static string FormatTimeSpan(TimeSpan ts) =>
|
|
$"{ts.Days:D2}:{ts.Hours % 24:D2}:{ts.Minutes % 60:D2}:{ts.Seconds % 60:D2}";
|
|
|
|
public static string FormatByteAmount(long totalBytes)
|
|
{
|
|
return totalBytes switch
|
|
{
|
|
> 1000000000 => $"{(double)totalBytes / 1073741824:F1} GB",
|
|
> 1000000 => $"{(double)totalBytes / 1048576:F1} MB",
|
|
> 1000 => $"{(double)totalBytes / 1024:F1} KB",
|
|
_ => $"{totalBytes} Bytes"
|
|
};
|
|
}
|
|
|
|
public static void Configure()
|
|
{
|
|
CommandSystem.Register("Admin", AccessLevel.Administrator, Admin_OnCommand);
|
|
}
|
|
|
|
[Usage("Admin"), Description(
|
|
"Opens an interface providing server information and administration features including client, account, and firewall management."
|
|
)]
|
|
public static void Admin_OnCommand(CommandEventArgs e)
|
|
{
|
|
e.Mobile.SendGump(new AdminGump(e.Mobile, AdminGumpPage.Clients));
|
|
}
|
|
|
|
public static int GetHueFor(Mobile m)
|
|
{
|
|
if (m == null)
|
|
{
|
|
return LabelHue;
|
|
}
|
|
|
|
switch (m.AccessLevel)
|
|
{
|
|
case AccessLevel.Owner:
|
|
case AccessLevel.Developer:
|
|
case AccessLevel.Administrator:
|
|
{
|
|
return 0x516;
|
|
}
|
|
case AccessLevel.Seer:
|
|
{
|
|
return 0x144;
|
|
}
|
|
case AccessLevel.GameMaster:
|
|
{
|
|
return 0x21;
|
|
}
|
|
case AccessLevel.Counselor:
|
|
{
|
|
return 0x2;
|
|
}
|
|
default:
|
|
{
|
|
if (m.Murderer)
|
|
{
|
|
return 0x21;
|
|
}
|
|
|
|
return m.Criminal ? 0x3B1 : 0x58;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static string FormatAccessLevel(AccessLevel level)
|
|
{
|
|
var v = (int)level;
|
|
|
|
if (v >= 0 && v < m_AccessLevelStrings.Length)
|
|
{
|
|
return m_AccessLevelStrings[v];
|
|
}
|
|
|
|
return "Unknown";
|
|
}
|
|
|
|
public void AddTextField(int x, int y, int width, int height, int index)
|
|
{
|
|
AddBackground(x - 2, y - 2, width + 4, height + 4, 0x2486);
|
|
AddTextEntry(x + 2, y + 2, width - 4, height - 4, 0, index, "");
|
|
}
|
|
|
|
public void AddClientHeader()
|
|
{
|
|
AddTextField(200, 20, 200, 20, 0);
|
|
AddButtonLabeled(200, 50, GetButtonID(4, 0), "Search For Name");
|
|
AddButtonLabeled(200, 80, GetButtonID(4, 1), "Search For IP Address");
|
|
}
|
|
|
|
public void AddAccountHeader()
|
|
{
|
|
AddPage(1);
|
|
|
|
AddLabel(200, 20, LabelHue, "Name:");
|
|
AddTextField(250, 20, 150, 20, 0);
|
|
|
|
AddLabel(200, 50, LabelHue, "Pass:");
|
|
AddTextField(250, 50, 150, 20, 1);
|
|
|
|
AddButtonLabeled(200, 80, GetButtonID(5, 6), "Add");
|
|
AddButtonLabeled(290, 80, GetButtonID(5, 7), "Search");
|
|
|
|
AddButton(384, 84, 0x15E1, 0x15E5, 0, GumpButtonType.Page, 2);
|
|
|
|
AddPage(2);
|
|
|
|
AddButtonLabeled(200, 20, GetButtonID(5, 31), "View All: Inactive");
|
|
AddButtonLabeled(200, 40, GetButtonID(5, 32), "View All: Banned");
|
|
AddButtonLabeled(200, 60, GetButtonID(5, 26), "View All: Shared");
|
|
AddButtonLabeled(200, 80, GetButtonID(5, 30), "View All: Empty");
|
|
|
|
AddButton(384, 84, 0x15E1, 0x15E5, 0, GumpButtonType.Page, 1);
|
|
|
|
AddPage(0);
|
|
}
|
|
|
|
public void AddFirewallHeader()
|
|
{
|
|
AddTextField(200, 20, 200, 20, 0);
|
|
AddButtonLabeled(320, 50, GetButtonID(6, 0), "Search");
|
|
AddButtonLabeled(200, 50, GetButtonID(6, 1), "Add (Input)");
|
|
AddButtonLabeled(200, 80, GetButtonID(6, 2), "Add (Target)");
|
|
}
|
|
|
|
private static List<object> GetAllSharedAccounts()
|
|
{
|
|
var table = new Dictionary<IPAddress, List<Account>>();
|
|
|
|
foreach (Account acct in Accounts.GetAccounts())
|
|
{
|
|
var theirAddresses = acct.LoginIPs;
|
|
|
|
for (var i = 0; i < theirAddresses.Length; ++i)
|
|
{
|
|
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 list = new List<object>();
|
|
|
|
// Lets find all the entries that have only one account
|
|
foreach (var kvp in table)
|
|
{
|
|
if (kvp.Value.Count > 1)
|
|
{
|
|
// Sort the accounts alphabetically
|
|
kvp.Value.Sort(AccountComparer.Instance);
|
|
|
|
// Can't avoid boxing because `m_List` in AdminGump is expecting List<object>
|
|
list.Add(kvp);
|
|
}
|
|
}
|
|
|
|
// Sort by highest accounts per IP first
|
|
list.Sort(SharedAccountDescendingComparer.Instance);
|
|
|
|
return list;
|
|
}
|
|
|
|
private static List<Account> GetSharedAccounts(IPAddress ipAddress)
|
|
{
|
|
var list = new List<Account>();
|
|
|
|
foreach (var account in Accounts.GetAccounts())
|
|
{
|
|
var acct = (Account)account;
|
|
|
|
var theirAddresses = acct.LoginIPs;
|
|
var contains = false;
|
|
|
|
for (var i = 0; !contains && i < theirAddresses.Length; ++i)
|
|
{
|
|
contains = ipAddress.Equals(theirAddresses[i]);
|
|
}
|
|
|
|
if (contains)
|
|
{
|
|
list.Add(acct);
|
|
}
|
|
}
|
|
|
|
list.Sort(AccountComparer.Instance);
|
|
return list;
|
|
}
|
|
|
|
private static List<Account> GetSharedAccounts(IPAddress[] ipAddresses)
|
|
{
|
|
var list = new List<Account>();
|
|
|
|
foreach (Account acct in Accounts.GetAccounts())
|
|
{
|
|
var theirAddresses = acct.LoginIPs;
|
|
var contains = false;
|
|
|
|
for (var i = 0; !contains && i < theirAddresses.Length; ++i)
|
|
{
|
|
var check = theirAddresses[i];
|
|
|
|
for (var j = 0; !contains && j < ipAddresses.Length; ++j)
|
|
{
|
|
contains = check.Equals(ipAddresses[j]);
|
|
}
|
|
}
|
|
|
|
if (contains)
|
|
{
|
|
list.Add(acct);
|
|
}
|
|
}
|
|
|
|
list.Sort(AccountComparer.Instance);
|
|
return list;
|
|
}
|
|
|
|
public static void BanShared_Callback(Mobile from, bool okay, Account a)
|
|
{
|
|
if (from.AccessLevel < AccessLevel.Administrator)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string notice;
|
|
List<Account> list = null;
|
|
|
|
if (okay)
|
|
{
|
|
list = GetSharedAccounts(a.LoginIPs);
|
|
|
|
for (var i = 0; i < list.Count; ++i)
|
|
{
|
|
list[i].SetUnspecifiedBan(from);
|
|
list[i].Banned = true;
|
|
}
|
|
|
|
notice = "All addresses in the list have been banned.";
|
|
}
|
|
else
|
|
{
|
|
notice = "You have chosen not to ban all shared accounts.";
|
|
}
|
|
|
|
from.SendGump(new AdminGump(from, AdminGumpPage.AccountDetails_Access_ClientIPs, 0, null, notice, a));
|
|
|
|
if (okay)
|
|
{
|
|
from.SendGump(new BanDurationGump(list));
|
|
}
|
|
}
|
|
|
|
public static void AccountDelete_Callback(Mobile from, bool okay, Account a)
|
|
{
|
|
if (from.AccessLevel < AccessLevel.Administrator)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (okay)
|
|
{
|
|
CommandLogging.WriteLine(
|
|
from,
|
|
$"{from.AccessLevel} {CommandLogging.Format(from)} deleting account {a.Username}"
|
|
);
|
|
a.Delete();
|
|
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.Accounts,
|
|
0,
|
|
null,
|
|
$"{a.Username} : The account has been deleted."
|
|
)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Information,
|
|
0,
|
|
null,
|
|
"You have chosen not to delete the account.",
|
|
a
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
public static void ResendGump_Callback(Mobile from, List<object> list, List<Account> rads, int page)
|
|
{
|
|
if (from.AccessLevel < AccessLevel.Administrator)
|
|
{
|
|
return;
|
|
}
|
|
|
|
from.SendGump(new AdminGump(from, AdminGumpPage.Accounts, page, list, null, rads));
|
|
}
|
|
|
|
public static void Marked_Callback(Mobile from, bool okay, bool ban, List<object> list, List<Account> rads, int page)
|
|
{
|
|
if (from.AccessLevel < AccessLevel.Administrator)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (okay)
|
|
{
|
|
if (!ban)
|
|
{
|
|
NetState.FlushAll();
|
|
}
|
|
|
|
for (var i = 0; i < rads.Count; ++i)
|
|
{
|
|
var acct = rads[i];
|
|
|
|
if (ban)
|
|
{
|
|
CommandLogging.WriteLine(
|
|
from,
|
|
$"{from.AccessLevel} {CommandLogging.Format(from)} banning account {acct.Username}"
|
|
);
|
|
acct.SetUnspecifiedBan(from);
|
|
acct.Banned = true;
|
|
}
|
|
else
|
|
{
|
|
CommandLogging.WriteLine(
|
|
from,
|
|
$"{from.AccessLevel} {CommandLogging.Format(from)} deleting account {acct.Username}"
|
|
);
|
|
acct.Delete();
|
|
rads.RemoveAt(i--);
|
|
list.Remove(acct);
|
|
}
|
|
}
|
|
|
|
from.SendGump(
|
|
new AdminNoticeGump(
|
|
$"You have {(ban ? "banned" : "deleted")} the account{(rads.Count == 1 ? "" : "s")}.",
|
|
() => ResendGump_Callback(from, list, rads, ban ? page : 0)
|
|
)
|
|
);
|
|
|
|
if (ban)
|
|
{
|
|
from.SendGump(new BanDurationGump(rads));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
from.SendGump(
|
|
new AdminNoticeGump(
|
|
$"You have chosen not to {(ban ? "ban" : "delete")} the account{(rads.Count == 1 ? "" : "s")}.",
|
|
() => ResendGump_Callback(from, list, rads, page)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
public static void FirewallShared_Callback(Mobile from, bool okay, Account a)
|
|
{
|
|
if (from.AccessLevel < AccessLevel.Administrator)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string notice;
|
|
|
|
if (okay)
|
|
{
|
|
for (var i = 0; i < a.LoginIPs.Length; ++i)
|
|
{
|
|
AdminFirewall.Add(a.LoginIPs[i]);
|
|
}
|
|
|
|
notice = "All addresses in the list have been firewalled.";
|
|
}
|
|
else
|
|
{
|
|
notice = "You have chosen not to firewall all addresses.";
|
|
}
|
|
|
|
from.SendGump(new AdminGump(from, AdminGumpPage.AccountDetails_Access_ClientIPs, 0, null, notice, a));
|
|
}
|
|
|
|
public static void Firewall_Callback(Mobile from, bool okay, Account a, object toFirewall)
|
|
{
|
|
if (from.AccessLevel < AccessLevel.Administrator)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string notice;
|
|
|
|
if (okay)
|
|
{
|
|
AdminFirewall.Add(toFirewall);
|
|
|
|
notice = $"{toFirewall} : Added to firewall.";
|
|
}
|
|
else
|
|
{
|
|
notice = "You have chosen not to firewall the address.";
|
|
}
|
|
|
|
from.SendGump(new AdminGump(from, AdminGumpPage.AccountDetails_Access_ClientIPs, 0, null, notice, a));
|
|
}
|
|
|
|
public static void RemoveLoginIP_Callback(Mobile from, bool okay, Account a, IPAddress ip)
|
|
{
|
|
if (from.AccessLevel < AccessLevel.Administrator)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string notice;
|
|
|
|
if (okay)
|
|
{
|
|
var ips = a.LoginIPs;
|
|
|
|
if (ips.Length != 0 && Equals(ip, ips[0]) && AccountHandler.IPTable.ContainsKey(ips[0]))
|
|
{
|
|
--AccountHandler.IPTable[ip];
|
|
}
|
|
|
|
var newList = new List<IPAddress>(ips);
|
|
newList.Remove(ip);
|
|
a.LoginIPs = newList.ToArray();
|
|
|
|
notice = $"{ip} : Removed address.";
|
|
}
|
|
else
|
|
{
|
|
notice = "You have chosen not to remove the address.";
|
|
}
|
|
|
|
from.SendGump(new AdminGump(from, AdminGumpPage.AccountDetails_Access_ClientIPs, 0, null, notice, a));
|
|
}
|
|
|
|
public static void RemoveLoginIPs_Callback(Mobile from, bool okay, Account a)
|
|
{
|
|
if (from.AccessLevel < AccessLevel.Administrator)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string notice;
|
|
|
|
if (okay)
|
|
{
|
|
var ips = a.LoginIPs;
|
|
|
|
if (ips.Length != 0 && AccountHandler.IPTable.ContainsKey(ips[0]))
|
|
{
|
|
--AccountHandler.IPTable[ips[0]];
|
|
}
|
|
|
|
a.LoginIPs = Array.Empty<IPAddress>();
|
|
|
|
notice = "All addresses in the list have been removed.";
|
|
}
|
|
else
|
|
{
|
|
notice = "You have chosen not to clear all addresses.";
|
|
}
|
|
|
|
from.SendGump(new AdminGump(from, AdminGumpPage.AccountDetails_Access_ClientIPs, 0, null, notice, a));
|
|
}
|
|
|
|
public override void OnResponse(NetState sender, in RelayInfo info)
|
|
{
|
|
var val = info.ButtonID - 1;
|
|
|
|
if (val < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var from = m_From;
|
|
|
|
if (from.AccessLevel < AccessLevel.Administrator)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (m_PageType == AdminGumpPage.Accounts)
|
|
{
|
|
if (m_State is List<Account> rads)
|
|
{
|
|
for (int i = 0, v = m_ListPage * 12; i < 12 && v < m_List.Count; ++i, ++v)
|
|
{
|
|
var acct = (Account)m_List[v];
|
|
|
|
if (info.IsSwitched(v))
|
|
{
|
|
if (!rads.Contains(acct))
|
|
{
|
|
rads.Add(acct);
|
|
}
|
|
}
|
|
else if (rads.Contains(acct))
|
|
{
|
|
rads.Remove(acct);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var type = val % 11;
|
|
var index = val / 11;
|
|
|
|
switch (type)
|
|
{
|
|
case 0:
|
|
{
|
|
AdminGumpPage page;
|
|
|
|
switch (index)
|
|
{
|
|
case 0:
|
|
{
|
|
page = AdminGumpPage.Information_General;
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
page = AdminGumpPage.Administer;
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
page = AdminGumpPage.Clients;
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
page = AdminGumpPage.Accounts;
|
|
break;
|
|
}
|
|
case 4:
|
|
{
|
|
page = AdminGumpPage.Firewall;
|
|
break;
|
|
}
|
|
case 5:
|
|
{
|
|
page = AdminGumpPage.Information_Perf;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
from.SendGump(new AdminGump(from, page));
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
switch (index)
|
|
{
|
|
case 0:
|
|
{
|
|
if (m_List != null && m_ListPage > 0)
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(from, m_PageType, m_ListPage - 1, m_List, null, m_State)
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
if (m_List != null /*&& (m_ListPage + 1) * 12 < m_List.Count*/)
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(from, m_PageType, m_ListPage + 1, m_List, null, m_State)
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
string notice = null;
|
|
var page = AdminGumpPage.Administer;
|
|
|
|
if (index >= 500)
|
|
{
|
|
page = AdminGumpPage.Administer_Access_Lockdown;
|
|
}
|
|
else if (index >= 400)
|
|
{
|
|
page = AdminGumpPage.Administer_Commands;
|
|
}
|
|
else if (index >= 300)
|
|
{
|
|
page = AdminGumpPage.Administer_Access;
|
|
}
|
|
else if (index >= 200)
|
|
{
|
|
page = AdminGumpPage.Administer_Server;
|
|
}
|
|
else if (index >= 100)
|
|
{
|
|
page = AdminGumpPage.Administer_WorldBuilding;
|
|
}
|
|
|
|
switch (index)
|
|
{
|
|
case 0:
|
|
{
|
|
page = AdminGumpPage.Administer_WorldBuilding;
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
page = AdminGumpPage.Administer_Server;
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
page = AdminGumpPage.Administer_Access;
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
page = AdminGumpPage.Administer_Commands;
|
|
break;
|
|
}
|
|
|
|
case 101:
|
|
{
|
|
InvokeCommand("TelGen");
|
|
notice = "Teleporters have been generated.";
|
|
break;
|
|
}
|
|
case 102:
|
|
{
|
|
InvokeCommand("MoonGen");
|
|
notice = "Moongates have been generated.";
|
|
break;
|
|
}
|
|
case 104:
|
|
{
|
|
InvokeCommand("DoorGen");
|
|
notice = "Doors have been generated.";
|
|
break;
|
|
}
|
|
case 103:
|
|
{
|
|
GenerateSpawners();
|
|
notice = "Spawners have been generated.";
|
|
break;
|
|
}
|
|
case 105:
|
|
{
|
|
InvokeCommand("SignGen");
|
|
notice = "Signs have been generated.";
|
|
break;
|
|
}
|
|
case 106:
|
|
{
|
|
InvokeCommand("Decorate");
|
|
notice = "Decorations have been generated.";
|
|
break;
|
|
}
|
|
case 107:
|
|
{
|
|
InvokeCommand("GenChamps");
|
|
notice = "Champions have been generated.";
|
|
break;
|
|
}
|
|
case 108:
|
|
{
|
|
InvokeCommand("DecorateMag");
|
|
notice = "Magincia Ruins decorations have been generated.";
|
|
break;
|
|
}
|
|
case 109:
|
|
{
|
|
InvokeCommand("GenStealArties");
|
|
notice = "Stealable Artifacts have been generated.";
|
|
break;
|
|
}
|
|
case 110:
|
|
{
|
|
InvokeCommand("SHTelGen");
|
|
notice = "Solen Hive teleporters have been generated.";
|
|
break;
|
|
}
|
|
case 111:
|
|
{
|
|
InvokeCommand("SecretLocGen");
|
|
notice = "Secret Locations have been generated.";
|
|
break;
|
|
}
|
|
case 112:
|
|
{
|
|
InvokeCommand("GenLeverPuzzle");
|
|
InvokeCommand("GenGauntlet");
|
|
notice = "Doom has been generated.";
|
|
break;
|
|
}
|
|
case 113:
|
|
{
|
|
InvokeCommand("GenKhaldun");
|
|
notice = "Khaldun puzzles have been generated.";
|
|
break;
|
|
}
|
|
|
|
case 114:
|
|
{
|
|
// 101
|
|
InvokeCommand("TelGen");
|
|
|
|
// 102
|
|
InvokeCommand("MoonGen");
|
|
|
|
// 103
|
|
InvokeCommand("DoorGen");
|
|
|
|
// 104
|
|
GenerateSpawners();
|
|
|
|
// 105
|
|
InvokeCommand("SignGen");
|
|
|
|
// 106
|
|
InvokeCommand("Decorate");
|
|
|
|
// 107
|
|
InvokeCommand("GenChamps");
|
|
|
|
// 108
|
|
InvokeCommand("DecorateMag");
|
|
|
|
// 109
|
|
InvokeCommand("GenStealArties");
|
|
|
|
// 110
|
|
InvokeCommand("SHTelGen");
|
|
|
|
// 111
|
|
InvokeCommand("SecretLocGen");
|
|
|
|
// 112
|
|
InvokeCommand("GenLeverPuzzle");
|
|
InvokeCommand("GenGauntlet");
|
|
|
|
// 113
|
|
InvokeCommand("GenKhaldun");
|
|
|
|
notice = "All the above options have been generated at once.";
|
|
break;
|
|
}
|
|
|
|
case 200:
|
|
{
|
|
InvokeCommand("Save");
|
|
notice = "The world has been saved.";
|
|
break;
|
|
}
|
|
case 201:
|
|
{
|
|
Shutdown(false, true);
|
|
break;
|
|
}
|
|
case 202:
|
|
{
|
|
Shutdown(false, false);
|
|
break;
|
|
}
|
|
case 203:
|
|
{
|
|
Shutdown(true, true);
|
|
break;
|
|
}
|
|
case 204:
|
|
{
|
|
Shutdown(true, false);
|
|
break;
|
|
}
|
|
case 205: // shutdown with delay and save
|
|
{
|
|
var t = new ShutdownTimer(this);
|
|
t.Start();
|
|
break;
|
|
}
|
|
case 210:
|
|
case 211:
|
|
{
|
|
var text = info.GetTextEntry(0)?.Trim();
|
|
|
|
if (string.IsNullOrEmpty(text))
|
|
{
|
|
notice = "You must enter text to broadcast it.";
|
|
}
|
|
else
|
|
{
|
|
notice = "Your message has been broadcasted.";
|
|
InvokeCommand($"{(index == 210 ? "BC" : "SM")} {text}");
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 300:
|
|
{
|
|
InvokeCommand("Kick");
|
|
notice = "Target the player to kick.";
|
|
break;
|
|
}
|
|
case 301:
|
|
{
|
|
InvokeCommand("Ban");
|
|
notice = "Target the player to ban.";
|
|
break;
|
|
}
|
|
case 302:
|
|
{
|
|
InvokeCommand("Firewall");
|
|
notice = "Target the player to firewall.";
|
|
break;
|
|
}
|
|
|
|
case 303:
|
|
{
|
|
page = AdminGumpPage.Administer_Access_Lockdown;
|
|
break;
|
|
}
|
|
|
|
case 310:
|
|
{
|
|
InvokeCommand("Set AccessLevel Player");
|
|
notice = "Target the player to change their access level. (Player)";
|
|
break;
|
|
}
|
|
case 311:
|
|
{
|
|
InvokeCommand("Set AccessLevel Counselor");
|
|
notice = "Target the player to change their access level. (Counselor)";
|
|
break;
|
|
}
|
|
case 312:
|
|
{
|
|
InvokeCommand("Set AccessLevel GameMaster");
|
|
notice = "Target the player to change their access level. (Game Master)";
|
|
break;
|
|
}
|
|
case 313:
|
|
{
|
|
InvokeCommand("Set AccessLevel Seer");
|
|
notice = "Target the player to change their access level. (Seer)";
|
|
break;
|
|
}
|
|
|
|
case 314:
|
|
{
|
|
if (from.AccessLevel > AccessLevel.Administrator)
|
|
{
|
|
InvokeCommand("Set AccessLevel Administrator");
|
|
notice = "Target the player to change their access level. (Administrator)";
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 315:
|
|
{
|
|
if (from.AccessLevel > AccessLevel.Developer)
|
|
{
|
|
InvokeCommand("Set AccessLevel Developer");
|
|
notice = "Target the player to change their access level. (Developer)";
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 316:
|
|
{
|
|
if (from.AccessLevel >= AccessLevel.Owner)
|
|
{
|
|
InvokeCommand("Set AccessLevel Owner");
|
|
notice = "Target the player to change their access level. (Owner)";
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 400:
|
|
{
|
|
notice = "Enter search terms to add objects.";
|
|
break;
|
|
}
|
|
case 401:
|
|
{
|
|
InvokeCommand("Remove");
|
|
notice = "Target the item or mobile to remove.";
|
|
break;
|
|
}
|
|
case 402:
|
|
{
|
|
InvokeCommand("Dupe");
|
|
notice = "Target the item to dupe.";
|
|
break;
|
|
}
|
|
case 403:
|
|
{
|
|
InvokeCommand("DupeInBag");
|
|
notice = "Target the item to dupe. The item will be duped at it's current location.";
|
|
break;
|
|
}
|
|
case 404:
|
|
{
|
|
InvokeCommand("Props");
|
|
notice = "Target the item or mobile to inspect.";
|
|
break;
|
|
}
|
|
case 405:
|
|
{
|
|
InvokeCommand("Skills");
|
|
notice = "Target a mobile to view their skills.";
|
|
break;
|
|
}
|
|
case 406:
|
|
{
|
|
InvokeCommand("Set Blessed False");
|
|
notice = "Target the mobile to make mortal.";
|
|
break;
|
|
}
|
|
case 407:
|
|
{
|
|
InvokeCommand("Set Blessed True");
|
|
notice = "Target the mobile to make immortal.";
|
|
break;
|
|
}
|
|
case 408:
|
|
{
|
|
InvokeCommand("Set Squelched True");
|
|
notice = "Target the mobile to squelch.";
|
|
break;
|
|
}
|
|
case 409:
|
|
{
|
|
InvokeCommand("Set Squelched False");
|
|
notice = "Target the mobile to unsquelch.";
|
|
break;
|
|
}
|
|
case 410:
|
|
{
|
|
InvokeCommand("Set Frozen True");
|
|
notice = "Target the mobile to freeze.";
|
|
break;
|
|
}
|
|
case 411:
|
|
{
|
|
InvokeCommand("Set Frozen False");
|
|
notice = "Target the mobile to unfreeze.";
|
|
break;
|
|
}
|
|
case 412:
|
|
{
|
|
InvokeCommand("Set Hidden True");
|
|
notice = "Target the mobile to hide.";
|
|
break;
|
|
}
|
|
case 413:
|
|
{
|
|
InvokeCommand("Set Hidden False");
|
|
notice = "Target the mobile to unhide.";
|
|
break;
|
|
}
|
|
case 414:
|
|
{
|
|
InvokeCommand("Kill");
|
|
notice = "Target the mobile to kill.";
|
|
break;
|
|
}
|
|
case 415:
|
|
{
|
|
InvokeCommand("Resurrect");
|
|
notice = "Target the mobile to resurrect.";
|
|
break;
|
|
}
|
|
case 416:
|
|
{
|
|
InvokeCommand("Move");
|
|
notice = "Target the item or mobile to move.";
|
|
break;
|
|
}
|
|
case 417:
|
|
{
|
|
InvokeCommand("Wipe");
|
|
notice = "Target bounding points.";
|
|
break;
|
|
}
|
|
case 418:
|
|
{
|
|
InvokeCommand("Tele");
|
|
notice = "Choose your destination.";
|
|
break;
|
|
}
|
|
case 419:
|
|
{
|
|
InvokeCommand("Multi Tele");
|
|
notice = "Choose your destination.";
|
|
break;
|
|
}
|
|
|
|
case 500:
|
|
case 501:
|
|
case 502:
|
|
case 503:
|
|
case 504:
|
|
{
|
|
AccountHandler.LockdownLevel = (AccessLevel)(index - 500);
|
|
|
|
if (AccountHandler.LockdownLevel > AccessLevel.Player)
|
|
{
|
|
notice = "The lockdown level has been changed.";
|
|
}
|
|
else
|
|
{
|
|
notice = "The server is now accessible to everyone.";
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 510:
|
|
{
|
|
var level = AccountHandler.LockdownLevel;
|
|
|
|
if (level > AccessLevel.Player)
|
|
{
|
|
var count = 0;
|
|
|
|
foreach (var ns in NetState.Instances)
|
|
{
|
|
var a = ns.Account;
|
|
|
|
if (a == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var hasAccess = false;
|
|
|
|
if (a.AccessLevel >= level)
|
|
{
|
|
hasAccess = true;
|
|
}
|
|
else
|
|
{
|
|
for (var j = 0; !hasAccess && j < a.Length; ++j)
|
|
{
|
|
var m = a[j];
|
|
|
|
if (m?.AccessLevel >= level)
|
|
{
|
|
hasAccess = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!hasAccess)
|
|
{
|
|
ns.Disconnect("Server has been locked down.");
|
|
++count;
|
|
}
|
|
}
|
|
|
|
if (count == 0)
|
|
{
|
|
notice = "Nobody without access was found to disconnect.";
|
|
}
|
|
else
|
|
{
|
|
notice = $"Number of players disconnected: {count}";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
notice = "The server is not currently locked down.";
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
from.SendGump(new AdminGump(from, page, 0, null, notice));
|
|
|
|
switch (index)
|
|
{
|
|
case 400:
|
|
{
|
|
InvokeCommand("Add");
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 4:
|
|
{
|
|
switch (index)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
{
|
|
var forName = index == 0;
|
|
|
|
var results = new List<NetState>();
|
|
|
|
var match = info.GetTextEntry(0)?.Trim().ToLower();
|
|
string notice = null;
|
|
|
|
if (string.IsNullOrEmpty(match))
|
|
{
|
|
notice = $"You must enter {(forName ? "a name" : "an ip address")} to search.";
|
|
}
|
|
else
|
|
{
|
|
foreach (var ns in NetState.Instances)
|
|
{
|
|
bool isMatch;
|
|
|
|
if (forName)
|
|
{
|
|
var m = ns.Mobile;
|
|
var a = ns.Account;
|
|
|
|
isMatch = m?.Name.InsensitiveContains(match) == true
|
|
|| a?.Username.InsensitiveContains(match) == true;
|
|
}
|
|
else
|
|
{
|
|
isMatch = ns.ToString().ContainsOrdinal(match);
|
|
}
|
|
|
|
if (isMatch)
|
|
{
|
|
results.Add(ns);
|
|
}
|
|
}
|
|
|
|
results.Sort(NetStateComparer.Instance);
|
|
}
|
|
|
|
if (results.Count == 1)
|
|
{
|
|
var ns = results[0];
|
|
var state = ns.Mobile ?? (object)ns.Account;
|
|
|
|
if (state is Mobile)
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.ClientInfo,
|
|
0,
|
|
null,
|
|
"One match found.",
|
|
state
|
|
)
|
|
);
|
|
}
|
|
else if (state is Account)
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Information,
|
|
0,
|
|
null,
|
|
"One match found.",
|
|
state
|
|
)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.Clients,
|
|
0,
|
|
results.ToList<object>(),
|
|
"One match found."
|
|
)
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.Clients,
|
|
0,
|
|
results.ToList<object>(),
|
|
notice ?? (results.Count == 0 ? "Nothing matched your search terms." : null)
|
|
)
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
index -= 2;
|
|
|
|
if (index < m_List?.Count)
|
|
{
|
|
if (m_List[index] is not NetState ns)
|
|
{
|
|
break;
|
|
}
|
|
|
|
var m = ns.Mobile;
|
|
var a = ns.Account as Account;
|
|
|
|
if (m != null)
|
|
{
|
|
from.SendGump(new AdminGump(from, AdminGumpPage.ClientInfo, 0, null, null, m));
|
|
}
|
|
else if (a != null)
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Information,
|
|
0,
|
|
null,
|
|
null,
|
|
a
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 5:
|
|
{
|
|
switch (index)
|
|
{
|
|
case 0:
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Information,
|
|
0,
|
|
null,
|
|
null,
|
|
m_State
|
|
)
|
|
);
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Characters,
|
|
0,
|
|
null,
|
|
null,
|
|
m_State
|
|
)
|
|
);
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Comments,
|
|
0,
|
|
null,
|
|
null,
|
|
m_State
|
|
)
|
|
);
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(from, AdminGumpPage.AccountDetails_Tags, 0, null, null, m_State)
|
|
);
|
|
break;
|
|
}
|
|
case 13:
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(from, AdminGumpPage.AccountDetails_Access, 0, null, null, m_State)
|
|
);
|
|
break;
|
|
}
|
|
case 14:
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Access_ClientIPs,
|
|
0,
|
|
null,
|
|
null,
|
|
m_State
|
|
)
|
|
);
|
|
break;
|
|
}
|
|
case 4:
|
|
{
|
|
from.Prompt = new AddCommentPrompt(m_State as Account);
|
|
from.SendMessage("Enter the new account comment.");
|
|
break;
|
|
}
|
|
case 5:
|
|
{
|
|
from.Prompt = new AddTagNamePrompt(m_State as Account);
|
|
from.SendMessage("Enter the new tag name.");
|
|
break;
|
|
}
|
|
case 6:
|
|
{
|
|
var un = info.GetTextEntry(0)?.Trim();
|
|
var pw = info.GetTextEntry(1)?.Trim();
|
|
|
|
Account dispAccount = null;
|
|
string notice;
|
|
|
|
if (string.IsNullOrEmpty(un))
|
|
{
|
|
notice = "You must enter a username to add an account.";
|
|
}
|
|
else if (string.IsNullOrEmpty(pw))
|
|
{
|
|
notice = "You must enter a password to add an account.";
|
|
}
|
|
else
|
|
{
|
|
var account = Accounts.GetAccount(un);
|
|
|
|
if (account != null)
|
|
{
|
|
notice = "There is already an account with that username.";
|
|
}
|
|
else
|
|
{
|
|
dispAccount = new Account(un, pw);
|
|
notice = $"{un} : Account added.";
|
|
CommandLogging.WriteLine(
|
|
from,
|
|
$"{from.AccessLevel} {CommandLogging.Format(from)} adding new account: {un}"
|
|
);
|
|
}
|
|
}
|
|
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
dispAccount != null ? AdminGumpPage.AccountDetails_Information : m_PageType,
|
|
m_ListPage,
|
|
m_List,
|
|
notice,
|
|
dispAccount ?? m_State
|
|
)
|
|
);
|
|
break;
|
|
}
|
|
case 7:
|
|
{
|
|
List<IAccount> results;
|
|
|
|
var match = info.GetTextEntry(0)?.Trim().ToLower();
|
|
|
|
if (string.IsNullOrEmpty(match))
|
|
{
|
|
results = Accounts.GetAccounts().ToList();
|
|
results.Sort(AccountComparer.Instance);
|
|
}
|
|
else
|
|
{
|
|
results = new List<IAccount>();
|
|
foreach (var acct in Accounts.GetAccounts())
|
|
{
|
|
if (acct.Username.InsensitiveContains(match))
|
|
{
|
|
results.Add(acct);
|
|
}
|
|
}
|
|
|
|
results.Sort(AccountComparer.Instance);
|
|
}
|
|
|
|
if (results.Count == 1)
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Information,
|
|
0,
|
|
null,
|
|
"One match found.",
|
|
results[0]
|
|
)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.Accounts,
|
|
0,
|
|
results.ToList<object>(),
|
|
results.Count == 0 ? "Nothing matched your search terms." : null,
|
|
new List<object>()
|
|
)
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 8:
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_ChangePassword,
|
|
0,
|
|
null,
|
|
null,
|
|
m_State
|
|
)
|
|
);
|
|
break;
|
|
}
|
|
case 9:
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_ChangeAccess,
|
|
0,
|
|
null,
|
|
null,
|
|
m_State
|
|
)
|
|
);
|
|
break;
|
|
}
|
|
case 10:
|
|
case 11:
|
|
{
|
|
if (m_State is not Account a)
|
|
{
|
|
break;
|
|
}
|
|
|
|
a.SetUnspecifiedBan(from);
|
|
a.Banned = index == 10;
|
|
CommandLogging.WriteLine(
|
|
from,
|
|
$"{from.AccessLevel} {CommandLogging.Format(from)} {a.Username} account {(a.Banned ? "banning" : "unbanning")}"
|
|
);
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
m_PageType,
|
|
m_ListPage,
|
|
m_List,
|
|
$"The account has been {(a.Banned ? "banned" : "unbanned")}.",
|
|
m_State
|
|
)
|
|
);
|
|
|
|
if (index == 10)
|
|
{
|
|
from.SendGump(new BanDurationGump(a));
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 12:
|
|
{
|
|
if (m_State is not Account a)
|
|
{
|
|
break;
|
|
}
|
|
|
|
var password = info.GetTextEntry(0)?.Trim();
|
|
var confirm = info.GetTextEntry(1)?.Trim();
|
|
|
|
string notice;
|
|
var page = AdminGumpPage.AccountDetails_ChangePassword;
|
|
|
|
if (string.IsNullOrEmpty(password))
|
|
{
|
|
notice = "You must enter the password.";
|
|
}
|
|
else if (confirm != password)
|
|
{
|
|
notice =
|
|
"You must confirm the password. That field must precisely match the password field.";
|
|
}
|
|
else
|
|
{
|
|
notice = "The password has been changed.";
|
|
a.SetPassword(password);
|
|
page = AdminGumpPage.AccountDetails_Information;
|
|
CommandLogging.WriteLine(
|
|
from,
|
|
$"{from.AccessLevel} {CommandLogging.Format(from)} changing password of account {a.Username}"
|
|
);
|
|
}
|
|
|
|
from.SendGump(new AdminGump(from, page, 0, null, notice, m_State));
|
|
|
|
break;
|
|
}
|
|
case 16: // view shared
|
|
{
|
|
if (m_State is not Account a)
|
|
{
|
|
break;
|
|
}
|
|
|
|
var list = GetSharedAccounts(a.LoginIPs);
|
|
|
|
if (list.Count > 1 || list.Count == 1 && !list.Contains(a))
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.Accounts,
|
|
0,
|
|
list.ToList<object>(),
|
|
null,
|
|
new List<object>()
|
|
)
|
|
);
|
|
}
|
|
else if (a.LoginIPs.Length > 0)
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Access_ClientIPs,
|
|
0,
|
|
null,
|
|
"There are no other accounts which share an address with this one.",
|
|
m_State
|
|
)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Access_ClientIPs,
|
|
0,
|
|
null,
|
|
"This account has not yet been accessed.",
|
|
m_State
|
|
)
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 17: // ban shared
|
|
{
|
|
if (m_State is not Account a)
|
|
{
|
|
break;
|
|
}
|
|
|
|
var list = GetSharedAccounts(a.LoginIPs);
|
|
|
|
if (list.Count > 0)
|
|
{
|
|
using var sb = ValueStringBuilder.Create();
|
|
sb.Append("You are about to ban ");
|
|
sb.Append(list.Count);
|
|
sb.Append(list.Count != 1 ? "accounts." : "account.");
|
|
sb.Append(" Do you wish to continue?");
|
|
|
|
for (var i = 0; i < list.Count; ++i)
|
|
{
|
|
sb.Append("<br>- ");
|
|
sb.Append(list[i].Username);
|
|
}
|
|
|
|
from.SendGump(
|
|
new WarningGump(
|
|
sb.ToString(),
|
|
420,
|
|
400,
|
|
okay => BanShared_Callback(from, okay, a)
|
|
)
|
|
);
|
|
}
|
|
else if (a.LoginIPs.Length > 0)
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Access_ClientIPs,
|
|
0,
|
|
null,
|
|
"There are no accounts which share an address with this one.",
|
|
m_State
|
|
)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Access_ClientIPs,
|
|
0,
|
|
null,
|
|
"This account has not yet been accessed.",
|
|
m_State
|
|
)
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 18: // firewall all
|
|
{
|
|
if (m_State is not Account a)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (a.LoginIPs.Length > 0)
|
|
{
|
|
from.SendGump(
|
|
new WarningGump(
|
|
$"You are about to firewall {a.LoginIPs.Length} address{(a.LoginIPs.Length != 1 ? "s" : "")}. Do you wish to continue?",
|
|
420,
|
|
400,
|
|
okay => FirewallShared_Callback(from, okay, a)
|
|
)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Access_ClientIPs,
|
|
0,
|
|
null,
|
|
"This account has not yet been accessed.",
|
|
m_State
|
|
)
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 20: // Change access level
|
|
case 21:
|
|
case 22:
|
|
case 23:
|
|
case 24:
|
|
{
|
|
if (m_State is not Account a)
|
|
{
|
|
break;
|
|
}
|
|
|
|
var newLevel = index switch
|
|
{
|
|
21 => AccessLevel.Counselor,
|
|
22 => AccessLevel.GameMaster,
|
|
23 => AccessLevel.Seer,
|
|
24 => AccessLevel.Administrator,
|
|
33 => AccessLevel.Developer,
|
|
34 => AccessLevel.Owner,
|
|
_ => AccessLevel.Player // 20
|
|
};
|
|
|
|
if (newLevel < from.AccessLevel || from.AccessLevel == AccessLevel.Owner)
|
|
{
|
|
a.AccessLevel = newLevel;
|
|
|
|
CommandLogging.WriteLine(
|
|
from,
|
|
$"{from.AccessLevel} {CommandLogging.Format(from)} changing access level of account {a.Username} to {a.AccessLevel}"
|
|
);
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Information,
|
|
0,
|
|
null,
|
|
"The access level has been changed.",
|
|
m_State
|
|
)
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 25:
|
|
{
|
|
if (m_State is not Account a)
|
|
{
|
|
break;
|
|
}
|
|
|
|
from.SendGump(
|
|
new WarningGump(
|
|
$"<center>Account of {a.Username}</center><br>You are about to <em><basefont color=red>permanently delete</basefont></em> the account. Likewise, all characters on the account will be deleted, including equipped, inventory, and banked items. Any houses tied to the account will be demolished.<br><br>Do you wish to continue?",
|
|
420,
|
|
280,
|
|
okay => AccountDelete_Callback(from, okay, a)
|
|
)
|
|
);
|
|
break;
|
|
}
|
|
case 26: // View all shared accounts
|
|
{
|
|
from.SendGump(new AdminGump(from, AdminGumpPage.Accounts_Shared));
|
|
break;
|
|
}
|
|
case 27: // Ban marked
|
|
{
|
|
var list = m_List;
|
|
|
|
if (list == null || m_State is not List<Account> rads)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (rads.Count > 0)
|
|
{
|
|
from.SendGump(
|
|
new WarningGump(
|
|
$"You are about to ban {rads.Count} marked account{(rads.Count == 1 ? "" : "s")}. Be cautioned, the only way to reverse this is by hand--manually unbanning each account.<br><br>Do you wish to continue?",
|
|
420,
|
|
280,
|
|
okay => Marked_Callback(from, okay, true, list, rads, m_ListPage)
|
|
)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
from.SendGump(
|
|
new AdminNoticeGump(
|
|
"You have not yet marked any accounts. Place a check mark next to the accounts you wish to ban and then try again.",
|
|
() => ResendGump_Callback(from, list, rads, m_ListPage)
|
|
)
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 28: // Delete marked
|
|
{
|
|
var list = m_List;
|
|
|
|
if (list == null || m_State is not List<Account> rads)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (rads.Count > 0)
|
|
{
|
|
from.SendGump(
|
|
new WarningGump(
|
|
$"You are about to <em><basefont color=red>permanently delete</basefont></em> {rads.Count} marked account{(rads.Count == 1 ? "" : "s")}. Likewise, all characters on the account{(rads.Count == 1 ? "" : "s")} will be deleted, including equipped, inventory, and banked items. Any houses tied to the account{(rads.Count == 1 ? "" : "s")} will be demolished.<br><br>Do you wish to continue?",
|
|
420,
|
|
280,
|
|
okay => Marked_Callback(from, okay, false, list, rads, m_ListPage)
|
|
)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
from.SendGump(
|
|
new AdminNoticeGump(
|
|
"You have not yet marked any accounts. Place a check mark next to the accounts you wish to ban and then try again.",
|
|
() => ResendGump_Callback(from, list, rads, m_ListPage)
|
|
)
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 29: // Mark all
|
|
{
|
|
if (m_List == null || m_State is not List<object>)
|
|
{
|
|
break;
|
|
}
|
|
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.Accounts,
|
|
m_ListPage,
|
|
m_List,
|
|
null,
|
|
m_List.ToList()
|
|
)
|
|
);
|
|
|
|
break;
|
|
}
|
|
case 30: // View all empty accounts
|
|
{
|
|
var results = new List<object>();
|
|
|
|
foreach (Account acct in Accounts.GetAccounts())
|
|
{
|
|
var empty = true;
|
|
|
|
for (var i = 0; empty && i < acct.Length; ++i)
|
|
{
|
|
empty = acct[i] == null;
|
|
}
|
|
|
|
if (empty)
|
|
{
|
|
results.Add(acct);
|
|
}
|
|
}
|
|
|
|
if (results.Count == 1)
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Information,
|
|
0,
|
|
null,
|
|
"One match found.",
|
|
results[0]
|
|
)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.Accounts,
|
|
0,
|
|
results,
|
|
results.Count == 0 ? "Nothing matched your search terms." : null,
|
|
new List<object>()
|
|
)
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 31: // View all inactive accounts
|
|
{
|
|
var results = new List<object>();
|
|
|
|
foreach (Account acct in Accounts.GetAccounts())
|
|
{
|
|
if (acct.Inactive)
|
|
{
|
|
results.Add(acct);
|
|
}
|
|
}
|
|
|
|
if (results.Count == 1)
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Information,
|
|
0,
|
|
null,
|
|
"One match found.",
|
|
results[0]
|
|
)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.Accounts,
|
|
0,
|
|
results,
|
|
results.Count == 0 ? "Nothing matched your search terms." : null,
|
|
new List<object>()
|
|
)
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 32: // View all banned accounts
|
|
{
|
|
var results = new List<object>();
|
|
|
|
foreach (Account acct in Accounts.GetAccounts())
|
|
{
|
|
if (acct.Banned)
|
|
{
|
|
results.Add(acct);
|
|
}
|
|
}
|
|
|
|
if (results.Count == 1)
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Information,
|
|
0,
|
|
null,
|
|
"One match found.",
|
|
results[0]
|
|
)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.Accounts,
|
|
0,
|
|
results,
|
|
results.Count == 0 ? "Nothing matched your search terms." : null,
|
|
new List<object>()
|
|
)
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 33: // Change access level (extended)
|
|
case 34:
|
|
{
|
|
goto case 20;
|
|
}
|
|
case 35: // Unmark house owners
|
|
{
|
|
var list = m_List;
|
|
var rads = m_State as List<Account>;
|
|
|
|
if (list == null || rads == null)
|
|
{
|
|
break;
|
|
}
|
|
|
|
var newRads = new List<Account>();
|
|
|
|
foreach (var acct in rads)
|
|
{
|
|
var hasHouse = false;
|
|
|
|
for (var i = 0; i < acct.Length && !hasHouse; ++i)
|
|
{
|
|
if (acct[i] != null && BaseHouse.HasHouse(acct[i]))
|
|
{
|
|
hasHouse = true;
|
|
}
|
|
}
|
|
|
|
if (!hasHouse)
|
|
{
|
|
newRads.Add(acct);
|
|
}
|
|
}
|
|
|
|
from.SendGump(
|
|
new AdminGump(from, AdminGumpPage.Accounts, m_ListPage, m_List, null, newRads)
|
|
);
|
|
|
|
break;
|
|
}
|
|
case 36: // Clear login addresses
|
|
{
|
|
if (m_State is not Account a)
|
|
{
|
|
break;
|
|
}
|
|
|
|
var ips = a.LoginIPs;
|
|
|
|
if (ips.Length == 0)
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Access_ClientIPs,
|
|
0,
|
|
null,
|
|
"This account has not yet been accessed.",
|
|
m_State
|
|
)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
from.SendGump(
|
|
new WarningGump(
|
|
$"You are about to clear the address list for account {a} containing {ips.Length} {(ips.Length == 1 ? "entry" : "entries")}. Do you wish to continue?",
|
|
420,
|
|
280,
|
|
okay => RemoveLoginIPs_Callback(from, okay, a)
|
|
)
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
index -= 50;
|
|
|
|
if (m_State is Account a && index >= 0 && index < a.Length)
|
|
{
|
|
var m = a[index];
|
|
|
|
if (m != null)
|
|
{
|
|
from.SendGump(new AdminGump(from, AdminGumpPage.ClientInfo, 0, null, null, m));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
index -= 6;
|
|
|
|
if (m_List != null && index >= 0 && index < m_List.Count)
|
|
{
|
|
if (m_List[index] is Account)
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Information,
|
|
0,
|
|
null,
|
|
null,
|
|
m_List[index]
|
|
)
|
|
);
|
|
}
|
|
else if (m_List[index] is KeyValuePair<IPAddress, List<Account>> kvp)
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.Accounts,
|
|
0,
|
|
kvp.Value.ToList<object>(),
|
|
null,
|
|
new List<object>()
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 6:
|
|
{
|
|
switch (index)
|
|
{
|
|
case 0:
|
|
{
|
|
var match = info.GetTextEntry(0)?.Trim();
|
|
|
|
string notice = null;
|
|
var results = new List<object>();
|
|
|
|
if (string.IsNullOrEmpty(match))
|
|
{
|
|
notice = "You must enter an IP to search.";
|
|
}
|
|
else
|
|
{
|
|
Firewall.ReadFirewallSet(firewallSet =>
|
|
{
|
|
foreach (var check in firewallSet)
|
|
{
|
|
var checkStr = check.ToString();
|
|
|
|
if (checkStr.ContainsOrdinal(match))
|
|
{
|
|
results.Add(check);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
if (results.Count == 1)
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.FirewallInfo,
|
|
0,
|
|
null,
|
|
"One match found.",
|
|
results[0]
|
|
)
|
|
);
|
|
}
|
|
else if (results.Count > 1)
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.Firewall,
|
|
0,
|
|
results,
|
|
$"Search results for : {match}",
|
|
m_State
|
|
)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
m_PageType,
|
|
m_ListPage,
|
|
m_List,
|
|
notice ?? "Nothing matched your search terms.",
|
|
m_State
|
|
)
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
var text = info.GetTextEntry(0)?.Trim();
|
|
|
|
if (string.IsNullOrEmpty(text))
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
m_PageType,
|
|
m_ListPage,
|
|
m_List,
|
|
"You must enter an address or CIDR to add.",
|
|
m_State
|
|
)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
IFirewallEntry firewallEntry;
|
|
try
|
|
{
|
|
firewallEntry = AdminFirewall.ToFirewallEntry(text);
|
|
}
|
|
catch
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
m_PageType,
|
|
m_ListPage,
|
|
m_List,
|
|
"That is not a valid address or CIDR.",
|
|
m_State
|
|
)
|
|
);
|
|
break;
|
|
}
|
|
|
|
CommandLogging.WriteLine(
|
|
from,
|
|
$"{from.AccessLevel} {CommandLogging.Format(from)} firewalling {firewallEntry}"
|
|
);
|
|
|
|
AdminFirewall.Add(firewallEntry);
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.FirewallInfo,
|
|
0,
|
|
null,
|
|
$"{firewallEntry} : Added to firewall.",
|
|
firewallEntry
|
|
)
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
InvokeCommand("Firewall");
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
m_PageType,
|
|
m_ListPage,
|
|
m_List,
|
|
"Target the player to firewall.",
|
|
m_State
|
|
)
|
|
);
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
if (m_State is IFirewallEntry)
|
|
{
|
|
CommandLogging.WriteLine(
|
|
from,
|
|
$"{from.AccessLevel} {CommandLogging.Format(from)} removing {m_State} from firewall list"
|
|
);
|
|
|
|
AdminFirewall.Remove(m_State);
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.Firewall,
|
|
0,
|
|
null,
|
|
$"{m_State} : Removed from firewall."
|
|
)
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
index -= 4;
|
|
|
|
if (index < m_List?.Count)
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(from, AdminGumpPage.FirewallInfo, 0, null, null, m_List[index])
|
|
);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 7:
|
|
{
|
|
if (m_State is not Mobile m ||
|
|
m.AccessLevel > from.AccessLevel || m.Account?.AccessLevel > from.AccessLevel)
|
|
{
|
|
break;
|
|
}
|
|
|
|
string notice = null;
|
|
var sendGump = true;
|
|
|
|
switch (index)
|
|
{
|
|
case 0:
|
|
{
|
|
var map = m.Map;
|
|
var loc = m.Location;
|
|
|
|
if (map == null || map == Map.Internal)
|
|
{
|
|
map = m.LogoutMap;
|
|
loc = m.LogoutLocation;
|
|
}
|
|
|
|
if (map != null && map != Map.Internal)
|
|
{
|
|
from.MoveToWorld(loc, map);
|
|
notice = "You have been teleported to their location.";
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
m.MoveToWorld(from.Location, from.Map);
|
|
notice = "They have been teleported to your location.";
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
var ns = m.NetState;
|
|
|
|
if (ns != null)
|
|
{
|
|
CommandLogging.WriteLine(
|
|
from,
|
|
$"{from.AccessLevel} {CommandLogging.Format(from)} kicking {CommandLogging.Format(m)}"
|
|
);
|
|
ns.Disconnect($"Kicked by {from}.");
|
|
notice = "They have been kicked.";
|
|
}
|
|
else
|
|
{
|
|
notice = "They are already disconnected.";
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
if (m.Account is Account a)
|
|
{
|
|
CommandLogging.WriteLine(
|
|
from,
|
|
$"{from.AccessLevel} {CommandLogging.Format(from)} banning {CommandLogging.Format(m)}"
|
|
);
|
|
a.Banned = true;
|
|
|
|
var ns = m.NetState;
|
|
|
|
ns?.Disconnect($"Banned by {from}.");
|
|
|
|
notice = "They have been banned.";
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 6:
|
|
{
|
|
Properties.SetValue(from, m, "Blessed", "False");
|
|
notice = "They are now mortal.";
|
|
break;
|
|
}
|
|
case 7:
|
|
{
|
|
Properties.SetValue(from, m, "Blessed", "True");
|
|
notice = "They are now immortal.";
|
|
break;
|
|
}
|
|
case 8:
|
|
{
|
|
Properties.SetValue(from, m, "Squelched", "True");
|
|
notice = "They are now squelched.";
|
|
break;
|
|
}
|
|
case 9:
|
|
{
|
|
Properties.SetValue(from, m, "Squelched", "False");
|
|
notice = "They are now unsquelched.";
|
|
break;
|
|
}
|
|
case 10:
|
|
{
|
|
Properties.SetValue(from, m, "Hidden", "True");
|
|
notice = "They are now hidden.";
|
|
break;
|
|
}
|
|
case 11:
|
|
{
|
|
Properties.SetValue(from, m, "Hidden", "False");
|
|
notice = "They are now unhidden.";
|
|
break;
|
|
}
|
|
case 12:
|
|
{
|
|
CommandLogging.WriteLine(
|
|
from,
|
|
$"{from.AccessLevel} {CommandLogging.Format(from)} killing {CommandLogging.Format(m)}"
|
|
);
|
|
m.Kill();
|
|
notice = "They have been killed.";
|
|
break;
|
|
}
|
|
case 13:
|
|
{
|
|
CommandLogging.WriteLine(
|
|
from,
|
|
$"{from.AccessLevel} {CommandLogging.Format(from)} resurrecting {CommandLogging.Format(m)}"
|
|
);
|
|
m.Resurrect();
|
|
notice = "They have been resurrected.";
|
|
break;
|
|
}
|
|
case 14:
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Information,
|
|
0,
|
|
null,
|
|
null,
|
|
m.Account
|
|
)
|
|
);
|
|
sendGump = false;
|
|
break;
|
|
}
|
|
case 15:
|
|
{
|
|
var reason = info.GetTextEntry(1)?.Trim();
|
|
|
|
if (string.IsNullOrWhiteSpace(reason))
|
|
{
|
|
reason = "";
|
|
}
|
|
|
|
CommandLogging.WriteLine(
|
|
from,
|
|
$"{from.AccessLevel} {CommandLogging.Format(from)} jailing {CommandLogging.Format(m)} - Reason: {reason}"
|
|
);
|
|
InvokeCommand($"Jail {m.Name} \"{reason}\"");
|
|
notice = $"Player has been sent to jail. Reason: {reason}";
|
|
break;
|
|
}
|
|
case 16:
|
|
{
|
|
CommandLogging.WriteLine(
|
|
from,
|
|
$"{from.AccessLevel} {CommandLogging.Format(from)} unjailing {CommandLogging.Format(m)}"
|
|
);
|
|
InvokeCommand($"Unjail {m.Name}");
|
|
notice = "Player has been unjailed.";
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (sendGump)
|
|
{
|
|
from.SendGump(new AdminGump(from, AdminGumpPage.ClientInfo, 0, null, notice, m_State));
|
|
}
|
|
|
|
switch (index)
|
|
{
|
|
case 3:
|
|
{
|
|
if (m.Account is Account a)
|
|
{
|
|
from.SendGump(new BanDurationGump(a));
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 4:
|
|
{
|
|
from.SendGump(new PropertiesGump(from, m));
|
|
break;
|
|
}
|
|
case 5:
|
|
{
|
|
from.SendGump(new SkillsGump(from, m));
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 8:
|
|
{
|
|
if (index < m_List?.Count)
|
|
{
|
|
if (m_State is not Account a)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (m_PageType == AdminGumpPage.AccountDetails_Access_ClientIPs)
|
|
{
|
|
from.SendGump(
|
|
new WarningGump(
|
|
$"You are about to firewall {m_List[index]}. All connection attempts from a matching IP will be refused. Are you sure?",
|
|
420,
|
|
280,
|
|
okay => Firewall_Callback(from, okay, a, m_List[index])
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 9:
|
|
{
|
|
if (index < m_List?.Count)
|
|
{
|
|
if (m_PageType == AdminGumpPage.AccountDetails_Access_ClientIPs)
|
|
{
|
|
var obj = m_List[index];
|
|
|
|
if (obj is not IPAddress ip)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (m_State is not Account a)
|
|
{
|
|
break;
|
|
}
|
|
|
|
var list = GetSharedAccounts(ip);
|
|
|
|
if (list.Count > 1 || list.Count == 1 && !list.Contains(a))
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.Accounts,
|
|
0,
|
|
list.ToList<object>(),
|
|
null,
|
|
new List<object>()
|
|
)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Access_ClientIPs,
|
|
0,
|
|
null,
|
|
"There are no other accounts which share that address.",
|
|
a
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 10:
|
|
{
|
|
if (index < m_List?.Count)
|
|
{
|
|
if (m_PageType == AdminGumpPage.AccountDetails_Access_ClientIPs)
|
|
{
|
|
var ip = m_List[index] as IPAddress;
|
|
|
|
if (ip == null)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (m_State is not Account a)
|
|
{
|
|
break;
|
|
}
|
|
|
|
from.SendGump(
|
|
new WarningGump(
|
|
$"You are about to remove address {ip} from account {a}. Do you wish to continue?",
|
|
420,
|
|
280,
|
|
okay => RemoveLoginIP_Callback(from, okay, a, ip)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void GenerateSpawners()
|
|
{
|
|
var folder = Core.SA ? "post-uoml" : "uoml";
|
|
|
|
var availableMaps = ExpansionInfo.CoreExpansion.MapSelectionFlags;
|
|
if (Core.SA && availableMaps.Includes(MapSelectionFlags.TerMur))
|
|
{
|
|
InvokeCommand("GenerateSpawners Data/Spawns/post-uoml/termur/**.json");
|
|
InvokeCommand("GenerateSpawners Data/Spawns/shared/termur/**.json");
|
|
}
|
|
|
|
if (availableMaps.Includes(MapSelectionFlags.Malas))
|
|
{
|
|
InvokeCommand($"GenerateSpawners Data/Spawns/{folder}/malas/**.json");
|
|
InvokeCommand("GenerateSpawners Data/Spawns/shared/malas/**.json");
|
|
}
|
|
|
|
if (availableMaps.Includes(MapSelectionFlags.Tokuno))
|
|
{
|
|
InvokeCommand($"GenerateSpawners Data/Spawns/{folder}/tokuno/**.json");
|
|
InvokeCommand("GenerateSpawners Data/Spawns/shared/tokuno/**.json");
|
|
}
|
|
|
|
if (availableMaps.Includes(MapSelectionFlags.Ilshenar))
|
|
{
|
|
InvokeCommand($"GenerateSpawners Data/Spawns/{folder}/ilshenar/**.json");
|
|
InvokeCommand("GenerateSpawners Data/Spawns/shared/ilshenar/**.json");
|
|
}
|
|
|
|
if (availableMaps.Includes(MapSelectionFlags.Trammel))
|
|
{
|
|
InvokeCommand($"GenerateSpawners Data/Spawns/{folder}/trammel/**.json");
|
|
InvokeCommand("GenerateSpawners Data/Spawns/shared/trammel/**.json");
|
|
}
|
|
|
|
if (availableMaps.Includes(MapSelectionFlags.Felucca))
|
|
{
|
|
InvokeCommand($"GenerateSpawners Data/Spawns/{folder}/felucca/**.json");
|
|
InvokeCommand("GenerateSpawners Data/Spawns/shared/felucca/**.json");
|
|
}
|
|
}
|
|
|
|
private void Shutdown(bool restart, bool save)
|
|
{
|
|
CommandLogging.WriteLine(
|
|
m_From,
|
|
$"{m_From.AccessLevel} {CommandLogging.Format(m_From)} shutting down server (Restart: {restart}) (Save: {save})"
|
|
);
|
|
|
|
if (save)
|
|
{
|
|
InvokeCommand("Save");
|
|
}
|
|
|
|
// Kill the server on a different thread otherwise we will dead lock
|
|
ThreadPool.QueueUserWorkItem(
|
|
_ =>
|
|
{
|
|
World.WaitForWriteCompletion();
|
|
Core.Kill(restart);
|
|
}
|
|
);
|
|
}
|
|
|
|
private void InvokeCommand(string c)
|
|
{
|
|
CommandSystem.Handle(m_From, $"{CommandSystem.Prefix}{c}");
|
|
}
|
|
|
|
public static void GetAccountInfo(IAccount a, out AccessLevel accessLevel, out bool online)
|
|
{
|
|
accessLevel = a.AccessLevel;
|
|
online = false;
|
|
|
|
for (var j = 0; j < a.Length; ++j)
|
|
{
|
|
var check = a[j];
|
|
|
|
if (check == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (check.AccessLevel > accessLevel)
|
|
{
|
|
accessLevel = check.AccessLevel;
|
|
}
|
|
|
|
if (check.NetState != null)
|
|
{
|
|
online = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void FilterAccess(List<object> list, Mobile from)
|
|
{
|
|
if (list == null || list.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (var i = list.Count - 1; i >= 0; i--)
|
|
{
|
|
var obj = list[i];
|
|
|
|
if (obj is Account acc && acc.AccessLevel > from.AccessLevel ||
|
|
obj is Mobile mob && mob.AccessLevel > from.AccessLevel ||
|
|
obj is NetState ns &&
|
|
(ns.Mobile?.AccessLevel > from.AccessLevel || ns.Account?.AccessLevel > from.AccessLevel))
|
|
{
|
|
list.RemoveAt(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
private class SharedAccountDescendingComparer : IComparer<object>
|
|
{
|
|
public static readonly IComparer<object> Instance = new SharedAccountDescendingComparer();
|
|
|
|
public int Compare(object x, object y)
|
|
{
|
|
if (x is not KeyValuePair<IPAddress, List<Account>> a)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (y is not KeyValuePair<IPAddress, List<Account>> b)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return b.Value.Count - a.Value.Count;
|
|
}
|
|
}
|
|
|
|
private class AddCommentPrompt : Prompt
|
|
{
|
|
private readonly Account m_Account;
|
|
|
|
public AddCommentPrompt(Account acct) => m_Account = acct;
|
|
|
|
public override void OnCancel(Mobile from)
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Comments,
|
|
0,
|
|
null,
|
|
"Request to add comment was canceled.",
|
|
m_Account
|
|
)
|
|
);
|
|
}
|
|
|
|
public override void OnResponse(Mobile from, string text)
|
|
{
|
|
if (m_Account != null)
|
|
{
|
|
m_Account.Comments.Add(new AccountComment(from.RawName, text));
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Comments,
|
|
0,
|
|
null,
|
|
"Comment added.",
|
|
m_Account
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
private class AddTagNamePrompt : Prompt
|
|
{
|
|
private readonly Account m_Account;
|
|
|
|
public AddTagNamePrompt(Account acct) => m_Account = acct;
|
|
|
|
public override void OnCancel(Mobile from)
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Tags,
|
|
0,
|
|
null,
|
|
"Request to add tag was canceled.",
|
|
m_Account
|
|
)
|
|
);
|
|
}
|
|
|
|
public override void OnResponse(Mobile from, string text)
|
|
{
|
|
from.Prompt = new AddTagValuePrompt(m_Account, text);
|
|
from.SendMessage("Enter the new tag value.");
|
|
}
|
|
}
|
|
|
|
private class AddTagValuePrompt : Prompt
|
|
{
|
|
private readonly Account m_Account;
|
|
private readonly string m_Name;
|
|
|
|
public AddTagValuePrompt(Account acct, string name)
|
|
{
|
|
m_Account = acct;
|
|
m_Name = name;
|
|
}
|
|
|
|
public override void OnCancel(Mobile from)
|
|
{
|
|
from.SendGump(
|
|
new AdminGump(
|
|
from,
|
|
AdminGumpPage.AccountDetails_Tags,
|
|
0,
|
|
null,
|
|
"Request to add tag was canceled.",
|
|
m_Account
|
|
)
|
|
);
|
|
}
|
|
|
|
public override void OnResponse(Mobile from, string text)
|
|
{
|
|
if (m_Account != null)
|
|
{
|
|
m_Account.AddTag(m_Name, text);
|
|
from.SendGump(new AdminGump(from, AdminGumpPage.AccountDetails_Tags, 0, null, "Tag added.", m_Account));
|
|
}
|
|
}
|
|
}
|
|
|
|
private class NetStateComparer : IComparer<NetState>
|
|
{
|
|
public static readonly IComparer<NetState> Instance = new NetStateComparer();
|
|
|
|
public int Compare(NetState x, NetState y)
|
|
{
|
|
if (x == null && y == null)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (x == null)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (y == null)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
var aMob = x.Mobile;
|
|
var bMob = y.Mobile;
|
|
|
|
if (aMob == null && bMob == null)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (aMob == null)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (bMob == null)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (aMob.AccessLevel > bMob.AccessLevel)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return aMob.AccessLevel < bMob.AccessLevel ? 1 : aMob.Name.InsensitiveCompare(bMob.Name);
|
|
}
|
|
}
|
|
|
|
private class AccountComparer : IComparer<IAccount>
|
|
{
|
|
public static readonly IComparer<IAccount> Instance = new AccountComparer();
|
|
|
|
public int Compare(IAccount x, IAccount y)
|
|
{
|
|
if (x == null && y == null)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (x == null)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (y == null)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
GetAccountInfo(x, out var aLevel, out var aOnline);
|
|
GetAccountInfo(y, out var bLevel, out var bOnline);
|
|
|
|
if (aOnline && !bOnline)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (bOnline && !aOnline)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (aLevel > bLevel)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return aLevel < bLevel ? 1 : x.Username.InsensitiveCompare(y.Username);
|
|
}
|
|
}
|
|
|
|
private class AdminNoticeGump : StaticNoticeGump<AdminNoticeGump>
|
|
{
|
|
public override int Width => 420;
|
|
public override int Height => 280;
|
|
|
|
public override string Content { get; }
|
|
|
|
public AdminNoticeGump(string content, Action callback) : base(callback) => Content = content;
|
|
}
|
|
|
|
public class ShutdownTimer : Timer
|
|
{
|
|
private readonly AdminGump _adminGump;
|
|
|
|
public ShutdownTimer(AdminGump gump) : base(TimeSpan.Zero, TimeSpan.Zero, 8) =>
|
|
_adminGump = gump;
|
|
|
|
protected override void OnTick()
|
|
{
|
|
if (Index >= 7)
|
|
{
|
|
AutoSave.SavesEnabled = false;
|
|
_adminGump.Shutdown(false, true);
|
|
return;
|
|
}
|
|
|
|
ReadOnlySpan<int> times = [15, 10, 5, 4, 3, 2, 1, 0];
|
|
var time = times[Index];
|
|
_adminGump.m_From.SendMessage(
|
|
$"The shard will shutdown in {time} minute{(time == 1 ? "s" : "")} for maintenance."
|
|
);
|
|
Interval = TimeSpan.FromMinutes(time - times[Index + 1]);
|
|
}
|
|
}
|
|
}
|
|
}
|