Как устроены онлайн игры? Создаем примитивную 2д онлайн игру

Добавлен , опубликован

Вступление

  • Как работают онлайн игры?
Этим вопросом задавался практически каждый, кто провел в них большое количество времени.
Разбираясь в этом вопросе, мне захотелось сделать её собственными руками.
Задачей было сделать максимально примитивное приложение, которое работало бы через интернет, позволяя игрокам с разных точек планеты подключаться и делать примитивные игровые действия.
А именно выбирать игровое лобби, писать в чат, и менять позицию своего "персонажа" в игровой локации.
  • Это не так сложно как ожидалось.
Механизм отправки сообщения на сервер и его трансляции всем клиентам на практике оказался прост, а задача сделать проект "любыми методами" не подразумевала идеальной технической реализации и оптимизации как в популярных онлайн играх.
Далее я поделюсь тем, какие решения в разработке я выбрал и как достиг того что получилось.


Как устроена эта игра?

  • Установка подключения и клиент-серверный обмен сообщениями.
Клиент подсоединяется к серверу по его url адресу. Далее они обмениваются сообщениями.
При подключении сервер "регистрирует" клиента в своей памяти, назначая ему случайный никнейм на возникший конект.
Далее назначает ему лобби, если лобби на сервере нет, создает его. После этого отправляет клиенту стартовую информацию о лобби в которое он зашел, оповещая в чате всех других игроков, находящихся в этом лобби.
  • Реакция на действия игрока
Как только игрок делает какое-либо действие, например пишет сообщение в чат, или кликает на 2д локацию, его клиент отправляет сообщение формата JSON, содержащее информацию о проделанной клиентом операции. Сервер принимает это сообщение, и на основе своих вычислений решает как ему на это отреагировать..
Например, если игрок что-то написал в чат, сервер определит что это за игрок, определит его лобби и добавит его сообщение в хранилище сообщений чата этого лобби, далее транслируя его всем другим клиентам и все они увидят на своем клиенте новое сообщение в чате.
Такая же логика и при клике игрока на канвас, игрок кликает, отправляется сообщение. Сервер его принимает, меняет внутри себя состояние локации 2д локации этого лобби, и транслирует информацию о новом состоянии локации всем игрокам этого лобби.
При отключении соединения, сервер оповещает всех игроков в лобби этого игрока о том что он вышел, и удаляет все данные о нем, обрывая связь.


Как я реализовал обмен сообщениями по сети?

  • Выбираем обмен сообщениями через websocket
Простота разработки стояла превыше оптимизации, поэтому я выбрал не самое удачное сетевое решение для онлайн игры, но достаточно легкое в реализации. Используя протоколы верхнего уровня, мой выбор пал на технологию Websocket.
Она позволяет установить клиенту и серверу постоянное подключение, по которому можно обмениваться сообщениями в реальном времени в двустороннем направлении. От сервера к клиенту, и от клиента к серверу.
  • Сообщения формата JSON
Тип сообщения достаточно тяжелый и имеет в себе лишнюю нагрузку, заголовки и прочее, одним словом JSON. Но с ним достаточно просто работать в отличии от закодированных бинарных, сжатых сообщений. В JSON ты отправляешь простой текст, который удобно без лишних действий читать и отлаживать.


Клиентская часть

  • Десктопное приложение или html-страница в браузере, сетевой обмен данными один и тот же
Первоначально идеей было создать десктопное приложение, но потом я подумал, что отличий десктопного приложения от html странички с javascript кодом на принципиальном уровне особо не будет. Вдобавок, случайному человеку легче набрать url в браузере, чем скачивать непонятные приложения от неизвестного автора и запускать их. Но принцип работы обмена данными по сети в обоих случаях выйдет один и тот же. Клиент устанавливает по заданному адресу websocket соединение и начинается обмен сообщениями. Запрос - ответ.
  • Из чего состоит клиентская часть?
Несколько html блоков с кнопками и формами текстового ввода, canvas на котором мы будем рисовать кубы наших игроков с никами.
И javascript обработчики этих кнопок и форм ввода.
  • "Сердце" кода клиентской части
