Перейти к контенту

Malandrinus

Жители
  • Число публикаций

    1 930
  • Регистрация

  • Последнее посещение

  • Дней в топе

    13
  • AMKoin

    120 [Подарить AMKoin]

Весь контент пользователя Malandrinus

  1. Это не отменяет всего остального, что делает Lua непригодным для серьёзной разработки, и также всё равно остаётся барьер между языками. Барьеры надо стирать. Вот как в случае с DLL. Статически линкуемые компоненты лучше dll потому что снимают многие барьеры. @TIGER_VLAD, на здоровье =)
  2. Таки отличная аналогия "Пульт" - это как раз хорошая аналогия уместного применения скриптов. Пульт же не заменяет телевизор, а только управляет им. А вот когда мы скриптами начинаем ваять кусок движка - это как если в пульт перенести приёмник и посылать в телевизор сам видеосигнал. Пульт в итоге будет тяжёлый, будет жрать батарейки и глючить. Вот сейчас в сталкире в точности второй вариант. Схемы логики. Я кажется обозначал это несколько раз. Я считаю Lua неподходящим для подобной работы. GUI тоже. У детской тележки четыре колеса. У автомобиля четыре колеса. Детская тележка и автомобиль - одинаковы. Ну нельзя же так. Я ведь даже упоминал одно из отличий, неужели нельзя было его прокомментировать? Кроме отсутствия жёсткой типизации я ещё могу перечислить: малое количество типов вообще, убогая работа с целыми типами, отсутствие препроцессора, бедные возможности ООП, нет обобщённого программирования, нет прямого доступа к памяти, таблицы отнюдь не заменяют все не свете коллекции. Ну т.е. вместо того, чтобы абстрагироваться от движка и красиво ваять скрипты надо за каждой мелочью обратно лезть в движок, чтобы эту каждую мелочь экспортировать. И чего мы в итоге добились? Вот для логики и недостаточно. Я конечно смеюсь, когда народ начинает обсуждать, какой синтаксический сахар работает быстрее, но вообще то эти разговоры не от хорошей жизни. Я с этого и начал. Скриптов явно перебор.
  3. Действительно ли ложного? Соображений, которые подтверждают эту мысль, достаточно много. Возьмём Lua, тем более что нет особенного смысла обсуждать что-то иное в контексте сталкира. 1. Язык примитивен. Это упрощает изучение (и программирование простых задач), но соответственно ограничивает инструментарий и усложняет решение действительно сложных задач. Здесь можно много чего перечислить, но упомяну хотя бы отсутствие жёсткой типизации. Новичкам это кажется благом. Опытный же программист лишается важного инструмента снижения сложности и контроля ошибок. 2. Ограничения библиотек и скриптовой модели. По-простому, что экспортировано, то и доступно, что из стандартных библиотек, что из API игры. Заведомо невозможно иметь настолько же полный доступ как к системе, так и к игре, как из С++. 3. Производительность. При уместном употреблении скриптов этот вопрос не должен возникать, однако скриптов явно перебор. поэтому не спасает даже LuaJIT. Если бы скрипты использовались только в уместном объёме и в уместных местах, то было бы по большому счёту без разницы, JIT там или голый интерпретируемый Lua. 4. Среда разработки, точнее её отсутствие. Таки нету её, и это реальный недостаток со всеми вытекающими, в первую очередь невозможность полноценной отладки. Т.е. сперва создали проблему использованием скриптов в неподобающих целях, а теперь превозмогаем? Может всё-таки просто не создавать проблемы? Ведь инструмента под Lua нет, под С++ есть, и Lua явно выглядит менее подходящим в целом для серьёзной работы.
  4. Не соглашусь. Общее потраченное на разработку время включает в себя также и отладку. Причём отладка занимает существенно большее время, нежели собственно внесение правок. А здесь как раз тот момент, который я выше упоминал: "Скрипты не предназначены для серьёзной алгоритмической работы". Отладить без серьёзного геморроя можно только что-то очень простое и самоочевидное. Прекондишены в диалогах - самое то. Схемы - на порядок сложнее. Сборка движка, если не требуется каждый раз собирать его с нуля, а просто скомпилировать пару файлов с последующей линковкой, занимает секунд 10-15, особенно если не заморачиваться на полную оптимизацию. Так что сравнивать надо уже время отладки. А вот тут начинается самое интересное. Если в случае отладки сложного скрипта нужно запускать игру десятки раз с вылетами и последующим анализом лога (который даёт далеко не полную информацию), то в случае отладки в среде разработки количество запусков сокращается на порядок из-за существенно большей информативности (можно остановить отладку, посмотреть все переменные, пройти по шагам и т.п.). Если вдаваться в лирику, то отладка скриптов в сталкире живо напомнила мне отладку программ на ассемблере под Спектрум (олдфаги помнят эту машинку). Грузишь с кассетного магнитофона ассемблер, грузишь с магнитофона программу, компилишь, запускаешь, отлаживаешь почти вслепую. От малейшего чиха всё перезагружается и начинаешь всё сначала: грузишь одно, другое... Лепота. Терпение развивало просто железное.
  5. Предлагаю обсудить такой вопрос, как роль скриптов в движке. Вероятно, многие "прикипели душой" к текущему раскладу в движке сталкира. Биндеры, логика, схемы, скриптовые гулаги воспринимаются многими как железобетонная данность. Однако в виду наличия исходников я бы пересмотрел роль скриптов в разработке. Начать можно с того, а с чего собственно вообще скрипты. Исторически никаких скриптов в сталкире не было. Все схемы логики были внутри, квестовая составляющая также прекрасно обходилась без скриптов. Есть даже билды, не помню за какой год и с каким номером, где это хорошо видно - вполне можно обойтись без скриптов. Добавили их позже. Официальная версия - чтобы загнать симуляцию в квестовые рамки. Вот здесь возникает вопрос, а что мешало это сделать без скриптов? Что мешало ограничить миграцию неписей и добавить аналог гулага и скриптовых работ на уровне движка? На мой взгляд, если бы изначально такая задача стояла, то не было бы никаких проблем с её реализацией. У меня есть только одна версия, почему так поступили. Я думаю, что на тот момент попросту уже ничего было не сделать с движком. Его сложность превысила пределы, за которыми можно оперативно внести такие изменения. В итоге со скриптами вышел мутант. Вместо того, чтобы использовать скрипты для склейки движковых компонент, скрипты по сути использовались для написания весомой части игровой логики, которая заменила собой часть внутренней. Большая часть движковых схем поведения осталась неактивной. Даже управление анимациями сделали через внешний планировщик. На мой взгляд, всё это совершенно ненормально. Скрипты попросту не предназначены для такой серьёзной алгоритмической работы, как написание схем поведения. Прекондишены в диалогах и квестах - вот разумный диапазон применения. Другое объяснение, зачем скрипты, может заключаться в том, что не хотели раскрывать исходники большому количеству людей. Скрипты это как раз и позволяют. Соответственно, вывод простой: роль скриптов при наличии открытой архитектуры и отсутствии коммерческих рамок надо радикально снижать. Логику реализовывать исключительно внутри движка. GUI - может да, может нет. Откровенно говоря, для окошек тоже нет особой нужды в скриптах. Мне до сих пор непонятно, зачем заскриптовано главное меню. Где можно оставить, это в квестовой логике, проверять сложные условия, которые тяжело сформулировать единственной инфопорцией, и выполнять примитивные действия там же (передача предметов, выдача денег и т.п.). Для последней задачи скриптовая модель требуется куда проще, чем она есть сейчас. Нужны только классы и функции для получения информации и простейших инвентарных манипуляций. Что думаете?
  6. Вдогонку к сказанному по поводу интерфейса редактора на wxWidgets. Вот тестовая сборка ЗП с дополнительным внешним окошком на базе wxWidgets, которое запускается в отдельном потоке. https://yadi.sk/d/RNFiyFwNufMrU Примечания (просто чтобы было понятно, что это и о чём вообще): В комплекте несколько файлов скриптов/конфигов, чтобы запускалось. В архиве всё нужное содержимое папки bin (подразумевается рантайм от 2015 студии). Все библиотеки вкомпилированы в экзешник, кроме openal32.dll (там какие-то проблемы с инициализацией при сборке в виде статической либы) и eax.dll, которая вообще имеется только в виде dll. В силу монолитной сборки рендер только один, четвёртый.. Это не рабочий движок "под ключ", а просто демонстрация присоединения внешнего GUI. Если повезёт, то получится запустить игру, но сохранение/загрузка в целом поломаны моими экспериментами. Внешнее окошко взято от балды. Это просто один из примеров от wxWidgets с какими-то пустыми фреймами с возможностью докинга. Никакого взаимодействия его с игрой нет. Поскольку окно рендера захватывает мышь, то надо переключаться по Alt-Tab, чтобы иметь возможность манипулировать этим окном.
  7. @Kirgudu, у меня вместо такой формы какая-то текстовая белиберда со словами о каких-то внутренних исключениях. Студия 2015. Да и всё равно, работает или нет. Я понял, что именно было сделано, и также понял, что для меня это не вариант. К чёрту всех этих мелкософтовских мутантов. Меня достало это стремление сжить со свету С++. Поскольку я не связан в данном случае никакими корпоративными ограничениями, то я и не обязан прогибаться под их мерзоту. Есть прекрасные альтернативы под чистые плюсы, хотя бы wxWidgets. Отлично работает со студией 2015, и там есть полный спектр GUI красот, включая docking. Проблему вижу только в том, что там некое время назад убрали сборку под ANSI строки, и сделали чисто под юникод. Это напрягает. Уже столкнулся с этим при работе над редактором диалогов (это в Stalker Plot Editor). Приходится там и здесь конвертировать туда-обратно. Но это пережить можно. А в целом надо просто завести в движке ещё один проект с интерфейсом, основанным на wxWidgets, класс приложения при этом надо создать не в основном потоке, а в отдельно созданном, чтобы была своя виндовая очередь сообщений.
  8. Не работает. Вместо формы появляется страница с сообщением о какой-то ошибке. По сети пишут, что и не должно работать, поскольку мелкософт дескать всех силком переводит под C#. Кто я такой, чтобы спорить с мелкософтом? =) Не хотят - и не надо, есть альтернативы.
  9. Насколько я понял, Microsoft так и не сделали редактора GUI для C++. И из набора доступных проектов в студии это тоже очевидно. Так что мой вопрос @SkyLoader-у по сути остаётся, что использовалось для GUI? C# ? А как тогда строится взаимодействие с запчастями от игры? Впрочем, вопрос уже чисто теоретический. Лично я так делать бы не стал в любом случае. Взял бы любой из доступных для С++ фреймворков для окошек, скажем wxWidgets, поскольку я с ним знаком, и не парился бы.
  10. @User_X.A.R26, Я понял проблему. Нельзя аргументом шаблона сделать слово "event", видимо ключевое слово. Переименовал - собралось. Здесь ранее заходила речь о передаче внутриигровых сообщений через нетпакеты. Вопрос стоял так: "какая альтернатива?" Вот набросал черновой вариант системы асинхронной передачи сообщений. В архиве по ссылке заголовок с классом и служебными макросами.
  11. Хотя бы как в сингле сталкира. Не соглашусь. Когда происходят пострелушки, то все эти манипуляции с экстраполяцией малозаметны. Вообще, когда один сетевой персонаж (непись или игрок, неважно) стреляет в другого, то это в сущности как в детской игре или в том анекдоте: "пиф паф, я попал, ты убит". Т.е. если хедшот был по экстраполированному положению, а это положение было неправильное на полметра-метр из-за задержки сети, то никто этого не заметит. Клиент посылает сигнал "я попал, получи хит", сервер сигнал транслирует, хит идёт. Никто ничего не заметит. Совсем другое дело монстр, который: во-первых маневрирует куда быстрее человека, во-вторых его атаки не удалённые, а близкие и основаны на физическом контакте (т.е. это не абстрактное "я в тебя выстрелил, умри", а вполне конкретное движение из конкретной точки в определённую сторону), в-третьих монстров обычно больше, нежели людей (и все они близко к игроку). Здесь уже будет совершенно очевидно, что сеть лагает со всеми вытекающими: внезапные телепортации, хиты из ниоткуда и т.п. Но вообще мне нравится это "проблема решена". пыс делали годами, ВГ делали, а воз и ныне там. А здесь раз - и проблема решена =) Исходники xray 2.0 - это конечно ни о чём. Но даже если это то, с чего начали сурвариум, и что с того? Я исхожу из того, что там присутствую все ключевые люди, которые отвечали за AI сталкира. Те, что делали рендер, свалили в 4A и, кстати, за пару лет сделали свою игру со своим рендером и своим ai (и вполне очевидно, что даже если делали с нуля, то знали что делать). При таком раскладе я вижу только одну прчину, почему до сих пор ВГ не сделали кооператив с мобами - потому что это таки сложно. @User_X.A.R26, ну вот я попробовал простой файл с шаблонами скомпилить в проекте C++/CLI. Не вышло, вылезла куча ошибок. Фичи C++11 не переваривает точно. Уж не знаю, как можно весь движок сталкира пересобрать под этим. Разве только поудалять большую часть кода на шаблонах. Ну может я что-то не понимаю. Давно с .NET не работал, так что наверное отстал от жизни.
  12. а как там с шаблонами C++ ? Движок их использует весьма интенсивно. Собственно вся игровая логика в xrGame строится на них. Плюс на шаблонах вся скриптовая система. Наверное можно это всё выкинуть, но тогда от игры останется голый рендер.
  13. Это ведь означает managed код. Или я что-то не понимаю? Как это всё совмещается с чисто С++ кодом игры?
  14. А не приходит такое соображение, что они там отсутствуют просто потому, что их нормально не сделать или сделать сложно? Ведь делают те же люди, что делали сталкира. Причём делают на постоянной основе, а не по вечерам ковыряются. Казалось бы, уж они то должны были за много лет запилить то, что не удалось в x-ray. И насчёт того, что "делать с нуля". Это серьёзно?! Это же разрабы сталкира почти в полном составе, включая Ясенева, который как раз и отвечал за весь AI (т.е. по сути почти весь xrGame). О работе с нуля здесь даже речи идти не может, всё уже есть. Если до сих пор не сделали, значит на то есть какие-то иные причины. Во-первых, в сингле множество действий происходит напрямую в обход серверной архитектуры. Но на мой взгляд это не главная проблема. При работе по сети главной проблемой будет синхронизация всех персонажей, включая и физические объекты. Здесь начинают гадить задержки сети. Я уверен, что неслучайно в сурвариуме первыми сделали (точнее анонсировали, что сделали) именно мобов-людей для пострелушек. При этом намного легче скрыть дефекты синхронизации. Мне почему-то кажется, что до мобов-монстров им ещё как до луны. Там всё гораздо сложнее, поскольку монстры находятся при атаке в непосредственном контакте с игроком, и любые косяки сети там будут вылезать сразу. Не мне судить насчёт "заранее мёртвый", но фирма вроде есть и работает. Вот анонс же свежий.
  15. .NET ? Мне собственно было интересно, какой вообще фреймворк. Забавно, как будто на заказ под разговор, что был здесь пару дней назад про сетевую игру, VOSTOK GAMES выпустил анонс обновления. https://survarium.com/ru/news/prevyu-obnovleniya-survarium-044 Говорят, что запилили таки ботов в сетевую игру. Но пока плюшевых =) Также я пока не понял, там только боты для пострелушек или есть монстры. Монстры - это совершенно иной уровень для синхронизации по сети. Пострелушечные боты в этом смысле куда проще. На мой взгляд, это подтверждает мою мысль: трудом энтузиастов на движке x-ray кооператива не сделать. Если уж у коммерчески мотивированной команды, которая пилит движок строго под кооператив и уже не первый год, до сих пор успехи только в подобном виде.
  16. Часто важен не только порядок, но и общее количество аргументов, в том числе и пустых. Скажем, передаётся последовательность выполняемых действий в виде имя действия, его аргументы, имя следующего действия, аргументы следующего действия и т.д. По-моему, в логике сталкира даже такое встречается.
  17. В этом документе речь идёт о стеке в общем смысле, как одной из структур данных. Стек Lua к этому отношения не имеет. Не могут. Ключом nil не может быть вообще, а значением может стать только в обход стандарта. Ещё раз повторяю, weak reference - это вообще не о том. Но в рамках заданных правил языка. В противном случае имеем неприятности. Если программист этих неприятностей в упор не видит, то этой самой квалификации у него попросту не хватает. То и означает. Например выжигание калёным железом всех случаев применения обсуждаемого нестандартного поведения. Жечь напалмом, а не обсуждать, как с этим жить. Пора уже избавляться от синдрома утёнка. Особенно в отношении заведомо кривых вещей. Допустим, передаём куда-то массив с аргументами и аргументы надо парсить в определённом порядке, и какие-то из них могут отсутствовать. Причём в ходе парсинга отсутствующие элементы тоже на самом деле парсятся, просто вместо значений подставляются некие значения по умолчанию. Соответственно, надо знать сколько этих аргументов, включая возможное количество "дырок" как в середмне, так и в конце. Надо также их перебрать все, включая и "дырки" (и те, что в конце, тоже).
  18. А какой фреймворк используешь для окошек?
  19. Вот. Замечательный пример. Поскольку для доступа к аргументам надо знать их количество, то есть специальная функция select(index, ...) для определения количества аргументов и доступа к нужному аргументу по индексу. Функция специально добавлена для того, чтобы не нарушать стандарт языка и не прибегать ко всяким небезопасным трюкам. Стек - это стек, таблица - это таблица. Это разные вещи. Список аргументов можно сконвертировать в таблицу, но тогда вылазят соответственно все ограничения стандарта на таблицы, включая невозможность надёжно узнать их количество, если реализация языка строго следует стандарту. И как раз для этого есть функция select. Это вообще не о том. =) Вот бы этот пуризм да в мирное русло, например применительно к вычищению всякой грязной нестандартщины из вашего любимого "того Сталкира". Но нет, вся энергия направляется на защиту заведомо ошибочного подхода. Ну так и я о том же. Разумеется, я не имел в виду "убрать все массивы вообще" =) Какой-то массив останется, просто он не должен быть неправильно сформирован. Ничто не мешает, например, заменить значения nil ну скажем строками "nil" и проверять эти значения, трактуя их как "пустой аргумент". Всё, проблемы нет. Кстати, именно так кое-где в движке и поступают.
  20. Понимаешь, в соответствии со стандартом языка невозможно сделать такие параметры со значениями nil. Если так поступаешь, то нарушаешь стандарт и нарываешься на неопределённое поведение. Неопределённое не значит, что совершенно невозможно сказать, что произойдёт. Например для случая этих массивов с "дырками в виде nil" поведение вполне известно для реализации Lua в ванильном сталкире. Но эти же массивы с дырками ведут себя совершенно иначе в реализации LuaJIT 2х. Там уже невозможно делать трюки, которые можно было делать в LuaJIT 1х и в частности работа с такими массивами в LuaJIT 2х практически полностью приведена в соответствие со стандартом, что означает невозможность определить его длину никаким способом. LuaJIT 2х уже становится всё более актуальным. Я сам вставлял его в игру. Хотя игра грузится и внешне работает, но тем не менее заметны некие отличия в поведении неписей. Т.е. что-то ломается и вполне возможно это связано именно с этими отличиями. Короче, просто категорически нельзя рассчитывать на некое нестандартное поведение. То, что разрабы пыс так поступали, говорит только об уровне скриптёров, которых они тогда набирали. Это никак не может быть ориентиром для всех остальных.
  21. Из того, что я понял, они взяли версию 5 и фактически мало что там поменяли. Однако вместо встроенной системы коллизий использовали свою (что ODE допускает). По идее актор - физический объект. Если оставить физические объкты в покое, то они должны устаканиться после короткого промежутка времени и отключиться, как только мелкие колебания попадут в заданный допуск. С чем связано то, что актору трудно стоять на динамическом объекте, так сразу и не скажешь. Может быть дефект системы коллизий, могут быть параметры дампинга, неправильный выбор массы или параметров автоотключения объектов. Т.е. только p4, я правильно понял? Поскольку совместить их все так сразу не выйдет. Можно, но надо переименовывать классы + менять процедуру переключения.
  22. @Nazgool, @Serge!, Ситуация с nil в качестве элементов массивов - это ситуация нестандартная, вызывающая неопределённое поведение. Следует вообще исключить её из практики. Поэтому обсуждение, как продублировать подобный массив с nil-ами - следует перевести в плоскость "как убрать необходимость его дублировать и вообще убрать такой массив".
  23. Если рассудить, то ничего ведь не мешает поставить, скажем, бочку на бочку. Ничего не падает, не скользит. А актор - тоже физический объект. Если в отличии от другой бочки неустойчивый, выходит дело не в ODE, а в его применении.
  24. @abramcumner, Думать о проблемах многопоточности, мягко говоря, слишком преждевременно. Для начала, ни о какой многопоточности сейчас речи вообще не может идти хотя бы в силу использования Lua. И каким боком многопоточность вообще относится к нетпакетам или даже к очередям сообщений? Или так. Каким образом нетпакеты относятся к очередям сообщений? Надеюсь, ты не ставишь знак равенства между этими вещами? Если ты поразмыслишь на досуге, то поймёшь, что это всё об одном и том же - о посылке сообщения. Синхронный при этом сигнал или нет - дело десятое. Вот даже в сталкире есть посылка синхронная (в сингле от клиента к серверу посылка закорочена на прямой вызов обработчика), а есть асинхронная (через очередь, от сервера к клиенту и ещё одна очередь внутри клиента). Но при этом всё делается через нетпакеты. Причина для этого очевидна - в сетевом режиме эти очереди в итоге шли через сеть, отсюда и бинарный буфер с упакованными аргументами, который удобно посылать по сети. Но в сингле в этом нет необходимости. Ещё раз. Синхронный будет сигнал или нет - дело десятое. Допустим, какой-то будет асинхронным, т.е. через очередь, но это должно определяться не тем, что этот сигнал когда-то посылался по сети, а иными соображениями, хотя бы и желанием распределить нагрузку во времени или между потоками. "всё на асинхронных сигналах" - это выглядит как догма. Где-то это надо, а где-то возможно нет. Я даже приводил примеры, где это не очень хорошо. Мне кажется, я достаточно подробно расписал недостатки. Буфер непрозрачен. У нас запредельно сложная система, а мы ещё повышаем её сложность подобным образом. Если бы аргументы передавались через некий класс/структуру или даже просто списком аргументов функции, то возможности для ошибок было бы куда меньше, и удобство отладки выросло бы на порядок. Вот же! Неужели не очевидно, что соображение "это сейчас работает" здесь неприменимо. Я же об этом и толкую. Да, это сейчас работает (хотя сколько усилий было очевидно перерасходовано, чтобы это таки заработало), но это уже никуда не стронуть. Сейчас даже просто понять, откуда и куда идёт одно единственное сообщение и какие у него аргументы, требует до нелепого колоссальных усилий. И это только такой относительно малый аспект, как использование нетпакетов. Там ещё много всего подобного, связанного с сетью.
  25. @abramcumner, Это я пытался намекнуть на перспективы запиливания полноценного кооператива. =) Речь же не о том, чего бы хотелось. Я пытался сказать, что сетевой режим - это один из факторов, мешающих развитию. Вот я совершенно уверен, что если бы пыс не пытались делать игру на сетевом движке, то у них бы всё получилось на год или два раньше, а может и ещё раньше. Не хочу на самом деле обсуждать рендер. Просто не вижу рационального в его смене с точки зрения развития движка. С моей точки зрения это ни хорошо ни плохо. В моём воображении это выглядит как отдельное окно с редактором. Чтобы можно было на два монитора разнести. =) По факту моих экспериментов как раз рендер пришивается относительно без проблем (он практически независим от всего остального). Больше всего проблем при сборке экзешника и xrGame. Короче, "легким допиливанием" там не обойдётся. Идут проблемы линковки, конфликтующих общих данных и функций, разделяемых между проектами модулей и т.п. Проблема в размере проекта. Количество этих конфликтов просто зашкаливает. Я ведь уже сказал. Многочисленные общие файлы между всеми проектами. Это делает общую структуру уязвимой к изменениям. Тронул там - полетело здесь. Про pipes ответ снят? Я веду речь о чём-то вроде делегатов или слотов из boost (идея у колбеков/евентов и очередей сообщений на самом деле одна и та же). Не знаю, как можно не видеть проблемы в использовании нетапкета как контейнера для аргументов сообщения. Меня они напрягают. Когда при отладке в обработчике я вижу безликий буфер, то совершенно невозможно понять, что там на самом деле находится. А то, что прочиталось, - это на самом деле то, что туда было записано? А я не сместился на байт? А что там за ещё данные вслед за теми, что я уже прочитал? А если пакет пересылается дальше с установленной позицией чтения? Я никаким чудом не догадаюсь, что там дальше за данные, пока не поймаю его в очередном обработчике (который ещё найти надо) и не разберу досконально последовательность чтения. Это же мерзость!

AMK-Team.ru

×
×
  • Создать...