Клиент-серверная архитектура казуальных сетевых игр

Добрый день. В данной статье я постараюсь дать общий очерк о роли сервера и пинга в анимации для казуальных игр. В статье вся информация представлена с уклоном в казуальные игры с TCP протоколами, в крупных коммерческих проектах часто используются UDP+TCP протоколы, и лайфхаков у них куда больше.

Для начала условимся, что архитектура это принятые и согласованные важные решения. Которые подчиняются триаде Витрувия: надежность, функциональность, красота. Далее бизнес-домен, это про базовые кирпичики сферы. Функциональные требования (ФТ). И более технические, нефункциональные требования: безопасность, соответствие законодательству, риски стейкхолдеров, интересы клиентов, стратегия, инфраструктура, жизненный цикл и так далее. Монолитная или разделенная архитектура? Эластичная или прогнозируемая масштабируемость? Реляционная БД или сочетание технологий? Синхронизированная или асинхронная обработка для таймингов? Предотвращение сбоев или незаметные сбои? Крупные или маленькие обновления? 

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

Рассмотрим протокол P2P. Под Peer-To-Peer (P2P) архитектурой подразумевается, что каждый из компьютеров подключается к общей сети, и каждый компьютер имеет контроль над данными игры. Другими словами, P2P это полное отсутствие безопасности. Здесь возможна и подмена данных, и несогласованность между двумя пирами. Конечно, P2P не является мракобесьем, и эта технология имеет место быть в некоторых случаях, например для игры в крестики-нолики без сервера, но по факту, P2P годится только для игр с небольшой продолжительностью игровой сессии и небольшим количеством одновременно играющих игроков в одной сессии. Сразу сделаю поправку, что на территории РФ и постсоветского пространства у очень большого количества геймеров установлены всякие MonsterDebugger и прочие читерские программы для извлечения и подмены данных, и разработчику приходится организовывать дополнительные методы защиты. Но защитить то, что находится на компьютере пользователя, очень сложно. Как же быть?

Просто! Всю работу с компьютера игрока мы сбросим на сервер, именно он будет обрабатывать действия игроков и просто рассылать результаты своей работы, это и называется клиент-серверной архитектурой.

Так как в прошлом, а иногда и в настоящем я флешер, то рассмотрим способы обмена данных с сервером на примере разработки флеш-игр (да, флеш не умеет использовать ничего, кроме TCP протоколов : )). Для начала зададимся самым жизненным вопросом современного школьника: как можно взломать игру на флеше? Перекомпилировать. Или использовать альтернативные Flash-клиенты с изменённым функционалом, или даже шестнадцатеричное редактирование. Но вся необходимая для игры информация обрабатывается на сервере! В таком случае атака на клиент нашей игры фактически бесполезна, он не имеет особой ценности, будучи программой только для демонстрации игрового процесса. Клиент отправляет сообщения серверу и при этом не имеет особого влияния на игровую логику. Итак, чтобы обеспечить безопасность, желательно все вычисления проводить на стороне сервера. Это ясно.

Но в таком случае появляется пинг. Пинг это время, которое тратит сообщение на путь от клиента к серверу и обратно. И по честному исправить ситуацию невозможно. Как результат, задержка добавляет игре дёрганности и прочих лагов. В быстрых играх, если игрок за 900мс пробежит 5 метров, то при лаге в 900мс игрок телепортируется на 5 метров. То же касается и эффектов. Если игрок наступил на мину и успел отбежать назад, но при лаге пинга эта  информация не успеет обновиться,  игрок будет отброшен взрывной волной совершенно не в ту сторону, в которую он рассчитывал. Думаю, проблема ясна. При этом latency 100мс это вполне ок и это могут заметить только профессиональные игроки, не казуальщики.

