пятница, 15 октября 2010 г.

Game from Scratch: Front of Waterfall

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

В первый раз забросил он невод, и отделил свет от тьмы.
Большие полотна текста плохо способствую пониманию. Поэтому для создания каркаса первого нашего (и позже второго нашего) сервиса полюбуемся на картинку.


Исследуем данное изображение в деталях.

Т.к. наши сервисы живут в событийной среде, им необходимо работать с событиями.
Для этого нам необходимо реализовать некий EventManager. Заниматься он будет обработкой возникших событий и перенаправление их к экземпляром всевозможных Dispatcher'ов.

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


Изучив нижнюю часть схемы мы наткнулись на важную вещь - удаленный сервис должен подписываться на события. Однако в случае его недоступности или состояния Offline наш FrontendService должен что-либо отвечать игрокам пытающимся войти в игру. Поэтому в момент когда игрок пытается войти в игру (LoginEvent), локальный обработчик исследует доступность LoginService и генерирует событие о недоступности оного для клиента (LoginServiceIsDown), либо создает событие аутенфикации (AuthenticateEvent), которое отправляется на обработку в NetworkEventDispatcher ответственный за связь с LoginService.

LoginService реализованный с точно такой же структурой проверяет доступно ли ему хранилище аккаунтов (и множество других проверок, например проверяет статус он-лайн игрока) и соответственно генерирует события отказа (AuthenticationFailed) или подтверждения аутенфикации (AuthenticationCompleate), детализируя их кодом ошибки или идентификатором сессии.

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

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

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

А дело в трех вещах.

1. Масштабирование;
2. Гибкость;
3. Ощущение собственной охуенности.

Самый важный пункт на самом деле гибкость. Так например у каждого сервиса может существовать своя собственная база данных.

Например для LoginService можно использовать MySQL или PostgreSQL. И оттуда же будут брать данные всякие форумы, панели для работы с пользователями и что угодно другое.

А сервис ответственный за сумку игрока будет работать с какой-нибудь key-value  NoSQL базой данных. Переодически сбрасывая туда изменения произошедшие с сумкой и экипировкой персонажа. Ему ведь не важна реляционная модель.

При этом сревис ответственный за позициионирование игрока будет брать данные из-той же NoSQL базы. А сервис ответственный за реализацию гильдий обратиться в SQL базу, дабы на сайте можно было отображать списки гильдий.

Можно не мудрствуя лукаво наоборот все хранить в MySQL и наслаждаться жизнью.

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

В следующем этапе мы займемся созданием FrontendServce, реализуем LocalDispatchManager и реализуем несколько событий - LoginEvent, TryToLogin, LoginServiseIsDown и напишем маленький скрипт эмулирующий клиент, для тестирования этого добра.

понедельник, 11 октября 2010 г.

Game from Scratch: In The Begining

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

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

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

Кому будет сие интересно? Это будет возможно любопытно людям, которые занимаются разработкой MMO-игр. В некотором смысле будет любопытно и для УЕБ-девелоперов. Мало любопытно будет любителям python'ов, twisted'ов и так далее.

In the begining
in principio erat Verbum et Verbum erat apud Deum et Deus erat Verbum
В данной статье мы поговорим о начале и началх и о том как от бесконечного балабольства перейти к делу. С самого начала свой возни в программировании, мне ужасно нравилось работать с данными, формализовывать их, выносить из кода константы во всевозможные конфиги и базы данных. Частично это называется Data-driven programming.

Но совсем в начале нужно поставить задачу. Т.к. у меня есть слово, пожалуй им воспользуюсь. Задача наша до ужаса банальна и проста и любит ей заниматься любой школьник с 14-16 летнего возраста. Мы хотим сделать игру!11 Пока мы не знаем как она будет выглядеть (поищите по форумам, большинство энтузиастов мрут на моменте - я не могу найти художников и программистов для того чтобы сделать супер-пупер аналог кризиса). Но знаем что это ММО игра. Предположим (нагло), что это будет "обычная" игрулька вроде Lineage 2 или World of Warcaft. Давайте запомним, сейчас у нас нет художников, денег и вообще нифига (или они есть но больше ничего нету) и мы хотим хоть что-нибудь делать. И делать мы будем то, что называется ядром.

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

Т.к. тут я повелитель клитора, то позволю себе окунуться немного в историю. Первыми полноценными ММО были всевозможные MUD'ы. Реализовывались они с помощью Си и представляли в себе монолитный блок который считывал данные из сокета и реагировал на команды игрока, параллельно выполняя некоторые задачи. Если вы школьник и хотите получить пищу для ума, поищите всевозможные ROM-based сервера и поразвлекайтесь их кастомизацией. Ежели голову не сломаете, научитесь программировать и даже немного понимать C.

