Как стать автором
Обновить

Idewavecore. Ретроспектива

Время на прочтение4 мин
Количество просмотров1.8K

Очень круто - запрограммировать механизм или программный модуль, заставив его выполнять твою волю. С похожими мыслями в конце 2018 я размышлял о том, что хочу сделать собственный WoW-сервер, который будет полностью мной управляем. Поизучав С++ исходники MANGOS, я пришел к выводу, что не смогу вот так взять и реализовать все свои идеи, не понимая, как же работает MMO RPG сервер от начала и до конца. И для этой цели я решил реализовать свой движок. С нуля.


Начну с того, что вначале из документации у меня были только исходники. Ответы же на многочисленные свои вопросы я с переменным успехом по крупицам искал на форумах (в этом плане - огромный респект сообществу MANGOS, - очень отзывчивый коллектив). Итого прошло несколько месяцев, прежде чем методом проб и ошибок я реализовал свой первый рабочий прототип Login сервера - и смог попасть на экран выбора персонажей.

Если вкратце, то Login Server (как и аутентификация в клиенте WoW) построены на использовании алгоритма SRP. Описание алгоритма выходит за пределы статьи, но если вкратце, то он позволяет идентифицировать пользователя без передачи пароля на сервер, благодаря чему пароль (даже в закэшированном виде) можно не хранить на сервере. Даже желательно.

Алгоритм шифрования же World сервера скорее всего отличается от клиента к клиенту (я сделал такой вывод при беглом изучении исходников сервера WoW 3.3.5a). Я разрабатывал сервер для WoW версии 2.4.3. Там используется что-то вроде шифра Цезаря. Хотя чаще (в исходниках) можно встретить название HeaderCrypt или Wowcrypt.

В версии 2.4.3 шифруются первые 6 байт: size (2) и opcode (4) каждого пакета на World сервере (кроме первого пакета). Соответственно, если перехватить пакет (sniff), то не получится определить, на какой opcode он будет отправлен. А если пакет большой, то он может быть разбит на несколько и чтобы их правильно достать из буфера - нужны эти самые первые 2 байта (size).

Процесс входа на сервер вкратце можно описать так:

Клиент последовательно обменивается данными с Login сервером и в случае успеха получает SRP токен (session key), который затем будет использован для создания токена шифрования/расшифровки пакетов (crypto key). Токен создается на этапе "send auth request" (см. схему выше). После чего на клиент отправляется auth response, суть которого - показать, что операция прошла успешно. Auth response - единственный незашифрованный пакет, последующие пакеты будут шифроваться с помощью crypto key. По поводу шифра - у меня он реализован так.

Процесс взаимодействия с World сервером простой - клиент отправляет зашифрованный пакет, на сервере он кладется в буфер, расшифровываются первые несколько байт (о чем я писал выше), получаем размер (size) и опкод (opcode) и если количество байт в буфере >= size, то мы получаем список необходимых хэндлеров (их может быть несколько) по заданному опкоду и передаем им size байт из буфера. Либо ожидаем получения остальных байт. Отдельного внимания заслуживает Update Packet. Он отличается от остальных пакетов более сложной структурой.

Расшифровывать нужно каждый пакет. Если какой-то пакет пропущен, то следующий (согласно алгоритму) будет расшифрован неправильно. На эти грабли я наступал с особой старательностью.

В чем суть моего сервера

Во-первых, я его писал на Python 3 (asyncio + SQLAlchemy). Точнее, на момент создания я еще не имел опыта с SQLAlchemy - я его обрел после, когда решил, что существующая у меня реализация работы с БД - ужасна во всех ее проявлениях (а еще попутно решил изучить новую технологию). И очередной раз переписал проект с нуля.

Во-вторых, подход к обработке данных тоже несколько отличался. Я предпочел отказаться от идеи глобального хранилища всех объектов (где каждый объект содержит также методы работы с ним) и создать специальные мэнеджеры (manager), для того, чтобы работать с каждым отдельным типом объектов: Item, Player, Unit и т.д. Т.е. если я хочу выполнить действие над Player, я использую PlayerManager, который выполняет нужное действие и удаляется из памяти. В первую очередь это сделано в угоду читабельности. К примеру, аналогичный класс в MANGOS лично для меня кажется громоздким (как и C++, несмотря на множество его преимуществ). Каждый отдельный класс объекта - это SQLAlchemy модель и он же - структура данных, хранящая текущее состояние. Таким образом, я использую такие объекты не только для взаимодействия с БД, но и для обмена структурированными данными между частями приложения.

В-третьих, я использую специальные классы - handler - совокупность которых я использую для обработки отдельно взятого опкода. Каждый хэндлер должен выполнять одну типовую задачу. И (опционально) он может вернуть респонс (опкод + данные - структура аналогична запросу), который затем будет отправлен клиенту.

В-четвертых, почти сразу я решил не делать еще один MANGOS движок, а вместо этого начал экспериментировать со свободой применения (что можно сделать помимо уже реализованного в других схожих проектах?). Начиналось все с идеи использования произвольного датасэта (данных, которые мы загружаем в базу). Короче, моя цель - скорее, творчество без границ, а не создание модифицированного (или blizzlike) сервера WoW.

В-пятых, я неоднократно переписывал код, чтобы сделать его читабельнее и, в принципе, у меня получилось сделать более-менее однородную архитектуру. Думаю, это - однозначный плюс для тех, кто будет читать исходный код. Для этих же целей при разработке упор был на ООП.

О самом сервере

Наверное, есть смысл сказать несколько слов о том, что собственно реализовано.

  1. Собственно, вход на сервер

  2. Создание/удаление персонажа

  3. Вход в мир

  4. Отображение надетых вещей и оружия

  5. Отображение окружающих игроков/мобов

  6. Броадкаст движения/чата (сказать/крикнуть)

  7. Погода

  8. Отображение сообщения дня

Что дальше

В процессе разработки сервера у меня возникла идея создания MMO RPG шаблонизатора, который предоставит формат описания игровых серверов (например, связка Login + World сервер, или шард-ориентированная архитектура, где каждая локация - отдельный сервер и т.д.), а так же серверов, имеющих отношение к игровым косвенно (например, web сервер, где будет сайт и форум). Именно для этого я создал фреймворк (но не совсем внятно описал его в своей предыдущей статье). И сейчас сервер переписываю на базе этого фреймворка.

Я уже наткнулся на ряд вещей, которые нужно будет отрефакторить, поэтому, к моменту завершения текущей цели я планирую выпустить ряд изменений в самом фреймворке. О чем я, возможно, напишу в будущем. Если к серии статей будет интерес.

P.S. продолжать развитие своего изначального проекта не планирую. Но, учитывая, что некоторые люди его используют в образовательных целях, то я рад, если кому-то еще он будет полезен.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Есть ли у вас опыт написания игрового сервера с нуля?
20% Да (есть полностью рабочий игровой сервер, сделанный с нуля)3
6.67% Да (но не довел до конца)1
20% Нету (и нет желания)3
53.33% Хочу попробовать сделать8
Проголосовали 15 пользователей. Воздержались 3 пользователя.
Теги:
Хабы:
+3
Комментарии1

Публикации

Истории

Работа

Ближайшие события