Решить проблему постановкой задачи программисту не получится, от пинга избавиться невозможно. Но можно создать иллюзию отсутствия таких задержек. Если игрок имеет хороший пинг, менее 20 мс, то игрока вполне можно обманывать. А именно, восстановить то, что должно быть на экране у второго игрока. Попросту угадать действия игрока, и использовать интерполяцию для сглаживания анимации. Есть много способов интерполяции, начиная с обычной линейной (плавное перемещение игрока из места, где он находится в место, где он должен быть), заканчивая интерполяций на основе кубических сплайнов,  но всё это полумеры, так как время, которое тратится на передвижение игрока к нужной позиции, способно ввести в заблуждение других игроков. А ведь все проблемы, связанные с задержками, вызваны взаимодействием игроков друг с другом. На сервере есть самая актуальная информация и действии игроков, и пока сервер не скажем игровым клиентам об этих действиях, эти действия не произойдут. А предугадывание действия оставим для полёта взрывчатого снаряда, предугадывание годится только для вещей с линейным движением.

Будем ориентироваться исключительно на сервер, получать информацию о действиях игрока только с сервера, пока сервер не скажет — ничего не меняется. Но игрок будет точно недоволен несвоевременной реакцией игрового персонажа на нажатие клавиш.  Игрок банально теряет контроль над своим героем. Что делать? Брать и физически насиловать дизайнеров и аниматоров, разумеется }:  ). Пусть анимация содержит задержки, вроде торможения персонажа после бега, приседание перед прыжком, поворот оружия перед выстрелом, это позволит скрыть несовершенства клиент-серверной архитектуры.  И, как очевидно, имеет смысл сделать игру чуть медленнее, чем мечтает арт-директор, это тоже поможет дать запас времени для обмена данными с сервером.

Из оптимизации есть кое что ещё: многопользовательские игры масштабируются по функции О(n^2), где n = 128, это игроки. Один из игроков сделал ход, соответственно, всем игрокам надо об этом сообщить, и для каждого игрока придётся создать подключение. Количество передаваемой информации, как вы видите, очень высоко, нагрузка на сеть растёт. Но ведь один игрок не может видеть сразу всех 128 игроков, ни при каких обстоятельствах. Тогда количество соединений можно оптимизировать. Если из этих 128 игроков половина в нашей команде, то количество информации об их действиях также можно оптимизировать. Ведь не факт, что они способны нанести нам урон или даже врезаться в нас по идеологии игры.

Вы уже знаете, что интерполяция с линейным сглаживанием весьма неоднозначное решение, и назвать хорошим его нельзя, а вот упомянутая интерполяция с использованием кубических сплайнов выглядит более интересно. Из школьного курса математики все помнят, что линейная функция может быть представлена уравнением с переменной первой степени, например x = 5*y. В кубической функции как минимум один член возводится в степень, x = 5*y ^2. Подробнее здесь.

Есть и другие хитрости, например все знают, что в MMORPG распространено перемещение персонажа кликом по локации. А ведь это так просто! Передали информацию о том, куда надо направить персонажа и запустили анимацию, никаких проблем. Персонаж бежит, игра рисует красивую живую картину, сервер успевает отрабатывать. В крайнем случае, можно уменьшить скорость игрока на несколько процентов, что даст запас времени в несколько десятков мс и это будет незаметно для игрока.

Немного углубимся в проблему написания приложения. Настраивать IP-адресацию на стороне клиента не удобно. Представьте типичного казуального игрока, который сам прописывает IP-адресацию. Так себе картинка. Вполне понятно желание избавить пользователя от такой настройки. Для этого используется DHCP, конфигуратор хостов. Настраивает не только IP, но и hostname , DNS и так далее.

Как это работает? Используется простая широковещательная рассылка, DHCP работает через протокол UDP. DHCP-клиент на компьютере знает, в каком формате и что отправлять. На третьем уровне модели OSI нет IP, поэтому destination 255.255.255.255, а source 0.0.0.0. На втором урвоне мы не знаем MAC-адрес, адрес назначения ffff.ffff.ffff.

  1. Первый этап DHCPDISCOVER, просто стремяем в сеть сообщением о том, что хотим получить хоть какой нибудь IP-адрес.
  2. Второй — получение на компьютер некого количества DHCP Offer.
  3. Третий шаг, когда получили то, что надо, отправляем DHCP Request, таким образом подтверждаем, что готовы взять такой IP-адрес.
  4. Четвертый шаг — сервер подтверждает отдачу IP-адреса. IP-адрес выдается в аренду серверу, минус во всем тут это тип взаимодействия — broadcast. DHTP-сервер должен стоять в одном проадкаст-домене с нашим компьютером, хочется централизованного нахожение DHTP-сервера.

