Example #1
0
 // This constructor is used to create dummy players (such as Console and /dummy)
 // It will soon be replaced by a generic Entity class
 internal Player( World _world, string _name )
 {
     world = _world;
     name = _name;
     nick = name;
     info = new PlayerInfo( _name, ClassList.highestClass );
 }
Example #2
0
 // This constructor is used to create dummy players (such as Console and /dummy)
 // It will soon be replaced by a generic Entity class
 internal Player( World world, string name ) {
     if( name == null ) throw new ArgumentNullException( "name" );
     World = world;
     Info = new PlayerInfo( name, RankManager.HighestRank, true, RankChangeType.AutoPromoted );
     spamBlockLog = new Queue<DateTime>( Info.Rank.AntiGriefBlocks );
     ResetAllBinds();
 }
Example #3
0
 public void Create( BoundingBox bounds, PlayerInfo createdBy ) {
     if( bounds == null ) throw new ArgumentNullException( "bounds" );
     if( createdBy == null ) throw new ArgumentNullException( "createdBy" );
     CreatedDate = DateTime.UtcNow;
     Bounds = bounds;
     CreatedBy = createdBy;
 }
Example #4
0
 // Throws a PlayerOpException if reason is required but missing.
 internal static void CheckKickReason( [CanBeNull] string reason, [NotNull] Player player, PlayerInfo targetInfo ) {
     if( player == null ) throw new ArgumentNullException( "player" );
     if( ConfigKey.RequireKickReason.Enabled() && String.IsNullOrEmpty( reason ) ) {
         const string msg = "Please specify a kick reason.";
         const string colorMsg = "&S" + msg;
         throw new PlayerOpException( player, targetInfo, PlayerOpExceptionCode.ReasonRequired, msg, colorMsg );
     }
 }
 /// <summary> Adds a new PlayerInfo entry for a player who has never been online, by name. </summary>
 /// <returns> A newly-created PlayerInfo entry. </returns>
 public PlayerInfo AddUnrecognizedPlayer( string name, Rank startingRank, RankChangeType rankChangeType ) {
     if( name == null ) throw new ArgumentNullException( "name" );
     if( startingRank == null ) throw new ArgumentNullException( "startingRank" );
     int id = GetNextID();
     PlayerInfo info = new PlayerInfo( id, name, startingRank, rankChangeType, false );
     trie.Add( name, info );
     return info;
 }
 /// <summary> Adds a new PlayerInfo entry for an actual, logged-in player. </summary>
 /// <returns> A newly-created PlayerInfo entry. </returns>
 public PlayerInfo AddPlayer( string name, Rank startingRank, RankChangeType rankChangeType, IPAddress address ) {
     if( name == null ) throw new ArgumentNullException( "name" );
     if( address == null ) throw new ArgumentNullException( "address" );
     int id = GetNextID();
     PlayerInfo info = new PlayerInfo( id, name, startingRank, rankChangeType, address );
     trie.Add( name, info );
     return info;
 }
Example #7
0
 public bool CanRevoke( PlayerInfo player ) {
     PermissionLimits limits = GetLimit( player );
     if( limits == null ) {
         return false;
     } else {
         return limits.CanRevoke;
     }
 }
Example #8
0
 // Normal constructor
 internal Player( World _world, string _name, Session _session, Position _pos ) {
     world = _world;
     name = _name;
     nick = name;
     session = _session;
     pos = _pos;
     info = world.db.FindPlayerInfo( this );
 }
Example #9
0
 internal static PlayerInfo AddSuperPlayer( ReservedPlayerID id, [NotNull] string name, [NotNull] Rank rank ) {
     if( name == null ) throw new ArgumentNullException( "name" );
     CheckIfLoaded();
     PlayerInfo newInfo = new PlayerInfo( (int)id, name, rank, RankChangeType.AutoPromoted, true ) {
         RaisePropertyChangedEvents = true
     };
     return newInfo;
 }
Example #10
0
 // Normal constructor
 internal Player( World world, string name, Session session, Position position ) {
     if( name == null ) throw new ArgumentNullException( "name" );
     if( session == null ) throw new ArgumentNullException( "session" );
     World = world;
     Session = session;
     Position = position;
     Info = PlayerDB.FindOrCreateInfoForPlayer( name, session.IP );
     spamBlockLog = new Queue<DateTime>( Info.Rank.AntiGriefBlocks );
     ResetAllBinds();
 }
Example #11
0
 PermissionLimits GetPlayerLimit( PlayerInfo player ) {
     PermissionLimits limiter;
     if( playerInclusions.TryGetValue( player.Name, out limiter ) ) {
         return limiter;
     } else if( Parent != null ) {
         return Parent.GetPlayerLimit( player );
     } else {
         return null;
     }
 }
Example #12
0
 public bool Can( PlayerInfo player, int quantity ) {
     if( ( Node.Flags & PermissionFlags.NeedsQuantity ) == 0 ) {
         throw new PermissionCheckException( "Quantity limit is not applicable to " + Node.Name );
     }
     PermissionLimits limits = GetLimit( player );
     if( limits == null ) {
         return false;
     } else {
         return ( limits.MaxQuantity <= quantity );
     }
 }
Example #13
0
 //Function to format and return a player's score entry. Returns null if no score found
 public static String GetPlayerScoreEntry( PlayerInfo player )
 {
     string toReturn = null;
     foreach ( String s in PlayerKillScores ) {
         if ( s.Contains( '$' ) ) {
             if ( s.Split( '$' )[0] == player.Name ) {
                 toReturn = s;
             }
         }
     }
     return toReturn;
 }
Example #14
0
 public PlayerOpException( [NotNull] Player player, PlayerInfo target,
                           PlayerOpExceptionCode errorCode,
                           [NotNull] string message, [NotNull] string messageColored )
     : base( message ) {
     if( player == null ) throw new ArgumentNullException( "player" );
     if( message == null ) throw new ArgumentNullException( "message" );
     if( messageColored == null ) throw new ArgumentNullException( "messageColored" );
     Player = player;
     Target = target;
     ErrorCode = errorCode;
     MessageColored = messageColored;
 }
Example #15
0
 // Throws a PlayerOpException if reason is required but missing.
 internal static void CheckRankChangeReason( [CanBeNull] string reason, [NotNull] Player player, PlayerInfo targetInfo, bool promoting ) {
     if( player == null ) throw new ArgumentNullException( "player" );
     if( ConfigKey.RequireRankChangeReason.Enabled() && String.IsNullOrEmpty( reason ) ) {
         string msg;
         if( promoting ) {
             msg = "Please specify a promotion reason.";
         } else {
             msg = "Please specify a demotion reason.";
         }
         string colorMsg = "&S" + msg;
         throw new PlayerOpException( player, targetInfo, PlayerOpExceptionCode.ReasonRequired, msg, colorMsg );
     }
 }
Example #16
0
 public PlayerInfo FindPlayerInfo( Player player ) {
     if( player == null ) return null;
     
     locker.EnterWriteLock();
     PlayerInfo info = tree.Get( player.name );
     if( info == null ) {
         info = new PlayerInfo( world, player );
         tree.Add( player.name, info );
         list.Add( info );
     }
     locker.ExitWriteLock();
     return info;
 }
Example #17
0
        public static bool FindPlayerInfo( string name, out PlayerInfo info )
        {
            if( name == null ) {
                info = null;
                return false;
            }

            bool noDupe;
            locker.EnterWriteLock();
            noDupe = tree.Get( name, out info );
            locker.ExitWriteLock();

            return noDupe;
        }
Example #18
0
 // returns the PREVIOUS state of the player
 public PermissionOverride Include( PlayerInfo info ) {
     if( info == null ) throw new ArgumentNullException( "info" );
     lock( playerPermissionListLock ) {
         if( includedPlayers.ContainsValue( info ) ) {
             UpdatePlayerListCache();
             return PermissionOverride.Allow;
         } else if( excludedPlayers.ContainsValue( info ) ) {
             excludedPlayers.Remove( info.Name.ToLower() );
             UpdatePlayerListCache();
             return PermissionOverride.Deny;
         } else {
             includedPlayers.Add( info.Name.ToLower(), info );
             UpdatePlayerListCache();
             return PermissionOverride.None;
         }
     }
 }
Example #19
0
        public static PlayerInfo AddFakeEntry( string name, RankChangeType rankChangeType ) {
            if( name == null ) throw new ArgumentNullException( "name" );

            PlayerInfo info;
            lock( AddLocker ) {
                info = Trie.Get( name );
                if( info != null ) throw new ArgumentException( "A PlayerDB entry already exists for this name." );

                var e = new PlayerInfoCreatingEventArgs( name, IPAddress.None, RankManager.DefaultRank, true );
                Server.RaisePlayerInfoCreatingEvent( e );
                if( e.Cancel ) throw new OperationCanceledException( "Cancelled by a plugin." );

                info = new PlayerInfo( name, e.StartingRank, false, rankChangeType );

                List.Add( info );
                Trie.Add( info.Name, info );
                UpdateCache();
            }
            Server.RaisePlayerInfoCreatedEvent( info, false );
            return info;
        }
Example #20
0
 // Adds a name to the tree.
 public bool Add( string name, PlayerInfo payload ) {
     StringNode temp = root;
     int code;
     for( int i = 0; i < name.Length; i++ ) {
         code = CharCode( name[i] );
         if( temp.children[code] == null ) {
             temp.children[code] = new StringNode();
         }
         if( temp.tag == EMPTY ) {
             temp.tag = (byte)code;
         } else {
             temp.tag = MULTI;
         }
         temp = temp.children[code];
     }
     if( temp.payload != null )
         return false;
     temp.payload = payload;
     count++;
     return true;
 }