Появление рынка видеоигр спровоцировало и создание MMORPG. Так появились всякие Ультимы. Ультимы были уже сложнее, но все еще продолжали наследовать прошлое. Приход же в мир эмуляторов Lineage 2 (как и утечка исходных кодов Lineage 2) подарили миру первое сегментирование ядра. Появился некий ужасный login service. Который иногда не работал и потому на остальной монолитный сервис попасть было нельзя.

Lineage 2 представлял собой одну большую емкость, содержащую в себе всех игроков, весь мир, всех мобов (даже если они были разделены по сервисам). И поэтому если где-нибудь случался на ГФШ большой сбор с участием игры мышцами, все игроки ощущали будто нырнули с головой в вату.

Появление World of Warcraft кардинально изменило ситуацию. Программисты и инжинеры Blizzard и раньше были бесподобны, но тут перевели разработку кардинально на новый уровень. Каждый материк WoW существовал как отдельная миска. Поэтому проблемы в миске А, не влияли на отличную работу миски Б. Последующие поколения создателей ядер MMO продолжали сегментировать логику.

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

Сервисы будут генерировать события и пересылать их тем сервисам, которые в этом заинтересованы. Внезапно мы откажемся от идеологии системы Request/Response в пользу идеи Event Only. В результате получи не синхронную среду, в которой каждый сервис будет отвечать за синхронизацию своего участка данных. Это безусловно приведет нас к проблемам, но любые проблемы нужно решать по мере их возникновения.

А скрипач, скрипач не нужен..

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


  1. Python2.7 - язык позволяющий писать хороший и простой в понимании код, не требующий огромных познаний в синтаксисе. А при должном усердии со стороны пейсателя и минимизирующий риски создания неведомой и непонятной хуйни;
  2. Twisted - но дабы всем было весело, будет у нас еще и неведомая и непонятная хуйня. Эта библиотека содержит не только асинхронное сетевое ядро, но еще и бесценную вещь - спосбность превратить вашу программу в полноценный сервис с логгированием, вашими собственными циклами обработки и во многом создать каркас для разработки.От чего-то мы откажемся (например от демонизации с использованием twistd и повсемостного втыкания zope.interfaces), что-то используем;
  3. Json - для работы сервисов нужен протокол. для передачи данных нужна сериализация. Т.к. мы используем twisted сетевой уровень нам не важен. Это может быть традиционный для туториалов LineReceiver протокол или суровый как Ъ бинарный протокол Length-Type, или еще чего-нибудь. Так же не важно TCP это или UDP. Важно как мы будем собственно сообщать сервису что случилось. Для этого мы будем создавать конструкцию в виде dict содержащего поля необходимые для всей идентификации. Для сериализации этого, нам и пригодится json. А сие можно будет отправлять соответственно хоть в Flash, хоть в Java, хоть в C++. Трудно представить языка программирования не имеющего десериализатора json сегодня;
  4. Xpath и ETree в libxml2 и python-lxml - для создания всевозможных данных (Data-Driven ога-ога) мы будем формализовывать их в виде XML. А еще частично будем хранить их в виде XML таблиц в памяти и производить быстрый поиск посредством xpath. Вы как хотите, а я люблю XML ^^
  5. NoSQL. Т.к. не известно чем все закончится. Наверняка нам понадобиться хранилище данных для игроков. Хранить структуры игроков мы будем благополучно в NoSQL хранилище. Каким оно будет, покажет момент, когда мы до него доберемся. 
  6. PyQT4 - В один прекрасный момент нам нужно будет создать игровой клиент. Т.к. у нас все еще нет графики сделаем его может быть с использованием PyQT4.
Вот такой у нас будет инструментарий. И давайте постараемся обойтись только этим.
Гена, вот то самое полотенце, которое ты просил
Чтобы уже начать что-то делать давайте разберемся с тем, с чего начнем.

В каждой современной игре есть набор сущностей которые нужно создать и нам. Самая большая и важная сущность это игровой мир. В D&D он назывался бы Plane. Т.к. этот мир состоит из множества регионов, соответственно нам нужно создать сервис отвечающий за регионы, который будет знать все о географии региона, о его населении и позиции игроков, нпц и мобов внутри этого участка мира.

Это первый набор данных с которого мы начнем. И приступив к началу оговорим и первый элемент того что мы делаем.

До встречи.

Отмазка от ответственности

Здравствуйте!

Те кто со мной не знаком должны принят к факту сведение о том, что я инфантильный и непрофессиональный мудак, истеричная сволочь, невменяемый и ненормальный.

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

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

В целом мне нет дела до вашего возраста, пола, социальной принадлежности, политических взглядов, социальной ориентации etc.

И да. Я считаю, что все вокруг - пидорасы.