Решается с помощью DHCP relay, который отдает сообщение в DHCP-сервер, посылает конкретный UDP с одинаковым source-портом. В роутер прилетает broadcast, роутер переделывает сообщение из broadcast в unicast. После этого сообщение улетает на DHCP-сервер юникастом, смотрит source IP, понимает подсеть и отвечает обратно юникастом. Так выдаются IP-адреса всем устройствам централизованно. Все это звучит довольно сложно.

На выручку приходит транспортный уровень сети: у нас есть сервер, на сервере крутится несколько сервисов. На сетевом и канальном уровне нет гарантий, что пакеты будут доставлены. Есть всякие сервисы с физическими адресами, логическими адресами, группировки хостов, но успешная доставка не гарантируется. Вторая проблема: каким образом серверу различать траффик? Поэтому транспортный уровень нужен для изоляции между приложениями и обеспечения нужного типа взаимодействия, когда нужна или не нужна гарантированная доставка пакетов.

Тут есть два протокола: TCP и UDP. Например, телефонный разговор это огромное количество маленьких пакетиков. Голос обрабатывется кодеком, он оцифровывается и передается маленькими порциями по сети. Предположим, что по буквам. Слово «привет» передается как «п-р-и-в-е-т». Если потерялся пакет с букаой «В», то через секунду он нам уже не нужен. А вот другой пример: перевод денег со счета на счет в банке. Нам важно перевести именно ту сумму, которую указал пользователь Нельзя терять ни одну цифру. В этом случае нужно ждать потерянную информацию.

TCP обеспечивает гарантию доставки пакетов или заканчивает сессию. Считается, что UDP быстрее TCP, но тут вопрос с количестве переданной информации. Если речь о небольшом размере информации, то UDP быстрее, а на 1Гб TCP будет быстрее.

Гарантировать доставку всей информации не просто. Клиент и сервер друг о друге ничего не знают. Пакеты нумериуются, и TCP для начала обмена данными устанавливает соединение. UDP не устанавливает соединение, поэтому оно и быстрее на небольшом количестве данных. Компьютер отправляет специальное TCP-сообщение с флагом TCP SYN, а на сервере весит некая программа, к которой можно подключаться к порту 80. И сокеты живут в ожидании новых подключений.

Пакет прилетел на сервер, и сервер отвечает флагами acknowledgement + syn. Говорит, что его точка отчета = y. Компьютер клиента отвечает серверу пакетом y+1, и после этого начинается обмен данными.

TCP для установки сессии использует минимум 3 пакета, это называется 3-way handshake. Даже если у вас скорость интернета 400 Гбит/сек, это никак не ускорит процесс установки соединения. Если сервер далеко, то будет большая задержка в миллисекундах. Распространение света между Питером и Москвой это 5-7 мс, а еще надо преобразовать свет в электричество, этот процесс невозможно ускорить. А между Москвой и Нью-Йорком задержка 200 мс.

А теперь UDP. Есть компьютер и сервер, компьютер просто отправляет пакет в сторону сервера. И нет функции проверки доставки пакетов. Если при написании приложения мы указали неправильный IP-адрес, мы этого никогда не узнаем.

Протоколы без установки соединения используются, когда требуется доставка небольших порций данных и когда потеря или порядок переданных сообщений не критичен, когда скорость передачи важнее гарантированной доставки всех сообщений. UDP хорош для онлайн-трансляций, VoIP (либо искажение голоса/потеря пакетов, либо задержка/дождаться всех пакетов). И UDP популярен в реализации онлайн-игр.

