Создание многопользовательской игры в сети или в Интернете

Привет, я написал многопользовательскую игру на Java, и мне было интересно, что мне нужно, чтобы узнать и/или что я должен использовать, чтобы сделать игру воспроизводимой по сети или в Интернете несколькими компьютерами. Я действительно не знаю, с чего начать, поэтому любой совет будет полезен, спасибо.

Ответ 1

Эти другие ответы являются довольно высокоуровневыми, что хорошо, но вы не хотите высокого уровня, вы хотите низкого уровня, как в "как я могу заставить его отправлять данные и что это значит, и что я посылаю и т.д." Вот что вы делаете:

Во-первых, TCP или UDP? Если вы не знаете, что из этих вещей, прочитайте на них, поскольку у меня нет места, чтобы дать хорошее объяснение на обоих здесь, но по вашему выбору знаю следующее:

  • TCP хорош для игр с пошаговыми играми или игр, где высокая латентность, как правило, в порядке, поскольку TCP гарантирует доставку пакетов, поэтому может потребоваться некоторое время для повторной доставки упавшего пакета. Это хорошо для таких вещей, как шахматы или что-то еще, что по очереди.
  • UDP хорош для игр, где вы не всегда заботитесь о надежности в сообщениях и предпочтете, чтобы данные просто продолжали отправлять, и если вы что-то пропустили, ну хорошо. Это хорошо для игр, которые играют в реальном времени игры, такие как HALO: Reach или Call of Duty. В тех случаях, когда вы отправляете позицию объекта, и объект никогда не попадает туда, лучше отправить новую позицию, чем повторно отправить старую позицию (которая теперь еще старше), поэтому не важно всегда гарантировать надежность. Тем не менее, вы ДОЛЖНЫ иметь определенные вещи на 100% надежными, поэтому вам все равно понадобятся определенные вещи, чтобы гарантировать доставку, например, создание объекта и уничтожение объекта. Это означает, что вам нужно реализовать свой собственный полунадежный протокол с приоритетом поверх UDP. Это сложно.

Итак, спросите себя, что важно, узнайте, как работают TCP и UDP, а затем сделайте разумный выбор.

Тем не менее, теперь вам нужно синхронизировать состояние объекта по сети. Это означает, что ваши объекты должны сериализоваться на то, что может быть представлено в потоке байтов и записано в сокет. Запись в сокет легко; если вы можете написать файл, который вы можете записать в сокет, это действительно не сложно. Важно то, что вы можете представлять объект в качестве буфера, поэтому, если у вашего объекта есть ссылки/указатели на другие объекты, вы не сможете просто отправить эти указатели, поскольку они отличаются от других клиентов, поэтому вам нужно преобразовать их в нечто общее для всех хостов. Это означает ID, хотя идентификатор объекта должен быть уникальным для всех хостов, поэтому у вас должен быть способ координации между хостами, так что ни один из двух хостов не создаст разные объекты с тем же идентификатором. Есть способы обработать узлы, которые делают это, но мы не будем беспокоиться об этом здесь (подсказка: используйте какое-то сопоставление между идентификатором хоста и сетевым идентификатором. Больше внимания: не делайте этого, если вам не нужно).

Итак, теперь вы можете отправлять данные, отлично, что теперь? Каждый раз, когда изменяется состояние игры, вы должны каким-то образом отправить обновление другим машинам. Здесь возникает архитектура клиент-сервер или одноранговое соединение, если вы хотите. Клиент-сервер проще реализовать. Кроме того, один хост, "действующий" как сервер, по-прежнему является Client-Server, и любой, кто говорит по-другому, ошибается.

Таким образом, ответственность сервера - "владеть" всем игровым состоянием. Только сервер может окончательно сказать, в каком состоянии находится объект. Если вы хотите переместить объект, вы сообщите серверу, что вы хотите переместить, однако сервер затем сообщает вам, что вы должны перемещать объект, вы не просто сделайте это (хотя часто бывает полезно какое-то прогнозирование на стороне клиента). Тогда сервер отправляет обновленное состояние объекта всем остальным хостам.

Итак, вы упомянули пошаговую игру, не так ли? Очень просто:

  • Вы собираетесь разрешить полный оборот клиенту, который сейчас его переводит. Как только этот клиент делает то, что они хотят сделать, отправьте результаты этого перехода на сервер. Затем сервер проверяет хосты клиента (не просто доверяйте клиенту, обманывая это так) и применяет их к своему состоянию объекта.
  • Как только сервер обновляется, он отправляет сообщения каждому другому клиенту с новым состоянием мира, и эти клиенты применяют эти обновления. Это включает в себя клиента, который только что сделал свою очередь; этот клиент должен обновлять свое мировое состояние, когда сервер сообщает об этом, поскольку вы хотите обеспечить согласованность с остальными хостами, и вы хотите, чтобы хост не обманывал.
  • Затем сервер отправляет сообщение с указанием его очереди. Вы могли бы отправить это одновременно с обновлением состояния мира на предыдущем шаге, это было бы хорошо. Просто будьте в курсе клиентов, пытающихся выйти из строя. Вот почему сервер имеет власть над миром; если клиент пытается обмануть, сервер может повредить их.

