Интерполяция в играх что это

[FAQ]Итерполяция, лерп и его важность в играх.

23 Apr 2013 в 21:38

Интерполяция в играх что это. Смотреть фото Интерполяция в играх что это. Смотреть картинку Интерполяция в играх что это. Картинка про Интерполяция в играх что это. Фото Интерполяция в играх что это

Интерполяция в играх что это. Смотреть фото Интерполяция в играх что это. Смотреть картинку Интерполяция в играх что это. Картинка про Интерполяция в играх что это. Фото Интерполяция в играх что это

23 Apr 2013 в 21:38 #1

Не всем игрокам сообщества Dota 2 известен такой параметр как интерполяция (далее лерп), но игроки более ранних произведений валв знают важность этого параметра, начиная с counter-strike он был очень полезен для адаптации полученных пакетов к внутрисерверной среде, ведь как-то нужно сгладить полученные 30 пакетов и отобразить действие которое происходит между ними, это очень важный инструмент в сетевой игре, который помогает сглаживать движение и в какой-то мере предугадывать положение хитбокса на местности.
Естественно у этого параметра есть минус- время обработки, соответственно и увеличивает время отклика.
Наша задача уменьшить и минимализировать это время отклика.

Q: Какими командами изменять lerp?
A: Lerp изменяется двумя параметрами самого временного отрезка на интерполяцию и интервалом интерполяции cl_interp cl_interp_ratio

Q: Где можно посмотреть какой у меня сейчас lerp?
A: В консоли, вызвав параметр cl_interp или в net_graph .

Q: Можно ли как-то рассчитать для себя идеальный параметр lerp?
A: Можно, cl_interp_ratio\cl_updaterate= 1\30 = 0.033, но это минимальное значение, которое не применимо в обычной игре дома т.к. накладываются огрехи в виде пинга, тикрейта, нагрузки и работы самого сервера, оборудования, которое обеспечивает связь. По сути дела лерп можно еще уменьшить в два раза увеличив количество команд посылаемых от сервера cl_updaterate, но valve ограничили эту переменную на 30.

Q: Зачем это нужно?
A: Уменьшить время отклика, отсюда следует улучшение ластхит потенциала на таких героях как Razor или Sniper, у которых сам хит проходит немного позже анимации. Игра будет быстрее реагировать на ваши посланные команды. Хуки будут вылетать раньше, блинки кастоваться быстрее.

Q: Какие минусы возникают при уменьшении лерпа?
A: Как и плюсы- они минорные- уменьшая временной отрезок вы также и уменьшаете расстояние поражение хитбокса в движении. Это важно лишь для такого героя как Pudge или Clockwerk.

Интерполяция в играх что это. Смотреть фото Интерполяция в играх что это. Смотреть картинку Интерполяция в играх что это. Картинка про Интерполяция в играх что это. Фото Интерполяция в играх что это
Дефолтный хитбокс в движении при lerp 0,066
Интерполяция в играх что это. Смотреть фото Интерполяция в играх что это. Смотреть картинку Интерполяция в играх что это. Картинка про Интерполяция в играх что это. Фото Интерполяция в играх что это
Хитбокс при lerp 0,033

Q: Так может мне увеличить лерп что бы мой скилл попадания хуком стал 100% точным?
A: Это не значит, что при увеличении лерпа вы станете попадать 32\32 хуков, но во время уменьшенного лерпа точные хуки всегда попадают и вы сможите хукать даже сквозь еще «не убитого» крипа. http://youtu.be/C-3O0Y9xmiM?t=23s
https://www.youtube.com/watch?v=y0FGwk72fYk

Надеюсь что-то из описанного здесь оказалось для вас полезным, на вас работал Battle Roar, эксклюзивно для портала dota2.ru. Попрошу модераторов закрепить тему, дабы труд не был напрасным.

Источник

О прогресс-барах, интерполяции и программистах за спиной

Способы заполнить шкалу.

Привет. С вами Маргарита Шаповалова (aka KiraLinch), младший геймдизайнер студии игровой разработки OctoBox Interactive.