Example #21
0
        // Searches for players starting with namePart.
        //     Returns false if more than one name matched.
        //     Returns true and sets info to null if no names matched.
        public bool Get( string namePart, out PlayerInfo info ) {
            StringNode temp = root;
            int code;
            for( int i = 0; i < namePart.Length; i++ ) {
                code = CharCode( namePart[i] );
                if( temp.children[code] == null ) {
                    info = null;
                    return true; // early detection of no matches
                }
                temp = temp.children[code];
            }

            if( temp.payload != null ) {
                info = temp.payload;
                return true; // exact match
            } else if( temp.tag == MULTI ) {
                info = null;
                return false; // multiple matches
            }
            for( ; temp.tag < MULTI; temp = temp.children[temp.tag] ) ;
            info = temp.payload;
            return true; // one autocompleted match
        }
Example #22
0
 public void Load() {
     if( File.Exists( FileName ) ) {
         using( StreamReader reader = File.OpenText( FileName ) ) {
             reader.ReadLine(); // header
             while( !reader.EndOfStream ) {
                 string[] fields = reader.ReadLine().Split( ',' );
                 if( fields.Length == PlayerInfo.fieldCount ) {
                     try {
                         PlayerInfo info = new PlayerInfo(world, fields );
                         tree.Add( info.name, info );
                         list.Add( info );
                     } catch( FormatException ex ) {
                         world.log.Log( "PlayerDB.Load: Could not parse a record: {0}.", LogType.Error, ex.Message );
                     } catch( IOException ex ) {
                         world.log.Log( "PlayerDB.Load: Error while trying to read from file: {0}.", LogType.Error, ex.Message );
                     }
                 }
             }
         }
         world.log.Log( "PlayerDB.Load: Done loading player DB ({0} records).", LogType.Debug, tree.Count() );
     } else {
         world.log.Log( "PlayerDB.Load: No player DB file found.", LogType.Warning );
     }
 }
Example #23
0
        public Zone(string raw, World world)
        {
            string[] parts = raw.Split(',');

            string[] header = parts[0].Split(' ');
            Name   = header[0];
            Bounds = new BoundingBox(Int32.Parse(header[1]), Int32.Parse(header[2]), Int32.Parse(header[3]),
                                     Int32.Parse(header[4]), Int32.Parse(header[5]), Int32.Parse(header[6]));

            Rank buildRank = RankManager.ParseRank(header[7]);

            // if all else fails, fall back to lowest class
            if (buildRank == null)
            {
                if (world != null)
                {
                    Controller.MinRank = world.BuildSecurity.MinRank;
                }
                else
                {
                    Controller.MinRank = null;
                }
                Logger.Log("Zone: Error parsing zone definition: unknown rank \"{0}\". Permission reset to default ({1}).", LogType.Error,
                           header[7], Controller.MinRank.Name);
            }
            else
            {
                Controller.MinRank = buildRank;
            }


            // Part 2:
            foreach (string player in parts[1].Split(' '))
            {
                if (!Player.IsValidName(player))
                {
                    continue;
                }
                PlayerInfo info = PlayerDB.FindPlayerInfoExact(player);
                if (info == null)
                {
                    continue;                // player name not found in the DB (discarded)
                }
                Controller.Include(info);
            }

            // Part 3: excluded list
            foreach (string player in parts[2].Split(' '))
            {
                if (!Player.IsValidName(player))
                {
                    continue;
                }
                PlayerInfo info = PlayerDB.FindPlayerInfoExact(player);
                if (info == null)
                {
                    continue;                // player name not found in the DB (discarded)
                }
                Controller.Exclude(info);
            }

            Controller.UpdatePlayerListCache();

            // Part 4: extended header
            if (parts.Length > 3)
            {
                string[] xheader = parts[3].Split(' ');
                CreatedBy = PlayerDB.FindPlayerInfoExact(xheader[0]);
                if (CreatedBy != null)
                {
                    CreatedDate = DateTime.Parse(xheader[1]);
                }
                EditedBy = PlayerDB.FindPlayerInfoExact(xheader[2]);
                if (EditedBy != null)
                {
                    EditedDate = DateTime.Parse(xheader[3]);
                }
            }
        }
Example #24
0
 public bool Unignore( PlayerInfo other ) {
     lock( ignoreLock ) {
         if( ignoreList.Contains( other ) ) {
             ignoreList.Remove( other );
             return true;
         } else {
             return false;
         }
     }
 }
Example #25
0
        /// <summary> Unbans given IP address and all accounts on that IP. </summary>
        /// <param name="targetAddress"> IP address that is being unbanned. </param>
        /// <param name="player"> Player who is unbanning. </param>
        /// <param name="reason"> Reason for unban. May be null or empty, if permitted by server configuration. </param>
        /// <param name="announce"> Whether unban should be publicly announced on the server. </param>
        /// <param name="raiseEvents"> Whether RemovingIPBan, RemovedIPBan, BanChanging, and BanChanged events should be raised. </param>
        /// <exception cref="ArgumentNullException"> targetAddress or player is null. </exception>
        /// <exception cref="PlayerOpException"> Permission or configuration issues arise, or if everyone has already been unbanned. </exception>
        public static void UnbanAll([NotNull] this IPAddress targetAddress, [NotNull] Player player, [CanBeNull] string reason,
                                    bool announce, bool raiseEvents)
        {
            if (targetAddress == null)
            {
                throw new ArgumentNullException("targetAddress");
            }
            if (player == null)
            {
                throw new ArgumentNullException("player");
            }

            if (reason != null)
            {
                reason = reason.Trim(' ');
            }
            if (reason != null && reason.Length == 0)
            {
                reason = null;
            }

            if (!player.Can(Permission.Ban, Permission.BanIP, Permission.BanAll))
            {
                PlayerOpException.ThrowPermissionMissing(player, null, "unban-all",
                                                         Permission.Ban, Permission.BanIP, Permission.BanAll);
            }

            // Check if player is trying to unban self
            if (targetAddress.Equals(player.IP) && !player.IsSuper)
            {
                PlayerOpException.ThrowCannotTargetSelf(player, null, "unban-all");
            }

            // Check if a non-bannable address was given (0.0.0.0 or 255.255.255.255)
            if (targetAddress.Equals(IPAddress.None) || targetAddress.Equals(IPAddress.Any))
            {
                PlayerOpException.ThrowInvalidIP(player, null, targetAddress);
            }

            PlayerOpException.CheckBanReason(reason, player, null, true);
            bool somethingGotUnbanned = false;

            lock ( BanListLock ) {
                CheckIfLoaded();
                // Unban the IP
                if (Contains(targetAddress))
                {
                    if (Remove(targetAddress, raiseEvents))
                    {
                        Logger.Log(LogType.UserActivity,
                                   "{0} unbanned {1} (UnbanAll {1}). Reason: {2}",
                                   player.Name, targetAddress, reason ?? "");

                        // Announce unban on the server
                        if (announce)
                        {
                            var can = Server.Players.Can(Permission.ViewPlayerIPs);
                            can.Message("&W{0} was unbanned by {1}", targetAddress, player.ClassyName);
                            var cant = Server.Players.Cant(Permission.ViewPlayerIPs);
                            cant.Message("&WAn IP was unbanned by {0}", player.ClassyName);
                        }

                        somethingGotUnbanned = true;
                    }
                }

                // Unban individual players
                PlayerInfo[] allPlayersOnIP = PlayerDB.FindPlayers(targetAddress);
                foreach (PlayerInfo targetAlt in allPlayersOnIP)
                {
                    if (targetAlt.BanStatus != BanStatus.Banned)
                    {
                        continue;
                    }

                    // Raise PlayerInfo.BanChanging event
                    PlayerInfoBanChangingEventArgs e = new PlayerInfoBanChangingEventArgs(targetAlt, player, true,
                                                                                          reason, announce);
                    if (raiseEvents)
                    {
                        PlayerInfo.RaiseBanChangingEvent(e);
                        if (e.Cancel)
                        {
                            continue;
                        }
                        reason = e.Reason;
                    }

                    // Do the ban
                    if (targetAlt.ProcessUnban(player.Name, reason))
                    {
                        if (raiseEvents)
                        {
                            PlayerInfo.RaiseBanChangedEvent(e);
                        }

                        // Log and announce ban
                        Logger.Log(LogType.UserActivity,
                                   "{0} unbanned {1} (UnbanAll {2}). Reason: {3}",
                                   player.Name, targetAlt.Name, targetAddress, reason ?? "");
                        if (announce)
                        {
                            Server.Message("&WPlayer {0}&W was unbanned by {1}&W (UnbanAll)",
                                           targetAlt.ClassyName, player.ClassyName);
                        }
                        somethingGotUnbanned = true;
                    }
                }
            }

            // If no one ended up getting unbanned, quit here
            if (!somethingGotUnbanned)
            {
                PlayerOpException.ThrowNoOneToUnban(player, null, targetAddress);
            }

            // Announce UnbanAll reason towards the end of all unbans
            if (announce && ConfigKey.AnnounceKickAndBanReasons.Enabled() && reason != null)
            {
                Server.Message("&WUnbanAll reason: {0}", reason);
            }
        }
