Особенности протокола в IO-играх

    Допустим, вы хотите создать IO-игру. Что-то похожее на agar.io, slither.io и тысячи их.

    Что такое IO -игры
    Такое название закрепилось за браузерными, клиент-серверными многопользовательскими играми в реальном времени.

    Механика таких игр обычно относительно проста и интерес достигается за счет эпических битв между большим количеством реальных игроков.

    Родоначальником жанра является agar.io

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

    Сейчас я так не думаю.

    В чем проблема


    Основная проблема, которую приходится решать всем разработчикам многопользовательских игр — достижение «одновременности» происходящего.

    Наивный подход: а давайте я буду передвигать своего героя, отсылать эти передвижения на сервер, а сервер станет рассылать их моим соперникам — не работает. Если поступить так, то ваш герой будет жить в реальном времени, а его соперники — в прошлом. Их движения будут отставать примерно на два пинга до сервера. Скажем, если пинг 100мс, то вы увидите где были соперники 200мс назад. Соответственно, играть будет невозможно.

    Постановка задачи


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

    • перемещения были плавными
    • у всех «пилотов» картинка была одинаковая и отображала все корабли в какой-то один момент времени

    Disclaimer:

    Итоговый результат — очень простой.

    Но попробуйте, для чистоты эксперимента придумать свой вариант до прочтения статьи, чтобы при написании комментариев избежать эффекта знания задним числом.

    Первое решение


    Пусть каждый клиент при отрисовке каждого кадра посылает на сервер положение курсора мыши.
    А сервер, получив его, немедленно считает текущие координаты корабля и рассылает их всем клиентам (включая и того, кто передал координаты мыши).

    Все просто. Одновременность здесь достигается.

    И это даже работает, пока у нас сервер стоит локально, а клиентов совсем немного.

    В условиях приближенных к реальным такой подход дал три вещи: бешеный трафик (который на порядок превосходил трафик того-же agar.io), периодические «прыжки» кораблей (вместо плавного полета) и дикие лаги, которые обычно начинались через 3-5 минут, а потом не проходили уже никогда и «лечились» только рестартом браузера.

    Вторая версия протокола


    Первая правка была очевидной: неправильно отсылать перемещения корабля сразу после получения управляющей команды от клиента.

    Если делать так, то периодичность поступления команд клиентам от сервера зависит от трех вещей:

    • частоты их отправки корабликом на сервер
    • загруженности канала от кораблика до сервера
    • и загруженности каналов от сервера до остальных клиентов

    Слишком много переменных. Измерения показывали, что команды, которые по-идее отправляются раз в 20мс приходят со случайным интервалом от 5 до 90 мс.

    Решение этой проблемы было простым — вместо немедленной отправки команды на клиент сделали отправку по таймеру — одна команда в 20мс.

    «Прыжки» корабликов заметно уменьшились…

    Третья версия


    … но не исчезли.

    В какой-то момент, повинуясь сетевым лагам, очередной пакет приходил с большой задержкой. Не через 20мс, а через 100, например.

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

    И тут возникла странноватая идея: а давайте в случае таких вот задержек все-таки будем двигать кораблик в ту же сторону, в которую он летел ранее.

    Может картинка станет более плавной? Идея эта не сработала, но она оказалась шагом в сторону правильного решения.

    И так, кораблик на клиенте теперь все время (то есть каждый кадр) летит со своей постоянной скоростью.

    С сервера приходят команды на изменение этой скорости, а также «поправки» для координат корабля.

    Результат? Вместо редких прыжков на длинные дистанции наши кораблики стали совершать серии прыжочков на короткие расстояния.

    Получалось, что с точки зрения клиента корабль находится в одном месте, а с точки зрения сервера — в другом.

    Присылаемые с сервера «поправки» и приводили к микропрыжкам.

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

    А давайте будем слать поправку пореже и посмотрим, что из этого выйдет!

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

    Четвертая версия


    А ведь это означает, что я совершенно зря смешал две характерных величины: FPS — необходимость перемещать кораблик по экрану 60 раз в секунду, и отправку управляющего сигнала от мыши.

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

    А она — примерно 200-250мс!

    С учетом пинга до сервера (примерно 100мс) это означало, что управляющие сигналы можно посылать примерно раз в 100-150мс, а плавность движения уже обеспечивать на клиенте.

    В итоге четвертая версия протокола выглядела так:

    • клиент отсылал на сервер положение мыши раз в 120мс
    • получив эту управляющую команду, сервер пересчитывал координаты корабля
    • асинхронно, на сервере работала нитка, которая ровно один раз в те же 120мс отправляла координаты и скорость корабля на клиенты
    • получив координаты, клиенты передвигали корабли в эту новую точку, а скорость использовали для плавного движения корабля с целью достижения приемлемого FPS

    Бонус от этой версии — резкое уменьшение трафика. Теперь он стал таким же, как на agar (кажется Matheus Valadares что-то знал!).

    Но самое главное — автоматически решилась проблема тяжелых лагов, наступающих через 3-5 минут и неизлечимых ничем.

    (Через несколько дней мне попалась вот эта статья и стало понятно, что именно там происходило).

    Теперь оставалась только одна проблема — мелкие прыжочки кораблей во время синхронизации.

    Версия пятая — конечная


    Если синхронизацию не делать или делать её редко, то местоположение кораблей на сервере и на клиентах оказывалось разным, что естественно было неприемлемо.

    Но терпеть прыжочки, которые превращались в прямо-таки полноценные прыжки при любой сетевой задержке, мы тоже не хотели.

    Первоначальная мысль выглядела так: А что если мы «размажем» синхронизацию по времени? Вот не будем сразу перемещать кораблик в нужную точку, а скажем ему переместиться туда кадров за 7-8?

    — Так, но ведь это — дополнительная задержка…
    — А кстати, мы ведь меняем скорость кораблика только раз в 120мс. Значит мы заранее знаем где он будет через 120мс!
    — С этого места подробнее!

    Так родилась простейшая (при её рассмотрении задним числом) идея: вместо синхронизации, сервер отправляет клиенту координаты корабля, на которых он должен быть через 120мс.

    И весь протокол стал таким:

    • клиент отсылает на сервер положение мыши раз в 120мс
    • получив эту управляющую команду, сервер пересчитывает координаты корабля
    • асинхронно, на сервере работает нитка, которая ровно один раз в те же 120мс отправляет на клиенты координаты, в которых корабль должен быть к концу следующих 120мс
    • получив координаты, клиенты делают простой расчет: если сейчас кораблик находится в точке А, то с какой скоростью надо передвигать его в течение следующих 120мс, чтобы он оказался в пришедшей с сервера точке B

    Этот протокол решил все проблемы. Он устойчив к задержкам пакетов.

    Даже если по какой-то причине в момент получения новой цели кораблик не достиг (или перелетел) прежнюю, это никак не скажется на плавности хода — лишь немного подкорректируется скорость.

    Сейчас мы довольны протоколом. Результат можно посмотреть здесь: https://spacewar.io

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 75

      0
      Если не секрет, то можете показать примерный код работы сервера?
        0
        Вообще, там очень немаленький репозиторий на golang. Открывать код мы не планируем.
        0

        Что-то не так у вас с физикой — инерции не хватает.

          0
          Игрушка казуальная. Так и задумано.
            –5

            Не надо путать казуалов и даунов.

              +2
              Не надо, да
              0
              Странная логика. Angry birds и Cut the rope, например, тоже казуальные игры, но с физикой там все в порядке.
                0

                Скорее поленились разбираться или ресурсов нет.
                Отскоки с одной скоростью и отсутствием контроля — ну сразу обламывает кайф игры.
                starblast.io вполе себе с физикой.


                А за статью спасибо.

                0
                Поменяли физику. Как вам сейчас?
                  0

                  Стало лучше, хотя мышкой всё-равно тяжко управлять. Удобнее было бы с клавы.

                +2
                Поменяйте сертификат на Letsencrypt — вас ждет сюрприз с Chrome Beta (который скорее всего выйдет из беты в середине марта)
                  0

                  Ну как в середине марта, версия под мак "Version 56.0.2924.87" уже не открывает.


                  Я что-то пропустил, но с каких пор хром не разрешает зайти на сайт даже если мне очень хочется, забив на сертификат? Раньше было под «advanced»
                    0
                    Вы правы. Перед официальным релизом обязательно так сделаем.
                      0

                      Можете подсказать, что не так случится с letsencrypt? Использую его везде, первый раз слышу, что что-то с марта поменяется.

                        +2
                        Хмм, да, звучит двусмысленно. Начиная с Chrome 57 сертификаты от Startcom и WoSign окончательно превращаются в тыкву. А Letsencrypt планирует жить и здравствовать
                          0

                          Спасибо, сняли камень с моей души) мигрировать была бы боль

                        0
                        Поменяли. Только не на letsencrypt а пока на comodo. Так быстрее было.
                        0
                        Если бы мне сказали реализовать управление в игре космическими кораблями на плохом канале (с плохим пингом), я бы вообще не использовал координаты (положение), разве только как средство дополнительной синхронизации.

                        Используем законы физики: тело, на которое не действует никакая сила, продолжает движение прямолинейно. F = ma, масса корабля либо фиксированная, либо уменьшается (если кто-то в игре реализует топливо, либо меняется, если кто-то в игре реализует релятивистскую физику), в простейшем случае — не меняется. В силу того, что даже ускорение нельзя менять мгновенно, мы просто передаём с клиента его текущее значение ускорения, задержки между передачами ускорения соответствуют «инерции педали».

                        Раз в N времени пользователь может поменять значение вектора ускорения (или значение скаляров ускорения и угла поворота рулей). Дальше чистая физика.
                          +2
                          В принципе, так все и есть. Только один момент — что вы предлагаете делать, если из-за задержки пакета с измененным ускорением, кораблик на клиенте продолжил лететь с прежним ускорением дольше, чем было надо?
                          Ошибки будут накапливаться. (Я пробовал так — правда будут)
                            0
                            «Педаль реагирует с задержкой» — и всё.

                            Да, я забыл сказать, что в ответ сервер присылает не «реакцию» на нажатие педали, а timestamps (в общем frame of reference) моментов изменения ускорения всех объектов. Имея трансляцию из frame of reference в локальное время, клиент может полностью воспроизвести изменение скоростей и просчитать траекторию. Т.к. ошибки округления накапливаются, иногда сервер присылает уточнённые абсолютные координаты объектов.

                            Если какой-то клиент получает информацию с задержкой, то он просто пересчитывает полёт объектов. Для эргономики внешнего вида я бы просто использовал в клиенте две координаты: синхронную с сервером и получившуюся из-за «продолжения движения» из-за лага. Дальше у нас простая задача: есть плавная идеальная тракетория и плавная ошибочная траектория, не меняя гладкости функции обеспечить переход ошибочной тракетории в идеальную. Вероятнее всего, для наблюдателя это будет выглядеть как «подруливание» чужих кораблей, тем более частое, чем хуже канал связи.

                            Задача перевода одной гладкой функции в другую реализуется очень просто — используем экстраполяцию идеальной тракетории на следующую точку, а потом интерполируем точки ошибочной тракетории плюс одна (две?) точки, экстраполированные из идеальной траектории. Дальше по заданной тракетории мы можем посчитать производную третьего порядка в нужных нам точках для определения изменения ускорения (что соответствует визуальному «подруливанию») корабля.
                              0
                              Да, теперь практически все совпало ( с точностью до используемой физики). Единственный скользкий момент здесь — синхронизация часов сервера ии клиента. Небольшое расхождение даст неприятный эффект.
                                0
                                Синхронизаций есть два вида: соответствие t0 на сервере и на клиенте (реализуется элементарно, достаточно запомнить смещение для первого пакета), и расхождение Δt через некоторое время. Вторым можно пренебречь.

                                Ключевым моментом является сохранение плавности — то есть отсутствие разрывов в третьей производной. По бедности — во второй, хотя будет ощущение неестественных рывков.
                                  0
                                  > достаточно запомнить смещение для первого пакета
                                  Не достаточно. Они не настолько равномерно ходят. Задача решаема, (например, так). Но несколько громоздко для игрушки.

                                  Но в целом, все, что вы говорите, правильно. Хотя и немного переусложнено, для наших целей.
                          0
                          Это уже и так освещалось подробнее тут. Без технических подробностей бесполезная статья. Хоть бы куски кода, какой сервер, где и прочее.
                            0
                            На мой вгляд, описание работающего пртокола, на создание которого было потрачено немало времени и услий — вещь небесполезная.
                            0
                            А в чем проблема просто держать на клиентах технические характеристики всех кораблей (скорость движения, ускорение, и так далее), и положения курсоров всех противников? Что бы, по сути, клиент и сервер просчитывали движения всех кораблей самостоятельно и одинаково?

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

                            Есть еще тема. Каждое действие игрока сразу отправляется на сервер, где попадает в общий лог действий игроков с временными метками. Каждый клиент получает такой же лог, как и все остальные. Имея ТТХ кораблей и лог, все клиенты будут отрисовывать игровое поле абсолютно идентично и без рассинхронизаций в принципе. Даже события типа «ранил», «убил» будут генерироваться самостоятельно и идентично на всех клиентах. Минус — если игрок двинул мышью, то его локальная копия игры отреагирует только, когда получит ответ от сервера с временной меткой
                              0
                              Проблема в том, что время (абсолютное) на сервере и на клиенте может не совпадать. И сделать его идеально одинаковым не получается.
                                0
                                этого и не нужно. Временная метка все равно проставляется исключительно сервером. Клиент берет ее за основу даже для своих событий (сам двинул мышкой).

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

                                  Судя по всему так сделали Blizzard в Heroes of the Storm. Реконнекта приходится ждать по 5-7 минут пока клиент просчитает все что происходило за период дисконнект-соединение.

                                  0
                                  В чем проблема синхронизирваться с сервером с учетом пинга и использовать абсолютное или лучше относительное время с периодическим пересчетом?
                                    0
                                    Можно, но сложновато. Не в том смысле, что лень делать. А в том, что каждый новый уровень сложности добавляет в систему хрупкости. Если можно не усложнять, нужно не усложнять.
                                +2
                                было интересно почитать, но играть в это Г невозможно
                                  0
                                  Почему? (правда интересно)
                                    0
                                    ohm ответил мне в личку. Вопрос снимаю. Спасибо за полезный фидбэк!
                                  +1
                                  Молодцы, граблей собрали :)
                                  Насчет физики, мой вариант сильно приятнее: space.playcode.io

                                  Кстати я тоже на Go пишу и интересна тема agar.io like игр.
                                  Напишите мне (в профиле есть контакты).
                                    0

                                    Да, приятнее управление, чем у ТС, но что будет при взаимодействии с массой других игроков.

                                      0
                                      Очень интересно.
                                      И использованный на бекенде язык тоже )
                                        +1
                                        Там нет бекенда.
                                        +1
                                        Сделали поближе к вашему варианту. Спасибо!
                                          0
                                          У нас получается работать удаленно :)
                                            0
                                            Кстати, вы наверное видели, здесь playcode.io/space есть исходный код.
                                              0
                                              Код не видел.
                                          0
                                          было интересно последить за ходом вашей мысли :)
                                            0
                                            Хотел показать, как простая, на первый взгляд, задача не всегда решается сходу.
                                            А простое решение часто создается не самым простым способом.
                                            +1

                                            Интересно было бы реализовать p2p коммуникацию. Допустим каждый посылает каждому свои координаты на следующую долю секунды. Каждый узел рассчитывает всю физику и рассылает результат пирам. Если показания расходятся (читер подделывает показания), то сеть автоматически распадается на непротиворечивые подсети. Таким образом читер будет играть сам с собой, а нормальные игроки друг с другом. Соответственно кластеров может быть сколько угодно, совершенно не нагружая сервер.

                                              0
                                              С чисто исследовательской точки зрения, соглашусь. Интересно. Но с практической — ненужная трата ресурсов.
                                                0

                                                Каких ресурсов? Наоборот, на сервере почти не тратим ресурсов, а клиентам так и так считать физику приходится для плавности движений.

                                                  0
                                                  человеческих :)
                                                0

                                                Можно даже не результат рассылать, а просто хеш от всех рассчитанных координат, векторов и.т.д.
                                                Этакий блокчейн для игры.
                                                Вот это реально красивая идея. Надеюсь уже кто-то что-то сделал подобное.

                                                +1
                                                Стоит FF последней версии, открыто 2 вкладки habr и сайт игры — тормоза страшные, корабли рывками прыгают и «телепортируются», даже меню выезжает с дерганьем. Через 30 сек FF выдал такое:

                                                An error occurred running the Unity content on this page. See your browser's JavaScript console for more info. The error was:
                                                NS_ERROR_NOT_CONNECTED:

                                                В консоли:
                                                An abnormal situation has occurred: the PlayerLoop internal function has been called recursively. Please contact Customer Support with a sample project so that we can reproduce the problem and troubleshoot it.

                                                (Filename: Line: 56)
                                                  0
                                                  Спасибо. Такой баг был в одной из прошлых версий юнити. Вроде его исправили. Но, похоже, снова появился. Он повторяется при перезапуске браузера?
                                                  Попробую собрать клиент последней версией юнити. Возможно, поможет.
                                                    0
                                                    Ошибка за пару минут игры не появилась, но тормоза остались те же, даже с отключенной музыкой, звуками и низкой графикой установленными в настройках. Попробовал запустить на хроме (но он уже на WinXp давно не обновлялся), то же самое — тормозит всё неимоверно. Вообще странно конечно… не знаю, может проблема с соединением к вашему серверу, коннект за пару минут 1 раз отваливался.
                                                      0
                                                      А FPS Какой показывает (слева вверху)?
                                                        0
                                                        Когда меню выезжает 3-5, в процессе игры 7-15
                                                          0
                                                          Значит просто не тянет железо. В норме должно быть 60. Минимум 45.
                                                          WebGL штука прожорливая

                                                          Можно физически уменьшить окно браузера — это помогает видеокарте.

                                                          Можно зайти в настройки и повыключать все что можно (фон, вращение, качество моделей)
                                                          Но играть на таких настройках грустно.
                                                            0
                                                            Куда мир катится… :)

                                                            Devil May Cry 4, Need for Speed: Most Wanted… работает без тормозов, а простенькую леталку на минимальных параметрах не тянет GeForce 630 и Athlon 64 3200+ 2.1Гц… вот он прогресс 2004 — 2017 и хвалебные песни «быстрой разработке».

                                                            P.S. при уменьшении окна браузера действительно +5 fps
                                                              0
                                                              Это так. WebGL — вообще сыроватая технология. Ожидается, что там будет прирост производителеьности еще довольно большой.

                                                              Кстати, если у вас комп с двумя видеокартами, то браузер всегда использует ту, что послабее.
                                                                0
                                                                WebGL — всего-лишь js-обертка над OGL ES. Расскажите поподробнее что именно в ней сырое? И, если не сложно, расскажите за счет чего ожидается большой прирост производительности, можно даже просто ссылкой на пруф.
                                                                  0
                                                                  Тут немного шире проблема. Я был неточен.
                                                                  Сам js на котором написана логика клиента — медленный и к тому ж однопоточный. Первый шаг в сторону его ускорения — asm.js Именно его использует Unity и, соответственно, эта игра.
                                                                  Дальше ждем webassembly а также поддержку многопоточности.
                                                                  Ну и работает пока нормально только webgl 1.0 (который обертка над opengl 2).
                                                                  Кроме того, реализация поддержки его браузерами пока не идеальна и улучшается почти с каждой версией.
                                                  +1
                                                  Достаточно же просто иметь на клиенте данные, которые нужны для расчёта будущих движений в предположении бездействия игроков. Исходя из этого можно постоянно иметь свою версию происходящего на клиенте и получать в дополнение к ней версию сервера с небольшой задержкой. Ну и плавно подправлять траектории в случае расхождения. В том же agar.io достаточно хранить направление движения каждого объекта. Там, правда, будут проблемы с поглощением объектов, так как непонятно, что делать, если в версии клиента оно произошло, а в версии сервера – нет. Ну или наоборот.
                                                    0
                                                    Мы ровно это и сделали в итоге.
                                                    +1
                                                    Решил поиграться, через несколько минут увидел следующее:

                                                    У Вас закончились все жизни!
                                                    Одна жизнь восстановится через 28м.26c.
                                                    Либо, Вы можете купить жизни или дроида: (далее контент донат-магазина)

                                                    Вы, что, серьезно???

                                                    Я смотрю, у вас маркетологи ушли не на много дальше программистов ))
                                                      0
                                                      Речь идет о внтуриигровой валюте.
                                                        0
                                                        Ну да, речь про игровую валюту, покупаемую в том числе (читай, в основном) за реальные деньги.

                                                        Ограничение игры по времени, продлеваемое за валюту — суть подписка.

                                                        Вы, естественно, считаете, что на вашу игру люди будут с удовольствием подписываться плюс арендовать (вы что, серьезно???) явно выраженные игровые преимущества, но меня не покидает чувство, что ваша бизнес-модель потерпит фиаско.
                                                          0
                                                          Тонкость в том, что поступающего из игры потока внутриигровой валюты должно хватать абсолютному большинству игроков. Статистика по нашим другим играм: 99% игроков играют бесплатно. 1% настоящих фанатов игры делают всю кассу.
                                                            0
                                                            Тогда тем более уберите эти ограничения времени. Ведь, я уверен, большую часть дохода приносит продажа шмота, а не продажа жизней. Это очень легко объясняется — люди покупают шмот чтобы нагибать, а не для того, чтобы погибать. Зато уровень лояльности ваших игроков заметно повысится и не исключено, у вас станет 2% настоящих фанатов ))
                                                              0
                                                              Не исключено, что вы правы. Балансом в этой игре мы еще всерьез не занимались. Релиза не было.
                                                      0

                                                      Почти все статьи на хабре про "как мы писали сетевую игру" проходит через одни и те же стадии :)

                                                        0
                                                        Неудивительно. Но я не нашел ни на Хабре ни где-то еще четкого описания такого протокола. Именно с поиска я и начал. Но везде были только общие слова про обеспечение одновременности.
                                                          0
                                                          Вы плохо искали, так как вверху в комменте я скидывал ссылку на хабр. Так намного более подробно все описано. Не знаю где вы в своей статье увидели описание протокола, там только описание подхода. И до сих пор не понимаю почему статья не затегана «я пиарюсь» — ссылка на сайт и технически статья ни о чем. Ничего личного, но это очень сырой материал.
                                                            0
                                                            Эту статью я читал. Там не описан протокол, там дается введение в проблемную область. Что, конечно, тоже очень полезно.

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


                                                            На сегодня 111 человек сохранили статью в избранное, что говорит о некоторой её полезности.

                                                            Насчет ссылки — тут дилемма. Поставь — вы упрекнете в рекламе. Не поставь — спросите: «Где пруфы, Билли?»
                                                              0
                                                              Вот же оно:

                                                              При всём уважении, это не протокол.

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

                                                      Only users with full accounts can post comments. Log in, please.