Освежила в памяти тему интерполяции и объяснила в заметке так, что сама ещё раз поняла. Немного теории и, конечно же, практическое применение на примере Sky Unlimited Inc.

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

Задача: улучшить обратную связь, сделав заполнение прогресс-баров более плавным в прототипе Sky Unlimited Inc. Это должно помочь игроку визуально лучше фиксировать изменения параметров.

«Ну, конечно! Можно просто сделать анимацию, ведь я умею это делать. Это легко и быстро», – подумала я и сделала. А потом подумала еще раз.

А как теперь сделать так, чтобы анимация отражала те изменения, которые испытывает параметр?

Путём простых размышлений я пришла к трём вариантам:

С размышлениями и вариантами я пошла к своему гуру. И гуру молвил: «Вспомни, как двигались стулья».

Какими способами я только не двигала стулья в Unreal. Но сила мысли и Lerp, кажется, были ответом на мой вопрос.

Lerp — это сленговое в среде программистов обозначение линейной интерполяции.

У вас есть два разных узла A и B. Представьте, что они соединены прямой линией. С шагом Alpha от точки A находится точка A1, которая показывает, насколько далеко по соединительной линии вы находитесь между узлами A и B.

Затем точка A становится точкой A1, и теперь уже новая A1 определяется с тем же шагом Alpha на меньшем отрезке. Так происходит до тех пор, пока вы асимптотически не приблизитесь к точке B, то есть A всегда стремится к B, но никогда не достигает ее фактически.

Текущее значение параметра «Эффективность» равное 0,3 подаётся на вход A. После действия игрока, по внутренней логике, число меняется и становится равным 0,4. Новое значение параметра подаётся на вход B.

Устанавливаем Alpha, равное 0,1, это 10% от отрезка между A и B.

В итоге Lerp возвращает значения, равные всем промежуточным значениям между A и B c Alpha 0,1 и асимптотически достигает B.

Вот так это выглядит в итоге с Lerp с Alpha=0,1.

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

Казалось, всё. Задача выполнена: всё работает хорошо, красиво, но.

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

В тот момент, когда я уже всё доделала с помощью Lerp, появился программист и говорит: «А чего не FInterp To?»

И показывает мне ещё один вариант решения поставленной задачи с помощью FInterp To Constant.

FInterp To Constant – это тоже интерполяция, но при заданной скорости и времени и до константы. То есть в независимости от того, какое расстояние от текущего положения к заданному предстоит преодолеть, это будет сделано с одинаковой скоростью и за одно и то же время, и гарантировано достигнет заданного значения. В отличие от FInterp To и Lerp, которые всегда приблизительно находятся возле конечной точки.

Текущее значение параметра Эффективность равное 0,3 подаётся на вход Current. После действия игрока по внутренней логике число меняется и становится равным 0,4. Новое значение параметра «Эффективность» подаётся на вход Target.

Устанавливаем скорость 0,25, а Delta Time берётся из Event Tick, и равна одному тику.

Источник

Player.IO. �?нтерполяция или удивительный мир обмана

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

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

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

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

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

�?нтерполяция или сглаживание

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

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

А в идеале решение должно выглядить таким образом:

Безусловно, самый простой метод интерполяции это линейное сглаживание — это означает, что мы берем разницу между тем, где находится объект сейчас и тем, где он должен быть, а потом делим эту разницу на количество кадров, за которые он должен оказаться в новой позиции. Тут главное помнить, что окончательная позиция должна быть идеальной с точки зрения синхронизации. То есть наша задача заключается в том, чтобы показать игроку, что должно происходить, чтобы сохранить правильную иллюзию движения.

Интерполяция в играх что это. Смотреть фото Интерполяция в играх что это. Смотреть картинку Интерполяция в играх что это. Картинка про Интерполяция в играх что это. Фото Интерполяция в играх что это

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

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

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

Экстраполяция или прогноз движения

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

Помните наш клиент-серверную архитектуру? Давайте вернемся к этой теме еще, чтобы увидеть, что одна из очень важных частей этой системы сейчас у нас отсутствует.