Проблема в том, что IP-адресов мало, и часть недоступна для компьютеров, например, диапазон 224.0.0.0 — 239.255.255.255 не может быть назначены на наш сетевой адаптер. Поэтому используется NUT, который группе адресов не из интернета присваивает один публичный IP-адрес, который смотрит в интернет. NUT технология ужасная и ретроградная, но она есть.

Порты. Мы вписываем некий URL в приложение (браузер), а так как браузер по умолчанию заточен под работу с вебом, то порт либо 80, либо 443. И мы можем не писать порт руками в браузерной строке. Мы его всегда знаем, он задан разработчиком в приложении. HTTP = 80, HTTPS = 443, DNS = 53. Можно в явном виде написать http://ya.ru:45/, тогда будем пытаться стучаться на порт 45. Если у яндекса нет странички на таком порту, то ничего не откроется.

Это про destination port, а source port выбирается случайным образом из 65 000 вариантов. Это позволяет создавать разные сессии между одним и тем же отправителем и получателем. А у сокетов используется 5-tuple: протокол, source IP, source port, destination IP, destination port. Эти 5 значений позволяют создавать уникальную сессию. Зачастую это source port. Единственный случай, когда порты могут закончиться, это балансировшики нагрузок.

Коммутация. Для обучения работы с сетью нужно не только читать, но и пробовать руками. Есть два варианта, либо эмуляция с помощью Cisco Packet Tracer, либо софт виртуализации, то есть настоящий софт настоящих устройств. Второй вариант предпочтительнее, и он может быть реализован через бесплатный и платный софт. Cisco Modeling Labs и EVE-NG — платные гипервизоры, для бесплатной работы заводим аккаунт на cisco.com и используем лабораторные работы как рабочие площадки. Либо купить себе роутер MikroTik, в котором много интересных настроек.

Native VLAN. Что такое коммутатор? Нам нужно коммутировать пакетики. У устройства есть порты, пакетики приходят в некий порт, покаывают свои заголовки и вылетают из другого порта. Коммутатор принимает решение на основе второго уровня модели OSI (Datalink), на котором участвуют mac-addresses. Все устройства, которые подключены к коммутатору, умеют общаться друг с другом через домен коммутации. Но если у нас в компании есть отделы админов, дизайнеров, программистов, то их устройства не должны уметь обшаться друг с другом. Нужно делить на подсети. Для деления используется 3-й уровень сетевой модели OSI. VLAN это способ отделить одни устройства от других на коммутаторе.

Итак, у нас есть коммутатор и сервер. Типичная задача: нужно заливать операционную систему на тысячи серверов, это делается с помощью DHCP + PXE Boot. Но по умолчанию наш сервер это просто железка, бесполезная и ничего не знающая о себе. А Trunk требуется с двух сторон. Прибегаем к помощи Native VLAN. Это роль access для Trunk-интерфейса. У интерфейса режим работы Trunk, и его native-VLAN = 1. Если в коммутатор в соответствующий интерфейс прилетает чистый Ethernet, то будет инкапсулирован VLAN ID 1.

Логическая и физическая топология в жизни очень редко совпадают. Благодаря VLAN мы создаем то, что хотим. Можно ли достучаться из одного VLAN в другой? ARP-запрос ничего не вернёт.

А теперь введем понятия протоколов. Коммутатор не знает, как устроена топология сети. Мы соединили как-то сеть, как на картинке выше, но само сетевое устройство такую картинку в себе не держит. Роутеры в курсе топологии, коммутаторы — нет. На L2 нам нужно рассказать коммутаторам про топологию. Если в сети появляется избыточность, то пакеты могут зациклиться и сеть перестанет работать. Но сеть должна быть избыточной для отказоустойчивости, нам нужны дополнительные пути на случай поломки отдельных интерфейсов.

Первый протокол это STP (Spanning Tree Protocol), который логически находит избыточность в сети и выключает избыточность. Он учит наши коммутаторы вести диалог друг с другом. Используются BPDU сообщения. В первую очередь выбирается основной девайс как верхушка дерева сети, с помощью Bridge ID (MAC-адрес + Priority). Наименьший MAC-адрес = самое старое устройтсво, поэтому Priority задаем ручками точку отсчета, хоть какое-то управление сетью.

