Библиотека сайта rus-linux.net
Фреймворк Thousand Parsec
Оригинал: "Thousand Parsec", глава из книги "The Architecture of Open Source Applications"Авторы: Alan Laudicina and Aaron Mavrinac
Дата публикации: 2012 г.
Перевод: Н.Ромоданов
Дата перевода: март 2013 г.
Creative Commons
Перевод был сделан в соответствие с лицензией Creative Commons. С русским вариантом лицензии можно ознакомиться здесь.
21.3. Поддерживаемые функциональные возможности
21.3.1. Хранение данных на сервере
В играх Thousand Parsec точно также как и во многих играх пошаговых стратегий, есть возможность продолжать игру в течение достаточно долгого времени. Поскольку часто игра продолжается гораздо дольше суточных ритмов отдельных игроков, серверный процесс в течение этого длительного периода может быть по ряду причин досрочно остановлен. Чтобы разрешить игрокам возобновлять игру с того места, где они остановились, на серверах Thousand Parsec есть возможность сохранять состояние всей Вселенной (или даже нескольких Вселенных) в базе данных. Эти функциональные возможности также касаются сохранения состояния однопользовательских игр, которые более подробно будут рассмотрены далее в этом разделе.
На основном сервере tpserver-cpp
предоставляется абстрактный интерфейс хранения данных и модульная система плагинов для подключения различных баз данных в качестве хранилища. На момент написания данной главы сервер tpserver-cpp
поставлялся с модулями для MySQL и SQLite.
В абстрактном классе Persistence
описаны функции, позволяющие серверу сохранять, обновлять или осуществлять поиск различных элементов игры (так, как это описано в разделе «Анатомия звездной империи Star Empire»). Когда в различных местах кода сервера происходит изменение состояния игры, база данных обновляется, так что не важно, где произойдет остановка сервера или его сбой; когда сервер будет запущен снова с сохраненными данными, игра продолжится с того же самого запомненного места.
21.3.2. Язык компонентов Thousand Parsec Component Language
Существует язык описания компонентов Thousand Parsec Component Language (TPCL), который позволяет клиентским программам создавать модели локально без взаимодействия с сервером — в результате обеспечивается мгновенная обратная реакция на изменение свойств, внешнего вида и правильности создания моделей. Это позволяет игроку интерактивно создавать, например, новые классы космических кораблей, изменяя в соответствие с имеющейся технологией конструкцию корабля, двигатели, приборы, защиту, вооружение и многое другое.
Язык TPCL является подмножеством языка Scheme с небольшими изменениями, но достаточно близкими к стандарту языка Scheme R5RS, так что можно использовать любой совместимый интерпретатор. Язык Scheme первоначально был выбран из-за его простоты, из-за большого количества прецедентов его использования в качестве встроенного языка, наличия интерпретаторов, реализованных на многих других языках, и, самое главное, из-за того, что это проект с открытым кодом и огромным количеством документации как по его использованию, так и по разработке для него интерпретаторов.
Рассмотрим следующий пример функции Requirements
(Требования), написанной на языке TPCL и используемой компонентами и свойствами. Функция входит в набор правил, размещаемых на серверной стороне и передаваемых клиентской программе по игровому протоколу:
(lambda (design) (if (> (designType.MaxSize design) (designType.Size design)) (if (= (designType.num-hulls design) 1) (cons #t "") (cons #f "Ship can only have one hull") ) (cons #f "This many components can't fit into this Hull") ) )
Читатели, знакомые с языком Scheme, без сомнения, легко разберутся с этим кодом. В игре (в клиентской программе и на сервере) этот код используется для проверки свойств других компоненов (MaxSize
, Size
и Num-Hulls
) с тем, чтобы убедиться, что конкретный компонент может быть добавлен в модель. Сначала проверяется, что Size
(Размер) компонента не превышает максимального размера, допустимого в модели, затем проверяется, что в модели не используются какие-нибудь другие корпуса, что гарантирует, что в конструкции нет никаких других корпусов. В последней проверке нам сообщается, что эта функция Requirements
(Требования) зависит от выбираемого корпуса корабля.
21.3.3. Описание сражения - BattleXML
На войне имеет значение каждый бой, от короткой перестрелки в глубоком космосе между эскадрильями малых слабо вооруженных кораблей-разведчиков и до масштабного окончательного столкновения двух флагманских флотов в небе над столицей мира. Во фреймворке Thousand Parsec конкретные особенности боевых действий обрабатываются в соответствие с набором правил и функциональные возможности клиентской программы никак не могут повлиять на детали боевых действий - как правило, игрок будет проинформирован о начале и результатах боевых действий с помощью сообщений и произойдут соответствующие изменения объектов (например, удаление уничтоженных кораблей). Хотя игрок, обычно, сосредотачивает свое внимание на общем ходе боевых действий, которое происходит в соответствии с наборами правил сложной механики боя, он может иметь право (или, по крайней мере, удовольствие) изучить детали боя более подробно.
Все это поступает в виде данных BattleXML. Данные о боевых действиях состоят из двух основных частей: медийного описания, в котором указываются подробности используемого графического сопровождения, и определения сражения, в котором указывается то, что, на самом деле, произошло во время битвы. Все это предназначено для тех, кто наблюдает за сражением и может в настоящее время пользоваться двумя вариантами графики: 2D и 3D. Конечно, поскольку характер битв полностью определен в наборе правил, то код, обрабатывающий набор правил, обязан обрабатывать данные BattleXML.
Медийное описание связано с природой наблюдателя и хранится в каталоге или архиве, содержащем XML-данных и все графические файлы или файлы модели, на которые есть ссылки. В самих данных описывает то, как средства мультимедиа должны использоваться для каждого типа корабля (или другого объекта), их анимация для таких действий, как стрельба и уничтожение, а также мультимедийная информация и подробности их вооружения. Предполагается, что файлы размещаются рядом с самим файлом XML и ссылки на родительские каталоги не допускаются.
Определение сражения не зависит от наблюдателя и мультимедийных средств. Во-первых, в нем описывается ряд объектов, имеющихся с каждой стороны в начале битвы, и указываются уникальные идентификаторы и информация, такая как имя, описание и тип. Затем, описывается каждый раунд битвы: движение объектов, огонь вооружений (с указанием объекта, ведущего огонь, и цели), повреждения объектов, уничтожение объектов и журнальное сообщение. Насколько подробно описывается каждый раунд сражения, диктуется набором правил.
21.3.4. Метасервер
Поиск общедоступного сервера Thousand Parsec, на котором можно играть, очень похож на определение местоположения одинокого невидимого корабля-разведчика в глубоком космосе - пугающая перспектива, если не знать, где искать. К счастью, общедоступные сервера могут оставлять объявления о себе на метасервере, положение которого, как центрального узла, в идеале должно быть хорошо известно игрокам.
В текущей реализации есть метасервер metaserver-lite
, PHP скрипт, который размещается на некотором центральном месте, например, на сайте Thousand Parsec. Поддерживающие сервера отправляют запрос HTTP, сообщающий об обновлении действий и содержащий тип, местоположение (протокол, хост и порт), набор правил, количество игроков, количество объектов, имя администратора и другую дополнительную информацию. По истечению определенного времени (по умолчанию, через 10 минут) листинги на сервере становятся неактуальными, поэтому предполагается, что серверы периодически обновляют информацию на метасервере
Тогда скрипт может, когда происходит вызов без указания действия, выдать список серверов с подробной информацией, имеющейся на сайте, и со вставленными ссылками с адресами URL (как правило, с именем схемы tp://), по которым можно щелкнуть мышкой. Действие badge позволяет, в качестве альтернативного варианта, предоставлять листинги, имеющиеся на сервере, в компактном формате "бейджик".
Чтобы получить список имеющихся серверов, клиентские программы могут с помощью действия get сделать запрос к метасерверу. В этом случае, метасервер возвращает клиентской программе для каждого сервера, указанного в списке, один или несколько фреймов Game
(Игра). В tpclient-pywx
результирующий список выдается в браузере сервера в окне первоначального подключения.
21.3.5. Однопользовательский режим
Для того, чтобы была возможность поддерживать сетевые многопользовательские игры, фреймворк Thousand Parsec разрабатывался с нуля. Тем не менее, ничто не мешает игроку вести стрельбу на локальном сервере, подключать несколько интеллектуальных клиентских программ и совершать гиперпрыжки в собственной однопользовательской Вселенной, которую можно завоевать. В проекте определены некоторые стандартные метаданные и функции, обеспечивающие упорядочивание этого процесса, что упрощает установку до запуска визарда в графическом пользовательском интерфейс или до двукратного щелчка по файлу со сценарием.
Ядром таких функциональных возможностей является XML DTD, определяющий формат метаданных, относящихся к возможностям и свойствам каждого компонента (например, сервера, интеллектуальной клиентской программы, набора правил). Пакеты компонентов поставляются с одним или несколькими такими файлами XML, и, в конечном итоге, все эти метаданные объединяются в ассоциативный массив, состоящий из двух основных частей: серверов и интеллектуальных клиентских программ. В метаданных сервера, как правило, можно найти метаданные для одного или нескольких наборов правил - они помещаются сюда потому, что, набор правил может быть реализован для более чем одного сервера, некоторые детали конфигурирования могут отличаться, так что в целом для каждой реализации необходимы отдельные метаданные. В каждой записи каждого из этих компонентов содержится следующая информация:
- Данные с описанием, включающие в себя краткое имя (двоичное), длинное имя (описательное) и описание.
- Установленную версию компонента и более раннюю версию, сохраненные данных для которой совместимы с установленной версией.
- Параметры командной строки (если они используются) и любые другие параметры, передаваемые в компонент принудительно.
- Набор параметров, которые может указывать игрок.
Параметры, устанавливаемые принудительно, игроком не конфигурируются и это, как правило, те параметры, которые позволяют компонентам надлежащим образом функционировать в локальном однопользовательском контексте. Параметры, конфигурируемые игроком, имеют свой собственный формат, в котором указываются такие подробности, как название и описание, тип данных, значения, используемые по умолчанию, и диапазон значений, а также формат строки для добавления параметра в основную командную строку.
Хотя возможны специальные случаи (например, предустановленные игровые конфигурации для конкретных клиентских программ), типичный процесс построения одинопользовательской игры включает в себя выбор набора совместимых компонентов. Выбор клиентской программы происходит неявно, поскольку игрок уже запустил одну из них с тем, чтобы сыграть в игру; хорошо разработанная клиентская программа помогает пользователю настроить все остальное. Естественно, следующее, что нужно сделать, это выбрать набор правил, так что игроку предоставляется список - на данный момент, ему нет необходимости вникать в детали, связанные с сервером. В случае, если выбранный набор правил реализован на нескольких установленных серверах (возможная, но редкая ситуация), игроку предлагается выбрать один из них; в противном случае соответствующий сервер выбирается автоматически. Далее, игроку будет предложено сделать настройки параметров для набора правил и для сервера, для которых уже указанные приемлемые используемые по умолчанию значения, которые взяты из метаданных. И наконец, если установлены какие-либо совместимые интеллектуальные клиентские программы, то игроку будет предложено настроить одну из них или несколько с тем, чтобы против них играть.
После того, как игра сконфигурирована, клиентская программа, используя информацию о командной строке, полученную из метаданных, запускает локальный сервер с соответствующими параметрами конфигурации (в том числе набором правил, его параметрами и любыми параметрами, которые добавляются к конфигурации сервера). Как только будет получено подтверждение, что сервер работает и к нему есть доступ, может быть, аналогичным образом с помощью расширений административного протокола, который был описан выше, будет запущена каждая из указанных интеллектуальных клиентских программ и будет проверено, что все они успешно подключены к игре. Если все пойдет хорошо, то тогда клиентская программа подключится к серверу точно также, как если бы она подключалась к онлайн-игре, и игрок может начать исследовать, торговать, завоевывать и делать что-нибудь еще, что есть в универсуме других возможностей.
Альтернативой, причем очень важной, использования функциональных возможностей однопользовательского режима является сохранение и загрузка игр и, что более или менее эквивалентно, загрузка сценариев, готовых для игры. В этом случае в сохраняемых данных (возможно, хотя и не обязательно, в одном файле) запоминаются конфигурационные данные однопользовательской игры, а также данные о текущем состоянии самой игры. Если в системе игрока установлены все необходимые компоненты совместимых версий, то запуск сохраненной игры или сценария выполняется абсолютно автоматически. Для сценариев, в частности, таким образом реализована привлекательная возможность вступить в игру одним щелчком мыши. Хотя во фреймворке Thousand Parsec в настоящее время нет специального редактора сценариев или клиентской программы с режимом редактирования, в концепции фреймворка, кроме обычного использования набора правил, предусмотрены некоторые средства сохранения данных и проверки их непротиворечивости и совместимости.
До сих пор описание этой функциональной возможности было сравнительно абстрактным. Если рассматривать ее более конкретно, то в проекте Thousand Parsec только клиентская библиотека Python libtpclient-py
имеет в настоящее время полную реализацию однопользовательского механизма. В библиотеке предоставлен класс SinglePlayerGame
, который при создании собственных экземпляров автоматически агрегирует все метаданные однопользовательского режима, доступные в системе (естественно, есть определенные рекомендации, касающиеся того, какие файлы XML должны быть установлены на данной платформе). После этого к объекту можно будет обращаться для получения информации по имеющимся компонентам: серверам, наборам правил, интеллектуальным клиентским программам и параметрам, которые хранятся в виде словарей (ассоциативные массивы языка Python). После того, как будет завершен общий процесс создания игры, описанный выше, типичная клиентская программа сможет выполнять следующие действия:
- С помощью
SinglePlayerGame.rulesets
сделать запрос списка доступных наборов правил и с помощьюSinglePlayerGame.rname
настроить объект в соответствие с выбранным набором правил. - С помощью
SinglePlayerGame.list_servers_with_ruleset
сделать запрос списка серверов, на которых реализован данный набор правил, если необходимо, предложить пользователю выбрать один из них и (или только) сконфигурировать набор правил с помощью методаSinglePlayerGame.sname
. - С помощью
SinglePlayerGame.list_rparams
иSinglePlayerGame.list_sparams
получить соответственно набор параметров для сервера и набора правил, и предложить игроку их сконфигурировать. - С помощью
SinglePlayerGame.list_aiclients_with_ruleset
найти имеющиеся интеллектуальные клиентские программы и предложить игроку сконфигурировать одну или нескольких из них с помощью параметров, которые можно получить черезSinglePlayerGame.list_aiparams
. - Запустить игру, вызвав метод
SinglePlayerGame.start
, который в случае, если подключение будет успешным, вернет порт TCP/IP. - В конце концов, вызвав метод
SinglePlayerGame.stop
, закончить игру (и уничтожить все процессы, запущенные сервером и интеллектуальными клиентскими программами).
Флагманская клиентская программа проекта Thousand Parsec, tpclient-pywx
, представляет собой дружественный пользователю визард, который, следуя такой процедуре, сначала предлагает вызвать для загрузки сохраненную игру или файл сценария. Дружественная для пользователя последовательность действий, разработанная для этого визарда, является примером хорошей разработки, созданной на основе процесса разработки программ с открытым исходным кодом, используемого в проекте: разработчик первоначально предложил совсем другой процесс, больше напоминающий работу автомобиля, когда все спрятано под капотом, но обсуждения в сообществе и некоторые совместные разработки привели к результату, более удобному для игрока.
И, в заключение, сохранение игр и сценариев в настоящее время реализовано на практике в сервере tpserver-cpp
, причем с поддержкой функциональных возможностей libtpclient-py
и интерфейса tpclient-pywx
. Это достигнуто с помощью модуля, сохраняющего данные и использующего SQLite — общедоступную СУБД с открытым исходным кодом, для которой не нужен внешний процесс, а все базы данных хранятся в одном файле. Сервер с помощью специально заданных параметров конфигурируется так, чтобы использовался модуль, сохраняющий данные в SQLite, если таковой имеется, и, как и обычно, на протяжении всей игры постоянно обновлялся файл базы данных (размещаемый в месте хранения временных файлов). Когда игрок решит сохранить состояние игры, файл базы данных копируется в заданное место, и к нему добавляется специальная таблица, в которой хранятся конфигурационные данные отдельного игрока. Читателю должно быть понять, как их впоследствии загружать.
Далее: 21.4. Усвоенные уроки