�?стинный экземпляр каждого объекта содержится исключительно на сервере. �?ли проще говоря, сервер содержит основую копию всех игровых данных. Событие не произойдет в действительности пока сервер не скажет, что это произошло. �? как вы можете убедиться, проблема зарыта именно здесь. Каждый раз, когда мы нажимаем клавишу, то наш персонаж не двигается до тех пор, пока сервер не пришлет сообщение с подтверждением. Это создает очень ощутимую задержку на реакцию вашей игры и приводит к плохой отзывчивости на действия игрока.

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

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

Что такое пинг?

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

Для рассчета пинга мы можем одновременно отправить простое сообщение на сервер и записать текущее время игры по вызову Date.getTime(). Когда сервер получит такое сообщение он должен отправить его обратно. Когда вы получаете это сообщение обратно от сервера, вы вызываете Date.getTime() вновь и разница между первым и вторым вызвом метода Date.getTime() и будет нашим пингом.

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

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

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

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

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

А так может выглядить наше предсказание:

Красный контур демонстрирует предсказание.

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

Заключение

�? так, подведем небольшой итог того, что необходимо использовать для сетевой игры:

Сейчас вы можете взять небольшой перерыв. Все эти понятия являются не простыми и могут вызвать не самые приятные ощущения в вашей голове. Так же вам будет полезно ознакомиться с исходниками примера, демонстрирующим всю описанную теорию. А когда вы немного передохнете, мы перейдем к нашей следующей теме: «Как бороться с задержкой?»

Ознакомтесь с обновленным примером сетевой игры. Новый пример практически не отличается от прошлого примера, основные изменения были произведены в методe enterFrameHandler() и в методе messageHandler() в секции обновления состояния. Более того пример подключается к той же ячейки сервера, что и первый пример, таким образом, открыв новый и старый пример в разных окнах браузера, вы можете наглядно увидеть разницу с интерполяцией и без в одной сетевой игре. Серверный код остался без изменений.

Источник

Мультиплеер в быстрых играх (Часть III: появление врага)

Интерполяция в играх что это. Смотреть фото Интерполяция в играх что это. Смотреть картинку Интерполяция в играх что это. Картинка про Интерполяция в играх что это. Фото Интерполяция в играх что это

Введение

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

В этой статье мы рассмотрим последствия одновременного подключения нескольких игроков к одному серверу.

От переводчика: в этой и последующих статьях для гифок используется демка, написанная мной и bogotoff из NerfGame.

Частота обновления сервера

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

Более удачным подходом было бы накапливать все команды без выполнения. Вместо этого мир будет обновляться периодично с низкой частотой, например 10 раз в секунду. Во время каждого обновления все накопленные команды применяются(возможно с несколькими шагами физики, чтобы она была более стабильной) и новое состояние игры рассылается по клиентам.
Прим. Перев. В быстрых играх вы скорее всего захотите выставить частоту обновления не меньше 20 раз в секунду(как в Overwatch) и поднимать его вплоть до 120 (как в CS:GO).

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

Справляемся с редкими обновлениями

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

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

Интерполяция в играх что это. Смотреть фото Интерполяция в играх что это. Смотреть картинку Интерполяция в играх что это. Картинка про Интерполяция в играх что это. Фото Интерполяция в играх что это

Интерполяция в играх что это. Смотреть фото Интерполяция в играх что это. Смотреть картинку Интерполяция в играх что это. Картинка про Интерполяция в играх что это. Фото Интерполяция в играх что это

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

Экстраполяция

Предположим, что вы делаете гонки. Машины довольно предсказуемы — если машина едет 100 м/с, через секунду она будет примерно на 100 метров впереди от того места, где она была.

Почему “примерно”? В течении этой секунды машина могла немного ускориться, или затормозить; немного повернуться. Ключевое слово здесь — немного. Машины устроены так, что позиция преимущественно зависит от предыдущей позиции, скорости и направления; и в меньшей степени от действий пользователя. Другими словами, гоночная машина не может мгновенно развернуться на 180 градусов.

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

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

Этот метод подойдет только для объектов с большой инерцией: машины, корабли.