Размер Bridge ID = 8 байт. На Priority коммутатора по сравнению с другими коммутаторами отводится 2 byte. Приоритет коммутатора по сравнению с остальными коммутаторами. Значение может быть в диапазоне 0 — 65535 с шагом 4096 (0, 4096, 8192 и т.п.). Также, каждому интерфейсу назначается коммулятивная метрика Cost. И по этой метрике строится сеть. Чем меньше значение метрики, тем лучше.

Итак, коммутаторы и весь второй уровень модели OSI это так себе область для гибкой работы в сетью. SIP помогает коммуницировать коммутаторам на тему топологии, но единственный коммутатор для отправки сообщений это Root Bridge. Проблемы: появляется время простоя, и если один интерфейс ломается, то 20 секунд ожидания перехода из состояния в состояние. На выручку приходит протокол RSTP, он про скорость. Мы забываем про таймеры, а реагируем на изменения. Меняется логика общения коммутаторов. Коммутаторы с RSTP могут сами отправлять сообщения. Так, вместо состояния disabled, blocking и listening, у нас остается одно состояние discarding. Learning и forwarding, остаются как были.

Теперь все хорошо, но каждый RSTP это один инстанс на VLAN. Решается проблема с помощью MSTP.

Вернемся к нашей любимой проблеме: борьбе с закольцовками. Общий совет, все надо строится треугольничками. Да, провайдеры могут микро-район сделать кольцом, им так дешевое, но дата-центры в основном треугольничками.

Теперь маршрутизация. На втором уровне модели OSI пакетик просто бегает по коммутируемой локальной сети и прилетает во все порты. Такой подход работает до 1 000 устройств, но интернет куда крупнее, да даже современный многоквартирый дом обладает большим количеством устройств. Отсюда возникает необходимость ограничивать broadcast domain, и мы вводим понятие нового устройства: роутер (маршрутизатор). У роутера нужно заполнять таблицу маршрутизации следующими способами:

  • прописываем статический маршрут. Посмотреть на Windows таблицу маршрутов легко, в powerShell пишем команду netstat -rn. Gateway будет Next Hop.
  • Динамическое заполнение таблицы маршрутизации. Существует два вида протоколов для заполнения таблицы маршрутизации: дистанционно-векторные и link-state.
Hub-spoke

Способы заполнения: есть набор из маршрутизаторов, и они расскащывают друг другу про сети, в которых осведомлены. И получается RIB.

На иллюстрации показано, как раз в 30 секунд отправлется сообщение во все интерфейсы, где включен RIP v2. Маршрутизаторы начинают общаться, R3 говорит R2, что у него есть сеть 10.0.0.0/24, и указывает значение дистанции = 0. R2 записывает это в своей таблице маршрутизации, и увеличивает значение до 1. R2 отправляет RIP update на R1, и идет запись, что 10.0.0.0/24 доступно через R2 с метрикой 2.

Если вдруг R3 умрет, то R2 удалит запись из таблицы. И привет закальцовке между R1 и R2, так как нет знания о топологии. Это фиксится с помощью split horizon. Но RIP работает на таймерах, а то что таймеры это плохо — мы уже выяснили.

Маршрутизация. BGP

ISP любят предоставлять отказоустойчивый сервис в виде двух раздельных кабелей, но они идут вместе и при физическом повреждении не работают вместе. Поэтому покупаем по одной сети у двух разных провайдеров. И осознаем, что IP-адреса привязаны к провайдеру. Вывод: нам нужны собственные IP-адреса. Мы идем в компанию IANA (0-223), смотрим на регистраторы по континентам (в Европе это RIPE).

  • African Network Information Centre (AfriNIC) — управляет адресным пространством в Африке
  • Asia Pacific Network Information Centre (APNIC) — управляет адресным пространством в Азия-тихоокеанском регионе 
  • American Registry for Internet Numbers (ARIN) — управляет адресным в США, Канаде и в некоторых странах карибского региона.
  • Latin American and Caribbean IP Address Regional Registry (LACNIC) правляет адресным в Латинской Америке и в оставшихся странах карибского региона.
  • Reséaux IP Européens Network Coordination Centre (RIPE NCC) — правляет адресным в Европе, на ближнем востоке и в Центральной Азии