Это все, что вам нужно сделать для пошаговой игры. Подсказка: используйте TCP Большая подсказка: TCP реализует нечто, называемое "алгоритмом Нагле", которое объединит ваши сообщения в один пакет. Это означает, что если вы отправляете два отдельных сообщения с двумя отдельными вызовами "Отправить", возможно, что другие хосты получат только один пакет при одном вызове "Получить", но этот пакет будет содержать содержимое ОБОИХ пакеты, которые были отправлены. Таким образом, если вы отправляете два 100-байтовых пакета с двумя вызовами для отправки, вы можете получить один 200-байтовый пакет на один вызов для приема. Это нормально, поэтому вам нужно как-то справиться с этим. Один трюк состоит в том, чтобы сделать каждый пакет одинакового размера, а затем просто прочитать, что многие байты из сокета каждый раз, когда вы проверяете ввод. Имейте в виду, что вы также можете получать частичные сообщения. Например, если вы отправляете два сообщения по 100 байт, их можно объединить в одно 200-байтовое сообщение. Затем, если вы читаете сокет на другом конце, но вы читаете с размером буфера 150 байт, вы будете иметь 150-байты, который содержит первый пакет и часть второго. Вам нужно будет сделать второй звонок, чтобы получить оставшуюся часть второго сообщения, так что СНИЖАЙТЕ СЛЕДУЕТ, КАК МНОГО ДАННЫХ, ВЫ ПОЛУЧЕНЫ, чтобы вы не пропустили часть пакета где-нибудь. Вот почему ваши пакеты одинакового размера полезны.

Существует множество других полезных приемов для уменьшения размера и частоты ваших сообщений и отслеживания игр, которые не основаны на поворотах и ​​действуют в режиме реального времени, но если у вас есть пошаговая игра, Правильная вещь, которую нужно сделать, - это, вероятно, использовать TCP и не беспокоиться ни о каком другом. Вот некоторые ссылки на полезные веб-сайты и статьи, которые предоставят вам дополнительную информацию о том, как выполняется сетевое программирование игры:

  • сайт Гленна Фидлера, здесь есть отличная информация.
  • 1500 лучников, Отличная статья о том, как реализовать технику, называемую детерминированным блокировкой, которая полезна для многих типов игр.

Сообщите мне, хотите ли вы получить более подробную информацию о любом из этих материалов или если у вас есть более конкретные вопросы.

Ответ 2

Если вы хотите добавить многопользовательскую функцию по сети, вам может быть полезно взглянуть на проект Netty для сборки инфраструктуры связи.

Но прежде чем вы сможете это сделать, вам нужно убедиться, что ваша игра имеет правильную "архитектуру". Вам нужно иметь большие модули: Клиент и Сервер.

Сервер отвечает за всю логику игры. В некотором смысле, это игровой движок. Клиент несет ответственность за запрос сервера для состояния игры, отображение его на проигрыватель, получение команд ввода и отправки проигрывателя на сервер.

Один из способов отделить наш клиентский и серверный код без проблем, связанных с обучением сетевому программированию, состоит в том, чтобы иметь две разные программы, которые запускаются из CLI. Последовательность выполнения может быть такой:

  • Сервер запускается для инициализации состояния игры, состояние игры сохраняется в файле.
  • Клиент запускается, он считывает состояние игры, как-то отображает его и получает некоторый вклад от игрока. Он сохраняет этот вход в файл.
  • Сервер запущен, он считывает команды с клиента, изменяет состояние игры и обновляет файл состояния.

Промойте и повторите. Это в основном то, что делают серверы PBEM.

Чтобы разделить клиент и сервер, имеет смысл определить "язык" для представления изменений состояния игры и команд, которые будут выполняться сервером. Это помогает, если клиент кэширует состояние и применяет изменения, отправленные с сервера.

Как только ваш код "Клиент" и "Сервер" полностью развязан, а "язык" полностью определен, вы готовы изменить механизм связи на основе файлов на основе сокетов.

Ответ 3

Один из возможных архитектурных подходов состоял бы в том, чтобы один экземпляр игры выступал в роли хоста (например, первого для начала). Он будет координировать игру и отправлять информацию о состоянии игры и поворачиваться к каждому из других игроков.

Когда игрок совершает движение, он отправляет информацию о перемещении на хост, которая обновляет состояние игры (и проверяет правильность перемещения и т.д.). Затем он отправляет новое игровое состояние каждому игроку, а также отправляет (вероятно, как отдельное сообщение) уведомление о следующем обращении к соответствующему клиенту и ждет его ответа.

В некотором смысле, хост выступает в качестве игрового сервера в этом сценарии, но может быть проще использовать/играть в том, что для игры в игру нет отдельного процесса.