RailUnion.net | |
http://railunion.net/blog/index/index_u-2874_b-90_r-567_sid-25ca04d60b9ed5c9a5dba1ac19d9c19d.html |
Автор: | StrVL [ 31.08.2010, 19:27 ] |
Тема блога: | Мультиплеер в trainz. Принцип. Технология. Протокол |
Несколько дней назад я обещал опубликовать мультиплеерный протокол для trainz. Вот, момент настал, решил назначить релиз на сегодня. Однако ж прежде следует ввести в курс дела тех, кто не читал. Как такового мультиплеера в trainz никогда не было и вряд ли он появится в ближайшее время. Тем не менее, как оказалось, реализовать его своими руками вполне возможно и даже несложно (правда, как говорил Эйнштейн, все в мире относительно ). Возможности траинзэтовсского скриптового движка позволяют так или иначе контролировать весь игровой процесс. То, что они не позволяют реализовать прямо, часто поддается реализации путем различных форм извращений над скриптами. Единственная не решаемая скриптом проблема состояла в том, чтобы каким-либо образом передать данные во «внешний мир». Немного общих слов. Этот вопрос был решен написанием внешнего приложения, внедряющегося в адресное пространство процесса trainz.exe и обменивающимся данными со скриптом через участок его памяти. Намеренно не стану сообщать, на каком языке она написана, так как тут же появятся [strike]проповедники языка истинного[/strike] «вумные» [strike]сионисты и пасквилянты[/strike] программисты , которым никак [strike]неймется[/strike] не удается [strike]не облить грязью всех вокруг и[/strike] свой [strike]фанатизм[/strike] богатый запас знаний [strike]засунуть в одно место[/strike] донести до [strike]«быдлокодеров»[/strike] невежественной толпы. Не буду вдаваться и в подробности реализации, скажу лишь самое основное. Участок памяти представляет собой строковый массив, единожды размещаемый скриптом в памяти. При инициализации в этот массив помещается особая сигнатура (к слову, всегда одна и та же), которую впоследствии и отыскивает в памяти процесса программа среди океана байт. Считывание-запись данных осуществляется по очереди то скриптом, то программой (определяется состоянием первого байта массива). Размер участка памяти строго фиксирован (пока 10 Кбайт) и в процессе игры не изменяется (впоследствии размер будет задаваться в правилах сессии). Кстати говоря, следует сказать пару слов по поводу следующего высказывания
Не спорю, ковыряться в чужом адресном пространстве не безопасно, но за время тестирования (более 50 запусков) не произошло ни одного "вылета" (до тех пор, пока не занялся голосовым чатом и не начал развешивать глобальный хук на клавиатуру ). Обмен данными между экземплярами программ-клиентов по сети осуществляется через DirectPlay. Что до вумных программистов, считающих интерфейсами зла все кроме сокетов, [strike]то пусть продолжают заниматься с winsock многопоточным онанизмом[/strike] желаю им приятного времяпровождения при конструировании давно изобретенных велосипедов. Так как на моих глючных F{пи-и-ик}YouBill’ьных окошках DirectX версии 8 и более новые частично не работает (зато отлично работает то, что криво работало в «чистой» системе ), пришлось довольствоваться DP из состава DirectX 7. Данные по сети передаются в сжатом с помощью библиотеки zlib виде. На настоящий момент реализован также голосовой чат. Чтобы можно было устанавливать соединения с кем-либо, нужно назначить во вкладке «голосовой чат» соответствующую горячую клавишу. Управление чатом следующее: нажимаем на клавишу «Alt», затем не отпуская его жмем и отпускаем назначенную данному игроку клавишу (Alt не отпускаем) – всё, «исходящий канал открыт». Для закрытия канала нужно всего-навсего отпустить «Alt». Голосовые сообщения отправляются по частям (1 часть – 0,2 мсек) также в сжатом виде. Для сжатия используется кодек speex (ох, и запыхался же с ним! ). Битрейт, однако же, получается просто неблагопристойно большой – 15-20 Кбайт/сек на сообщение (несмотря на то, что кодеку назначено сжимать до 15 Кбит/сек). Поддерживается только одно одновременное исходящее соединение (при этом оно может быть широковещательным и транслироваться сразу всем) и теоретически неограниченное количество входящих. Пожалуй, довольно лелеять функционал программы-клиента. Тем более, что сама программа ещё до конца не отлажена, и то ли из-за несовместимости глобальных клавиатурных хуков с отладчиком, то ли из-за неправильного использования 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 ). Метод ConnectionOptionsChanged вызывается при каких-либо изменениях состояния соединения (установлено/разорвано соединение, подключился новый/отключился существующий игрок, …). Естественно, просто так обработчик сообщений исполнять своих функций не будет. Чтобы он начал обрабатывать сообщения, ссылку на него нужно передать модему, для чего у последнего есть процедура AddHandler. Она помещает ссылку на обработчик в особую иерархическую структуру – список обработчиков, после чего пакеты с соответствующим куидом начнут обрабатываться. На один и тот же куид может быть назначено несколько обработчиков. Можно и удалить обработчик из списка с помощью метода RemoveHandler. Существует статический класс LocalPlayerOwnership для описания объектов, доступ к которым локальному игроку открыт. Можно использовать его методы, чтобы определить, управляет ли некоторым объектом локальный игрок:
|
Автор: | StrVL [ 11.09.2010, 15:13 ] |
Напротив, скоро будет готов первый «Release Candidat» (правда, стоит ли напоминать о действии давнишнего программистского обычая неумышленно выпускать глючный релиз в качестве первого…). Вчера ночью успешно завершил работу над сцеплением-расцеплением поездов, добавил возможность задавать на голосовой чат любые комбинации клавиш (специально для мазохистов Alt + F4 ) (не стал делать Num/Caps/Scroll Lock, Win, Power-Sleep). Предстоит ночь серьезного тестирования, и если оно пройдет успешно (в чем, у меня, честно говоря, сомнения), то останется написать только краткую справку. Скоро надо будет тестеров набирать...
Сделано. При нажатии клавиши (системное событие KeyDown) рассылается широковещательное сообщение (PostMessage), major которого равен NotifyKeyPressed, а minor - виртуальый код клавиши.
Если честно, о технологии всерьез пока не размышлял. Вскользь мелькала идея применить старый прадедовский механизм динамических библиотек (метод прост до крайности и известен почти всем, кому понадобится написать плагин, так зачем прибегать к чему-либо ещё?). Впрочем, всему свое время. |
Автор: | agmike [ 11.09.2010, 16:06 ] |
А в случае, когда нажато несколько клавиш одновременно, мессежды о них будут высланы последовательно? |
Автор: | StrVL [ 11.09.2010, 19:19 ] |
Прочитав сообщение, решил немного модифицировать механизм. Теперь рассылается два сообщения отдельно: NotifyKeyUp и NotifyKeyDown (нажатие и отпуск клавиши соответственно). Это позволит «отлавливать» комбинацию из нескольких одновременно нажатых клавиш. Да, сами сообщения будут высланы последовательно в том порядке, в котором клавиши были нажаты/отпущены. |
Автор: | agmike [ 11.09.2010, 23:28 ] |
Отлично, спасибо. |
Автор: | agmike [ 29.09.2010, 21:33 ] |
StrVL, скажи что-нибудь |
Автор: | StrVL [ 01.10.2010, 16:54 ] |
Давно (относительно) хотел сообщить, да не решался, что пока разработка мультиплеера приостановлена в связи с чрезвычайной (нет, не то [ ], что вы могли подумать ) необходимостью защиты отчета по производственной практике и кое-чего ещё (скорее всего, на грядущей неделе покончу со всем этим и разморожу работы). Хотя до релиза, честно говоря, оставалась пара полных дней кропотливой работы. Однако пока не до этого. |
Автор: | agmike [ 03.10.2010, 20:09 ] |
Ждемс. |
Автор: | StrVL [ 19.10.2010, 15:45 ] |
Напряженка на учебе только крепчает. Дневник практики заставили переписывать, «кое-что» хоть и проставили «авансом», тем не менее, надо досдавать. Ещё нужно прекратить кормить обещаниями преподавателя (вернее, двух – работаем на два фронта ) и написать-таки давно обещанную публикацию (две). В общем, все настолько печально, что порой хочется … Тем не менее, как говорилось, объем работы не так уж велик, поэтому решено целиком уделить мультиплееру грядущие ноябрьские праздники. |
Автор: | AlexanderG [ 20.10.2010, 00:00 ] |
Ждем с нетерпением! |
Часовой пояс: UTC + 4 часа | |
Powered by phpBB © 2002, 2006 phpBB Group www.phpbb.com |
Blogs powered by User Blog Mod © EXreaction www.lithiumstudios.org |