context: /*
   * DAWN OF LIGHT - The first free open source DAoC server emulator
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version 2
   * of the License, or (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   *
   */
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Reflection;
  
  using DOL.AI.Brain;
  using DOL.Database;
  using DOL.Events;
  using DOL.GS.Housing;
  using DOL.GS.Keeps;
  using DOL.GS.PacketHandler;
  using DOL.GS.ServerProperties;
  using DOL.Language;
  using log4net;
  
  namespace DOL.GS.ServerRules
  {
  	public abstract class AbstractServerRules : IServerRules
  	{
  		/// <summary>
  		/// Defines a logger for this class.
  		/// </summary>
  		private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  
  		/// <summary>
  		/// This is called after the rules are created to do any event binding or other tasks
  		/// </summary>
  		/// <param name="e"></param>
  		/// <param name="sender"></param>
  		/// <param name="args"></param>
  		public virtual void Initialize()
  		{
  			GameEventMgr.AddHandler(GamePlayerEvent.GameEntered, new DOLEventHandler(OnGameEntered));
  			GameEventMgr.AddHandler(GamePlayerEvent.RegionChanged, new DOLEventHandler(OnRegionChanged));
  			GameEventMgr.AddHandler(GamePlayerEvent.Released, new DOLEventHandler(OnReleased));
  			m_invExpiredCallback = new GamePlayer.InvulnerabilityExpiredCallback(ImmunityExpiredCallback);
  		}
  
  		/// <summary>
  		/// Allows or denies a client from connecting to the server ...
  		/// NOTE: The client has not been fully initialized when this method is called.
  		/// For example, no account or character data has been loaded yet.
  		/// </summary>
  		/// <param name="client">The client that sent the login request</param>
  		/// <param name="username">The username of the client wanting to connect</param>
  		/// <returns>true if connection allowed, false if connection should be terminated</returns>
  		/// <remarks>You can only send ONE packet to the client and this is the
  		/// LoginDenied packet before returning false. Trying to send any other packet
  		/// might result in unexpected behaviour on server and client!</remarks>
  		public virtual bool IsAllowedToConnect(GameClient client, string username)
  		{
  			if (!client.Socket.Connected)
  				return false;
  
  			// Ban account
  			IList<DBBannedAccount> objs;
  			objs = DOLDB<DBBannedAccount>.SelectObjects(DB.Column(nameof(DBBannedAccount.Type)).IsEqualTo("A").Or(DB.Column(nameof(DBBannedAccount.Type)).IsEqualTo("B")).And(DB.Column(nameof(DBBannedAccount.Account)).IsEqualTo(username)));
  			if (objs.Count > 0)
  			{
  				client.IsConnected = false;
  				client.Out.SendLoginDenied(eLoginError.AccountIsBannedFromThisServerType);
  				log.Debug("IsAllowedToConnect deny access to username " + username);
  				return false;
  			}
  
  			// Ban IP Address or range (example: 5.5.5.%)
  			string accip = client.TcpEndpointAddress;
  			objs = DOLDB<DBBannedAccount>.SelectObjects(DB.Column(nameof(DBBannedAccount.Type)).IsEqualTo("I").Or(DB.Column(nameof(DBBannedAccount.Type)).IsEqualTo("B")).And(DB.Column(nameof(DBBannedAccount.Ip)).IsLike(accip)));
  			if (objs.Count > 0)
  			{
  				client.IsConnected = false;
  				client.Out.SendLoginDenied(eLoginError.AccountIsBannedFromThisServerType);
  				log.Debug("IsAllowedToConnect deny access to IP " + accip);
  				return false;
  			}
  
  			GameClient.eClientVersion min = (GameClient.eClientVersion)Properties.CLIENT_VERSION_MIN;
  			if (min != GameClient.eClientVersion.VersionNotChecked && client.Version < min)
  			{
  				client.IsConnected = false;
  				client.Out.SendLoginDenied(eLoginError.ClientVersionTooLow);
  				log.Debug("IsAllowedToConnect deny access to client version (too low) " + client.Version);
  				return false;
  			}
  
  			GameClient.eClientVersion max = (GameClient.eClientVersion)Properties.CLIENT_VERSION_MAX;
  			if (max != GameClient.eClientVersion.VersionNotChecked && client.Version > max)
  			{
  				client.IsConnected = false;
  				client.Out.SendLoginDenied(eLoginError.NotAuthorizedToUseExpansionVersion);
  				log.Debug("IsAllowedToConnect deny access to client version (too high) " + client.Version);
  				return false;
  			}
  
  			if (Properties.CLIENT_TYPE_MAX > -1)
  			{
  				GameClient.eClientType type = (GameClient.eClientType)Properties.CLIENT_TYPE_MAX;
  				if ((int)client.ClientType > (int)type)
  				{
  					client.IsConnected = false;
  					client.Out.SendLoginDenied(eLoginError.ExpansionPacketNotAllowed);
  					log.Debug("IsAllowedToConnect deny access to expansion pack.");
  					return false;
  				}
  			}
  
  			/* Example to limit the connections from a certain IP range!
  			if(client.Socket.RemoteEndPoint.ToString().StartsWith("192.168.0."))
  			{
  				client.Out.SendLoginDenied(eLoginError.AccountNoAccessAnyGame);
  				return false;
  			}
  			 */
  
  
  			/* Example to deny new connections on saturdays
  			if(DateTime.Now.DayOfWeek == DayOfWeek.Saturday)
  			{
  				client.Out.SendLoginDenied(eLoginError.GameCurrentlyClosed);
  				return false;
  			}
  			 */
  
  			/* Example to deny new connections between 10am and 12am
  			if(DateTime.Now.Hour >= 10 && DateTime.Now.Hour <= 12)
  			{
  				client.Out.SendLoginDenied(eLoginError.GameCurrentlyClosed);
  				return false;
  			}
  			 */
  
  			Account account = GameServer.Database.FindObjectByKey<Account>(username);
  
  			if (Properties.MAX_PLAYERS > 0)
  			{
  				if (WorldMgr.GetAllClients().Count >= Properties.MAX_PLAYERS)
  				{
  					// GMs are still allowed to enter server
  					if (account == null || (account.PrivLevel == 1 && account.Status <= 0))
  					{
  						// Normal Players will not be allowed over the max
  						client.IsConnected = false;
  						client.Out.SendLoginDenied(eLoginError.TooManyPlayersLoggedIn);
  						log.Debug("IsAllowedToConnect deny access due to too many players.");
  						return false;
  					}
  
  				}
  			}
  
  			if (Properties.STAFF_LOGIN)
  			{
  				if (account == null || account.PrivLevel == 1)
  				{
  					// GMs are still allowed to enter server
  					// Normal Players will not be allowed to Log in
  					client.IsConnected = false;
  					client.Out.SendLoginDenied(eLoginError.GameCurrentlyClosed);
  					log.Debug("IsAllowedToConnect deny access; staff only login");
  					return false;
  				}
  			}
  
  			if (!Properties.ALLOW_DUAL_LOGINS)
  			{
  				if ((account == null || account.PrivLevel == 1) && client.TcpEndpointAddress != "not connected")
  				{
  					foreach (GameClient cln in WorldMgr.GetAllClients())
  					{
  						if (cln == null || client == cln) continue;
  						if (cln.TcpEndpointAddress == client.TcpEndpointAddress)
  						{
  							if (cln.Account != null && cln.Account.PrivLevel > 1)
  							{
  								break;
  							}
  							client.IsConnected = false;
  							client.Out.SendLoginDenied(eLoginError.AccountAlreadyLoggedIntoOtherServer);
  							log.Debug("IsAllowedToConnect deny access; dual login not allowed");
  							return false;
  						}
  					}
  				}
  			}
  
  			return true;
  		}
  
  
  		/// <summary>
  		/// Called when player enters the game for first time
  		/// </summary>
  		/// <param name="e">event</param>
  		/// <param name="sender">GamePlayer object that has entered the game</param>
  		/// <param name="args"></param>
  		public virtual void OnGameEntered(DOLEvent e, object sender, EventArgs args)
  		{
  			StartImmunityTimer((GamePlayer)sender, ServerProperties.Properties.TIMER_GAME_ENTERED * 1000);
  		}
  
  		/// <summary>
  		/// Called when player has changed the region
  		/// </summary>
  		/// <param name="e">event</param>
  		/// <param name="sender">GamePlayer object that has changed the region</param>
  		/// <param name="args"></param>
  		public virtual void OnRegionChanged(DOLEvent e, object sender, EventArgs args)
  		{
  			StartImmunityTimer((GamePlayer)sender, ServerProperties.Properties.TIMER_REGION_CHANGED * 1000);
  		}
  
  		/// <summary>
  		/// Called after player has released
  		/// </summary>
  		/// <param name="e">event</param>
  		/// <param name="sender">GamePlayer that has released</param>
  		/// <param name="args"></param>
  		public virtual void OnReleased(DOLEvent e, object sender, EventArgs args)
  		{
  			GamePlayer player = (GamePlayer)sender;
  			StartImmunityTimer(player, ServerProperties.Properties.TIMER_KILLED_BY_MOB * 1000);//When Killed by a Mob
  		}
  
  		/// <summary>
  		/// Should be called whenever a player teleports to a new location
  		/// </summary>
  		/// <param name="player"></param>
  		/// <param name="source"></param>
  		/// <param name="destination"></param>
  		public virtual void OnPlayerTeleport(GamePlayer player, GameLocation source, Teleport destination)
  		{
  			// override this in order to do something, like set immunity, when a player teleports
  		}
  
  		/// <summary>
  		/// Starts the immunity timer for a player
  		/// </summary>
  		/// <param name="player">player that gets immunity</param>
  		/// <param name="duration">amount of milliseconds when immunity ends</param>
  		public virtual void StartImmunityTimer(GamePlayer player, int duration)
  		{
  			if (duration > 0)
  			{
  				player.StartInvulnerabilityTimer(duration, m_invExpiredCallback);
  			}
  		}
  
  		/// <summary>
  		/// Holds the delegate called when PvP invulnerability is expired
  		/// </summary>
  		protected GamePlayer.InvulnerabilityExpiredCallback m_invExpiredCallback;
  
  		/// <summary>
  		/// Removes immunity from the players
  		/// </summary>
  		/// <player></player>
  		public virtual void ImmunityExpiredCallback(GamePlayer player)
  		{
  			if (player.ObjectState != GameObject.eObjectState.Active) return;
  			if (player.Client.IsPlaying == false) return;
  
  			player.Out.SendMessage("Your temporary invulnerability timer has expired.", eChatType.CT_System, eChatLoc.CL_SystemWindow);
  
  			return;
  		}
  
  
  		public abstract bool IsSameRealm(GameLiving source, GameLiving target, bool quiet);
  		public abstract bool IsAllowedCharsInAllRealms(GameClient client);
  		public abstract bool IsAllowedToGroup(GamePlayer source, GamePlayer target, bool quiet);
  		public abstract bool IsAllowedToJoinGuild(GamePlayer source, Guild guild);
  		public abstract bool IsAllowedToTrade(GameLiving source, GameLiving target, bool quiet);
  		public abstract bool IsAllowedToUnderstand(GameLiving source, GamePlayer target);
  		public abstract string RulesDescription();
  
  		public virtual bool IsAllowedToMoveToBind(GamePlayer player)
  		{
  			return true;
  		}
  
  		public virtual bool CountsTowardsSlashLevel(DOLCharacters player)
  		{
  			return true;
  		}
  
  		/// <summary>
  		/// Is attacker allowed to attack defender.
  		/// </summary>
  		/// <param name="attacker">living that makes attack</param>
  		/// <param name="defender">attacker's target</param>
  		/// <param name="quiet">should messages be sent</param>
  		/// <returns>true if attack is allowed</returns>
  		public virtual bool IsAllowedToAttack(GameLiving attacker, GameLiving defender, bool quiet)
  		{
  			if (attacker == null || defender == null)
  				return false;
  
  			//dead things can't attack
  			if (!defender.IsAlive || !attacker.IsAlive)
  				return false;
  
  			GamePlayer playerAttacker = attacker as GamePlayer;
  			GamePlayer playerDefender = defender as GamePlayer;
  
  			// if Pet, let's define the controller once
  			if (defender is GameNPC)
  				if ((defender as GameNPC).Brain is IControlledBrain)
  					playerDefender = ((defender as GameNPC).Brain as IControlledBrain).GetPlayerOwner();
  
  			if (attacker is GameNPC)
  				if ((attacker as GameNPC).Brain is IControlledBrain)
  					playerAttacker = ((attacker as GameNPC).Brain as IControlledBrain).GetPlayerOwner();
  
  			if (playerDefender != null && (playerDefender.Client.ClientState == GameClient.eClientState.WorldEnter || playerDefender.IsInvulnerableToAttack))
  			{
  				if (!quiet)
  					MessageToLiving(attacker, defender.Name + " is entering the game and is temporarily immune to PvP attacks!");
  				return false;
  			}
  
  			if (playerAttacker != null && playerDefender != null)
  			{
  				// Attacker immunity
  				if (playerAttacker.IsInvulnerableToAttack)
  				{
  					if (quiet == false) MessageToLiving(attacker, "You can't attack players until your PvP invulnerability timer wears off!");
  					return false;
  				}
  
  				// Defender immunity
  				if (playerDefender.IsInvulnerableToAttack)
  				{
  					if (quiet == false) MessageToLiving(attacker, defender.Name + " is temporarily immune to PvP attacks!");
  					return false;
  				}
  			}
  
  			// PEACE NPCs can't be attacked/attack
  			if (attacker is GameNPC)
  				if ((((GameNPC)attacker).Flags & GameNPC.eFlags.PEACE) != 0)
  					return false;
  			if (defender is GameNPC)
  
gt: 				if ((((GameNPC)defender).Flags & GameNPC.eFlags.PEACE) != 0)