Автор: | StrVL [ 31.08.2010, 19:27 ] |
Тема блога: | Мультиплеер в trainz. Принцип. Технология. Протокол |
Несколько дней назад я обещал опубликовать мультиплеерный протокол для trainz. Вот, момент настал, решил назначить релиз на сегодня. Однако ж прежде следует ввести в курс дела тех, кто не читал. Как такового мультиплеера в trainz никогда не было и вряд ли он появится в ближайшее время. Тем не менее, как оказалось, реализовать его своими руками вполне возможно и даже несложно (правда, как говорил Эйнштейн, все в мире относительно ![]() Немного общих слов. Этот вопрос был решен написанием внешнего приложения, внедряющегося в адресное пространство процесса trainz.exe и обменивающимся данными со скриптом через участок его памяти. Намеренно не стану сообщать, на каком языке она написана, так как тут же появятся [strike]проповедники языка истинного[/strike] «вумные» [strike]сионисты и пасквилянты[/strike] программисты ![]() Кстати говоря, следует сказать пару слов по поводу следующего высказывания
Не спорю, ковыряться в чужом адресном пространстве не безопасно, но за время тестирования (более 50 запусков) не произошло ни одного "вылета" (до тех пор, пока не занялся голосовым чатом и не начал развешивать глобальный хук на клавиатуру ![]() Обмен данными между экземплярами программ-клиентов по сети осуществляется через DirectPlay. Что до вумных программистов, считающих интерфейсами зла все кроме сокетов, [strike]то пусть продолжают заниматься с winsock многопоточным онанизмом[/strike] желаю им приятного времяпровождения при конструировании давно изобретенных велосипедов. Так как на моих глючных F{пи-и-ик}YouBill’ьных окошках DirectX версии 8 и более новые частично не работает (зато отлично работает то, что криво работало в «чистой» системе ![]() На настоящий момент реализован также голосовой чат. Чтобы можно было устанавливать соединения с кем-либо, нужно назначить во вкладке «голосовой чат» соответствующую горячую клавишу. Управление чатом следующее: нажимаем на клавишу «Alt», затем не отпуская его жмем и отпускаем назначенную данному игроку клавишу (Alt не отпускаем) – всё, «исходящий канал открыт». Для закрытия канала нужно всего-навсего отпустить «Alt». Голосовые сообщения отправляются по частям (1 часть – 0,2 мсек) также в сжатом виде. Для сжатия используется кодек speex (ох, и запыхался же с ним! ![]() ![]() Пожалуй, довольно лелеять функционал программы-клиента. Тем более, что сама программа ещё до конца не отлажена, и то ли из-за несовместимости глобальных клавиатурных хуков с отладчиком, то ли из-за неправильного использования CopyMemory (никак не получается сыскать источник-рассадник багов), все вместе с отладчиком систематически накрывается медным тазом, сопровождаясь победоносным восклицанием Билла: «память не может быть “read”!». (знаете, порой так хочется открыть kernel32.exe в каком-нибудь PELord’е, откуда родом эта CopyMemory, и в константе подменить «read» на какое-нибудь непристойное выражение… ![]() Протокол Полагаю, что всех уже утомил своими пустыми разглагольствованиями, поэтому этот раздел постараюсь написать коротко и ясно. За передачу данных в скрипте от игры до игры отвечает предназначенный для этой цели статический класс Modem. Данные передаются вот такими пакетами:
Пара слов о типе. Много разных дополнений могут одновременно посылать и получать пакеты. Чтобы различные объекты не путали пакеты, и таким образом не воцарился хаос, каждому пакету назначен тип, определяющий, какой объект его породил. При отправке пакета в поле type нужно ОБЯЗАТЕЛЬНО занести kuid вашего объекта. Можно, конечно, указать чужой kuid, но во избежание путаницы этого лучше не делать. В поле DestPlayerNum может принимать не только значения номеров игроков получателя, но и дополнительные 2 специальных значения: -1 – отправка служебного сообщения программе (для отправки специальных сообщений, например, управляющих голосовым чатом) -2 – отправка широковещательного сообщения (сообщения, отправляемого всем игрокам одновременно) Поле SrcPlayerNum можно не указывать: оно будет назначено при отправке сообщения. Так как фрагментация пакетов не поддерживается, длина пакета ограничена размером выходного буфера, и вам следует следить, чтобы она не была превышена (иначе пакет не будет помещен в очередь). Для этого у класса Modem есть метод public bool VeryBigPacket(Packet p), возвращающий истину, если пакет не умещается в буфере отправки. У класса Modem есть много различных public-методов, но в вашем распоряжении есть только следующие (остальные, что называется «системные»):
В поле TypeOfIncomingPackets ОБЯЗАТЕЛЬНО должен быть установлен куид пакетов, которые обрабатываются этим обработчиком. Никакие другие пакеты в данный обработчик поступать не будут (в принципе можно указать любой куид, не обязательно, чтобы он совпадал с куидом вашего объекта). При поступлении каждого сообщения с указанным куидом вызвается метод RecievePacket (блин, только сейчас узнал, что правильно пишется Receive ![]() Естественно, просто так обработчик сообщений исполнять своих функций не будет. Чтобы он начал обрабатывать сообщения, ссылку на него нужно передать модему, для чего у последнего есть процедура AddHandler. Она помещает ссылку на обработчик в особую иерархическую структуру – список обработчиков, после чего пакеты с соответствующим куидом начнут обрабатываться. На один и тот же куид может быть назначено несколько обработчиков. Можно и удалить обработчик из списка с помощью метода RemoveHandler. Существует статический класс LocalPlayerOwnership для описания объектов, доступ к которым локальному игроку открыт. Можно использовать его методы, чтобы определить, управляет ли некоторым объектом локальный игрок:
|
Автор: | TRam_ [ 31.08.2010, 21:33 ] |
>Размер участка памяти строго фиксирован (пока 10 Кбайт) и в процессе игры не изменяется (впоследствии размер будет задаваться в правилах сессии). о, я такое делал ![]() |
Автор: | UTUBE [ 01.09.2010, 00:22 ] |
Всем добрый вечер.Ну а как этим всем пользоваться,или ещё рано пока? Спасибо. |
Автор: | kemal [ 01.09.2010, 20:31 ] |
А обязательно писать Modem.AddHandler(me); ? Или можно сделать так, чтобы приёмом сообщений занимался отдельный класс? |
Автор: | AlexanderG [ 01.09.2010, 21:11 ] |
>В поле DestPlayerNum может принимать не только значения номеров игроков получателя, но и дополнительные 2 специальных значения: -1 – отправка служебного сообщения программе (для отправки специальных сообщений, например, управляющих голосовым чатом) -2 – отправка широковещательного сообщения (сообщения, отправляемого всем игрокам одновременно)< Лучше не использовать -1, это общепринятый аналог отсутствующего/ошибочного значения. |
Автор: | StrVL [ 01.09.2010, 21:24 ] |
В смысле, нужно ли делать обработчиком класс, представляющий реальный игровой объект (т. е. наследуемый от какого-нибудь Buildable/BaseIndustry и прочих и указываемый в конфиге)? Нет, не обязательно, можно написать отдельный класс-обработчик, но AddHandler вызывать все же необходимо:
Не вижу смысла использовать номер игрока в качестве признака ошибочного сообщения. Да и вообще я как бы не выделял "ошибочные" сообщения (а пустые тем более). В случае ошибки (неверный номер игрока, превышение лимита по размеру сообщения, неверный тип) сообщение просто не будет помещено в очередь отправки или отброшено в процессе доставки, не доходя до получателя. |
Автор: | agmike [ 06.09.2010, 16:20 ] |
Ох вау. Хочется только спросить, есть/планируется ли возможность передачи данных из ТРС в другие программы и наоборот, самое очевидное применение этому — выносной контроллер. Хотя я вряд ли стал бы это делать, меня больше волнует возможность добавить в игру новые клавиши с клавиатуры, желательно все. |
Автор: | StrVL [ 06.09.2010, 19:19 ] |
Возможность подключения «плагинов» запланирована, но в первой версии однозначно её не будет. Что касается клавиш, то уже сейчас в программе используется отслеживание нажатия всех клавиш клавиатуры (нужно для голосового чата). Могу немного дописать код так, чтобы соответствующие события дополнительно посылались в скрипт trainz’, и в последнем рассылалось широковещательное сообщение (PostMessage) о нажатии клавиши. Кстати, есть небольшая вероятность, что небольшая модификация кода позволит также «блокировать» нажатия заданных клавиш, чтобы оное не возымело какого-нибудь стандартного действия в trainz. Небольшая потому, что скорее всего trainz работает с клавиатурой напрямую средствами DirectInput (или аналога). В этом случае, как говорится, «номер не пройдет». Однако нужно ли это? Просто мне кажется, что большинство отнесется крайне скептически (либо лениво) к локомотиву, для работы которого потребуется где-то дополнительно скачивать и, самое главное, каждый раз запускать «какую-то» прогу. Пара слов о стадии разработки мультиплеера (некоторых, наверное, интересует ![]() |
Автор: | agmike [ 06.09.2010, 22:57 ] |
За клавиши голосую, броадкастом. Блокировать ничего не надо, кейбоард.тхт легко редактируется и реально в нем используется пара десятков строк максимум. |
Автор: | AlexanderG [ 08.09.2010, 11:12 ] |
Насчет плагинов, у меня, когда я этим вопросом занимался, была мысль использовать сокеты — общаться с плагинами через лупбек-интерфейс. Это просто программируется и обеспечивает всю необходимую функциональность. |
Автор: | AlexanderG [ 11.09.2010, 00:42 ] |
Ну что, все сдохло? |
Часовой пояс: UTC + 4 часа | |
Powered by phpBB © 2002, 2006 phpBB Group www.phpbb.com |
Blogs powered by User Blog Mod © EXreaction www.lithiumstudios.org |