Если нам нужны собственные адреса и мы крупные ребята, то можно обратиться за персональными IP-адресами. Для отказоустойчивых сервисов используются блоки IP-адерсов. С такими адресами вы сами являетесь частью интернета, с ответственностью за маршрутизацию. Нужны IGP (internal). И EGP (external) для обеспечения маршрутизации между компаниями.

RIPE знает, что компании А был выдан адрес 1.0.0.0/24, а компании Б был выдан адрес 2.0.0.0/24, и только такой адрес компания имеет право анонсировать провайдеру. Но мир не без разгильдяйства, поэтому можно заанонсировать префикс чужой компании, и сломать им сеть.

У вас задача. Нужно сравнить два одинаковых префикса в разных протоколах маршрутизации. У них одинаковая маска. И метрику не сравнить, в RIP метрика максимум 15 на количестве маршрутизаторов, а в OSPF метрика завязана на скорость интерфейсов и максимум 65 000. 

Получается, нельзя сравнить метрику разных протоколов маршрутизации. Мы не сможем посмотреть на метрику и сказать, какой маршрут лучше.

Попробуем понять, как роутер выбирает лучший маршрут. Первое — длина префикса. Не важно, из какого маршрутизации получен префикс, всегда выбирается наиболее специфичный. Из двух адресов:

  • 10.0.0.0/31
  • 10.0.0.0/32 — выиграет этот, он более специфичный. 

Маска 31 используется, когда роутеры подключены друг к другу напрямую, и такая маска делает подсеть на два ip-адреса. Стандартный вариант это 4 адреса: адрес сети, два используемых и четвертый broadcast. Неплохая экономия денег компании, когда каждый IP это $30.

Представим что есть интерфейс Gb01 с ip 10.0.0.1/24. Второй интерфейс Gb00, в который мы получаем анонс OSPF на сеть 10.0.0.1.0/25. И если в роутер прилетит трафик с 10.0.1.2, то трафик улетит в 10.0.0.1.0/25. Потому что трафик всегда идет в более специфичный маршрут. Если такой есть. 

Второй параметр это AD (Administrative distance), наиболее благоприятный протокол маршрутизации. У протокола OSPF эта дистанция 110, у RIP 120, и наиболее низкое значение AD делает маршрут наиболее приоритетным. Приятная часть в том, что мы можем влиять на значение AD. 

TCP это чуть ли не базовый протокол интернета, то есть набор правил обмена данными внутри сети. Вроде про маршрутизацию все понятно, но есть несколько нюансов по TCP. TCP «гарантирует» доставку, но у него есть некое количество попыток отправки данных. Для отправки данных между хостами должна быть связь. Помимо стандартных x+1 и y+1 еще есть понятие размера окна: 2 16 = 65 535 байт, которые мы можем отправить без получения подтверждения об получении. Сейчас сети со скоростью 100 Гигабит /сек, и 65 535 байт это очень мало. TCP это BDP, то есть он зависим от времени обмена информацией между отправителем и получателем.

BGP, ему недостаточно метрики, нужна еще Network Layer Reachability Information (NLRI). BGP работает поверх протокола TCP и использует TCP порт 179. Так как TCP обеспечивает гарантию доставки, то внутри протокола не имплементировано никаких подтверждений доставки,

Резюмируя, проблема синхронизации в играх — это обман, в котором участвуют и дизайнеры, и программисты. Сначала нужно делать проект технически правильно, и исходя из результата уже накручивать красоту, это и есть задача дизайнера и художника-аниматора, который работает в команде над IT проектом.