Особого внимания заслуживают здесь только несколько строк кода на которых все держится. Благодаря высоким уровням абстракции, предоставленными умными людьми, нам не нужно самостоятельно копаться в реализации нижних уровней протоколов и сетевых драйверов.
Javascript дает нам API для работы с вебсокетами, и всего одной строчкой мы создаем websocket подключение, а закулисами под этой строкой происходит множество непонятных нам действий, благодаря которым чипы вашего компьютера могут обмениваться сигналами с чипами серверного компьютера.
вот так мы устанавливаем websocket соединение по адресу
	const socket = new WebSocket('ws://localhost:8080/ws'); // - замените url на адрес и порт своего сервера

А этой функцией мы читаем пришедшие по этому соединению сообдения
        //слушаем сервер
        socket.onmessage = function(event) {
            const message = JSON.parse(event.data);  
			//далее пишем реакцию вашего клиента на полученное сообщение	
		}
А вот такой строкой мы отправляем сообщение серверу
    socket.send(JSON.stringify(jsonChatMessage));
Я буду приводить вам только самые важные строки кода, на которых держится все мое приложение. Весь остальной код это уже попытка их организовать во что-то большее и сложное.


Серверная часть

  • Выбор технологий для серверной части
В качестве языка для сервера я выбрал Golang, на котором практикуюсь в нынешнее время. Но websocket должны поддерживать и другие популярные языки.
  • Главные моменты в серверном коде, благодаря которому обмен сообщениями возможен
Когда клиент набирает url в браузере его встречают серверные обработчики этих адресов

Если клиент обратится по url сервера, сервер даст ему html страничку.
	fs := http.FileServer(http.Dir(basePath)) 
	mux.Handle("/", http.StripPrefix("/", fs))

Как только html страница будет получена клиентом, она обратится по другому url к серверу, чтобы установить websocket соединение
	mux.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
		transport.ServeWs(MyHub, MyConnectionsManages, w, r)
	})

Клиент в браузер вводит url, функция-обработчик на стороне сервера на это реагирует и отправляет что-то в ответ клиенту.
Теперь рассмотрим как на стороне сервера формируется подключение по websocket в обработчике websocket url
		conn, err := upgrader.Upgrade(w, r, nil)
		if err != nil {
			log.Println("Ошибка установки соединения WebSocket:", err)
			return
		}  
		defer conn.Close()
  • Подключение также создается одной строкой, или выдаст ошибку если что-то пошло не так внутри.
Конструкция defer в golang автоматически закроет его как функция-обработчик завершится
Этой строкой кода, сервер будет читать входящие сообщения по этому соединению
	err := wsCon.conn.ReadJSON(&msgData)
	if err != nil {
		log.Printf("Error reading JSON: %v", err)
		break
	} else {
		cm.DistributeMessage(wsCon.Name, msgData)
	}
Получили, проверили на ошибки? Все хорошо? Делаем какие-то вычисления с этим сообщением
Вот так отправляем сообщение клиенту
	err := conn.conn.WriteMessage(websocket.TextMessage, messageJSON)
  • Все так просто?
Да, в эти небольшие строчки кода умещается основная работа всего моего клиент-серверного проекта. Все остальные конструкции кода - это попытка организовать эти элементарные сетевые механизмы в более сложную и надежную систему.
Как вы можете наблюдать, все устроено довольно просто, а умные люди, разрабатывающие языки программирования и сетевые библиотеки для них, уже написали для нас свои API, благодаря которым все сложные сетевые взаимодействия превращаются для нас в 2-3 строки кода.


Как организована серверная логика?

  • Механизмы, координирующие множество подключений и сообщений
Подключенный клиент добавляется в пул подключений в сущности hub
Все пришедшие от него JSON сообщения конвертируются в структуры golang написанные мной чтобы сервер мог ими манипулировать.
Специальные утилиты такие как connections manager и exchanger работают как посредники между пулом всех активных соединений hub и конкретным websocket соединением, определяя кого в какое лобби назначить, или кому какое сообщение в данный момент отправить и так далее.
  • Конкурентный доступ к данным
Также в golang есть хорошие инструменты реализующие многопоточность и конкурентный доступ к общим данным. Чтобы когда несколько клиентов одновременно обращались за какой-то одной ячейкой памяти нужной им, не произошло непонятных ситуаций и ошибок. Именно этим меня привлекает golang и той легкостью с которой в нем все это реализуется.


