Правильная архитектура MMO эмулятора

    Предыстория/Мотивация


    Все началось с хобби в начале 2020 года — с очередной попытки написания эмулятора игрового сервера Lineage 2 "по новому". Перед этим шагом было несколько попыток распиливания монолита существующих решений на рынке по новым практикам разработки, но затея оказалась тщетной, ибо те монолиты, которые и по сей день существуют и участвуют в так называемом "продакшен-пиратстве", имеют сильную связанность компонентов и решения поставленных задач, сопоставимые с началом 2000х годов, когда сфера только начинала развиваться. А самое главное, что монолит не заточен на построение распределенной архитектуры и, как следствие, обладает низкой эффективностью.


    image


    Было принято решение взять часть бизнес-логики (основной составляющей обработки действий игрока) из допотопных проектов эмуляторов и создать современный/масштабируемый эмулятор игрового сервера Lineage 2 Prelude Of War.


    Исследование


    Что на рынке


    Возьмем, к примеру, EVE Online — проект который смог. Архитектура серверной части позволяет при относительно средних вычислительных мощностях, обеспечивать:


    • низкую задержку игрок-сервер;
    • стабильный онлайн до 20тыс. игроков.

    Что там внутри:


    • Stackless Python & Infiniband;
    • публичные узлы подключения игроков (Proxy);
    • внутренние узлы-контроллеры солнечных систем (SOL);
    • контроллеры твердотельных накопителей для операций ввода/вывода (Blade);
    • SQL Server Cluster.

    А общая концепция архитектуры проекта заключается в stateless контроллерах игрового мира и хранением всего состояния игрового мира в базе данных с высокой доступностью за счет дисков.


    Чтож, отлично, некий план стека технологий вырисовывался, но, нет… Не все так просто. Поддержка физических машин для такой архитектуры — слишком дорогое удовольствие, и на рынке Lineage 2 оно никому не нужно.


    Главное, что можно выделить из архитектуры EVE Online:


    • распределенное состояние игровых объектов;
    • распределенный контроль игровых регионов (солнечных систем).

    Продолжим.
    Дальнейшим примером выступает официальный сервер PTS Lineage 2, написанный на C++.


    Что внутри:


    • сервисная архитектура;
    • сервис взаимодействия с базой данных;
    • сервис аутентификации;
    • 2 сервиса-контроллера игровой логики игроков и NPC;
    • SQL Server.

    Минусы:


    • не поддерживает масштабируемость сервисов;
    • ограничение игроков в 5 тыс. на одной инсталляции;
    • full stateful.

    Можно сразу отсечь этот вариант, но в нем можно проследить некую логику разработчиков с попыткой заложить на будущее поддержку масштабирования, при котором выделяются четкие уровни ответственности каждого сервиса. Увы, по сей день нет никаких новостей по этому поводу от компании NCSoft, которой принадлежат права на разработку игры, а на минуточку, игра разрабатывается с 2002 года.


    Из этого решения можно выделить также некоторые важные моменты на перспективу:


    • сервисная архитектура;
    • разделение ответственности.

    Spring cloud стек


    Казалось бы, при чем тут "кровавый" Энтерпрайз к многопользовательской игровой архитектуре, спросит читатель. Все достаточно просто — нас абсолютно не интересуют веб-решения, построенные с помощью этой технологии, важная составляющая — микросервисная архитектура и подходы, которые используются в эталонных приложениях.


    image


    Стандартная архитектура на этом стеке представляет собой набор инфраструктурных сервисов, взаимодействующих между собой с помощью REST API или Event Queue (брокеры).


    Главными компонентами выступают:


    • discovery-service — сервис-регистратор, обнаружения и хранения мета-информации каждого инфраструктурного микросервиса;
    • config-server — сервис централизованной конфигурации инфраструктурных микросервисов;
    • api-gateway — маршрутизация запросов от клиента во внутреннюю кухню по метаинформации из discovery-service.

    Красиво, удобно, новомодно, а главное — заставляет описывать каждый микросервис, как stateless приложение.


    И из этого решения можно выделить важные архитектурные преимущества:


    • api-шлюз для взаимодействия между микросервисами и клиентами;
    • discovery-service, как неотъемлемая часть архитектуры.

    Базовое представление архитектуры


    Ознакомившись с решениями на данном этапе исследования, можно уже выделить представление.


    Какой стек будет использоваться:


    • spring boot — удобный микрофреймворк построения приложения с инъекцией зависимостей/из коробки поддержкой конфигурации приложения;
    • project reactor — проект, посвященных реактивным потокам данных;
    • reactor netty — реактивная обертка над сетевым фреймворком netty;
    • r2dbc — реактивная реализация драйвера взаимодействия с базой данных.

    Почерпнуть полезной информации по netty можно тут.


    Служебные подсистемы:


    • PostgreSQL (базовое состояние);
    • KeyDB (промежуточное состояние системных компонентов).

    Перед составом сервисов небольшая вводная в игровой клиент Lineage2.


    Сетевая логика клиента


    Чтобы пролить больше света на архитектуру решения, нужно сначала понять как работает игровой клиент с сервером и региональной составляющей мира.


    Клиент работает сразу с двумя серверами:


    1. сервер авторизационных данных игрока — через него происходит первый этап подключения и проверка логина: пароля игрока, а также дальнейший вход в лобби;
    2. сервер игровой бизнес логики — второй этап подключения клиента, после пройденных проверок на сервере авторизации (предигровое лобби выбора персонажей, вход в игровой мир)

    Протокол, по которому клиент взаимодействует с серверами — TCP.
    Каждый пакет состоит из размера пакета (2 байта), типа пакета (1 байт) и блока параметров(переменная длина). В дополнение к этому, в пакетах сервера авторизации, в конце добавляется контрольная сумма и дополняется нулями так, чтобы размер пакета был кратен 8-ми байтам. Контрольная сумма может быть рассчитана следующей функцией:


    public int checksum(ByteBuf buf) {
        int checksum = 0;
        while (buf.isReadable(8)) {
            checksum ^= buf.readInt();
        }
    
        return checksum;
    }

    Протокол lineage использует 6 разных типов данных:


    • char – может принимать значение от -128 до 127. Имеет длину 1 байт
    • short – может принимать значение от -32768 до 32767. Имеет длину 2 байта
    • int – может принимать значение от -2147483648 до 2147483647. Имеет длину 4 байта
    • int64 (long) – может принимать значение от -9223372036854775808 до 9223372036854775807. Имеет длину 8 байт.
    • float – может принимать значение от 2.22507e-308 до 1.79769e+308. Имеет длину 8 байт
    • string – текстовая строка в юникоде(UTF8). Каждая буква представлена двумя байтами, первый байтом код буквы, а второй – номер кодовой таблицы. Индикатором конца строки служит символ с кодом 0.

    Пакеты сервера авторизации шифруются по алгоритму Blowfish. Пакет Init содержит динамический Blowfish ключ случайно генерируемый для каждого клиента. Этот пакет сначала шифруется по алгоритму XOR (ключ генерируется случайным образом и помещается в конце пакета), а потом шифруется по алгоритму Blowfish, статическим ключом. По умолчанию статический ключ — 6B 60 CB 5B 82 CE 90 B1 CC 2B 6C 55 6C 6C 6C 6C. Все последующие пакеты будут шифроваться динамическим Blowfish ключом. Пакет LoginRequest дополнительно шифруется по алгоритму RSA. Ключ состоит из следующих частей: B = 1024, E = 65537, N = передается в пакете Init. Вместе эти 3 части составляют целый RSA ключ.
    Байты N в пакете зашифрованы функцией:


    RSA crypt
    public byte[] scrambledRSA(KeyPair keyPair) {
        byte[] scrambledModulus = ((RSAPublicKey) keyPair.getPublic()).getModulus().toByteArray();
        if (scrambledModulus.length == 0x81 && scrambledModulus[0] == 0) {
            scrambledModulus = Arrays.copyOfRange(scrambledModulus, 1, 0x81);
        }
    
        // step 1 : 0x4d-0x50 <-> 0x00-0x04
        for (int i = 0; i < 4; i++) {
            byte temp = scrambledModulus[i];
            scrambledModulus[i] = scrambledModulus[0x4d + i];
            scrambledModulus[0x4d + i] = temp;
        }
    
        // step 2 : xor first 0x40 bytes with last 0x40 bytes
        for (int i = 0; i < 0x40; i++) {
            scrambledModulus[i] = (byte) (scrambledModulus[i] ^ scrambledModulus[0x40 + i]);
        }
    
        // step 3 : xor bytes 0x0d-0x10 with bytes 0x34-0x38
        for (int i = 0; i < 4; i++) {
            scrambledModulus[0x0d + i] = (byte) (scrambledModulus[0x0d + i] ^ scrambledModulus[0x34 + i]);
        }
    
        // step 4 : xor last 0x40 bytes with first 0x40 bytes
        for (int i = 0; i < 0x40; i++) {
            scrambledModulus[0x40 + i] = (byte) (scrambledModulus[0x40 + i] ^ scrambledModulus[i]);
        }
    
        return scrambledModulus;
    }

    Для расшифровки можно воспользоваться следующей функцией:


    Decrypt key
    private byte[] decryptXorModulus(byte[] xor) {
        // step 1 xor last 0x40 bytes with first 0x40 bytes
        for (int i = 0; i < 0x40; i++) {
            xor[0x40 + i] = (byte) (xor[0x40 + i] ^ xor[i]);
        }
    
        // step 2 xor bytes 0x0d-0x10 with bytes 0x34-0x38
        for (int i = 0; i < 4; i++) {
            xor[0x0d + i] = (byte) (xor[0x0d + i] ^ xor[0x34 + i]);
        }
    
        // step 3 xor first 0x40 bytes with last 0x40 bytes
        for (int i = 0; i < 0x40; i++) {
            xor[i] = (byte) (xor[i] ^ xor[0x40 + i]);
        }
    
        // step 4 : 0x00-0x04 <-> 0x4d-0x50
        for (int i = 0; i < 4; i++) {
            final byte temp = xor[i];
            xor[i] = xor[0x4d + i];
            xor[0x4d + i] = temp;
        }
    
        if (xor.length == 0x81 && xor[0] == 0) {
            xor = Arrays.copyOfRange(xor, 1, 0x81);
        }
    
        return xor;
    }
    
    public PublicKey parseScrambledModulus(byte[] scrambledModulus) {
        scrambledModulus = decryptXorModulus(scrambledModulus);
    
        final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    
        final BigInteger modulus = new BigInteger(1, scrambledModulus);
        final RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(modulus, RSAKeyGenParameterSpec.F4);
        try {
            return keyFactory.generatePublic(rsaPublicKeySpec);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException(e);
        }
    }

    Функции шифрования и дешифрации игрового трафика:


    Enc/Dec
    public void decryptData(ByteBuf byteBuf) {
           int read = 0;
           while (byteBuf.isReadable()) {
               final int sub = byteBuf.readByte() & 0xFF;
               byteBuf.setByte(byteBuf.readerIndex() - 1, sub ^ inKey[(byteBuf.readerIndex() - 1) & 15] ^ read);
               read = sub;
           }
    
           shiftKey(inKey, byteBuf.writerIndex());
       }
    
       public void encryptData(ByteBuf byteBuf) {
           if (!cryptEnable) {
               cryptEnable = true;
               return;
           }
    
           byteBuf.resetReaderIndex();
    
           int read = 0;
           while (byteBuf.isReadable()) {
               final int sub = byteBuf.readByte() & 0xFF;
               read = sub ^ outKey[(byteBuf.readerIndex() - 1) & 15] ^ read;
               byteBuf.setByte(byteBuf.readerIndex() - 1, read);
           }
    
           shiftKey(outKey, byteBuf.writerIndex());
    
           byteBuf.resetReaderIndex();
       }
    
       public void shiftKey(byte[] key, int size) {
           int old = key[8] & 0xff;
           old |= (key[9] << 8) & 0xff00;
           old |= (key[10] << 0x10) & 0xff0000;
           old |= (key[11] << 0x18) & 0xff000000;
    
           old += size;
    
           key[8] = (byte) (old & 0xff);
           key[9] = (byte) ((old >> 0x08) & 0xff);
           key[10] = (byte) ((old >> 0x10) & 0xff);
           key[11] = (byte) ((old >> 0x18) & 0xff);
       }

    **С каждым кодированным/декодированным пакетом ключ изменяется на длину пакета, поэтому нужно использовать два отдельных экземпляра ключа – один для шифрования исходящих пакетов, второй для расшифровки входящих. Все пакеты шифруются начиная с 3-го байта, т.е. размер пакета никогда не шифруется.


    Из выше перечисленного выделяются три уровня ответственности решения:


    1. сервер работы в базой данных;
    2. сервер авторизации игроков;
    3. сервер бизнес-логики игроков (окрестратор).

    Игровой мир/карта/система регионов


    Если покопаться внутри клиента, то можно обнаружить файлы карты игрового мира с кодами регионов {x}_{y}.unr (расширение unreal level map) и как следствие должна быть региональная сетка игрового мира. В версии клиента, который я использую, мир состоит из 28 регионов в ширину (x) и 26 регионов в высоту (y). Каждый регион имеет блоки размером 256х256 координат. Каждый блок разделен на 8х8 ячеек.
    Общая высота мира (y) — 851968 координат.
    Общая ширина мира (х) — 917504 координат.
    Из полученных данных нехитрым математическими преобразованиями можем нарисовать региональную сетку на карте игрового клиента.


    image


    Расчеты
     int REGION_WH = 222; // предрасчитаная ширина региона для изображения (легитимно только для моего варианта собранной карты из клиента)
     private void renderGrid(BufferedImage img, Graphics2D g, FontMetrics fontMetrics) {
            //          draw grid
            for (int x = 19, y; x-- > 0; ) {
                for (y = 17; y-- > 0; ) {
                    final String name = (x + 10) + "_" + (y + 10);
                    final int strx = ((x * REGION_WH) + (REGION_WH / 2)) - (fontMetrics.stringWidth(name) / 2);
                    final int stry = (y * REGION_WH) + (REGION_WH / 2) + (fontMetrics.getHeight() / 2);
    
                    g.drawString(name, strx, stry);
                    g.drawLine(0, (y * REGION_WH) + REGION_WH, img.getWidth(), (y * REGION_WH) + REGION_WH);
                    g.drawLine((x * REGION_WH) + REGION_WH, 0, (x * REGION_WH) + REGION_WH, img.getHeight());
                }
            }
        }

    Центральные регионы игрового мира X=19 Y=18, с которых начинается отсчет координат, а также стоит учитывать что ось Y инвертированная по отношению к изображению, поэтому координаты отрицательными значениями идут выше отметки (0, 0), а положительные — соответственно ниже.


    Существует также внутренняя кухня для регионов, где рассчитываются ячейки.


    Итак, из грид сетки можно увидеть, что мир построен из каждого отдельного куска карты. Отсюда рождается идея контроллеров части регионов карты (региональных ячеек) по оси Х и, собственно, находящихся в них объектов. Т.е. фактически это будет кластер, с центральным оркестратором распределения игровых объектов по его же шардам.


    То самое


    Из чего должно состоять решение:


    1. discovery-gateway-service — сервис, который содержит информацию обо всех инфраструктурных сервисах приложения. Каждый микросервис регистрируется на сервере discovery, и discovery знает все инфраструктурные сервисы, работающие на каждом порту и IP-адресе, и, выполняет функции балансировки/маршрутизации пакетов между ними;
    2. data-service — сервис, который отвечает за работу с базой PG и предоставляет свое АПИ инфраструктурным сервисам. Идея сервиса такова, что каждый другой сервис не должен взаимодействовать с базой напрямую, т.е. разделение ответственности, но может обратиться или записать некоторые свои данные используя Net обертку;
    3. auth-service — сервис, обеспечивающий механизм входной точки доступа игрового клиента (центрального шлюза) аутентификации игровых аккаунтов и распределения игроков по выбранным серверам в списке серверов (зоны);
    4. game-gateway-orcehstrator — сервис, обеспечивающий механизм входной точки доступа игрового клиента (центрального шлюза) авторизированных игровых аккаунтов после Auth Service и распределения игроков по Game Shard. Дополнительно хранит механизм кодеков/декодеров шифрования клиента с привязкой сессионных ключей каждого живого аккаунта, т.е. основная функция прием шифрованных запросов от игрового клиента, дешифровка и ретрансляция запросов на дочернюю игровую ноду привязанную к игроку по региональной сетке (Game Shard);
    5. game-shard — сервис, обеспечивающий основную бизнес логику обработки входящих пакетов от Game Orchestrator, взаимодействие с остальными инфраструктурными сервисами, обработкой действий игрока и отправки результата по сессионным идентификаторам обратно в Game Orchestrator;
    6. npc-shard — аналогично Game Shard, только в случаи для NPC (Non Player Character).

    Любой сервис может масштабироваться.
    Протокол общения сервисов — TCP.
    Стоит добавить, что общение сервисов между собой не блокирующее, т.е. сервис на определенное действие не ждет ответа. Можно разделить такую логику на два обработчика — producer и consumer данных. В момент получения результата с течением времени, механика продолжит выполнение того или иного действия.


    Пример:


    1. Shard запрашивает данные игрока у data-service, отправляя пакет с информацией ReqCharInfo (produce). При этом работа потока не блокируется для ожидания результата.
    2. Data-service принимает пакет ReqCharInfo (consume), выполняет некоторую логику забора данных из базы данных или кешей, отправляет обработанную информацию обратно в Shard пакетом RespCharInfo (produce).
    3. Shard принимает некоторым обработчиком данные игрока (consume) и выполняет следующий кусок логики манипуляции с данными.

    Распределённое состояние


    Фактически, что нам известно из многопользовательской игры — существует два состояния игрового объекта:


    1. базовое
    2. промежуточное

    Базовое состояние — первичные данные, такие как уровень/наименование/тип/идентификатор etc., из которых составляется базовое представление объекта при инициализации в игровом мире.


    Промежуточное состояние — динамические данных объекта, изменяемые посредством игровых механик, взаимодействия игрок-объект/объект-объект, в реальном времени.


    С базовым состоянием все достаточно просто — его можно хранить в любой реляционной базе данных, а с промежуточным уже начинаются проблемы на стадии планирования.


    Промежуточное состояние должно хранится в памяти приложения для быстрых расчетов и доступа, но в распределенной архитектуре это состояние должно хранится в централизованном хранилище, достаточно быстрым для темпа выбранной игры. Т.е. на каждого игрока в среднем рейт нормальной игры (атака, передвижение, манипуляции с предметами, системные задачи обработки) начинается с отметки в 100 rps. И по логике, распределение игроков в мире осуществляется так:


    • 15% торговля
    • 85% внутриигровая активность (осады, события, пвп контент и пр.)

    Возьмем к примеру средне статистический онлайн с официального сервера в воскресенье
    image


    4122 игрока подключены к серверу. Рассчитаем нагрузку 4122 х 0.85 х (50 — 150 rps) = от 175 krps до 525 krps read/write. Не хило.


    Какую систему использовать для хранения?
    Требования:


    • отказоустойчивость
    • масштабирование
    • хранение и обработка большого количества данных игровых объектов
    • низкий latency и потребление ресурсов

    Было перепробовано множество подсистем хранения промежуточного состояния, укажу наиболее перспективные из них:


    1. Redis/KeyDB
    2. Hazelcast
    3. Apache Cassandra (как отдельно так и в режиме embed шарда)
    4. Apache Ignite (как отдельно так и в режиме embed шарда)

    Они все прекрасно работают с небольшими пакетами данных, и заявленные графики производительности на сайтах этих систем исключительная правда для небольших моделей.
    А модели игровых объектов промежуточного состояния достаточно жирные — в среднем 100 — 200 полей различных типов данных.


    У всех наблюдаются одни и те же проблемы, точнее это даже не проблемы самих систем, а проблемы построения функционала получения/записи данных:


    • блокирующие вызовы операций чтения/записи;
    • сериализация/десериализация данных.

    И, как следствие — потребление большого количества ресурсов.


    P.s. исследование продолжалось в течении почти года.


    Уже на данном этапе понятно, что из проекта ничего не выйдет. Погодите ка, что это? Off-Heap память? Серьезно?


    В связи с неудовлетворительными показателями испробованных систем, было принято решение взять за основу off-heap таблицы хранения данных с прямым доступом в память процесса. Но это дает дополнительную реализацию функционала репликации. За основу off-heap системы хранения структуры <K,V> был взят алгоритм LSM-дерева поскольку обеспечивает быструю доступность данных по индексам при большой частоте записи и использует механизм слияния одного слоя памяти в другой (heap -> off-heap) с кастомной сериализацией объектов, основанной на ручной записи в буфер данных (Externalizable). Так же стоит дополнить, что в некоторых кейсах десериализации, объекты в их полном представлении не нужны, а нужна лишь часть данных. Поэтому была написана обертка, которая умеет работать с конфигурируемым View уровнем с перечислением необходимых полей. По факту это прямое чтение данных по смещению в байтовом массиве.


    Главное преимущество еще одного костыля кастомной системы — прямой неблокирующий доступ/запись данных в памяти 1 сервиса. А как же stateless — по факту да, пришлось пренебречь этой концепцией и хранить промежуточное состояние в памяти одного сервиса и реплицировать данные внутри кластера. Единственно что оставили — распределенные счетчики и задачи, которые хранятся в KeyDB и обрабатываются фреймворком redisson.


    Немного статистики тестов

    Xeon E5-2689 8 x 2.6, DDR 3 ECC 1866
    Смешанный режим, 10 миллионов объектов, длина ключа 8 байт, длина значения 3567 байт


    sync mode


    avg write: 3128 ns
    avg read: 2741 ns


    write all: 25617 ms — 400k rps
    read all: 21430 ms — 476k rps


    async mode


    avg write: 2689 ns
    avg read: 2145 ns


    write all: 16573 ms — 603k rps
    read all: 11620 ms — 860k rps


    CPU factor: 30% wo socket io


    При желании можно разогнать показатели еще больше.


    Представление кластера (оркестратор — шард)


    Как описывалось выше, оркестратор, фактически master уровень кластера, распределяет регионы для шарда (slave). Мы знаем, что игровой мир можно разделить на n-частей по оси X и эти самые части игрового мира отдаются под контроль шарда.


    Как это выглядит визуально.
    image


    А что там по отказоустойчивости?


    1. Оркестратор может быть масштабируемым с хранением состояния кластера в KeyDB.
    2. По отключению шарда от кластера и его восстановлению, осуществляется балансировка регионов, как по живым шардам, так и по вновь подключенным.

    Алгоритм балансировки кластера основан на Consistent Hashing, но со своими нюансами:


    1. балансировка затрагивает абсолютно все состояние кластера, а не его отдельный элемент
    2. балансировка осуществляться влево, вправо и в две стороны по горизонтальному представлению кластера

    image


    Конечная архитектура


    Базовое представление
    image


    Показана схема взаимодействия инфраструктурных сервисов внутри кластера.
    Дополнительно был реализован функционал discovery zones — где шлюзом между ними выступает auth-service (2 зоны — 2 разных сервера) и игровой клиент дает возможность выбирать на этапе авторизации, на какой сервер игрок хочет зайти.


    Представление зон обнаружения
    image


    Представление взаимодействия игрового клиента с инфраструктурой
    image


    Представление репликации данных
    image


    Может показаться, что в этой схеме, узким местом является Discovery Service. Нет, он также масштабируемый с инициализацией подключений от других сервисов. Единственная проблема — нужно перезапускать всю инфраструктуру для расширения инстансов сервиса.


    Заключение


    Абсолютно любую MMO игру можно адаптировать под архитектуру, описанную в статье, поскольку у любой игры есть региональное распределение.


    В рамках анализа было представлено обобщение архитектур, используемых в играх MMO, и рассмотрены методы для распределения игрового мира на несколько сервисов/машин.


    Основными аспектами решения являются:


    • масштабируемость любого сервиса;
    • балансировка нагрузки алгоритмом RR, который обеспечивает discovery-gateway-service при общении сервис-сервис;
    • для orchestrator действуют правила RR распределения соединений как и для всех остальных сервисов;
    • availability & partition tolerance;
    • consistency — скорее нет, чем да, поскольку нужно дополнительно еще продумать распределенные блокировки;
    • разделение ответственности компонентов системы;
    • региональное распределение/контроль игрового мира, как распределение нагрузки игрового мира на решение.

    Результаты получились отличными, на примере живого игрока (клиента) — нет никаких визуальных аффектов трансфера из одного шарда в другой при пересечении границы подконтрольных регионов. Шарду достаточно отправить пакет с идентификатором игрока для инициализации процедуры переключения управляющего контекста на другой шард. А обеспечивается эта функциональность репликацией дельты промежуточного состояния игрока в память других шардов кластера.


    image


    Надеюсь кому-нибудь это будет полезно.

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 62

      +1
      Начал читать и сразу резануло:
      А самое главное, что монолит не заточен на построение распределенной архитектуры и, как следствие, обладает низкой эффективностью.

      Распределенная (микросервисная) архитектура это в первую очередь про масштаб. Масштаб команды, масштаб нагрузки, масштаб проекта.
        +5
        тоже не понял этого. статья выглядит как распределенная архитектура ради распределенной архитектуры
        +2
        А Akka рассматривалась? У Microsoft весь бэк Halo на Orleans построен — так что акторная модель вполне жизнеспособна
          +1
          Не знаком с этой технологией и как ее можно применить к решению, поэтому нет.
          0
          так а какие результаты в вашей реализации сервера? из статьи непонятно написали ли вы его до конца или нет. какой онлайн держит?
            0
            «до конца», смотря что Вы имеете ввиду — полностью функциональную инфраструктуру или полностью готовый сервер для запуска «здесь и сейчас». Инфраструктура была подготовлена, все сервисы и механики описанные в статье были реализованы в т.ч. и базовая логика игрока (перемещение, статистика, инвентарь, etc.). Часть статистики я указал в статье по внутренним подсистемам, а онлайн — в данный момент я способен генерировать автотестами, при доступных мощностях моей серверной станции до 18 тыс. игроков в разных регионах игрового мира в т.ч. и выполнение операций передвижения.
              0
              Это слишком базовые вещи.

              Как именно поддерживаются все механики сервера? Передвижение не игроков, а NPC, их социальное поведение, а не тупо стояние на месте. Там многие ходят порыкивают, травку жуют, есть и более сложные поведенческие скрипты.

              Расскажите как игроки в вашем мире могут взаимодействовать, например присутствуя в одном городе в количестве 500+ человек? Тут как бы больше на клиент нагрузка идет, и особо нет смысла в поддержании всего на одном месте.

              Минусы:
              не поддерживает масштабируемость сервисов;
              ограничение игроков в 5 тыс. на одной инсталляции;

              Кроме поддержки какого-то максимального онлайна, для него должно быть достаточно контента. А его в Lineage2 как раз примерно на 5000 игроков уже впритык. Зачастую на официальном сервере при превышении среднего онлайна в 3000, уже старались регистрироваться на другом шарде. Ну и ограничение в 5000 чисто на уровне кода, его можно убрать и проверить насколько действительно нагрузочный тест покажет.
              Плюс существующие сервисы легко разносятся на разные машины, а MSSQL кластеризируется сам по себе.
                0
                Детальную реализацию можно очень долго и нудно обсуждать, в данном случае делится какими-то best practice с моей стороны «как это сделано, а как это» считаю излишним, поскольку это закрытая информация. Да и статья совершенно не про количество контента в самой игре.
                  +1
                  Ну закрытая и закрытая. Мы в свое время разрабатывали активную реализацию на джава, и не слишком сильно шифровались.
                  А не делиться не то, чтобы готовым кодом, а даже простыми вопросами о реализации — ну крутитесь в своем компоте, зачем тогда вообще статья?
                    –4
                    Статья не предусматривает подробный гайд, как реализовывать ту или иную механику, а лишь несет ознакомительный характер — как правильно выстраивать архитектуру бэкенд решения для многопользовательской игры. Любая информация по вашим вопросам общедоступна в исходниках уже существующих решений. Вы можете спокойно открыть их и ответить сами на свои вопросы по игровой механике. Спасибо.
                      +2
                      Причем тут гайд.

                      Я спрашивал — как вы проверяли, что оригинальный бэкенд не держит подобную нагрузку, если ограничение в 5000 сделано не потому, что сильно перенагружено, а просто для того, чтобы в шард, контент которого рассчитан на 3000-4000 человек, не налезало сверх головы.
                      Насколько я помню, когда я держал шард еще 4-х хроник, то при наличии онлайна около 1000-2000 человек, больше всего напрягался компонент, отвечающий за поведение NPC. А основной сервер не более 10-15% cpu на древних каких-то двухядерных атлонах и 4 гб оперативки. Да, для 5000 возможно было бы лучше гигабитный канал, а не 100мбитный. Но «шардировать» пространство? Это не поможет, если много игроков соберутся в одном «квадрате». А гигабит легко выдержит 5000 игроков.
                      Вы ведь тестировали нагрузку, имитируя онлайн, а не проверяя что должно быть с каналом?

                      Делать универсальный бэкенд для «любой ММО», не вникая в суть этого ММО — явно не подход. Например банальная вещь — Lineage2 — псевдо3д игра. Геодата у нее хитро оптимизирована для минимальной нагрузки на клиент и AI.

                      Думаете в eve online именно архитектура позволяет 20.000 игрокам играть с минимальными лагами?
                      Насколько я знаю, eve online — полный 3д, с кучей нюансов. И решить вопрос масштабирования конкретной локации пришлось трюком со временем, а не шардированием пространства на кусочки (что скорее всего практически нереально сделать красиво).

                        –4
                        Я спрашивал — как вы проверяли, что оригинальный бэкенд не держит подобную нагрузку

                        Простите, но где?

                        10-15% cpu на древних каких-то двухядерных атлонах и 4 гб оперативки

                        А вы проверяли в текущей реальности свои же показатели, перед тем как написать? Явно же нет.
                        Вам точно хватило на запуск С4 4гб ОЗУ? Да это же смешно, С4 Retail минимум требует 8гб ОЗУ не считая вспомогательный систем WS.

                        Вы почему-то в упор не видите очевидной реальности — статья абсолютно не заставляет никого брать и делать сейчас свой L2 сервер, пользуясь этой архитектурой, а наоборот — дает эталон архитектуры, которой не стоит пренебрегать в том или ином проекте. Дальнейшая полемика бессмысленна.
                          +2
                          — Вот как надо делать бэкенд!
                          — А как вы решили такую-то проблему?
                          — Это военная тайна.
                          — Но всё же, как?
                          — Не скажу!
                          — Ладно, поясните тогда, почему ваше решение правильное?
                          — Идите прочь, я Д'Артаньян!

                          Дальнейшая полемика бессмысленна.

                          Вы правы, действительно бессмысленна.

                          статья абсолютно не заставляет никого брать и делать сейчас свой L2 сервер, пользуясь этой архитектурой, а наоборот — дает эталон архитектуры, которой не стоит пренебрегать в том или ином проекте.

                          Да, не заставляет.
                          Однако настолько громкое название статьи («Правильная архитектура MMO эмулятора») и радикальная позиция автора («архитектура подходит к любой игре, и точка») вызывает много вопросов, на которые вопрошающие так и не получают конкретного ответа.
                          0
                          Дельные комменты. Возможно 18 тысяч «ходящих» и на старом железе не проблема повторить. Только вот если у вас в мире живёт 18 тысяч игроков, то надо быть готовым к тому что условная треть может оказаться на осаде и быстро накернит сервер.

                          Меня больше впечатлит не 18 тысяч ботов которые просто ходят по миру, а 2-4 тысячи игроков которые дерутся у замка. Причём это обязательно должны быть «живые» клиенты-боты, которые по настоящему подключаются к серверу.

                          И NPC и мобы так же могут очень сильно нагружать сервера. В некоторых эмуляторах их вообще во время осад отключали, т.к других вариантов не было.
                            0
                            Прикол в том, что серверу совершенно все равно где находятся игроки — рядом или далеко.
                            Основная нагрузка при нахождении игроков рядом приходится на сеть и на клиент, а не на CPU.
                            Отправить сообщение 2000 игрокам с точки зрения CPU несильная разница.
                            А вот отправить 2000 лишних пакетов?
                            А 2000 игроков когда кастуют что-то в бою каждые пару секунд — в одной локации это насколько больше трафика нужно отправлять каждому игроку?
                            При этом опять таки, нагрузка на CPU — не большая.

                            p.s. Насколько я помню, как раз NPC и составляли основную нагрузку на процессор в оригинальном сервере.
                              0
                              Прикол в том, что серверу совершенно все равно где находятся игроки — рядом или далеко.
                              А 2000 игроков когда кастуют что-то в бою каждые пару секунд — в одной локации это насколько больше трафика нужно отправлять каждому игроку? При этом опять таки, нагрузка на CPU — не большая.

                              Нагрузка на CPU, когда игроки находятся в одном месте и воюют — в разы больше, чем когда они в разных местах.

                              Если одно АОЕ задевает 20 игроков — надо для каждого игрока просчитать повреждения, а там уже десятки формул и проверок. Какой уровень, какой шмот, какие баффы, является противником или союзником. И этих АОЕ каждую секунду так же кидаются десятки.

                              Ни один сервер не любит массовых баталий. Сейчас это конечно уже не так очевидно, потому что железо стало в десятки раз мощнее и появились SSD, а количество игроков в игровых мирах увеличилось не так значительно.

                              Фри шард образца 2001года на POL эмуляторе тянул 350 игроков, но уже захлёбывался когда был массовый бой примерно 50х50.
                              Так же сервер очень плохо переносил «прокачку резиста», когда в одной кучке собиралось по 10-30 игроков и постоянно использовали массовый каст дебаффа друг на друге.

                              Рост нагрузки при массовом пвп — не линейный. Лучше чтобы дрались 100 кучек по 10 игроков, чем 1000 игроков в одном месте.
                                0
                                Если одно АОЕ задевает 20 игроков — надо для каждого игрока просчитать повреждения, а там уже десятки формул и проверок. Какой уровень, какой шмот, какие баффы, является противником или союзником. И этих АОЕ каждую секунду так же кидаются десятки.


                                Ну реально, это для современных процессоров просто семечки.
                                Про фришард, у меня вроде более-менее норм воспоминания, я уже говорил что держал и свой шард, и общался с «коллегами» других шардов.

                                Основная проблема высокого онлайна упиралась больше не в CPU, а во внешний канал. Из CPU в официальном шарде, напрягался в основном процесс отвечающий за NPC.

                                И соответственно вопрос о 17.000 живых игроках, я уверен что в первую очередь упрется в то, как организовано сетевое подключение и балансировка игроков, и намекал автору статьи, что имитация 17.000 человек на локалхосте это некорректно. Что 5000 лимит в том же Lineage2 стоял не потому, что он не мог выдержать, а потому что существуют расчету по комфортному количеству людей в созданном контенте, и проблемы с внешним каналом, а не невозможности просчитать нахождение игроков рядом. Плюс опять таки — на стороне сервера просчитать урон это одно. А на стороне клиента отрисовать тысячи игроков рядом, на обычных домашних десктопах?

                                Архитектура должна оптимизировать в первую очередь то, где находится реальное бутылочное горлышко, а не придуманное.
                            0

                            C EVE Online долго страдали разработчики. И писали об этом.
                            Там не только в архитектуре дело.
                            Там очень многое на дизайне висит.
                            Из примеров (далеко не полных, и все о чем я говорю — не раз писалось CCP):


                            • из Jita выкинуто вообще все что только можно (миссии агентов, возможность майнить)
                            • прикрыта возможность запускать слишком много дронов — как порезкой бонусов тому же Dominix'у так и установкой явных ограничений на количество дронов для карриеров с мазершипами (чтобы они не запускали их сотнями за раз — по drone bandwith то могут, и нести на борту их тоже могут)
                            • полетов на слишком высокой скорости — быть не должно а если игроки находят — ну ребаланс будет значит (речь именно про полеты в локальном гриде)
                            • Time Delay — мы признаем что система БУДЕТ лагать в некоторых случаях, и в некоторых случаях — зверски. Но пусть хоть одинаково и предсказуемо у всех кто в данной солсистеме. Ну да, послетают таймеры активации но решаемо другими путям
                            • убирать необходимость для достижения важных целей летать огромными флотами (нет — это осталось, но можно и без этого достичь чего то)
                            • MSSQL — живет на RAMSAN'ах ("рамдиск в терабайт", если еще не подняли размеры)
                            • игрокам запретили варпать в ноль на гейты и они начали создавать массово букмарки с читерными схемами (в результате у каждого этих букмарков — десятки тысяч) — ок разрешим в ноль опять но только на ручном управлении а букмарки рядом с гейтами — потрем и запретим делать
                            • рынок — на отдельные серверы (вроде по регионам деление), как и чат, как и сервисы обслуживающие персонажа
                            • возможность кому то из руководства конкретной корпорации попросить reinforced-ноду где то за сутки (ну вот знают что вероятно будет мочилово в системе — можно просить).
                            • из одной системы влиять на другую напрямую — невозможно. ну разве что маркет.
                            • переход между системами либо через джамп-гейты (при этом джамп-гейт может либо предложить позднее попробовать либо вообще сказать про перегрузку) либо прыжок корабля с джамп-двигателем(они на маяк прыгают и это тоже не мгновенно) либо мобильные порталы можно открыть (некоторые корабли умеют), опять же не мгновенно либо еще некоторая экзотика (вормхолы например) но опять же — это просто переход и сервер несущий целевую ноду даже за некоторое время (пусть даже минуты) знает что будет переход. Система где никого нет — может вообще быть в оффлайне (загрузится когда нужна будет).
                              кстати там не 20к игроков система держит. она спокойно держит 200-300к игроков в онлайне.

                            А вот как работает подход, чем то похожий на описанный — можно в SecondLife наблюдать. Куча проблема с переходами между рядом стоящими регионами, особенно если высокий пинг. И пофиксить что-то не особо получается. Хотя потихоньку решают.

                      0
                      Нцсофт уже много хроник подряд все больше и больше контента пихает в инстансы, индивидуальные для отдельных игроков/пати/кк, так что по сути тезис что «контента на слишком большой онлайн не хватит» как бы уже не сильно верен.
                      Сейчас уже по сути больше ограничение из-за того что само железо не вытянет слишком большой онлайн, без ощутимых лагов. Опять же в этом большей частью архитектура самого сервера виновата.
                  +3
                  Вы сравниваете теплое с мягким — у EVE такая структура мира, что можно выделять хоть отдельный сервер на отдельную солнечную систему. В случае единого мира и такой сетки, как вы нарисовали (половина континента в одной ноде, а половина в другой) взаимодействие между нодами будет сложнее, чем между игроками и сервером. Расчет линии видимости или действий АИ на границе будет очень сложным, а что будет если на линию разграничения попадет замок во время осады — я вообще не возьмусь прогнозировать.
                  В ВоВ как раз смена карты (данжи, БГ, остров ночных эльфов) позволяет использовать под каждую локацию отдельную ноду, хоть это и используется только для Battlegrounds.
                    0
                    И на этот счет есть пару, довольно простых решений, которые так или иначе уберут аффект подсистемы контроля границ «на осадах». Да, в целом я с Вами полностью согласен — где-то сложно, а где-то просто. Но архитектура применима абсолютно к любой MMO. Достаточно ее правильно отобразить в том или ином случае.
                      0
                      В ВоВ как раз смена карты (данжи, БГ, остров ночных эльфов) позволяет использовать под каждую локацию отдельную ноду, хоть это и используется только для Battlegrounds.

                      А вы уверены? Или вы сейчас не о современном ВоВе, а о каком-то древнем? Уже давно при смене локации имеется приличный шанс перелететь на другой шард.

                        +1
                        О древнем. Техническая возможность для этого была, но не использовалась, только на аренах и батлграундах. Что изменилось за последние 5-10 лет не знаю, хотя по логике онлайн ведь не растет уже особо.
                          0

                          На самом деле, очень многое изменилось. Шард под каждую зону и даже саб-зоны, особенно во время эвентов, практически бесшовный переход в сценарии на открытом пространстве. Технологии они очень сильно подтянули за последние годы.

                          0
                          Не знаю как сейчас дела обстоят, но несколько лет назад смена шарда сопровождалась некоторыми неприятными явлениями.
                          Особенно сильно это проявлялось, когда появилась возможность сделать свою крепость — сделать так, чтобы друзья в пати попали в тот же шард с твоей крепостью — целый квест нещадно приправленный надеждой на рандом.
                          В общем, не очень комфортно было играть, когда появилось шардирование такое…
                            0
                            Ух ты! А что крепость даёт? Это для гильдий, или для одного игрока? А войны гильдий и захваты замков там ещё не прикрутили?
                              0

                              Для одного игрока.
                              https://wowpedia.fandom.com/wiki/Garrison


                              А войны гильдий и захваты замков там ещё не прикрутили?

                              Не прикрутят. Большинство игроков в ВоВ не заинтересованы в этих вещах.

                                0
                                Не знаю как сейчас, но раньше это было только для игрока. Причем при заходе в крепость игрок попадает в «свой собственный мирок», который никто кроме него не видел (на этом месте каждый игрок видит свою крепость).
                                Что дает… а хз… Немножко ресурсов…
                                Скажу лучше так. Как по мне, близзы, чтобы увеличить аудиторию, в какой-то момент начали впихивать всякую несуразную чушь в игру (чушь — именно в контексте «первоначальной» ММО-игры, по отдельности, эти механики вполне себе неплохи).
                                Это были и «покемоны» — ловишь по Азероту существ и воюешь ими.
                                Это были и «весёлые фермы» — где мужественный паладин в сияющих доспехах выращивает свеклу и копает картошку.
                                И «крепость» — одна из механик пришитых белыми нитками, это такой микроменеджмент крепости, наполненный до безобразия одинаковыми повторяющимеся ежедневными заданиями.
                                Короче забудьте про крепость, скорее всего она вам не понадобится никогда :)
                                0

                                Не совсем. Шардирование и phasing — разные вещи. Если вы про гарнизоны в WoD — там работало фазирование, которое как раз-таки и ломалось довольно часто. Шардирование к тому времени в игре уже было и довольно активно использовалось, начиная с пандарии (предыдущего аддона). Шардинг в WoD можно было увидеть, проходя квест-лайны и стартуя последний квест-сценарий в открытом мире, который незаметно переносил игрока на инстанс-шард со сценарием.

                                  0
                                  Видимо, я у себя в голове объединил эти два явления в одно.
                                  Спасибо за разъяснение.

                                  А вот когда бегая по локации (или по границе локации, точно не помню уже, кажется всё же не по границе), я видел как другой игрок то исчезал, то появлялся, и при этом в чате были сообщения переключения глобальных каналов, и кроме этого у ника игрока был написан сервер, на который я изначально не заходил — это ведь шардинг?
                              0
                              Darkfall Online был сделан без шардирования (т.е все в едином мире, на одном большом кластере) и можно было воевать на стыках зон. Подгрузки были только при телепортации в данжи, но при этом данжи не были на отдельных инстансах т.к в данжи было было попасть через эксплойт геометрии.
                                0
                                там давно дошло до отдельного сервера на Грид…
                                +1
                                Эх линейка, до сих пор лежит на винте архив C4 ванильного сервера от NCSoft, ну и куча исходников личных. В свое время дорабатывали его до C5. Всё через dll-inject. Даже парсер skilldata был пропатчен на добавление любых новых скиллов без проблем. Даже внешность и пол можно было поменять через скиллы/эффекты и телепортироваться куда угодно.
                                  0

                                  Насколько помнится, основная фишка л2 это эпические баталии абсолютно всего сервера в 1 точке, что практически обнуляет профит шардирования. Все топовые сервера тех далеких времен были pts с кучей самопальных костылей, а константно фризящие жаба эмуляторы считались дном… Возможно сегодня что-то поменялось?

                                    0
                                    Программу, написанную на плюсах, тоже можно считать, как Вы выразились — «константно фризящей». Не в ЯП дело.
                                      0
                                      Ну во времена С1-С4 на оффе эмуляторы только-только по сути начинали развивать, так что да — тогда они были тем еще адком, почти ничего не могущим и не умеющим.
                                      Сейчас же с этим все более-менее нормально.
                                      Насчет фризов — этим и офф-сервера грешат бывает — на любом ЯП можно написать программу так что она будет «константно тормозить» при определенных условиях. К тому же там, из-за их своеобразной архитектуры работы с бд, это приводит к возможности с гарантией дюпать вещи и т.д. Из последнего подобного — косяки на серверах Essence — думаю те кто в теме помнят об этом и о том какой срач на эту тему был на офф форуме у Инновы.
                                      В линейке по сути сейчас самое узкое место — это сетевая часть, а конкретно пакеты и их размеры. И самая проблема как раз таки в ситуациях когда куча игроков тусуется в одном месте — на осадах, в городах и т.п. В эти моменты клиентам летят просто дикие объемы пакетов с состоянием и изменяющимся статусом окружающих игроков, того что они кастуют и т.д. и т.п. Притом сами эти пакеты достаточно немаленькие по размерам. Хотя конечно нцсофт уже несколько хроник подряд старается это дело оптимизировать, делая все больше и больше крупных пакетов динамическими по структуре, т.е. отсылая их не полностью, а только те части что реально изменились.
                                      Ну и еще сам допотопный движок клиента вносит конечно свою лепту. Все же как бы корейцы не старались, но UE2 полноценно в многопроцессорность и все такое не может, так что в ситуациях «куча народа на экране» клиент тормозит и лагает даже на вполне современном железе…

                                      З.Ы. Сам потихоньку вожусь с одной из версий эмуляторов, на базе ветки Phoenix/OverWorld, так что по сути знаю о чем говорю…
                                        0
                                        оптимизация обработки пакетов сетевого трафика это отдельная грустная и долгая песня…
                                        CCP вон даже обработку фрагментарных пакетов реализовывали… сейчас правда наверное проще географический распределенные ноды разместить на крупных IX и сделать оптимизацию пакетной обработки через что-то похожее на «Оптимизирующее сжатие трафика»…
                                          0
                                          А вообще, есть ли в игре оптимизация вида: «Если в данный момент идёт осада и вокруг много игроков, то я не буду отправлять анимацию кастов от игрока который стоит очень далеко»? Ну или чтобы в пиковые нагрузки отключались некоторые анимации (социальные, анимация применения предметов типа SSок)?
                                            0
                                            Существует несколько настроек клиента, которые позволяют ограничивать видимость PC/NPC, но чтобы полностью отключать анимацию того или иного действия — нужно делать реализацию привязки некоторых настроек игрока, и резать по ним исходящие пакеты.
                                              0
                                              Существует несколько настроек клиента, которые позволяют ограничивать видимость PC/NPC

                                              Игроку в этом случае не будут пакеты отправляться? Я думал это только для того, чтобы их на уровне клиента не рисовало.
                                                0
                                                Будут, радиус оповещения является константой на серверной стороне, но тоже решаемо кастомизацией.
                                              0
                                              Ну пакеты обычно рассылаются только в определенном радиусе или в текущем + соседних регионах (достаточно маленьких по размерам). При желании опять же дальность рассылки в определенных условиях вполне настраивается + можно не слать определенные пакеты в таких условиях, не так уж сильно нужные для информирования клиента об изменениях.
                                          0

                                          Про блокирующие read/write-операции у Apache Ignite не понял. Всегда можно сделать async read/write. Данные, кстати, хранятся в оффхипе и, если вам надо получить лишь часть данных, а не весь объект, то такая возможность тоже есть

                                            +2

                                            Что-то это не выглядит как высокая производительность.


                                            Во-первых, не нужно сохранять в persistent каждый чих. Сохранение либо по завершении сделки, либо по решению алгоритма (редкий дроп), либо по таймауту. Всё остальное — in memory.


                                            500k в секунду — это одна NVME'шка DC-уровня.

                                              0
                                              Сохранять что и куда? Persist каких-либо промежуточных данных происходит в память шарда, а уже, как Вы и сказали, основного состояния, часть которого хранится в базе, выполняется по таймауту в data-service. Уточните утверждение, абсолютно не понятен посыл.
                                                0

                                                Может, я что-то не понял. У вас in-memory сколько tps делает? Хотя бы миллиард выжимаете?

                                                  0
                                                  Скорее всего. Я описывал два состояния игровых объектов — базовое и промежуточное. Базовое — складывается в базу по таймауту. Промежуточное — складывается в off-heap память и реплицируется в кластере. Уточните пожалуйста — tps.
                                                    0

                                                    transactions per second.

                                              +1
                                              А я бы пошёл от обратного. Сначала отпрофайлил бы некий текущий код, получив самые «горячие» места сервера, и именно их бы оптимизировал. 20% оптимизаций дало бы 80% выигрыша, или как-то так :)
                                                +1
                                                А что при такой архитектуре делать с Штурмом Адены?
                                                те почему выбрана именно такая механика Шардирования — почему не выбрана тактика тех же CCP когда шарды привязываются к узлам с максимальной нагрузкой?
                                                  0
                                                  Я, довольно таки, плохо знаком с архитектурой решения ССР, и как они осуществляют балансировку ибо в публичных данных этого не написано. Предположительно они делают тоже самое — при подключении нового шарда, игроки распределяются равномерно между ними. Но и им это проще делать, поскольку данные централизовано хранятся на RAID массивах. У моего решения — в памяти, а шарить память между двумя процессами, даже с помощью nmap, гиблая затея. Здесь, возможно, же, все тоже самое. Первоначальное состояние S1 (100 игроков), подняли S2, оркестратор перебросил какой-то процент игроков, охватывающий те регионы, в которых они находятся, на S2.
                                                    0
                                                    просто при географическом Шардинге у вас встает вопрос передачи данных о взаимодействии больших групп игроков. и что Будет происходить при Обсчете взаимодействия скажем 1000 игроков? а если они будут постоянно мигрировать через границу шарда? или например половина игроков буде по одну сторону, а вторая по другую?

                                                    А еще не совсем тривиальный вопрос — если я на бегу через границу уроню предмет в зоне одного шарда он у меня останется? или Дуплицируется если я его успею выложить до прохода обновления данных между шардами?

                                                    У CCP все несколько веселее — ищите Дев блоги где они рассказывали про физику и логику работы своей системы в 2018-19 годах навряд ли есть что то более новое.
                                                    Если что на физике у них RAM-Drive для БД тогда было сейчас скорее всего переехали на IN-memory…
                                                      0
                                                      просто при географическом Шардинге у вас встает вопрос передачи данных о взаимодействии больших групп игроков. и что Будет происходить при Обсчете взаимодействия скажем 1000 игроков

                                                      Дельта данных каждого игрока уже существует во всех шардах кластера средствами репликации. Так что это особого аффекта не должно вызвать, как и сейчас с перемещением. А взаимодействие игрок-игрок на границах — обмен трафика между шардами.

                                                      А еще не совсем тривиальный вопрос — если я на бегу через границу уроню предмет в зоне одного шарда он у меня останется? или Дуплицируется если я его успею выложить до прохода обновления данных между шардами?


                                                      Предмет перекочует вместе с игроком в том состоянии, в котором он находился до балансировки. Т.е. тут можно рассмотреть два варианта
                                                      1. игрок выбросил предмет в регионе 1, перешел через границу, и видит его в выброшенном месте
                                                      2. игрок выбросил предмет в регионе 1 и в этот же момент произошло добавление нового шарда, который сузил границы региона 1 и поделил его напополам. В последствии предмет «перекочевал» обратно к игроку в новый шард.

                                                      P.s. чтобы каких-то неочевидных багов не происходило, я блокирую любое телодвижение объектов в мире при «расширении/сужении» кластера, увы операция сама по себе дорогостоящая.
                                                        0
                                                        Дельта данных каждого игрока уже существует во всех шардах кластера средствами репликации. Так что это особого аффекта не должно вызвать, как и сейчас с перемещением. А взаимодействие игрок-игрок на границах — обмен трафика между шардами.

                                                        и насколько быстро оно работает? Вот два шарда 500 человек в одном 500 человек в Другом — атакуют друг-друга через границу шарда… что-то мне сомнительно что у вас будет происходить всё без проблем — по сути БД будет обновляться Двумя шардами, нагрузка на БД практический удваивается, а то и учетверяется, тк запросы идут под одними тем же объектам для двух шардов, реплики здесь не особо помогут тк взаимодействие идет МЕЖДУ объектами на разных шардах…

                                                        В общем думается мне что без механизма динамического шардирвоания который создаст Зону в которую уместятся обе группы игроков и выделит им отдельный шард у вас не будет без явных фризов и багов… технический я бы Шардировал именно по группам игроков по сути генерируя для каждой группы псевдо мир в котором они крутятся и старался бы сделать систему в которой не было вобще привязки к статичным объектам…

                                                        Предмет перекочует вместе с игроком в том состоянии, в котором он находился до балансировки. Т.е. тут можно рассмотреть два варианта
                                                        1. игрок выбросил предмет в регионе 1, перешел через границу, и видит его в выброшенном месте
                                                        2. игрок выбросил предмет в регионе 1 и в этот же момент произошло добавление нового шарда, который сузил границы региона 1 и поделил его напополам. В последствии предмет «перекочевал» обратно к игроку в новый шард.


                                                        вы делаете расчет на идеальный мир — "… а он имеет свойство тормозить!!!" — игрок бежит в шарде №1 и выкидывает объект ровно за Тик до границы объект выпадает тк условия проверки проходят нормально — у игрока объект в ТЕОРИИ должен пропасть Транзакция на удаление Объекта в БД улетела — НО она не мгновенная(!!!) и Вот тут начинается веселье, тк Игрок попадает на другой Шард где ЕЩЁ нет информации о Отсутствии объекта у Игрока и он может выложить его повторно… И возникает очень интересная коллизия — объект Валиден и есть На шарде №1 Объект Валиден и Есть на шарде №2 и Объекта нет у игрока о чем прилетает подтверждение из БД в реплики обоих шардов…

                                                        Даже если у вас есть четкое отслеживание ID-ов Объектов и каждый ID уникален и это проверяется — вопрос в том как у вас происходит обработка свободно «лежащих» на «Земле» объектов? Если вы думаете что тут проблема только с Дюп-ом объектов то вы сильно заблуждаетесь Босы, Мобы и NPC тоже Дюпаются…
                                                          0
                                                          Ну смотрите, идея виртуальных регионов тоже уже предусмотрена, и включатся они должны в момент этих самых скоплений персонажей на границах, как и с массовыми мероприятиями (вдруг замок попадет в границу). Сама бд не нагружена от слова совсем, а вот сокет — да. Для промежуточного состояния используется не бд, а память и репликация, также для консистентной обработки планируется использовать распределённый CAS ключей.
                                                            0
                                                            а зачем тогда Вобще Статические Шарды? делаете по умолчанию Для каждого игрока динамический шард при приближении к другому игроку сливаете Их на новый шард если у них возникает взаимодействие, если нет то просто Отображаете «Тень» — да система более Сложная, но гораздо более гибкая тк реально пропадает всякая привязка и ограничения.
                                                              0
                                                              Интересная идея, возможно будет взята на вооружение. Спасибо
                                                  0
                                                  Эта работа проведена для обучения или «Just fro fun»?
                                                  Кому в настоящий момент может понадобиться сервер Lineage 2 для больших онлайнов?
                                                    0
                                                    Эта работа была проведена для создания готовой платформы/решения разработки.
                                                      0
                                                      Странно. В 2021 году я не знаю проектов L2 кроме оффициальных серверов, где нагрузка может быть больше чем несколько сотен одновременных игроков.
                                                        0
                                                        L2 уже давно в небытие. Есть же Aion и другие ;)
                                                          0
                                                          Понял, обкатка идеи на том что было.

                                                  Only users with full accounts can post comments. Log in, please.