16 комментариев

  1. Саша Власов

    Здравствуйте! У меня следующий вопрос: как запустить SWF на маке?

    • your-scorpion (Author)

      Если не один из браузеров не воспроизводит, то попробуйте standalone версию Flash-плеера.

      • Igor cloobok

        С какими настройками вы озвучивали ваши мобильные приложения и игры?

        • your-scorpion (Author)

          -6 децибел, ogg для chrome и unity, mp3 для flash.

          • Никита Красов

            Привет! а каким редактором пользоваться для озвучки игр? спасибо!

          • Цветков Максим (Author)

            Довольно широкий вопрос. В редакторах, в UE4 точно есть свои внутренние инструменты для этого. Либо как стандарт — Wwise или Fmod. Wwise умеет худо-бедно интегрироваться в UE4.

  2. Василий Гаала

    Добрый день, не могли бы подсказать, для игр нужно дополнительно использовать субтитры, если есть озвучка на множестве языков?

    • Цветков Максим (Author)

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

      + посмотрел на официальную статистику за 2018 год: более 60% игроков играют с включенными субтитрами.

  3. Илья

    Вопрос не совсем по теме, но как оптимизировать графику для веб-игр или крутых сайтов, там же спрайты могут и 100 мегабайт достигать! И еще не понятно, как одна и таже картинка весит по разному в редакторе и в проводнике.

    • Цветков Максим (Author)

      В проводнике размер картинки рассчитан со сжатием, а некоторые редакторы показывают картинку без сжатия. 1 байт на канал, 4 байта на пиксель, 4х8 = 32 бита на пиксель. 2048х512х32/8/1024/1024 = 4 мб.

      Простое решение это TinyPNG, но это компрессор и будет уменьшать цветовую палитру. Отличный вариант это сжимать через терминал. Но там есть ограничение на 500 картинок в месяц.

      Раз возник такой вопрос, то надежды на дизайнера нет, и юзайте TinyPNG как плагин в галпе/вебпаке при билде проекта, вот npm. Можно сжимать jpeg с помощью mozjpeg, а альфу держать отдельно в 2 бита, если без мягкой прозрачности. Если делаете для веба и все серьезно, то используйте для дебага nvidia nsight, может вам надо не спрайты оптимизировать, а шейдера и батчинг.

  4. Ilia Zviagin

    Добрый день! возник вопрос с коллегами, даже спор. WASD раскладка клавиатуры это самое оптимальное для управления персонажем?

    • Цветков Максим (Author)

      С момента выхода WoW, WASD точно стало стандартом. А устоялась еще в 1995-м году на чемпионате по Quake. Используется и для управления камерой. Время от времени люди ищут альтернативу, вроде WADX + S для приседания, или ESDF (в которой труднее дотянуться до Shift или Ctrl). Бывают даже всякие ASXC.

      Немного путаницы вносят новомодные клавиатуры с раскладкой Dvorak, у которой клавиши не повторяют печатную машинку.

  5. Варвара

    Может ли межсетевой экран создавать проблемы при маршрутизации?

    • Цветков Максим (Author)

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

  6. Anton Kolonin

    Каков процесс формирования архитектуры?

    • Цветков Максим (Author)

      1) выделить ответственных
      2) Договориться о процессах, документации (Event Storming)
      3) Event Sourcing — лепим стикеры и выделчем главные сущности у каждого поддомена.
      4) Собрать ФТ и неФТ
      5) Проработать Use Cases
      6) Начать реализовывать архитектуру.

      Артефакты: домен, поддомены, общий для бизнеса тезаурус, потоки данных, схема инфраструктуры, схема сервисов, SLA.

      Есть разные методологии для архитектуры, Clean architecture, Hexagonal (порты и адаптеры), Onion, DDD, CQS, CQRS, n-level.

      Например, для LMS:
      ФТ будет расписание, вебинары, список студентов, задача домашних работ и проставление оценок, прием платежей, квизы.
      неФТ: аккаунт с прориетарным CPM, 200k MAU, все в 2-х часовой отрезок по 11 часовым поясам, франшизная модель, данные клиентов остаются в стране.

«Взаимодействуя с данным сайтом, вы, как пользователь, автоматически даете согласие согласие на обработку персональных данных» Согласие

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.