Сигнал посылаем: «Вы что это там?» —
А нас посылают... обратно.
(В.Высоцкий, «Тау-Кита»)
В последнее время рубрика «Игра своими руками» часто отсутствовала. Дело в том, что было принято решение переделать и усилить трехмерную версию движка ЛКИ-Creator. Вскоре он все же появится в нашем журнале, работы близятся к завершению. А пока мы решили ответить на один из самых частых запросов: рассказать, как приделать к игре сетевой режим.
Новые компоненты
Как обычно, первое, что нам нужно сделать — это переустановить пакет ЛКИ-Creator. Как это делается, мы рассказывали не раз — обратитесь к первым двум занятиям с ЛКИ-Сreator. Текст этих статей есть на нашем компакт-диске.
Если вы все сделали правильно, у вас появилось в панели LKI несколько новых компонентов. Нас пока интересуют два: TLKIHost и TLKIClient. Первый понадобится в «серверной» части программы, второй — в «клиентской». Оба они никакого визуального эффекта не дают, поэтому, куда на форме их ставить — совершенно неважно.
У обоих есть общее свойство Port, определяющее для сервера, какой порт занимать, а для клиента — по какому порту подключаться. У хоста есть также свойство Active, определяющее, включен ли он в настоящий момент.
Замечание: не стоит выставлять его в true с самого начала. Лучше сделайте это после того, как все данные программы будут инициализированы. Иначе возможны ошибки и накладки, связанные с тем, что, предоставленный сам себе, Host немедленно начинает работу.
У клиента, чтобы соединиться, запускается процедура Connect. Предварительно надо установить значение IPAddress в соответствии с адресом вашей хост-программы (можете посмотреть ее в одноименном свойстве хоста).
Программа MUD
Теперь надо бы опробовать на практике эти компоненты. Для этой цели попробуем сделать небольшую программу текстовой онлайновой игры — MUD, точнее, две программы — сервер и клиент. Не будем пока осложнять задачу графикой, благо от ее использования в работе сетевых компонентов абсолютно ничего не меняется.
Замечание: если обозначение MUD вам ничего не говорит — загляните в статью «Куда катятся виртуальные миры». Она была у нас в февральском номере, а сейчас вы можете найти ее в разделе «Руководства» компакт-диска.
Итак, создаем два проекта: один будет MUD-клиентом, другой — серверной программой. Примеры эти лежат в каталоге «Клиент и сервер MUD» раздела «Игра своими руками» нашего диска, распакуйте их, как обычно.
Замечание: вовсе не всегда нужно ставить эти компоненты в разные программы. Например, большинство современных стратегий отлично совмещает их, и одна и та же программа по необходимости может служить тем и другим. Когда вы собираетесь поиграть по локальной сетке, скажем, в Heroes of Might & Magic, происходит именно это.
На клиентской форме стоят: TLKIClient, текстовое окно, в котором идут сообщения от сервера, и строка ввода. На серверной — TLKIHost, кнопочка активации (она вызывает включение хоста) и окошко, в которое пишутся поступающие от клиента сообщения (это совершенно необязательно, просто для удобства).
Ведение журналов |
Чтобы следить за происходящим в нашей сети, есть несколько средств. Первое использовано в примере. У TLKIHost и TLKIClient есть свойство LogMemo, по умолчанию оно равно nil. Но если присоединить к нему компонент типа TMemo, в его окошке будет отображаться журнал (log) поступивших / отправленных сообщений, а также список ошибок и так далее. Рекомендуется поначалу пользоваться им активно. Оба компонента обладают также свойством LogOn. Если оно выставлено в true, то в файле net.log ведется тот же самый журнал. Только не забудьте снять с файлов защиту от записи, а то аналогичные функции ЛКИ-Creator’а вызывали у невнимательных пользователей регулярные ошибки... Поначалу стоит отображать в журналах все поступившие сообщения. Но если вы сделаете действительно серьезную игру, особенно — в реальном времени, поток сообщений «заспамит» ваши журналы. Поэтому стоит ввести фильтрацию. Сделать это можно в том случае, если у вас подключен дешифровщик сообщений — компонент TLKIMessageParser. В нем вам надо установить в true параметр LogFiltered, а затем у каждой команды, которую вы хотите видеть в журнале, пометить свойство Log. Ошибки и сбои будут регистрироваться в журнале даже при включенной фильтрации. Это важно: большинство ошибок в сетевом обмене, вроде неверного соединения, неправильной команды и так далее, не вызывает ни падения программы, ни даже окна сообщения. Дело в том, что сбои в сетевом протоколе — дело нормальное, и зачастую программа сама справляется с автокоррекцией пакетов. Эти ошибки пишутся исключительно в «журналы» — в окно и в файл, если они включены. Поэтому крайне не рекомендуется отключать запись log-файла, пока программа не будет отлажена полностью; более того, очень многие даже коммерческие продукты не отключают ее и после этого, чтобы в случае чего информация об ошибке сохранилась. Конечно, в этом случае надо оставлять запись в log-файл только ошибок. |
Уже описанного вполне достаточно, чтобы хост и клиент могли соединиться (это можно сделать с одной машины) и «поздороваться» между собой.
(У обоих компонентов есть свойство Greeting, определяющее, какая строка будет отправляться в качестве приветствия.)
Список соединившихся клиентов хранится у хоста в параметре ClientList. Его элементы имеют тип TLKIConnection.
Расшифровка команд
Теперь хорошо бы наладить какое-нибудь осмысленное общение между программами. Для этого существует еще один новый компонент — TLKIMessageParser.
Его можно ставить как в хост, так и в клиент, в обоих случаях он будет разбирать пришедшие команды и запускать процедуры их обработки; в случае же неверной команды он вызовет сообщение об ошибке, но выгружать программу из памяти не будет. Ошибка поступит только в окно и файл журнала (если они вообще ведутся).
Установив TLKIMessageParser на форму, следует проставить этот компонент в свойстве Parser у соответственно TLKIHost или TLKLIClient; у самого же компонента надо указать компонент хоста или клиента в параметре Connection.
Этот компонент — по сути своей список команд (TLKIInstruction). Каждая команда обладает такими свойствами:
Key. Это — ключевое слово, определяющее команду, так сказать оператор, посылаемый от одной программы к другой. Вы сами придумываете внутренний язык для вашего хоста и клиента. Рекомендуется для скорости работы делать команды короткими — 3-4 символа, не более. Например, пусть KILL означает «атаковать», W — «идти на запад», N — «идти на север»... Соmmand. Это свойство определяет процедуру, которую следует запустить при получении команды. Процедуру вы можете описать сами, но у нее обязательно должны быть такие параметры:(Parser : TLKIMessageParser; Param: string);
Log. Определяет, записывать ли команду в журнал (смотрите «Ведение журналов»).Замечание: порядок команд в списке важен. Самые употребительные рекомендуется ставить повыше, тогда программа будет работать заметно быстрее. Сравнение строк — не самая скоростная операция, и чем меньше их понадобится — тем лучше.
Обработчик команды — параметр Command — можно описывать, в принципе, как угодно. Param — это список параметров в текстовом виде, через запятую. Для удобства в ЛКИ-Сreator’е определены такие функции (параметр у всех этих функций — строка):
MoreParam : boolean выдает true, если в строке есть еще параметры (то есть, в ней есть хоть какие-то значащие символы); GetParamWord : string берет следующий параметр как строку и удаляет его из списка параметров; GetParamInt : integer берет следующий параметр, как целое число, и удаляет его из списка параметров; если это оказалось не целое число — выдается ошибка. GetParamReal : double, GetParamBool : boolean работают так же, но с другими типами данных.Посылка сообщений
Тут вообще нет никаких хитростей. Простейшая команда TLKIConnection.Send(Msg: string) отправляет текстовое сообщение. Для добавления параметров пользуйтесь процедурой AddParam(var ParamList : string; Param); Param при этом может быть строкой, целым числом, вещественным числом или логической переменной.
В случае вещественного числа есть еще один, необязательный, параметр: сколько значащих цифр нужно передавать. Экономьте на нем: обычно 3-4 цифр для большинства задач достаточно. Если установить его, скажем, в 10, ваш сетевой обмен возрастет в разы (обычно в играх вещественными числами задаются координаты, а значит, их в обмене оказывается больше всего).
Собственно говоря, это все, что необходимо для создания сетевого режима вашей игры. Остальное — «соль и перец по вкусу».
Кодирование
Пока что наши команды отправлялись открытым текстом. Для первого примера это вполне нормально, но может возникнуть законное желание зашифровать обмен. Для этого в ЛКИ-Creator есть несложная процедура (хотя в серьезном коммерческом продукте ее, конечно, придется усовершенствовать).
Установите вашим хосту и клиенту в true параметр Coding и укажите код в параметре CodeString : string. Ставьте его подлиннее, и лучше пусть это будет абракадабра. Только позаботьтесь, чтобы она была одинаковой у клиента и хоста...
Это важно: как бы хорошо вы ни зашифровали ваш обмен, в серьезном проекте вам обязательно нужно будет ввести проверку команды на легальность. То есть — выяснить, мог ли в принципе пользователь прислать такое сообщение. Потому что рано или поздно раскалывают любой код...
Aelfrica MUD
Не так давно нами проводился конкурс по деланию игр; один из победителей, Евгений Кашин, представил нам проект MUD-игры по средневековой Европе. Сейчас проект продолжает развиваться, и через некоторое время будет выставлен на нашем сайте. Те примеры, которые приведены на диске к этой статье — фрагменты кода Евгения, приведенные к ЛКИ-стандарту.
Вскоре предполагается в рамках «Клуба интеллектуальных компьютерных игр» организовать набор миростроителей, world builders, для этой игры: такие проекты обычно делаются большой командой. Сам мир представляет собой сказочно-мифологическую Европу, примерно такую, как она описана в повести «Три сердца и три льва» Пола Андерсона. Это — фэнтези-мир, но в нем есть множество знакомых стран, народов и городов. Подробнее об этом мире можно узнать по адресу www.aelfrica.ru.
Евгений сделал в процессе подготовки проекта немало полезной работы, которая может пригодиться и нашим читателям. Хотя она нужна далеко не для всякой игры, мы решили включить ее в пакет ЛКИ-Creator. Установка дополнительного модуля LKIEdit.pas приведет к появлению у вас на панели LKI компонентов TLKIFlagEdit, позволяющем очень удобно редактировать длинный список свойств с текстовым описанием, считанным из файла, и TLKINameEdit — уникальным компонентом специально для написания игр по-русски.
TLKINameEdit
Этот компонент позволяет редактировать слово и все его падежи. Поскольку в русском языке слова склоняются, делать, скажем, диалоги с произвольным именем персонажа заметно сложнее, чем в английском. TLKINameEdit предложит пользователю выбрать разумный способ склонять имя по всем падежам (кнопка «По умолчанию») — и даст возможность исправить список, если что-то не так. Для MUD такая функция неоценима, но ее могут использовать и во многих других играх, где используются текстовые сообщения.
TLKIFlagEdit
А TLKIFlagEdit не раз пригодится при написании, например, редактора свойств монстров в ролевой игре или стратегии. В достаточно сложных играх список свойств очень длинен, и редактировать их средствами Delphi долго и неудобно.
Этот компонент нужен тогда, когда свойства занимают один бит — либо оно есть, либо нет — а список довольно велик. Хранятся такие свойства в TLKISet — обычном множестве байтовых чисел. При желании можно сократить диапазон множества, подредактировав тип TLKISet в модуле LKIString.pas.
Обратите внимание на параметр ScriptName — в нем содержится имя файла, который описывает список свойств. Каждому свойству соответствует число — это код, в который преобразуется флаг с таким названием. Например, строка: «7 Плавает» означает, что параметр «плавать» будет храниться в TLKISet как семерка. И код «if 7 in FlagEdit.Flags» означает: «если монстр плавает, то...».