Интерполяция

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

При этом вы все еще не можете наивно применять обновления с сервера: игроки будут телепортироваться каждые 100 мс и при каждом потерянном/задержавшемся пакете.

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

Пусть вы получаете позиции в момент t = 1000. Вы уже получили информацию о t = 900, так что от t = 1000 до t = 1100, вы показываете что этот игрок делал с t = 900 до t = 1000. Таким образом вы показываете настоящее передвижение врагов, но на 100 мс позже.

Интерполяция в играх что это. Смотреть фото Интерполяция в играх что это. Смотреть картинку Интерполяция в играх что это. Картинка про Интерполяция в играх что это. Фото Интерполяция в играх что это

Учтите, что при использовании этой техники игроки видят немного разное состояние мира, так как себя игрок видит в настоящем времени(это необходимо для отсутствие лага при вводе), а врагов видит в прошлом(это было бы даже без интерполяции, из-за ненулевого пинга). Но даже в быстрых играх задержка на 100 мс обычно незаметна.

Интерполяция в играх что это. Смотреть фото Интерполяция в играх что это. Смотреть картинку Интерполяция в играх что это. Картинка про Интерполяция в играх что это. Фото Интерполяция в играх что это

Но есть исключения — если нужна высокая пространственная и временная точность, например при выстреле одного игрока в другого. Так как другие игроки видны в прошлом, вы целитесь с задержкой в 100 мс + пинг, то есть туда, где враг был более 100 мс назад! С этим мы будем разбираться в следующей статье.
Интерполяция в играх что это. Смотреть фото Интерполяция в играх что это. Смотреть картинку Интерполяция в играх что это. Картинка про Интерполяция в играх что это. Фото Интерполяция в играх что это

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

Но другие игроки все еще для нас были проблемой. В этой статье мы рассмотрели два способа справиться с ними:

Экстраполяция — применяется когда позиция может быть предсказана исходя из предыдущей информации — позиции, скорости и ускорения.

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

В итоге игрок видит себя в настоящем, а остальных игроков — в прошлом. Обычно это создает отличный игровой опыт.

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

Источник

Кадровая интерполяция, а-ля 60фпс из ничего для игр?

Что знаете по этому поводу? Тв-шника с такой функцией у меня нет, но я бы хотел попробовать фейковые 60 фпс из 30-ти.
Есть ли какой-то софт по этому поводу?

Понятно, что будет инпут лаг и бла-бла-бла, но, просто, интересно попробовать.

В неспешных играх типа кроссинга вполне нормально работает, но в динамичных превращается в говно.

Ну там главное чтобы исходные 30фпс были стабильными, иначе каждая просадка будет прям сильно на нервы действовать, на фоне общей плавности. И на мой взгляд как раз в динамичных максимальный эффект, особенно в 2Д-играх, где всё движется «в плоскости экрана».

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

Я все-таки думаю такая штука будет полезна в играх, где частота кадра варьируется от 50-60, чтобы просадки не особо ощущались.

Вот заикнулись об этом и я подумкал тут. А фигли новым консолям ее не заиметь? Как бы ряд моделей тв имеют очень щедящий импут (дорогие Самсы, случайные модели ЛыЖек) и по факту это позволит разрабам схалтурить слегка на стабильности. При этом надо будет не с 30 кадров повышать до 120, а фактически с 40-50 до 60. Что-то подобное замутить будет не то чтобы сложно людям, которые делают тв(сони) и сотрудничают с самсунгом (майки). В теории это люто выгодная хня для синглплеерных проектов.

UPD ах да, в любом видео (фильмы/тв) известен следующий кадр, а в играх нет. попробуй-ка построй промежуточный кадр между текущим и следующим (который не известен), гений.

Да и вообще, я говорил о развитии технологии и не уплавнении с 30 кадров. При работе с 45-50 кадрами и нынешние телевизоры сделают всё очень даже удобоваримо (если не дешевить, офк). А уж если целенаправленно пилить для одной консоли, а не штук 6 вариаций на каждый класс телевизора с их доп обработкой цветов, может выйти вполне себе ничего.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *