using System; using System.Diagnostics; using System.IO; using Server.Gumps; using Server.Mobiles; using Server.Network; namespace Server.Misc { public class ClientVerification { private static readonly bool m_DetectClientRequirement = true; private static readonly OldClientResponse m_OldClientResponse = OldClientResponse.LenientKick; private static readonly TimeSpan m_AgeLeniency = TimeSpan.FromDays(10); private static readonly TimeSpan m_GameTimeLeniency = TimeSpan.FromHours(25); public static ClientVersion Required { get; set; } public static bool AllowRegular { get; set; } = true; public static bool AllowUOTD { get; set; } = true; public static bool AllowGod { get; set; } = true; public static TimeSpan KickDelay { get; set; } = TimeSpan.FromSeconds(20.0); public static void Initialize() { EventSink.ClientVersionReceived += EventSink_ClientVersionReceived; // ClientVersion.Required = null; // Required = new ClientVersion( "6.0.0.0" ); if (m_DetectClientRequirement) { string path = Core.FindDataFile("client.exe"); if (File.Exists(path)) { FileVersionInfo info = FileVersionInfo.GetVersionInfo(path); if (info.FileMajorPart != 0 || info.FileMinorPart != 0 || info.FileBuildPart != 0 || info.FilePrivatePart != 0) Required = new ClientVersion(info.FileMajorPart, info.FileMinorPart, info.FileBuildPart, info.FilePrivatePart); } } if (Required != null) { Utility.PushColor(ConsoleColor.White); Console.WriteLine("Restricting client version to {0}. Action to be taken: {1}", Required, m_OldClientResponse); Utility.PopColor(); } } private static void EventSink_ClientVersionReceived(NetState state, ClientVersion version) { string kickMessage = null; if (state.Mobile?.AccessLevel != AccessLevel.Player) return; if (Required != null && version < Required && (m_OldClientResponse == OldClientResponse.Kick || (m_OldClientResponse == OldClientResponse.LenientKick && DateTime.UtcNow - state.Mobile.CreationTime > m_AgeLeniency && state.Mobile is PlayerMobile mobile && mobile.GameTime > m_GameTimeLeniency))) { kickMessage = $"This server requires your client version be at least {Required}."; } else if (!AllowGod || !AllowRegular || !AllowUOTD) { if (!AllowGod && version.Type == ClientType.God) kickMessage = "This server does not allow god clients to connect."; else if (!AllowRegular && version.Type == ClientType.Regular) kickMessage = "This server does not allow regular clients to connect."; else if (!AllowUOTD && state.IsUOTDClient) kickMessage = "This server does not allow UO:TD clients to connect."; if (!AllowGod && !AllowRegular && !AllowUOTD) { kickMessage = "This server does not allow any clients to connect."; } else if (AllowGod && !AllowRegular && !AllowUOTD && version.Type != ClientType.God) { kickMessage = "This server requires you to use the god client."; } else if (kickMessage != null) { if (AllowRegular && AllowUOTD) kickMessage += " You can use regular or UO:TD clients."; else if (AllowRegular) kickMessage += " You can use regular clients."; else if (AllowUOTD) kickMessage += " You can use UO:TD clients."; } } if (kickMessage != null) { state.Mobile.SendMessage(0x22, kickMessage); state.Mobile.SendMessage(0x22, "You will be disconnected in {0} seconds.", KickDelay.TotalSeconds); Timer.DelayCall(KickDelay, () => { if (state.Connection != null) { Console.WriteLine("Client: {0}: Disconnecting, bad version", state); state.Dispose(); } }); } else if (Required != null && version < Required) { switch (m_OldClientResponse) { case OldClientResponse.Warn: { state.Mobile.SendMessage(0x22, "Your client is out of date. Please update your client.", Required); state.Mobile.SendMessage(0x22, "This server recommends that your client version be at least {0}.", Required); break; } case OldClientResponse.LenientKick: case OldClientResponse.Annoy: { SendAnnoyGump(state.Mobile); break; } } } } private static void KickMessage(Mobile from, bool okay) { from.SendMessage("You will be reminded of this again."); if (m_OldClientResponse == OldClientResponse.LenientKick) from.SendMessage( "Old clients will be kicked after {0} days of character age and {1} hours of play time", m_AgeLeniency, m_GameTimeLeniency); Timer.DelayCall(TimeSpan.FromMinutes(Utility.Random(5, 15)), () => SendAnnoyGump(from)); } private static void SendAnnoyGump(Mobile m) { if (m.NetState != null && m.NetState.Version < Required) { Gump g = new WarningGump(1060637, 30720, $"Your client is out of date. Please update your client.
This server recommends that your client version be at least {Required}.

You are currently using version {m.NetState.Version}.

To patch, run UOPatch.exe inside your Ultima Online folder.", 0xFFC000, 480, 360, okay => KickMessage(m, okay), false); g.Draggable = false; g.Closable = false; g.Resizable = false; m.SendGump(g); } } private enum OldClientResponse { Ignore, Warn, Annoy, LenientKick, Kick } } }