Example #26
0
        public Zone([NotNull] string raw, [CanBeNull] World world)
            : this()
        {
            if (raw == null)
            {
                throw new ArgumentNullException("raw");
            }
            string[] parts = raw.Split(',');

            string[] header = parts[0].Split(' ');
            Name   = header[0];
            Bounds = new BoundingBox(Int32.Parse(header[1]), Int32.Parse(header[2]), Int32.Parse(header[3]),
                                     Int32.Parse(header[4]), Int32.Parse(header[5]), Int32.Parse(header[6]));

            // If no ranks are loaded (e.g. MapConverter/MapRenderer)(
            if (RankManager.Ranks.Count > 0)
            {
                Rank buildRank = Rank.Parse(header[7]);
                // if all else fails, fall back to lowest class
                if (buildRank == null)
                {
                    if (world != null)
                    {
                        Controller.MinRank = world.BuildSecurity.MinRank;
                    }
                    else
                    {
                        Controller.ResetMinRank();
                    }
                    Logger.Log(LogType.Error,
                               "Zone: Error parsing zone definition: unknown rank \"{0}\". Permission reset to default ({1}).",
                               header[7], Controller.MinRank.Name);
                }
                else
                {
                    Controller.MinRank = buildRank;
                }
            }

            // If PlayerDB is not loaded (e.g. ConfigGUI)
            if (PlayerDB.IsLoaded)
            {
                // Part 2:
                if (parts[1].Length > 0)
                {
                    foreach (string playerName in parts[1].Split(' '))
                    {
                        if (!Player.IsValidPlayerName(playerName))
                        {
                            Logger.Log(LogType.Warning,
                                       "Invalid entry in zone \"{0}\" whitelist: {1}", Name, playerName);
                            continue;
                        }
                        PlayerInfo info = PlayerDB.FindPlayerInfoExact(playerName);
                        if (info == null)
                        {
                            Logger.Log(LogType.Warning,
                                       "Unrecognized player in zone \"{0}\" whitelist: {1}", Name, playerName);
                            continue; // player name not found in the DB (discarded)
                        }
                        Controller.Include(info);
                    }
                }

                // Part 3: excluded list
                if (parts[2].Length > 0)
                {
                    foreach (string playerName in parts[2].Split(' '))
                    {
                        if (!Player.IsValidPlayerName(playerName))
                        {
                            Logger.Log(LogType.Warning,
                                       "Invalid entry in zone \"{0}\" blacklist: {1}", Name, playerName);
                            continue;
                        }
                        PlayerInfo info = PlayerDB.FindPlayerInfoExact(playerName);
                        if (info == null)
                        {
                            Logger.Log(LogType.Warning,
                                       "Unrecognized player in zone \"{0}\" whitelist: {1}", Name, playerName);
                            continue; // player name not found in the DB (discarded)
                        }
                        Controller.Exclude(info);
                    }
                }
            }
            else
            {
                RawWhitelist = parts[1];
                RawBlacklist = parts[2];
            }

            // Part 4: extended header
            if (parts.Length > 3)
            {
                string[] xheader = parts[3].Split(' ');
                if (xheader[0] == "-")
                {
                    CreatedBy   = null;
                    CreatedDate = DateTime.MinValue;
                }
                else
                {
                    CreatedBy   = xheader[0];
                    CreatedDate = DateTime.Parse(xheader[1]);
                }

                if (xheader[2] == "-")
                {
                    EditedBy   = null;
                    EditedDate = DateTime.MinValue;
                }
                else
                {
                    EditedBy   = xheader[2];
                    EditedDate = DateTime.Parse(xheader[3]);
                }
            }
        }
Example #27
0
        internal static void SwapPlayerInfo([NotNull] PlayerInfo p1, [NotNull] PlayerInfo p2)
        {
            if (p1 == null)
            {
                throw new ArgumentNullException("p1");
            }
            if (p2 == null)
            {
                throw new ArgumentNullException("p2");
            }
            lock ( AddLocker ) {
                lock ( SaveLoadLocker ) {
                    if (p1.IsOnline || p2.IsOnline)
                    {
                        throw new Exception("Both players must be offline to swap info.");
                    }
                    Swap(ref p1.BanDate, ref p2.BanDate);
                    Swap(ref p1.BandwidthUseMode, ref p2.BandwidthUseMode);
                    Swap(ref p1.BanStatus, ref p2.BanStatus);
                    Swap(ref p1.BannedBy, ref p2.BannedBy);
                    Swap(ref p1.BannedUntil, ref p2.BannedUntil);
                    Swap(ref p1.BanReason, ref p2.BanReason);
                    Swap(ref p1.BlocksBuilt, ref p2.BlocksBuilt);
                    Swap(ref p1.BlocksDeleted, ref p2.BlocksDeleted);
                    Swap(ref p1.BlocksDrawn, ref p2.BlocksDrawn);
                    Swap(ref p1.DisplayedName, ref p2.DisplayedName);
                    Swap(ref p1.FirstLoginDate, ref p2.FirstLoginDate);
                    Swap(ref p1.FrozenBy, ref p2.FrozenBy);
                    Swap(ref p1.FrozenOn, ref p2.FrozenOn);
                    Swap(ref p1.ID, ref p2.ID);
                    Swap(ref p1.IsFrozen, ref p2.IsFrozen);
                    //Swap( ref p1.IsHidden, ref p2.IsHidden );
                    Swap(ref p1.LastFailedLoginDate, ref p2.LastFailedLoginDate);
                    Swap(ref p1.LastFailedLoginIP, ref p2.LastFailedLoginIP);
                    //Swap( ref p1.LastIP, ref p2.LastIP );
                    Swap(ref p1.LastKickBy, ref p2.LastKickBy);
                    Swap(ref p1.LastKickDate, ref p2.LastKickDate);
                    Swap(ref p1.LastKickReason, ref p2.LastKickReason);
                    //Swap( ref p1.LastLoginDate, ref p2.LastLoginDate );
                    //Swap( ref p1.LastSeen, ref p2.LastSeen );
                    //Swap( ref p1.LeaveReason, ref p2.LeaveReason );
                    Swap(ref p1.MessagesWritten, ref p2.MessagesWritten);
                    Swap(ref p1.MutedBy, ref p2.MutedBy);
                    Swap(ref p1.MutedUntil, ref p2.MutedUntil);
                    //Swap( ref p1.Name, ref p2.Name );
                    //Swap( ref p1.Online, ref p2.Online );
                    Swap(ref p1.Password, ref p2.Password);
                    //Swap( ref p1.PlayerObject, ref p2.PlayerObject );
                    Swap(ref p1.PreviousRank, ref p2.PreviousRank);

                    Rank p1Rank = p1.Rank;
                    p1.Rank = p2.Rank;
                    p2.Rank = p1Rank;

                    Swap(ref p1.RankChangeDate, ref p2.RankChangeDate);
                    Swap(ref p1.RankChangedBy, ref p2.RankChangedBy);
                    Swap(ref p1.RankChangeReason, ref p2.RankChangeReason);
                    Swap(ref p1.RankChangeType, ref p2.RankChangeType);
                    Swap(ref p1.TimesBannedOthers, ref p2.TimesBannedOthers);
                    Swap(ref p1.TimesKicked, ref p2.TimesKicked);
                    Swap(ref p1.TimesKickedOthers, ref p2.TimesKickedOthers);
                    Swap(ref p1.TimesVisited, ref p2.TimesVisited);
                    Swap(ref p1.TotalTime, ref p2.TotalTime);
                    Swap(ref p1.UnbanDate, ref p2.UnbanDate);
                    Swap(ref p1.UnbannedBy, ref p2.UnbannedBy);
                    Swap(ref p1.UnbanReason, ref p2.UnbanReason);

                    list.Sort(PlayerIDComparer.Instance);
                }
            }
        }
Example #28
0
        static void ZoneAddHandler(Player player, CommandReader cmd)
        {
            World playerWorld = player.World;

            if (playerWorld == null)
            {
                PlayerOpException.ThrowNoWorld(player);
            }

            string givenZoneName = cmd.Next();

            if (givenZoneName == null)
            {
                CdZoneAdd.PrintUsage(player);
                return;
            }

            if (!player.Info.Rank.AllowSecurityCircumvention)
            {
                SecurityCheckResult buildCheck = playerWorld.BuildSecurity.CheckDetailed(player.Info);
                switch (buildCheck)
                {
                case SecurityCheckResult.BlackListed:
                    player.Message("Cannot add zones to world {0}&S: You are barred from building here.",
                                   playerWorld.ClassyName);
                    return;

                case SecurityCheckResult.RankTooLow:
                    player.Message("Cannot add zones to world {0}&S: You are not allowed to build here.",
                                   playerWorld.ClassyName);
                    return;

                    //case SecurityCheckResult.RankTooHigh: // TODO
                }
            }

            Zone           newZone        = new Zone();
            ZoneCollection zoneCollection = player.WorldMap.Zones;

            if (givenZoneName.StartsWith("+"))
            {
                // personal zone (/ZAdd +Name)
                givenZoneName = givenZoneName.Substring(1);

                // Find the target player
                PlayerInfo info = PlayerDB.FindByPartialNameOrPrintMatches(player, givenZoneName);
                if (info == null)
                {
                    return;
                }
                givenZoneName = info.Name;

                // Make sure that the name is not taken already.
                // If a zone named after the player already exists, try adding a number after the name (e.g. "Notch2")
                newZone.Name = givenZoneName;
                for (int i = 2; zoneCollection.Contains(newZone.Name); i++)
                {
                    newZone.Name = givenZoneName + i;
                }

                newZone.Controller.MinRank = info.Rank.NextRankUp ?? info.Rank;
                newZone.Controller.Include(info);
                player.Message("Zone: Creating a {0}+&S zone for player {1}&S. Place a block or type /Mark to use your location.",
                               newZone.Controller.MinRank.ClassyName, info.ClassyName);
                player.SelectionStart(2, ZoneAddCallback, newZone, CdZoneAdd.Permissions);
            }
            else
            {
                // Adding an ordinary, rank-restricted zone.
                if (!World.IsValidName(givenZoneName))
                {
                    player.Message("\"{0}\" is not a valid zone name", givenZoneName);
                    return;
                }

                if (zoneCollection.Contains(givenZoneName))
                {
                    player.Message("A zone with this name already exists. Use &H/ZEdit&S to edit.");
                    return;
                }

                newZone.Name = givenZoneName;

                string rankName = cmd.Next();
                if (rankName == null)
                {
                    player.Message("No rank was specified. See &H/Help zone");
                    return;
                }
                Rank minRank = RankManager.FindRank(rankName);

                if (minRank != null)
                {
                    string name;
                    while ((name = cmd.Next()) != null)
                    {
                        if (name.Length == 0)
                        {
                            continue;
                        }

                        PlayerInfo info = PlayerDB.FindByPartialNameOrPrintMatches(player, name.Substring(1));
                        if (info == null)
                        {
                            return;
                        }

                        if (name.StartsWith("+"))
                        {
                            newZone.Controller.Include(info);
                        }
                        else if (name.StartsWith("-"))
                        {
                            newZone.Controller.Exclude(info);
                        }
                    }

                    newZone.Controller.MinRank = minRank;
                    player.SelectionStart(2, ZoneAddCallback, newZone, CdZoneAdd.Permissions);
                    player.Message("Zone: Place a block or type &H/Mark&S to use your location.");
                }
                else
                {
                    player.MessageNoRank(rankName);
                }
            }
        }
