Показать сообщение отдельно
Непрочитано 13.02.2017, 13:10   #1
Пользователь

Автор темы (Топик Стартер) Реализация KnownList

Вопрос к думающим людям.
В процессе разработки эмулятора на C# подошел к тому, что пора реализовать KnownList - списки всех объектов, находящихся в определенном радиусе от персонажа\моба.
Пока что решил реализовать его следующим образом.

При входе в игру мы чекаем всех персонажей в онлайне и если они в определенном радиусе от персонажа - то добавляем входящего в игру в их knownlist и соответственно их добавляем в knownlist нашего персонажа.
knownlist - подписан на событие Player_Enterworld
Код:
        private void Player_EnterWorld(Player player)
        {
            var ownerPosition = Owner.CurrentPosition;
            if (player.ObjectId == Owner.ObjectId)//Логика действий, если текущий персонаж входит в мир
            {
                var ownerPlayer = Owner as Player;
                OnlinePlayers.Locker.EnterReadLock();
                foreach (var playerInGame in OnlinePlayers.GetAllOnlinePlayers)
                {
                    if (!ownerPosition.CheckIfInRange(KnownListConfigs.PlayersKnownListRange, playerInGame) || playerInGame.ObjectId == ownerPlayer?.ObjectId) continue;
                    AddPlayerWithLock(playerInGame);
                    playerInGame.KnownContaineer.AddPlayerWithLock(ownerPlayer);//Добавляем текущего игрока в кновн лист игрока из списка
                }
                OnlinePlayers.Locker.ExitReadLock();
            }
            else//Логика действий, если другие персонажи входят в мир, но не данный объект.
            {
                if (!ownerPosition.CheckIfInRange(KnownListConfigs.PlayersKnownListRange, player)) return;
                AddPlayerWithLock(player);//Для других
            }
        }
То что чекать весь онлайн - это х*евая затея это я и так понимаю, но об этом позже.

И так теперь нужно подумать, ведь персонаж передвигается и соответственно kownlist должен постоянно изменятся.
По этому реализовал следующий статический метод.
Код:
        public static void RecalcKnownPlayersList(Player ownerPlayer)
        {
            var ownerPosition = ownerPlayer.CurrentPosition;
            var ownerKnownListLocker = ownerPlayer.KnownContaineer.KnownPlayerLocker;
            var deletedPlayersIdList = new List<int>();
            ownerKnownListLocker.EnterReadLock();
            foreach (var knownPlayer in ownerPlayer.KnownContaineer.KnownPlayers.Values)
            {
                if (ownerPosition.CheckIfInRange(KnownListConfigs.PlayersKnownListRange, knownPlayer)) continue;
                deletedPlayersIdList.Add(knownPlayer.ObjectId);
                knownPlayer.KnownContaineer.RemovePlayerWithLock(ownerPlayer.ObjectId);
            }
            ownerKnownListLocker.ExitReadLock();
            var addedPlayersList = new List<Player>();
            OnlinePlayers.Locker.EnterReadLock();
            foreach (var playerInGame in OnlinePlayers.GetAllOnlinePlayers)
            {
                if (!ownerPosition.CheckIfInRange(KnownListConfigs.PlayersKnownListRange, playerInGame) || playerInGame.ObjectId == ownerPlayer.ObjectId) continue;
                addedPlayersList.Add(playerInGame);
                playerInGame.KnownContaineer.AddPlayerWithLock(ownerPlayer);//Добавляем текущего игрока в кновн лист игрока из списка
            }
            OnlinePlayers.Locker.ExitReadLock();
            ownerKnownListLocker.EnterWriteLock();
            foreach (var addedPlayer in addedPlayersList)
                ownerPlayer.KnownContaineer.AddPlayerWithoutLock(addedPlayer);
            foreach (var deletedPlayerId in deletedPlayersIdList)
                ownerPlayer.KnownContaineer.RemovePlayerWithoutLock(deletedPlayerId);
            ownerKnownListLocker.ExitWriteLock();
        }
данный метод опять же чекает список онлайна и удаляет\добавляет персонажей если нужно всем сразу так сказать.
На реализацию удаления из листа овнера далеких игроков не обращать внимания, позже сделаю покрасивее через итератор.

по поводу пробежки по всему онлайн листу.
Так как нагрузка на данный метод растет по экспоненте думаю это не совсем лучший вариант. Потом разобью мир на регионы и пробежка будет уже по текущему региону и по его соседним регионам. Дабы не было такой ситуации что объекты находящиеся в соседнем регионе грузились только при пересечении границы.


А вот собственно вопрос. Когда запускать пересчет данного листа?.
приатачил его к приходу пакета MoveToLock. но фиг его знает.

PS. На сервере который я разрабатываю инстанс зон не будет.
PSS. По поводу локеров. Используется ReadWriteLockSlim - самый быстрый из доступных локеров в .net

И вот собственно методы добавления удаления
Код:
        private void AddPlayerWithLock(Player player)
        {
            KnownPlayerLocker.EnterWriteLock();
            if (IsPlayerExists(player.ObjectId))
            {
                KnownPlayerLocker.ExitWriteLock();
                return;
            }
            KnownPlayers.Add(player.ObjectId, player);
            KnownPlayerLocker.ExitWriteLock();
            if (Owner.ObjType != GameObject.ObjectType.Player) return;
            var ownerPlayer = Owner as Player;
            ownerPlayer?.SendPacket(new PlayerInfoPacket(ownerPlayer.GameClientConnection, player));
        }
        private void AddPlayerWithoutLock(Player player)
        {
            if (IsPlayerExists(player.ObjectId)) return;
            KnownPlayers.Add(player.ObjectId, player);
            if (Owner.ObjType != GameObject.ObjectType.Player) return;
            var ownerPlayer = Owner as Player;
            ownerPlayer?.SendPacket(new PlayerInfoPacket(ownerPlayer.GameClientConnection, player));
        }
        private void RemovePlayerWithLock(int playerObjectId)
        {
            KnownPlayerLocker.EnterWriteLock();
            if (!IsPlayerExists(playerObjectId))
            {
                KnownPlayerLocker.ExitWriteLock();
                return;
            }
            KnownPlayers.Remove(playerObjectId);
            KnownPlayerLocker.ExitWriteLock();
            if (Owner.ObjType != GameObject.ObjectType.Player) return;
            var ownerPlayer = Owner as Player;
            ownerPlayer?.SendPacket(new DeleteObjectPacket(ownerPlayer.GameClientConnection, playerObjectId));
        }
        private void RemovePlayerWithoutLock(int playerObjectId)
        {
            if (!IsPlayerExists(playerObjectId)) return;
            KnownPlayers.Remove(playerObjectId);
            if (Owner.ObjType != GameObject.ObjectType.Player) return;
            var ownerPlayer = Owner as Player;
            ownerPlayer?.SendPacket(new DeleteObjectPacket(ownerPlayer.GameClientConnection, playerObjectId));
        }
- Сразу уведомляют клиента об изменении.
Alay вне форума Ответить с цитированием