Поскольку никаких наработок «нету», решил попробовать заняться идеей сей. Вот что удалось состряпать за пять дней
:
Мультиплеер реализован в виде отдельного правила для сессии. Перед игрой нужно открыть нужную карту в trainz и, добавив мультиплеерное правило, экспортировать в программу список поездов и стрелок. После чего назначить игроков и утвердить, кто чем может управлять. Списки эти сохраняются в файле bin, он понадобится хосту при создании сессии.
Собственно, на настоящий момент доступны только две вещи: переключать стрелки и «водить» поезда (сцепление-расцепление, автомашинист ещё не реализовано (с автомашинистом вообще проблема, так как если задать заблокированному поезду какую-нибудь команду (load например), то блокировка сбросится)).
Каждой стрелке можно назначить игрока, или несколько игроков, которыми она может управляться (у всех остальных она будет по умолчанию «AllowManualControl(false)»). Можно сделать стрелку, как тут выражались, «нецентрализованной», тогда все будут иметь возможность её переключать.
Каждый поезд может быть назначен только одному игроку (или никому). Синхронное движение поездов осуществляется методом периодического слежения за расстоянием от поезда до ближайшего объекта (посредством GSTrackSearch). У каждого игрока, не управляющего данным поездом, скриптом моделируется зависимость этого расстояния от времени с помощью интерполяционного полинома Лагранжа, при этом учитывается расстояние на данном компьютере (иначе со временем погрешность накопится и будет полный рассинхрон), после чего вызывается SetVelocity. Вообще, конечно, предпочтительней был бы метод кратных узлов, но как оказалось, на Auran Script весьма затруднительно решать систему линейных уравнений (во всяком случае, по моим обожаемым формулам Крамера) из-за проблем с точностью float
. Хотя очень-очень хотелось бы реализовать этот метод. А тут, как видно, поезда «дергаются». В общем, над этим ещё надо поработать.
Что касается программы, то метод взаимодействия с trainz, надо полагать, не отличается оригинальностью… Сеть же реализована посредством DirectPlay (сокеты были посланы по недопустимому адресу, так как чтобы они обеспечивали необходимый функционал, с ними нужно эшелон велосипедов доизобретать, а мне лень…
), причем доисторической версии – из DirectX 7.0. Поздние версии DirectX у меня почему-то отказываются создавать интерфейс на DirectPlay
. Да, в общем-то, это, я думаю, не принципиально. Обмен данными с trainz происходит с периодом в полсекунды (период можно и поменять, но этого пока хватает и я с ним ещё не экспериментировал). Кстати, данные по сети передаются в сжатом посредством библиотеки zlib виде (порой мощно пакеты сжимаются, иной раз становятся на 60% короче
). У меня, на моей тормозной беспроводке, которую вечно «душит» лифтёрное отделение за стеной, пакеты от игры до игры доходят за ~ 1-1,5 сек.
В игре же, за передачу данных отвечает статический класс Modem. Для передачи данных достаточно скопировать и подключить модуль с этим классом. Данные представлены структурой Packet, содержащей два поля данных major и minor. Указывается также номер игрока-получателя сообщения (номер минус 2 – широковещательная рассылка пакета). Кроме этого, каждый пакет имеет «тип», заданный куидом. Тип нужен для того, чтобы определять, какой класс скрипт какие пакеты должен получать. Указываешь в типе куид своего дополнения и получаешь пакеты, отправленные экземплярами только твоего дополнения у других игроков.