Как получилось сделать игру доступную в интернете?

  • Докер вездесущий
Все очень просто, я собрал docker образ в котором была как клиентская часть index.html, так и собранное в бинарный файл приложение сервера.
Загрузил его на docker hub.
  • Дешевые хостинги
Далее нашел дешевый хостинг серверов, арендовал серверную машину с операционной системой linux.
После этого установил на нее докер, скачал с docker hub свой образ и собственно, запустил контейнер на основе этого образа, поигравшись с портами и другими конфигами. Немного усилий и приложение доступно в интернете.


Полезные ссылки

В конечном итоге у меня все получилось и в эту игру вы можете сыграть прямо сейчас, попрыгать кубом по канвасу, и початиться с друзьями в разных лобби:
  • Сыграть в поделку
Торопитесь, через месяц хостинг игры закончится
  • Исходный код проекта:
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
38
Раздели хотябы на параграфы и опубликуем

P.s. nginx умеет рулить вебсокеты без отдельного роута /ws, прямо туда же где и http сервер слушается и по тому же порту
4
Раздели хотябы на параграфы и опубликуем

P.s. nginx умеет рулить вебсокеты без отдельного роута /ws, прямо туда же где и http сервер слушается и по тому же порту
не нашел параграфы, сделал отступы больше
текста в статье буквально кот наплакал
все самое важное, 6 строчек кода на которых все стоит
38
Ну, я имел ввиду двойной перенос строки по сути.
Опубликовал
30
Я так понимаю, сервер доверяет всем данным и можно слать всякое?
38
nazarpunk, забыли дописать в статью пару простых томов про sso, oauth2, rbac, antifraud, antiddos, ipsec и incident response для чайников?)
4
Я так понимаю, сервер доверяет всем данным и можно слать всякое?
задудосить можно спокойно
а обрабатывает по идее только json'ы c определенными полями внутри
все остальное или не будет прослушиваться, или выдаст ошибку если json корявый
30
а обрабатывает по идее только json'ы c определенными полями внутри
А если писать в эти определённые поля всякое, сервер будет доверять?

nazarpunk, забыли дописать в статью пару простых томов про sso, oauth2, rbac, antifraud, antiddos, ipsec и incident response для чайников?)
Читать в 2к24 про банальный CRUD моветон.
4
а обрабатывает по идее только json'ы c определенными полями внутри
А если писать в эти определённые поля всякое, сервер будет доверять?

nazarpunk, забыли дописать в статью пару простых томов про sso, oauth2, rbac, antifraud, antiddos, ipsec и incident response для чайников?)
Читать в 2к24 про банальный CRUD моветон.
изначально высоких амбиций и претензий было мало
задачей было сделать примитивную онлайн-игру, доступную в интернете и рассказать о самых главных строчках кода за счет которых работает обмен сообщений, тот же самый круд
в 2024 такое читать человеку который никогда о том как мультиплеер не делал вполне себе интересные ответы на вопросы
касаемо "отправить серверу всякое", sql инъекции не особо помогут, что еще в какое-нибудь поле "text" в json месейджа чата, или неадекватные координаты в клик позиции на канвас как-то серверу навредить не должны особо
но тут я не особо эксперт-безопасник, сделал как могу и поделился этим, а тут даже на главную опубликовали
написать банальный круд было неплохим таким достижением 😄
25
Нужен подробный гайд как в одно лицо создать мили ртс с неткодом, и можно перекатываться из варкрафта в геймдев
2
По сути самое простое веб серверное приложение. в самой простой реализации. Это и близко не про то как устроены онлайн игры.
Я бы знакомиться с данной темой рекомендовал бы начинать с "Джошуа Глейзер, Санджай Мадхав - Многопользовательские игры. Разработка сетевых приложений.", а не с таких вот статей...
4
По сути самое простое веб серверное приложение. в самой простой реализации. Это и близко не про то как устроены онлайн игры.
Я бы знакомиться с данной темой рекомендовал бы начинать с "Джошуа Глейзер, Санджай Мадхав - Многопользовательские игры. Разработка сетевых приложений.", а не с таких вот статей...
моя статья для тех кто боится начинать делать онлайн игры
ваша книга для тех кто хочет раскрыть потенциал своего мозга
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.