познавательная информация для разработчиков Базы данных в онлайн-играх. От Аллодов Онлайн до Skyforge Когда говорят про разработку игр, обычно речь идет о шейдерах, графике, AI и т.д. Крайне редко затрагивается серверная часть игровых проектов, а ещё реже — базы данных. Исправим это досадное недоразумение: сегодня я расскажу о нашем опыте работы с базами данных, который мы приобрели в ходе разработки Аллодов Онлайн и нашего нового проекта Skyforge. Обе эти игры — клиентские MMORPG. В первой зарегистрировано несколько миллионов игроков. Вторая разрабатывается студией в строжайшей секретности в недрах Allods Team. Эволюция Игровая база — это типичная OLTP-система (много маленьких и коротких транзакций). Но использование баз данных в играх несколько отличается от их использования в вебе, банках и прочем энтерпрайзе. Во-первых, это связано с тем, что модель данных в играх существенно сложнее, чем в банках. Во-вторых, большинство программистов в геймдеве вышли из сурового мира C++, прихватив с собой бороду и любовь к бинарной запаковке. Абсолютно все они, если им надо сохранить персонажа на диск, первым делом хотят сериализовать его в файл. Именно так всё и начиналось в Аллодах Онлайн. Программисты сделали файловое хранилище, но быстро одумались и переписали всё под MySQL. Проект успешно запустили, люди играли, опыт копился. Что у нас было в Аллодах: - Java, MySQL - Шарды. И каждый из них был рассчитан на некое ограниченное число игроков, находящихся онлайн - Это количество игроков выдавало примерно 200 транзакций в секунду - Сервис, который работает с базой, был однотредовым, т.к. этого было достаточно для такого количества транзакций Через несколько лет стартовал Skyforge. У Skyforge были совсем другие требования, и поэтому пришлось пересмотреть наш подход к работе с базами. Вот эти требования: - У нас больше нет шардов. У нас один большой единый мир - Мы рассчитываем наш сервер на 100000 игроков, находящихся онлайн, а возможно и больше - По нашим прикидкам, эти игроки должны выдавать более 7000 транзакций в секунду - Мы всё так же пишем на Java, но с MySQL мы перешли на PostgreSQL Ну что же, давайте заглянем за кулисы и посмотрим, к чему мы пришли в ходе эволюционного развития нашей технологической мысли. Архитектура Во-первых, наш сервер распределённый. База тоже распределённая. Во-вторых, архитектура нашего сервера — сервисно-ориентированная. Это значит, что всё представлено в виде сервисов, которые обмениваются сообщениями. В игре существуют десятки сервисов, но прямой доступ к базе данных есть только у сервисов исполнения транзакций. Нужно только учесть, что все изображённые на картинке элементы существуют в нескольких экземплярах. - На серверах игровой механики находятся аватары. Аватар — это Java-объект, представляющий нашего игрока. Серверов игровой механики в несколько раз больше, чем серверов баз данных. - Все сервера общаются с базой данных посредством специального интерфейса. Этот интерфейс содержит сотни методов, скрывает от программистов игровой механики распределённую сущность базы и обеспечивает понятный контракт: один метод — одна транзакция. Нужно понимать, что это не один класс с сотней методов, а один класс с десятью методами, который отдают маленькие «подинтерфейсы» с десятью методами каждый. Эдакие «паки» операций. - Сервис БД (базы данных) выполняет пришедшие операции и записывает их результаты в базу. Сервис БД и сама БД находятся на одном физическом сервере, чтобы не тратить лишнее время на сеть. Аватар как кэш Эта нехитрая схема имеет один важный момент. Наш аватар нужен для работоспособности игровой механики, но как побочный эффект он же фактически является кэшем над базой данных. Все запросы вида «Покажи мне предметы этого игрока» или «А где находится аватар Василий?» обслуживаются этим аватаром. Когда игрок входит в игру, мы загружаем его аватара, и он живёт до тех пор, пока игрок онлайн. Такой нехитрый трюк позволяет снять с базы большую часть запросов на чтение и даже часть запросов на запись. Мы делим все данные игрока на две категории: - Неважные данные, потерю которых игрок может пережить. К ним относятся позиция на карте, уровень здоровья и т.п. Такие данные мы «накапливаем» у аватара и периодически, а также один раз при выходе из игры скидываем в базу. - Важные данные, потеря которых будет для игрока болезненной. К ним относятся предметы, деньги, квесты и подобные вещи. С этими данными всё гораздо сложнее. Мы стараемся сделать так, чтобы игрок не потерял эти данные никогда, т.к. на них он потратил очень много времени и сил. Поэтому их надо сохранять в базу синхронно. Именно сохранение важных данных и создаёт основную нагрузку на нашу базу. Итак, как же мы синхронизируем состояние наших важных данных в базе и аватара, который находится на другом сервере? Всё на самом деле довольно просто. Рассмотрим схему взятия предмета. - Сервер игровой механики присылает запрос к сервису базы данных «взять предмет ХХХ». - Сервер БД выполняет необходимые проверки (достаточно ли в сумке места, не нужно ли «застекать» эту вещь и так далее). После этого он сохраняет обновлённое состояние сумки аватара в базу. - Только если сохранение прошло успешно, аватару отсылается обновление состояния его сумки. Аватар, в свою очередь, отправляет обновления в игровой клиент. В результате игрок увидит, что у него появился предмет, только тогда, когда предмет надёжно сохранён в базу. PostgreSQL В Skyforge мы отказались от MySQL по совокупности причин, перечисленных ниже. - В MySQL все фичи размазаны по различным движкам хранения. Что-то было в InnoDB, что-то в MyISAM, что-то в движке MEMORY. Это сильно усложняло жизнь. - В MySQL сломан механизм распределённых транзакций, который нам очень хотелось использовать. Разработчики MySQL обещали его починить только к шестой версии, которой нет ещё даже в планах. - В MySQL был сломан механизм группового коммита. Его починили в версии 5.5, и этот пункт уже не актуален. - В MySQL на самом деле довольно много багов, странно работающих фич и весьма ограниченный оптимизатор запросов. PostgreSQL решал все эти проблемы, взамен давая только проблему с автовакуумом. Базу NoSQL мы решили не брать, т.к. у нас очень высокие требования к консистентности данных, а ни одна в мире NoSQL-база не может консистентно и транзакционно переложить предмет от одного аватара другому. Eventual consistency в этом случае нас не очень устраивал, т.к. это сильно портит game experience. Гибридная схема данных То, что мы используем PostgreSQL, ещё не значит, что мы должны хранить данные в реляционном виде. Реляционную базу можно использовать в качестве key-value storage Полностью реляционная модель нас не устраивает, т.к. содержит в себе несколько узких мест, критичных для производительности. Например, у нас есть игрок, а у него есть квесты. Игрок может выполнить сотни квестов, и при входе в игру нам надо будет их все показать. Если пользоваться реляционной моделью, придётся делать запрос на выдачу сотни строк из базы, а это медленно. С другой стороны, нереляционная модель имеет множество минусов: отсутствие констрейнтов, невозможность обновить данные частично и т.п. После разнообразных экспериментов мы сошлись на том, что нас удовлетворяет связка реляционной модели, в которой часть полей содержит нереляционные данные. В Аллодах и до недавнего времени в Skyforge мы часть данных сериализовали бинарно и хранили в качестве полей в таблицах. Но буквально три недели назад мы наконец-то всё поняли и теперь храним данные в реляционной схеме с JSON-вставками. Выглядит это примерно так: # select * from avatar limit 1; id | 144115188075857124 position | {"point":{"x":7402.2793,"y":6080.2197,"z":51.42402},"yaw":0.0,"map":"id:132646944","isLocal":false,"isValid":true} death_descriptor | {"deathTime":-1,"respawnTime":-1,"sparkReturnDelay":-1,"recentDeathTimesArray":[]} health | 1250 mana_descriptor | {"mana":{"8":300}} avatar_client_info | \x character_race_class_res_id | 26209282 character_sex_res_id | 550995 last_online_time | 1371814800726 Такая схема позволяет нам использовать все бонусы реляционной модели и компенсировать её узкие места нереляционными вставками на JSON. Кроме того, PostgreSQL 9.3 позволяет делать запросы по JSON. Таким образом, мы получаем эдакий коктейль два в одном — PostgreSQL и MongoDB по цене PostgreSQL. Virtual shards Чтобы справиться с нагрузкой на запись, мы шардируем нашу базу данных по аккаунтам. Для этого мы в ID сущности кодируем номер шарда, на котором живёт аватар, и аккаунт. ID состоит из двух частей: первый байт — номер шарда, остальные — ID сущности внутри шарда. long id = <shard_id> <account_id> В игре существует несколько десятков сервисов баз данных. Каждый из них однотредовый и работает со своей маленькой базой. Несколько таких маленьких баз мы помещаем на один физический сервис. Такой подход используют многие интернет-гиганты, называется он virtual shards и решает проблему перебалансировки. Допустим, у нас есть два физических сервера, и на них лежит по 15 маленьких шардов. Если у нас вдруг появилось много пользователей и всё стало тормозить, мы просто покупаем ещё один сервер и перекладываем на него по 5 шардов со старых серверов. И вместо схемы 2х15 мы очень просто получаем схему 3х10. Таким образом, можно очень просто осуществлять перебалансировку без необходимости распиливать данные внутри базы данных. --- добавлено: 24 дек 2014 в 22:00 --- p.s. уберите ограничение в 10к символов, не получается все запостить(
Какой первый попадется в списке - тому и выдаст.
Версия 4.3 потоковая загрузка elements.data, surfaces.pck и configs.pck при запуске программы исправлена ошибка отображения пака смайлов (белый лист) переделана загрузка item_ext_desc.txt и увеличено быстродействие добавлено отображение иконок кланов в списке кланов (в конфиге сервер части новые параметры servid, default_icon) исправлена ошибка кеширования смайлов при изменении размера в настройках вывод количества уникальных IP в списке персонажей онлайн (для версии протоколов 70+) сортировка по клику на поле в списке персонажей онлайн возможность выдачи голда/предмета игрокам онлайн с уникальными IP (для версии протоколов 70+) вывод IP последнего входа в списке игроков клана (для версии протоколов 70+) поиск аккаунтов по логину / email теперь более строгий (для поиска вариантов используйте символы % и _) возможность отправки голда / почты выбранным игрокам из списка клана и списка онлайна поддержка новых функций для версии протокола < 70, связанных с IP через модифицированный authd (для заказа модификации своего authd обратитесь к разработчику) в конфиге сервер части новые параметры auth_mod, auth_mod_table, auth_mod_userid, auth_mod_ip сервер часть при запуске показывает процессы, которые занимают порт (если он занят) при падении сервер части, процессы, запущенные через пвадмин не занимают порт добавлены проверки при запуске сервер части и автоматическое удаление lock файла, если процесс не запущен возможность отключить MySql запросы (в конфиге сервер части новый параметр disable_mysql) возможность копирования данных выделенных аккаунтов в буфер обмена добавлены подсказки на некоторых полях при редактировании персонажа
[IMG]
Попробуй галочку поставить/снять широкоформатный, если она есть конечно на 1.3.6
Изначально назначение у того поля на скрине не цвет, а тип бонусов - рандомные или фиксированные, а цвет это уже вторичная функция.
Из переписки видно, что адекватность саппорта на уровне 16-ти летнего школьника, который очевидно не прав в каких-то вопросах, но отчаянно пытается выкрутиться, особенно после такого [17:57:06] Роман Support: Идите в суд) [17:57:09] Роман Support: Адресов нет Т.е. он считает, что раз на сайте не указан адрес, то ему ничего не будет. Наивно и глупо.
На самом деле самым сложным будет найти название нужного действия модели, я помню добавлял действия под скиллы от других классов.
Ссылки на gfx эффекты при конкретном действии прописываются в ecm файле персонажа.
Модифицировать ядро.
Сильно сомневаюсь, что есть такие файлы от разрабов. А вот что кто-то решил "спрятать" реальную версию и заменил вручную - вполне.
Версия 4.2 добавлено кеширование паков смайлов для более быстрой загрузки добавлена пауза при перезапуске MYSQL сервер часть больше не завершает работу при ошибках коннекта к MYSQL добавлена поддержка 109, 112, 114, 115, 120, 123 версий elements.data исправлена ошибка при перезапуске данжа через ГМ консоль (не запускался GLink) добавлен редактор октета реинкарнации добавлен редактор октета меридианов добавлен редактор октета фракций добавлен редактор октета Realm Data звуковой сигнал при потере связи с сервер частью и падении данжа просмотр, снятие приложений, удаление писем персонажа добавлен просмотр описания и цвета предмета из configs.pck (в информации октета предметов) добавлен редактор октета Task Data добавлен редактор октета Task Complete добавлен редактор октета Task Finish Time добавлена поддержка tasks.data добавлен просмотр описания в списке выбора сохраненных предметов чтение elements.data, surfaces.pck и configs.pck сделано в отдельных потоках добавление строки в локальный чат при отправке сообщения из пвадмина кнопки управления ГМ правами перенесены во всплывающее меню аккаунта исправлена ошибка разбора октета с бонусами "Параметры неизвестны" отправка сообщения дублирована в окно просмотра чата возможность отправки сообщения при выдаче банов вывод дополнительной информации в списке персонажей онлайн (для версии протоколов 70+) вывод IP последнего входа на аккаунт при выборе (для версии протоколов 70+) вывод среднего времени онлайн персонажа при наведении на поле с датой создания (для версии протоколов 70+) возможность добавить персонажа в клан (возможность восстановления кланов без игроков) возможность выгнать персонажа из клана (кроме мастера клана) исправлен баг при удалении ГМ прав (не указывался zoneid) поиск по lkfield1 и lkfield2 сортировка по полям в списке персонажей онлайн (по клику на заголовке) И немного скринов [IMG] [IMG]
я думаю что gamedb как раз все равно какие там слоты, он просто хранит данные в базе.
[media]
С обновлением все узнаете)
Мне бы хоть с этой версией разобраться, до андроида ещё рановато.
Лучше сделайте возможность редактировать первое сообщение своей темы. p.s. и кнопки апнуть тему я что-то не вижу нигде в своих темах
Попробуй убрать эту строку header('Content-Transfer-Encoding: binary');
Смотреть надо в GBK кодировке assert failed:"0 && "加锁的时间过长"" in file spinlock.c:119 Ну а дальше гугл транслейт. Подобная ошибка у меня лично была при проблемах с винтом, когда он подвешивал систему переодически.
раз http://i.imgur.com/T01kNMx.png и два http://i.imgur.com/A1hxwXM.png
Имена участников (разделяйте запятой).