Example #29
0
        static void ZoneEditHandler(Player player, CommandReader cmd)
        {
            bool   changesWereMade = false;
            string zoneName        = cmd.Next();

            if (zoneName == null)
            {
                player.Message("No zone name specified. See &H/Help ZEdit");
                return;
            }

            Zone zone = player.WorldMap.Zones.Find(zoneName);

            if (zone == null)
            {
                player.MessageNoZone(zoneName);
                return;
            }

            string nextToken;

            while ((nextToken = cmd.Next()) != null)
            {
                // Clear whitelist
                if (nextToken.Equals("-*"))
                {
                    PlayerInfo[] oldWhitelist = zone.Controller.ExceptionList.Included;
                    if (oldWhitelist.Length > 0)
                    {
                        zone.Controller.ResetIncludedList();
                        player.Message("Whitelist of zone {0}&S cleared: {1}",
                                       zone.ClassyName, oldWhitelist.JoinToClassyString());
                        Logger.Log(LogType.UserActivity,
                                   "Player {0} cleared whitelist of zone {1} on world {2}: {3}",
                                   player.Name, zone.Name, player.World.Name,
                                   oldWhitelist.JoinToString(pi => pi.Name));
                    }
                    else
                    {
                        player.Message("Whitelist of zone {0}&S is empty.",
                                       zone.ClassyName);
                    }
                    continue;
                }

                // Clear blacklist
                if (nextToken.Equals("+*"))
                {
                    PlayerInfo[] oldBlacklist = zone.Controller.ExceptionList.Excluded;
                    if (oldBlacklist.Length > 0)
                    {
                        zone.Controller.ResetExcludedList();
                        player.Message("Blacklist of zone {0}&S cleared: {1}",
                                       zone.ClassyName, oldBlacklist.JoinToClassyString());
                        Logger.Log(LogType.UserActivity,
                                   "Player {0} cleared blacklist of zone {1} on world {2}: {3}",
                                   player.Name, zone.Name, player.World.Name,
                                   oldBlacklist.JoinToString(pi => pi.Name));
                    }
                    else
                    {
                        player.Message("Blacklist of zone {0}&S is empty.",
                                       zone.ClassyName);
                    }
                    continue;
                }

                if (nextToken.StartsWith("+"))
                {
                    PlayerInfo info = PlayerDB.FindByPartialNameOrPrintMatches(player, nextToken.Substring(1));
                    if (info == null)
                    {
                        return;
                    }

                    // prevent players from whitelisting themselves to bypass protection
                    if (!player.Info.Rank.AllowSecurityCircumvention && player.Info == info)
                    {
                        switch (zone.Controller.CheckDetailed(info))
                        {
                        case SecurityCheckResult.BlackListed:
                            player.Message("You are not allowed to remove yourself from the blacklist of zone {0}",
                                           zone.ClassyName);
                            continue;

                        case SecurityCheckResult.RankTooLow:
                            player.Message("You must be {0}+&S to add yourself to the whitelist of zone {1}",
                                           zone.Controller.MinRank.ClassyName, zone.ClassyName);
                            continue;
                        }
                    }

                    switch (zone.Controller.Include(info))
                    {
                    case PermissionOverride.Deny:
                        player.Message("{0}&S is no longer excluded from zone {1}",
                                       info.ClassyName, zone.ClassyName);
                        changesWereMade = true;
                        break;

                    case PermissionOverride.None:
                        player.Message("{0}&S is now included in zone {1}",
                                       info.ClassyName, zone.ClassyName);
                        changesWereMade = true;
                        break;

                    case PermissionOverride.Allow:
                        player.Message("{0}&S is already included in zone {1}",
                                       info.ClassyName, zone.ClassyName);
                        break;
                    }
                }
                else if (nextToken.StartsWith("-"))
                {
                    PlayerInfo info = PlayerDB.FindByPartialNameOrPrintMatches(player, nextToken.Substring(1));
                    if (info == null)
                    {
                        return;
                    }

                    switch (zone.Controller.Exclude(info))
                    {
                    case PermissionOverride.Deny:
                        player.Message("{0}&S is already excluded from zone {1}",
                                       info.ClassyName, zone.ClassyName);
                        break;

                    case PermissionOverride.None:
                        player.Message("{0}&S is now excluded from zone {1}",
                                       info.ClassyName, zone.ClassyName);
                        changesWereMade = true;
                        break;

                    case PermissionOverride.Allow:
                        player.Message("{0}&S is no longer included in zone {1}",
                                       info.ClassyName, zone.ClassyName);
                        changesWereMade = true;
                        break;
                    }
                }
                else
                {
                    Rank minRank = RankManager.FindRank(nextToken);

                    if (minRank != null)
                    {
                        // prevent players from lowering rank so bypass protection
                        if (!player.Info.Rank.AllowSecurityCircumvention &&
                            zone.Controller.MinRank > player.Info.Rank && minRank <= player.Info.Rank)
                        {
                            player.Message("You are not allowed to lower the zone's rank.");
                            continue;
                        }

                        if (zone.Controller.MinRank != minRank)
                        {
                            zone.Controller.MinRank = minRank;
                            player.Message("Permission for zone \"{0}\" changed to {1}+",
                                           zone.Name,
                                           minRank.ClassyName);
                            changesWereMade = true;
                        }
                    }
                    else
                    {
                        player.MessageNoRank(nextToken);
                    }
                }

                if (changesWereMade)
                {
                    zone.Edit(player.Info);
                }
                else
                {
                    player.Message("No changes were made to the zone.");
                }
            }
        }
Example #30
0
 public bool IsIgnoring( PlayerInfo other ) {
     lock( ignoreLock ) {
         return ignoreList.Contains( other );
     }
 }
Example #31
0
        /// <summary> Bans given IP address and all accounts on that IP. All players from IP are kicked.
        /// Throws PlayerOpException on problems. </summary>
        /// <param name="targetAddress"> IP address that is being banned. </param>
        /// <param name="player"> Player who is banning. </param>
        /// <param name="reason"> Reason for ban. May be empty, if permitted by server configuration. </param>
        /// <param name="announce"> Whether ban should be publicly announced on the server. </param>
        /// <param name="raiseEvents"> Whether AddingIPBan, AddedIPBan, BanChanging, and BanChanged events should be raised. </param>
        /// <exception cref="ArgumentNullException"> targetAddress or player is null. </exception>
        /// <exception cref="PlayerOpException"> Permission or configuration issues arise, or if everyone has already been banned. </exception>
        public static void BanAll([NotNull] this IPAddress targetAddress, [NotNull] Player player, [CanBeNull] string reason,
                                  bool announce, bool raiseEvents)
        {
            if (targetAddress == null)
            {
                throw new ArgumentNullException("targetAddress");
            }
            if (player == null)
            {
                throw new ArgumentNullException("player");
            }

            if (reason != null)
            {
                reason = reason.Trim(' ');
            }
            if (reason != null && reason.Length == 0)
            {
                reason = null;
            }

            if (!player.Can(Permission.Ban, Permission.BanIP, Permission.BanAll))
            {
                PlayerOpException.ThrowPermissionMissing(player, null, "ban-all",
                                                         Permission.Ban, Permission.BanIP, Permission.BanAll);
            }

            // Check if player is trying to ban self
            if (targetAddress.Equals(player.IP) && !player.IsSuper)
            {
                PlayerOpException.ThrowCannotTargetSelf(player, null, "ban-all");
            }

            // Check if a non-bannable address was given (0.0.0.0 or 255.255.255.255)
            if (targetAddress.Equals(IPAddress.None) || targetAddress.Equals(IPAddress.Any))
            {
                PlayerOpException.ThrowInvalidIP(player, null, targetAddress);
            }

            // Check if any high-ranked players use this address
            PlayerInfo[] allPlayersOnIP        = PlayerDB.FindPlayers(targetAddress);
            PlayerInfo   infoWhomPlayerCantBan = allPlayersOnIP.FirstOrDefault(info => !player.Can(Permission.Ban, info.Rank));

            if (infoWhomPlayerCantBan != null)
            {
                PlayerOpException.ThrowPermissionLimitIP(player, infoWhomPlayerCantBan, targetAddress);
            }

            PlayerOpException.CheckBanReason(reason, player, null, false);
            bool somethingGotBanned = false;

            lock ( BanListLock ) {
                CheckIfLoaded();
                // Ban the IP
                if (!Contains(targetAddress))
                {
                    IPBanInfo banInfo = new IPBanInfo(targetAddress, null, player.Name, reason);
                    if (Add(banInfo, raiseEvents))
                    {
                        Logger.Log(LogType.UserActivity,
                                   "{0} banned {1} (BanAll {1}). Reason: {2}",
                                   player.Name, targetAddress, reason ?? "");

                        // Announce ban on the server
                        if (announce)
                        {
                            var can = Server.Players.Can(Permission.ViewPlayerIPs);
                            can.Message("&W{0} was banned by {1}", targetAddress, player.ClassyName);
                            var cant = Server.Players.Cant(Permission.ViewPlayerIPs);
                            cant.Message("&WAn IP was banned by {0}", player.ClassyName);
                        }
                        somethingGotBanned = true;
                    }
                }

                // Ban individual players
                foreach (PlayerInfo targetAlt in allPlayersOnIP)
                {
                    if (targetAlt.BanStatus != BanStatus.NotBanned)
                    {
                        continue;
                    }

                    // Raise PlayerInfo.BanChanging event
                    PlayerInfoBanChangingEventArgs e = new PlayerInfoBanChangingEventArgs(targetAlt, player, false,
                                                                                          reason, announce);
                    if (raiseEvents)
                    {
                        PlayerInfo.RaiseBanChangingEvent(e);
                        if (e.Cancel)
                        {
                            continue;
                        }
                        reason = e.Reason;
                    }

                    // Do the ban
                    if (targetAlt.ProcessBan(player, player.Name, reason))
                    {
                        if (raiseEvents)
                        {
                            PlayerInfo.RaiseBanChangedEvent(e);
                        }

                        // Log and announce ban
                        Logger.Log(LogType.UserActivity,
                                   "{0} banned {1} (BanAll {2}). Reason: {3}",
                                   player.Name, targetAlt.Name, targetAddress, reason ?? "");
                        if (announce)
                        {
                            Server.Message("&WPlayer {0}&W was banned by {1}&W (BanAll)",
                                           targetAlt.ClassyName, player.ClassyName);
                        }
                        somethingGotBanned = true;
                    }
                }
            }

            // If no one ended up getting banned, quit here
            if (!somethingGotBanned)
            {
                PlayerOpException.ThrowNoOneToBan(player, null, targetAddress);
            }

            // Announce BanAll reason towards the end of all bans
            if (announce && ConfigKey.AnnounceKickAndBanReasons.Enabled() && reason != null)
            {
                Server.Message("&WBanAll reason: {0}", reason);
            }

            // Kick all players from IP
            Player[] targetsOnline = Server.Players.FromIP(targetAddress).ToArray();
            if (targetsOnline.Length > 0)
            {
                string kickReason;
                if (reason != null)
                {
                    kickReason = String.Format("Banned by {0}: {1}", player.Name, reason);
                }
                else
                {
                    kickReason = String.Format("Banned by {0}", player.Name);
                }
                for (int i = 0; i < targetsOnline.Length; i++)
                {
                    if (targetsOnline[i].Info.BanStatus != BanStatus.IPBanExempt)
                    {
                        targetsOnline[i].Kick(kickReason, LeaveReason.BanAll);
                    }
                }
            }
        }
Example #32
0
 // Throws a PlayerOpException if reason is required but missing.
 internal static void CheckKickReason([CanBeNull] string reason, [NotNull] Player player, PlayerInfo targetInfo)
 {
     if (player == null)
     {
         throw new ArgumentNullException("player");
     }
     if (ConfigKey.RequireKickReason.Enabled() && String.IsNullOrEmpty(reason))
     {
         const string msg      = "Please specify a kick reason.";
         const string colorMsg = "&S" + msg;
         throw new PlayerOpException(player, targetInfo, PlayerOpExceptionCode.ReasonRequired, msg, colorMsg);
     }
 }
Example #33
0
        public static PlayerInfo FindPlayerInfoOrPrintMatches([NotNull] Player player, [NotNull] string partialName, bool includeSelf)
        {
            if (player == null)
            {
                throw new ArgumentNullException("player");
            }
            if (partialName == null)
            {
                throw new ArgumentNullException("partialName");
            }
            CheckIfLoaded();

            // If name starts with '!', return matches for online players only
            if (partialName.Length > 1 && partialName[0] == '!')
            {
                partialName = partialName.Substring(1);
                Player targetPlayer = Server.FindPlayerOrPrintMatches(player, partialName, includeSelf, false, true);
                if (targetPlayer != null)
                {
                    return(targetPlayer.Info);
                }
                else
                {
                    player.Message("No online players found matching \"{0}\"", partialName);
                    return(null);
                }
            }

            // Repeat last-used player name
            if (partialName == "-")
            {
                if (player.LastUsedPlayerName != null)
                {
                    partialName = player.LastUsedPlayerName;
                }
                else
                {
                    player.Message("Cannot repeat player name: you haven't used any names yet.");
                    return(null);
                }
            }

            // Make sure player name is valid
            if (!Player.ContainsValidCharacters(partialName))
            {
                player.MessageInvalidPlayerName(partialName);
                return(null);
            }

            // Search for exact matches first
            PlayerInfo target = FindPlayerInfoExact(partialName);

            // If no exact match was found, look for partial matches
            if (target == null || target == player.Info && !includeSelf)
            {
                PlayerInfo[] targets = FindPlayers(partialName);
                if (!includeSelf && targets.Length > 1)
                {
                    targets = targets.Where(p => p != player.Info).ToArray();
                }

                if (targets.Length == 0)
                {
                    // No matches
                    player.MessageNoPlayer(partialName);
                    return(null);
                }
                else if (targets.Length > 1)
                {
                    // More than one match
                    Array.Sort(targets, new PlayerInfoComparer(player));
                    player.MessageManyMatches("player", targets.Take(25).ToArray());
                    return(null);
                } // else: one match!
                target = targets[0];
            }

            // If a single name has been found, set it as LastUsedPlayerName
            if (includeSelf || target != player.Info)
            {
                player.LastUsedPlayerName = target.Name;
            }
            return(target);
        }
Example #34
0
        static void ZoneEditHandler(Player player, Command cmd)
        {
            bool   changesWereMade = false;
            string zoneName        = cmd.Next();

            if (zoneName == null)
            {
                player.Message("No zone name specified. See &H/Help ZEdit");
                return;
            }

            Zone zone = player.WorldMap.Zones.Find(zoneName);

            if (zone == null)
            {
                player.MessageNoZone(zoneName);
                return;
            }

            string name;

            while ((name = cmd.Next()) != null)
            {
                if (name.StartsWith("+"))
                {
                    if (name.Length == 1)
                    {
                        CdZoneEdit.PrintUsage(player);
                        break;
                    }
                    PlayerInfo info = PlayerDB.FindPlayerInfoOrPrintMatches(player, name.Substring(1));
                    if (info == null)
                    {
                        return;
                    }

                    // prevent players from whitelisting themselves to bypass protection
                    if (!player.Info.Rank.AllowSecurityCircumvention && player.Info == info)
                    {
                        if (!zone.Controller.Check(info))
                        {
                            player.Message("You must be {0}+&S to add yourself to this zone's whitelist.",
                                           zone.Controller.MinRank.ClassyName);
                            continue;
                        }
                    }

                    switch (zone.Controller.Include(info))
                    {
                    case PermissionOverride.Deny:
                        player.Message("{0}&S is no longer excluded from zone {1}",
                                       info.ClassyName, zone.ClassyName);
                        changesWereMade = true;
                        break;

                    case PermissionOverride.None:
                        player.Message("{0}&S is now included in zone {1}",
                                       info.ClassyName, zone.ClassyName);
                        changesWereMade = true;
                        break;

                    case PermissionOverride.Allow:
                        player.Message("{0}&S is already included in zone {1}",
                                       info.ClassyName, zone.ClassyName);
                        break;
                    }
                }
                else if (name.StartsWith("-"))
                {
                    if (name.Length == 1)
                    {
                        CdZoneEdit.PrintUsage(player);
                        break;
                    }
                    PlayerInfo info = PlayerDB.FindPlayerInfoOrPrintMatches(player, name.Substring(1));
                    if (info == null)
                    {
                        return;
                    }

                    switch (zone.Controller.Exclude(info))
                    {
                    case PermissionOverride.Deny:
                        player.Message("{0}&S is already excluded from zone {1}",
                                       info.ClassyName, zone.ClassyName);
                        break;

                    case PermissionOverride.None:
                        player.Message("{0}&S is now excluded from zone {1}",
                                       info.ClassyName, zone.ClassyName);
                        changesWereMade = true;
                        break;

                    case PermissionOverride.Allow:
                        player.Message("{0}&S is no longer included in zone {1}",
                                       info.ClassyName, zone.ClassyName);
                        changesWereMade = true;
                        break;
                    }
                }
                else if (name.ToLower().StartsWith("msg="))
                {
                    zone.Message    = name.Substring(4) + " " + (cmd.NextAll() ?? "");
                    changesWereMade = true;
                    player.Message("Zedit: Custom denied messaged changed to '" + zone.Message + "'");
                    break;
                }
                else
                {
                    Rank minRank = RankManager.FindRank(name);

                    if (minRank != null)
                    {
                        // prevent players from lowering rank so bypass protection
                        if (!player.Info.Rank.AllowSecurityCircumvention &&
                            zone.Controller.MinRank > player.Info.Rank && minRank <= player.Info.Rank)
                        {
                            player.Message("You are not allowed to lower the zone's rank.");
                            continue;
                        }

                        if (zone.Controller.MinRank != minRank)
                        {
                            zone.Controller.MinRank = minRank;
                            player.Message("Permission for zone \"{0}\" changed to {1}+",
                                           zone.Name,
                                           minRank.ClassyName);
                            changesWereMade = true;
                        }
                    }
                    else
                    {
                        player.MessageNoRank(name);
                    }
                }
            }
            if (changesWereMade)
            {
                zone.Edit(player.Info);
            }
            else
            {
                player.Message("No changes were made to the zone.");
            }
        }
Example #35
0
 internal static void PlayerUnbannedHandler(PlayerInfo player, Player unbanner, string reason)
 {
     PlayerSomethingMessage(unbanner, "unbanned", player, reason);
 }
Example #36
0
 /// <summary> Searches for player names starting with namePart, returning just one or none of the matches. </summary>
 /// <param name="partialName"> Partial or full player name. </param>
 /// <param name="result"> PlayerInfo to output (will be set to null if no single match was found). </param>
 /// <returns> true if one or zero matches were found, false if multiple matches were found. </returns>
 internal static bool FindOneByPartialName( [NotNull] string partialName, [CanBeNull] out PlayerInfo result ) {
     if( partialName == null ) throw new ArgumentNullException( "partialName" );
     CheckIfLoaded();
     return provider.FindOneByPartialName( partialName, out result );
 }
Example #37
0
 public bool Ignore( PlayerInfo other ) {
     lock( ignoreLock ) {
         if( !ignoreList.Contains( other ) ) {
             ignoreList.Add( other );
             return true;
         } else {
             return false;
         }
     }
 }
Example #38
0
        /// <summary> Bans given IP address. All players from IP are kicked. If an associated PlayerInfo is known,
        /// use a different overload of this method instead. Throws PlayerOpException on problems. </summary>
        /// <param name="targetAddress"> IP address that is being banned. </param>
        /// <param name="player"> Player who is banning. </param>
        /// <param name="reason"> Reason for ban. May be empty, if permitted by server configuration. </param>
        /// <param name="announce"> Whether ban should be publicly announced on the server. </param>
        /// <param name="raiseEvents"> Whether AddingIPBan and AddedIPBan events should be raised. </param>
        /// <exception cref="ArgumentNullException"> targetAddress or player is null. </exception>
        /// <exception cref="PlayerOpException"> Permission or configuration issues arise, or if IP is already banned. </exception>
        public static void BanIP([NotNull] this IPAddress targetAddress, [NotNull] Player player, [CanBeNull] string reason,
                                 bool announce, bool raiseEvents)
        {
            if (targetAddress == null)
            {
                throw new ArgumentNullException("targetAddress");
            }
            if (player == null)
            {
                throw new ArgumentNullException("player");
            }

            if (reason != null)
            {
                reason = reason.Trim(' ');
            }
            if (reason != null && reason.Length == 0)
            {
                reason = null;
            }

            // Check if player can ban IPs in general
            if (!player.Can(Permission.Ban, Permission.BanIP))
            {
                PlayerOpException.ThrowPermissionMissing(player, null, "IP-ban", Permission.Ban, Permission.BanIP);
            }

            // Check if a non-bannable address was given (0.0.0.0 or 255.255.255.255)
            if (targetAddress.Equals(IPAddress.None) || targetAddress.Equals(IPAddress.Any))
            {
                PlayerOpException.ThrowInvalidIP(player, null, targetAddress);
            }

            // Check if player is trying to ban self
            if (targetAddress.Equals(player.IP) && !player.IsSuper)
            {
                PlayerOpException.ThrowCannotTargetSelf(player, null, "IP-ban");
            }

            lock ( BanListLock ) {
                CheckIfLoaded();
                // Check if target is already banned
                IPBanInfo existingBan = Get(targetAddress);
                if (existingBan != null)
                {
                    string msg;
                    if (player.Can(Permission.ViewPlayerIPs))
                    {
                        msg = String.Format("IP address {0} is already banned.", targetAddress);
                    }
                    else
                    {
                        msg = String.Format("Given IP address is already banned.");
                    }
                    string colorMsg = "&S" + msg;
                    throw new PlayerOpException(player, null, PlayerOpExceptionCode.NoActionNeeded, msg, colorMsg);
                }

                // Check if any high-ranked players use this address
                PlayerInfo infoWhomPlayerCantBan = PlayerDB.FindPlayers(targetAddress)
                                                   .FirstOrDefault(info => !player.Can(Permission.Ban, info.Rank));
                if (infoWhomPlayerCantBan != null)
                {
                    PlayerOpException.ThrowPermissionLimitIP(player, infoWhomPlayerCantBan, targetAddress);
                }

                PlayerOpException.CheckBanReason(reason, player, null, false);

                // Actually ban
                IPBanInfo banInfo = new IPBanInfo(targetAddress, null, player.Name, reason);
                bool      result  = Add(banInfo, raiseEvents);

                if (result)
                {
                    Logger.Log(LogType.UserActivity,
                               "{0} banned {1} (BanIP {1}). Reason: {2}",
                               player.Name, targetAddress, reason ?? "");
                    if (announce)
                    {
                        // Announce ban on the server
                        var can = Server.Players.Can(Permission.ViewPlayerIPs);
                        can.Message("&W{0} was banned by {1}", targetAddress, player.ClassyName);
                        var cant = Server.Players.Cant(Permission.ViewPlayerIPs);
                        cant.Message("&WAn IP was banned by {0}", player.ClassyName);
                        if (ConfigKey.AnnounceKickAndBanReasons.Enabled() && reason != null)
                        {
                            Server.Message("&WBanIP reason: {0}", reason);
                        }
                    }

                    // Kick all players connected from address
                    string kickReason;
                    if (reason != null)
                    {
                        kickReason = String.Format("IP-Banned by {0}: {1}", player.Name, reason);
                    }
                    else
                    {
                        kickReason = String.Format("IP-Banned by {0}", player.Name);
                    }
                    foreach (Player other in Server.Players.FromIP(targetAddress))
                    {
                        if (other.Info.BanStatus != BanStatus.IPBanExempt)
                        {
                            other.Kick(kickReason, LeaveReason.BanIP);   // TODO: check side effects of not using DoKick
                        }
                    }
                }
                else
                {
                    // address is already banned
                    string msg;
                    if (player.Can(Permission.ViewPlayerIPs))
                    {
                        msg = String.Format("{0} is already banned.", targetAddress);
                    }
                    else
                    {
                        msg = "Given IP address is already banned.";
                    }
                    string colorMsg = "&S" + msg;
                    throw new PlayerOpException(player, null, PlayerOpExceptionCode.NoActionNeeded, msg, colorMsg);
                }
            }
        }
Example #39
0
 static bool PlayerIsInactive([NotNull] IDictionary <IPAddress, List <PlayerInfo> > playersByIP, [NotNull] PlayerInfo player, bool checkIP)
 {
     if (playersByIP == null)
     {
         throw new ArgumentNullException("playersByIP");
     }
     if (player == null)
     {
         throw new ArgumentNullException("player");
     }
     if (player.BanStatus != BanStatus.NotBanned || player.UnbanDate != DateTime.MinValue ||
         player.IsFrozen || player.IsMuted || player.TimesKicked != 0 ||
         player.Rank != RankManager.DefaultRank || player.PreviousRank != null)
     {
         return(false);
     }
     if (player.TotalTime.TotalMinutes > 30 || player.TimeSinceLastSeen.TotalDays < 30)
     {
         return(false);
     }
     if (IPBanList.Get(player.LastIP) != null)
     {
         return(false);
     }
     if (checkIP)
     {
         return(playersByIP[player.LastIP].All(other => (other == player) || PlayerIsInactive(playersByIP, other, false)));
     }
     return(true);
 }
Example #40
0
 // warn player if others are still online from target's IP
 static void WarnIfOtherPlayersOnIP( Player player, PlayerInfo targetInfo, Player except ) {
     Player[] otherPlayers = Server.Players.FromIP( targetInfo.LastIP )
                                           .Except( except )
                                           .ToArray();
     if( otherPlayers.Length > 0 ) {
         player.Message( "&WWarning: Other player(s) share IP with {0}&W: {1}",
                         targetInfo.ClassyName,
                         otherPlayers.JoinToClassyString() );
     }
 }
 public static void Untempban(Player player, PlayerInfo target)
 {
     if (!target.IsBanned) return;
     else
         target.Unban(player, "Tempban Expired", true, true);
 }
Example #42
0
 public void Edit( PlayerInfo editedBy ) {
     if( editedBy == null ) throw new ArgumentNullException( "editedBy" );
     EditedDate = DateTime.UtcNow;
     EditedBy = editedBy;
 }
Example #43
0
        static void CheckPaidStatusCallback(SchedulerTask task)
        {
            PlayerInfo info = (PlayerInfo)task.UserState;

            info.AccountType = Player.CheckPaidStatus(info.Name);
        }
 public static IEnumerable <Player> NotIgnoring([NotNull] this IEnumerable <Player> source, [NotNull] PlayerInfo playerInfo)
 {
     if (source == null)
     {
         throw new ArgumentNullException("source");
     }
     if (playerInfo == null)
     {
         throw new ArgumentNullException("playerInfo");
     }
     foreach (Player otherPlayer in source)
     {
         if (!otherPlayer.IsIgnoring(playerInfo))
         {
             yield return(otherPlayer);
         }
     }
 }
Example #45
0
 // freeze target if player is allowed to do so
 static void FreezeIfAllowed( Player player, PlayerInfo targetInfo ) {
     if( targetInfo.IsOnline && !targetInfo.IsFrozen && player.Can( Permission.Freeze, targetInfo.Rank ) ) {
         try {
             targetInfo.Freeze( player, true, true );
             player.Message( "Player {0}&S has been frozen while you retry.", targetInfo.ClassyName );
         } catch( PlayerOpException ) { }
     }
 }
Example #46
0
        internal static PlayerInfo LoadFormat1(string[] fields)
        {
            PlayerInfo info = new PlayerInfo {
                Name = fields[0]
            };

            if (fields[1].Length == 0 || !IPAddress.TryParse(fields[1], out info.LastIP))
            {
                info.LastIP = IPAddress.None;
            }

            info.Rank = Rank.Parse(fields[2]) ?? RankManager.DefaultRank;
            fields[3].ToDateTimeLegacy(ref info.RankChangeDate);
            if (fields[4].Length > 0)
            {
                info.RankChangedBy = fields[4];
            }

            switch (fields[5])
            {
            case "b":
                info.BanStatus = BanStatus.Banned;
                break;

            case "x":
                info.BanStatus = BanStatus.IPBanExempt;
                break;

            default:
                info.BanStatus = BanStatus.NotBanned;
                break;
            }

            // ban information
            if (fields[6].ToDateTimeLegacy(ref info.BanDate))
            {
                if (fields[7].Length > 0)
                {
                    info.BannedBy = Unescape(fields[7]);
                }
                if (fields[10].Length > 0)
                {
                    info.BanReason = Unescape(fields[10]);
                }
            }

            // unban information
            if (fields[8].ToDateTimeLegacy(ref info.UnbanDate))
            {
                if (fields[9].Length > 0)
                {
                    info.UnbannedBy = Unescape(fields[9]);
                }
                if (fields[11].Length > 0)
                {
                    info.UnbanReason = Unescape(fields[11]);
                }
            }

            // failed logins
            fields[12].ToDateTimeLegacy(ref info.LastFailedLoginDate);

            if (fields[13].Length > 1 || !IPAddress.TryParse(fields[13], out info.LastFailedLoginIP))      // LEGACY
            {
                info.LastFailedLoginIP = IPAddress.None;
            }
            // skip 14
            fields[15].ToDateTimeLegacy(ref info.FirstLoginDate);

            // login/logout times
            fields[16].ToDateTimeLegacy(ref info.LastLoginDate);
            fields[17].ToTimeSpanLegacy(ref info.TotalTime);

            // stats
            if (fields[18].Length > 0)
            {
                Int32.TryParse(fields[18], out info.BlocksBuilt);
            }
            if (fields[19].Length > 0)
            {
                Int32.TryParse(fields[19], out info.BlocksDeleted);
            }
            Int32.TryParse(fields[20], out info.TimesVisited);
            if (fields[20].Length > 0)
            {
                Int32.TryParse(fields[21], out info.MessagesWritten);
            }
            // fields 22-23 are no longer in use

            if (fields[24].Length > 0)
            {
                info.PreviousRank = Rank.Parse(fields[24]);
            }
            if (fields[25].Length > 0)
            {
                info.RankChangeReason = Unescape(fields[25]);
            }
            Int32.TryParse(fields[26], out info.TimesKicked);
            Int32.TryParse(fields[27], out info.TimesKickedOthers);
            Int32.TryParse(fields[28], out info.TimesBannedOthers);

            info.ID = Int32.Parse(fields[29]);
            if (info.ID < 256)
            {
                info.ID = PlayerDB.GetNextID();
            }

            byte rankChangeTypeCode;

            if (Byte.TryParse(fields[30], out rankChangeTypeCode))
            {
                info.RankChangeType = (RankChangeType)rankChangeTypeCode;
                if (!Enum.IsDefined(typeof(RankChangeType), rankChangeTypeCode))
                {
                    info.GuessRankChangeType();
                }
            }
            else
            {
                info.GuessRankChangeType();
            }

            fields[31].ToDateTimeLegacy(ref info.LastKickDate);
            if (!fields[32].ToDateTimeLegacy(ref info.LastSeen) || info.LastSeen < info.LastLoginDate)
            {
                info.LastSeen = info.LastLoginDate;
            }
            Int64.TryParse(fields[33], out info.BlocksDrawn);

            if (fields[34].Length > 0)
            {
                info.LastKickBy = Unescape(fields[34]);
            }
            if (fields[34].Length > 0)
            {
                info.LastKickReason = Unescape(fields[35]);
            }

            fields[36].ToDateTimeLegacy(ref info.BannedUntil);
            info.IsFrozen = (fields[37] == "f");
            if (fields[38].Length > 0)
            {
                info.FrozenBy = Unescape(fields[38]);
            }
            fields[39].ToDateTimeLegacy(ref info.FrozenOn);
            fields[40].ToDateTimeLegacy(ref info.MutedUntil);
            if (fields[41].Length > 0)
            {
                info.MutedBy = Unescape(fields[41]);
            }
            info.Password = Unescape(fields[42]);
            // fields[43] is "online", and is ignored

            byte bandwidthUseModeCode;

            if (Byte.TryParse(fields[44], out bandwidthUseModeCode))
            {
                info.BandwidthUseMode = (BandwidthUseMode)bandwidthUseModeCode;
                if (!Enum.IsDefined(typeof(BandwidthUseMode), bandwidthUseModeCode))
                {
                    info.BandwidthUseMode = BandwidthUseMode.Default;
                }
            }
            else
            {
                info.BandwidthUseMode = BandwidthUseMode.Default;
            }

            if (fields.Length > 45)
            {
                if (fields[45].Length == 0)
                {
                    info.IsHidden = false;
                }
                else
                {
                    info.IsHidden = info.Rank.Can(Permission.Hide);
                }
            }

            if (info.LastSeen < info.FirstLoginDate)
            {
                info.LastSeen = info.FirstLoginDate;
            }
            if (info.LastLoginDate < info.FirstLoginDate)
            {
                info.LastLoginDate = info.FirstLoginDate;
            }

            return(info);
        }
Example #47
0
        internal static PlayerInfo LoadFormat0(string[] fields, bool convertDatesToUtc)
        {
            PlayerInfo info = new PlayerInfo {
                Name = fields[0]
            };

            if (fields[1].Length == 0 || !IPAddress.TryParse(fields[1], out info.LastIP))
            {
                info.LastIP = IPAddress.None;
            }

            info.Rank = Rank.Parse(fields[2]) ?? RankManager.DefaultRank;
            DateTimeUtil.TryParseLocalDate(fields[3], out info.RankChangeDate);
            if (fields[4].Length > 0)
            {
                info.RankChangedBy = fields[4];
                if (info.RankChangedBy == "-")
                {
                    info.RankChangedBy = null;
                }
            }

            switch (fields[5])
            {
            case "b":
                info.BanStatus = BanStatus.Banned;
                break;

            case "x":
                info.BanStatus = BanStatus.IPBanExempt;
                break;

            default:
                info.BanStatus = BanStatus.NotBanned;
                break;
            }

            // ban information
            if (DateTimeUtil.TryParseLocalDate(fields[6], out info.BanDate))
            {
                if (fields[7].Length > 0)
                {
                    info.BannedBy = fields[7];
                }
                if (fields[10].Length > 0)
                {
                    info.BanReason = UnescapeOldFormat(fields[10]);
                    if (info.BanReason == "-")
                    {
                        info.BanReason = null;
                    }
                }
            }

            // unban information
            if (DateTimeUtil.TryParseLocalDate(fields[8], out info.UnbanDate))
            {
                if (fields[9].Length > 0)
                {
                    info.UnbannedBy = fields[9];
                }
                if (fields[11].Length > 0)
                {
                    info.UnbanReason = UnescapeOldFormat(fields[11]);
                    if (info.UnbanReason == "-")
                    {
                        info.UnbanReason = null;
                    }
                }
            }

            // failed logins
            if (fields[12].Length > 1)
            {
                DateTimeUtil.TryParseLocalDate(fields[12], out info.LastFailedLoginDate);
            }
            if (fields[13].Length > 1 || !IPAddress.TryParse(fields[13], out info.LastFailedLoginIP))      // LEGACY
            {
                info.LastFailedLoginIP = IPAddress.None;
            }
            // skip 14

            // login/logout times
            DateTimeUtil.TryParseLocalDate(fields[15], out info.FirstLoginDate);
            DateTimeUtil.TryParseLocalDate(fields[16], out info.LastLoginDate);
            TimeSpan.TryParse(fields[17], out info.TotalTime);

            // stats
            if (fields[18].Length > 0)
            {
                Int32.TryParse(fields[18], out info.BlocksBuilt);
            }
            if (fields[19].Length > 0)
            {
                Int32.TryParse(fields[19], out info.BlocksDeleted);
            }
            Int32.TryParse(fields[20], out info.TimesVisited);
            if (fields[20].Length > 0)
            {
                Int32.TryParse(fields[21], out info.MessagesWritten);
            }
            // fields 22-23 are no longer in use

            if (fields.Length > MinFieldCount)
            {
                if (fields[24].Length > 0)
                {
                    info.PreviousRank = Rank.Parse(fields[24]);
                }
                if (fields[25].Length > 0)
                {
                    info.RankChangeReason = UnescapeOldFormat(fields[25]);
                }
                Int32.TryParse(fields[26], out info.TimesKicked);
                Int32.TryParse(fields[27], out info.TimesKickedOthers);
                Int32.TryParse(fields[28], out info.TimesBannedOthers);
                if (fields.Length > 29)
                {
                    info.ID = Int32.Parse(fields[29]);
                    if (info.ID < 256)
                    {
                        info.ID = PlayerDB.GetNextID();
                    }
                    byte rankChangeTypeCode;
                    if (Byte.TryParse(fields[30], out rankChangeTypeCode))
                    {
                        info.RankChangeType = (RankChangeType)rankChangeTypeCode;
                        if (!Enum.IsDefined(typeof(RankChangeType), rankChangeTypeCode))
                        {
                            info.GuessRankChangeType();
                        }
                    }
                    else
                    {
                        info.GuessRankChangeType();
                    }
                    DateTimeUtil.TryParseLocalDate(fields[31], out info.LastKickDate);
                    if (!DateTimeUtil.TryParseLocalDate(fields[32], out info.LastSeen) || info.LastSeen < info.LastLoginDate)
                    {
                        info.LastSeen = info.LastLoginDate;
                    }
                    Int64.TryParse(fields[33], out info.BlocksDrawn);

                    if (fields[34].Length > 0)
                    {
                        info.LastKickBy = UnescapeOldFormat(fields[34]);
                    }
                    if (fields[35].Length > 0)
                    {
                        info.LastKickReason = UnescapeOldFormat(fields[35]);
                    }
                }
                else
                {
                    info.ID = PlayerDB.GetNextID();
                    info.GuessRankChangeType();
                    info.LastSeen = info.LastLoginDate;
                }

                if (fields.Length > 36)
                {
                    DateTimeUtil.TryParseLocalDate(fields[36], out info.BannedUntil);
                    info.IsFrozen = (fields[37] == "f");
                    if (fields[38].Length > 0)
                    {
                        info.FrozenBy = UnescapeOldFormat(fields[38]);
                    }
                    DateTimeUtil.TryParseLocalDate(fields[39], out info.FrozenOn);
                    DateTimeUtil.TryParseLocalDate(fields[40], out info.MutedUntil);
                    if (fields[41].Length > 0)
                    {
                        info.MutedBy = UnescapeOldFormat(fields[41]);
                    }
                    info.Password = UnescapeOldFormat(fields[42]);
                    // fields[43] is "online", and is ignored
                }

                if (fields.Length > 44)
                {
                    if (fields[44].Length != 0)
                    {
                        info.BandwidthUseMode = (BandwidthUseMode)Int32.Parse(fields[44]);
                    }
                }
            }

            if (info.LastSeen < info.FirstLoginDate)
            {
                info.LastSeen = info.FirstLoginDate;
            }
            if (info.LastLoginDate < info.FirstLoginDate)
            {
                info.LastLoginDate = info.FirstLoginDate;
            }

            if (convertDatesToUtc)
            {
                if (info.RankChangeDate != DateTime.MinValue)
                {
                    info.RankChangeDate = info.RankChangeDate.ToUniversalTime();
                }
                if (info.BanDate != DateTime.MinValue)
                {
                    info.BanDate = info.BanDate.ToUniversalTime();
                }
                if (info.UnbanDate != DateTime.MinValue)
                {
                    info.UnbanDate = info.UnbanDate.ToUniversalTime();
                }
                if (info.LastFailedLoginDate != DateTime.MinValue)
                {
                    info.LastFailedLoginDate = info.LastFailedLoginDate.ToUniversalTime();
                }
                if (info.FirstLoginDate != DateTime.MinValue)
                {
                    info.FirstLoginDate = info.FirstLoginDate.ToUniversalTime();
                }
                if (info.LastLoginDate != DateTime.MinValue)
                {
                    info.LastLoginDate = info.LastLoginDate.ToUniversalTime();
                }
                if (info.LastKickDate != DateTime.MinValue)
                {
                    info.LastKickDate = info.LastKickDate.ToUniversalTime();
                }
                if (info.LastSeen != DateTime.MinValue)
                {
                    info.LastSeen = info.LastSeen.ToUniversalTime();
                }
                if (info.BannedUntil != DateTime.MinValue)
                {
                    info.BannedUntil = info.BannedUntil.ToUniversalTime();
                }
                if (info.FrozenOn != DateTime.MinValue)
                {
                    info.FrozenOn = info.FrozenOn.ToUniversalTime();
                }
                if (info.MutedUntil != DateTime.MinValue)
                {
                    info.MutedUntil = info.MutedUntil.ToUniversalTime();
                }
            }

            return(info);
        }
Example #48
0
 // Throws a PlayerOpException if reason is required but missing.
 internal static void CheckRankChangeReason([CanBeNull] string reason, [NotNull] Player player, PlayerInfo targetInfo, bool promoting)
 {
     if (player == null)
     {
         throw new ArgumentNullException("player");
     }
     if (ConfigKey.RequireRankChangeReason.Enabled() && String.IsNullOrEmpty(reason))
     {
         string msg;
         if (promoting)
         {
             msg = "Please specify a promotion reason.";
         }
         else
         {
             msg = "Please specify a demotion reason.";
         }
         string colorMsg = "&S" + msg;
         throw new PlayerOpException(player, targetInfo, PlayerOpExceptionCode.ReasonRequired, msg, colorMsg);
     }
 }
Example #49
0
        public Zone( string raw, World world ) {
            string[] parts = raw.Split( ',' );

            string[] header = parts[0].Split( ' ' );
            Name = header[0];
            Bounds = new BoundingBox( Int32.Parse( header[1] ), Int32.Parse( header[2] ), Int32.Parse( header[3] ),
                                      Int32.Parse( header[4] ), Int32.Parse( header[5] ), Int32.Parse( header[6] ) );

            Rank buildRank = RankManager.ParseRank( header[7] );
            // if all else fails, fall back to lowest class
            if( buildRank == null ) {
                if( world != null ) {
                    Controller.MinRank = world.BuildSecurity.MinRank;
                } else {
                    Controller.MinRank = null;
                }
                Logger.Log( "Zone: Error parsing zone definition: unknown rank \"{0}\". Permission reset to default ({1}).", LogType.Error,
                            header[7], Controller.MinRank.Name );
            } else {
                Controller.MinRank = buildRank;
            }


            // Part 2:
            foreach( string player in parts[1].Split( ' ' ) ) {
                if( !Player.IsValidName( player ) ) continue;
                PlayerInfo info = PlayerDB.FindPlayerInfoExact( player );
                if( info == null ) continue; // player name not found in the DB (discarded)
                Controller.Include( info );
            }

            // Part 3: excluded list
            foreach( string player in parts[2].Split( ' ' ) ) {
                if( !Player.IsValidName( player ) ) continue;
                PlayerInfo info = PlayerDB.FindPlayerInfoExact( player );
                if( info == null ) continue; // player name not found in the DB (discarded)
                Controller.Exclude( info );
            }

            Controller.UpdatePlayerListCache();

            // Part 4: extended header
            if( parts.Length > 3 ) {
                string[] xheader = parts[3].Split( ' ' );
                CreatedBy = PlayerDB.FindPlayerInfoExact( xheader[0] );
                if( CreatedBy != null ) CreatedDate = DateTime.Parse( xheader[1] );
                EditedBy = PlayerDB.FindPlayerInfoExact( xheader[2] );
                if( EditedBy != null ) EditedDate = DateTime.Parse( xheader[3] );
            }
        }
Example #50
0
        static void LoadInternal(StreamReader reader, string header)
        {
            int version = IdentifyFormatVersion(header);

            if (version > FormatVersion)
            {
                Logger.Log(LogType.Warning,
                           "PlayerDB.Load: Attempting to load unsupported PlayerDB format ({0}). Errors may occur.",
                           version);
            }
            else if (version < FormatVersion)
            {
                Logger.Log(LogType.Warning,
                           "PlayerDB.Load: Converting PlayerDB to a newer format (version {0} to {1}).",
                           version, FormatVersion);
            }

            int emptyRecords = 0;

            while (true)
            {
                string line = reader.ReadLine();
                if (line == null)
                {
                    break;
                }
                string[] fields = line.Split(',');
                if (fields.Length >= PlayerInfo.MinFieldCount)
                {
#if !DEBUG
                    try {
#endif
                    PlayerInfo info;
                    switch (version)
                    {
                    case 0:
                        info = PlayerInfo.LoadFormat0(fields, true);
                        break;

                    case 1:
                        info = PlayerInfo.LoadFormat1(fields);
                        break;

                    default:
                        // Versions 2-5 differ in semantics only, not in actual serialization format.
                        info = PlayerInfo.LoadFormat2(fields);
                        break;
                    }

                    if (info.ID > maxID)
                    {
                        Logger.Log(LogType.Warning,
                                   "PlayerDB.Load: Adjusting wrongly saved MaxID ({0} to {1}).",
                                   maxID, info.ID);
                        maxID = info.ID;
                    }

                    // A record is considered "empty" if the player has never logged in.
                    // Empty records may be created by /Import, /Ban, and /Rank commands on typos.
                    // Deleting such records should have no negative impact on DB completeness.
                    if ((info.LastIP.Equals(IPAddress.None) || info.LastIP.Equals(IPAddress.Any) || info.TimesVisited == 0) &&
                        !info.IsBanned && info.Rank == RankManager.DefaultRank)
                    {
                        Logger.Log(LogType.SystemActivity,
                                   "PlayerDB.Load: Skipping an empty record for player \"{0}\"",
                                   info.Name);
                        emptyRecords++;
                        continue;
                    }

                    // Check for duplicates. Unless PlayerDB.txt was altered externally, this does not happen.
                    if (Trie.ContainsKey(info.Name))
                    {
                        Logger.Log(LogType.Error,
                                   "PlayerDB.Load: Duplicate record for player \"{0}\" skipped.",
                                   info.Name);
                    }
                    else
                    {
                        Trie.Add(info.Name, info);
                        list.Add(info);
                    }
#if !DEBUG
                }
                catch (Exception ex) {
                    Logger.LogAndReportCrash("Error while parsing PlayerInfo record: " + line,
                                             "fCraft",
                                             ex,
                                             false);
                }
#endif
                }
                else
                {
                    Logger.Log(LogType.Error,
                               "PlayerDB.Load: Unexpected field count ({0}), expecting at least {1} fields for a PlayerDB entry.",
                               fields.Length, PlayerInfo.MinFieldCount);
                }
            }

            if (emptyRecords > 0)
            {
                Logger.Log(LogType.Warning,
                           "PlayerDB.Load: Skipped {0} empty records.", emptyRecords);
            }

            RunCompatibilityChecks(version);
        }