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

Язык Lua. Общие вопросы программирования


Рекомендуемые сообщения

С чего начинать и где взять.

 

Установка Lua:
http://www.amk-team.ru/forum/index.php?showtopic=11584&p=629106

 

Руководство «Программирование на языке Lua», третье издание:
http://www.amk-team.ru/forum/index.php?showtopic=11584&p=905308

Изменено пользователем Kirgudu
Ссылка на комментарий

Хотелось бы прояснить вопрос по созданию дочерних классов/объектов.

Вопрос: Чем плохо создавать 'дочерний' класс/объект, даже может быть и не связанный с 'родительским' классом/объектами сутью или общностями применения/использования?

 

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

Если опустить различные "обще(не)принятости" и т.п. и субъективности - то собственно для ресурсов, производительности и т.п. чем "чистый" объект предпочтительнее "зависимого"?

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Ссылка на комментарий

Artos,

По производительности - безразлично. Хотя методы - не всегда просто ссылки. В luabind функции реализованы на перегруженном операторе индексации, который возвращает функции. Их даже удалить скорее всего не получится. Методы, добавленные в Lua, это скорее всего просто ссылки. Я не пробовал, но можно попытаться их удалить. Прототипный подход к наследованию такую возможность в принципе допускает.

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

Изменено пользователем ColR_iT
Полностью цитировать предыдущий пост - не имее смысла.
 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий
malandrinus: ... зачем может потребоваться строить один класс на основе другого, никак с ним не связанного
Не стал пока поднимать этот вопрос, т.к. связан или нет тоже может быть субъективным понятием ... ;-)

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

Ранее тут приводился пример с событиями (ивентами), т.е. создается класс "event".

class "event" --/ (by xStream @>-`-,--`,--)
function event:__init(name)
...
end

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

class "timer" (event) --/ дочерний класс
function timer_xs:__init() super("timer")
...
end

Учитывая что в самом классе таймеров в наличии обращение к классу "event":

event("timer"):register(...) иль event("timer"):unregister(...)

напрашиваеся сделать:

self:unregister(...) и self:unregister(...) - что доступно для дочернего класса и не заморачиваться и в др. ситуациях, если вдруг понадобились родительские методы, а просто их использовать.

Вот в таком контексте, когда сущности (объекты событий и объекты таймеров) могут быть и не связаны (это в моем извращенном воображении они все же связаны), а именно удобство использования - каковы могут быть минусы и интересует?

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Ссылка на комментарий
Не стал пока поднимать этот вопрос, т.к. связан или нет тоже может быть субъективным понятием ... ;-)

Нуну :) Так давайте же начнем смешивать различные сущности, так, просто на всякий случай. Объекты луабинда создаются копированием таблиц с метатаблицами, таким образом, все объекты имеют свой инстанс методов и хранилищ. Соответственно, тупо не наследуя то, что не нужно, уже получаем выигрыш в памяти на хранение объекта и времени на его создание. Как минимум такая причина, думаю, убедительна, если оторваться от парадигмы.

....

Таймеры мне скоро плешь проедят :) Все зависит от принципа реализации. Делая один ивент "timer" (тут вообще напрашивается тогда вопрос - а нахрена тогда несколько объектов?), а внутри устраивая разбор полетов, - это одно, а использование объектов, не работающих вообще в принципе с ивентами - это другое. Правильнее всего, имхо, - создать систему объектов, которые просто проверяются на апдейт (вот тут то и нужна песочница и event-driven модуль), а к каждому присобачен колбек. Плюс таймер несет произвольные данные для срабатывания. Это позволит легко сохранять/восстанавливать, а срабатывать и вызывать напрямую. Тут вообще не особо нужна event-driven модель.

Изменено пользователем xStream

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

Ссылка на комментарий

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

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

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

 

Сорри, xStream, но я пока не вижу для себя убедительности в ответе. Мизерность выигрыша во времени создания и несколько байт в памяти, которые уничтожаются при исчезновении созданного объекта - неубедительны (особено на фоне кодов исходной игры, где "тонны" растранжириваются). "Лишние" символы/строки в кодах и времена их парсинга/выполнения - того же порядка ...

 

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

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Ссылка на комментарий

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

2), 3) туда же... - описание механизма. Я не претендую на то, что это важные факторы и сама закрою на них глаза при гораздо более важных плюсах.

 

Я просто поддержала тему. Парадигма - не абстрактна, она описывает иерархию. Пример из реальной жизни: есть у тебя велосипед, двухколесный, красивый, 18 скоростей, тут ты прикручиваешь на руль стиральную машину, которая должна подключаться к электросети. Аналогия понятна?

Учитывая, что множественного наследования в ЛУА нет, то рано или поздно, как минимум уткнешься в вопрос - а что и от кого и зачем наследовать. Если делать "на всякий случай", то я не понимаю в этом ни логики, ни смысла. Подозреваю, что ты тоже не понимаешь, а именно "на всякий случай" случай делаешь. На всяких случай не делают. Добавляют не хватающее по мере необходимости, но никак не наоборот - не пихают все в кучу. Опять же, тебе нужны факты, аргументы. Есть аксиомы, заложенные таким подходом программирования. Не с того конца подходишь. Как сказал выше malandrinus, "уменьшить время разработки и/или уменьшить объём кода за счёт повторного использования готового кода." Могу только добавить "повторного использования там, где он нужен".

 

Про таймеры заговорила, потому что были в твоем посте, не надо валить со своей головы на мою. :) Не зачем на меня тут наезжать, сам же поднял этот пример.

 

 

Добавлено через 5 мин.:

Во! Сообразила, как, наверное, правильно написать.

Дело не в использовании на всякий случай (self:unregister(...) и self:unregister(...) - что доступно для дочернего класса и не заморачиваться и в др. ситуациях, если вдруг понадобились родительские методы, а просто их использовать.), а в такой вещи, как проектирование и архитектура кода. Код сначала проектируется, учитываются все возможные зависимости, область применения и так далее. После этого составляется иерархия, по которой становится ясно, что от чего наследуется и зачем. Если в таймерах предусматривается какой-то регистрирование-отрегистрирование (не понимаю, правда, зачем от объекта отрегистрировать его же собственный единственный колбек, ну да лано. Таймеры злополучные взяты как пример), наследуемся, если они никогда в жизни этого не будут использовать (как уже сказала - зависит от решения, архитектуры), то и наследовать то смысла нет.

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

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

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

Ссылка на комментарий

Artos,

У класса события и таймера разные роли. В этой системе есть две роли: событие и подписчик на событие. В библиотеке boost они названы соответственно signal и slot. В .NET - event и delegate. Роль события - во первых предоставлять программе интерфейс для вызова события. Во вторых, событие взаимодействует со слотами или подписчиками. Для этого где-то как-то имеется список "подписантов" и имеется внутренний функционал для их перебора и передачи им аргументов события. С событием же может быть связан интерфейс подписывания/отписывания хотя и не обязательно.

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

 

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

 

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

 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий

xStream, не самый удачный прием в дискуссии - использование абсурдных сравнений ... Как ранее велосипед на гусеничном ходу (был упомянут kamikazze) или сейчас со стиральной машинкою. Таким приемом только забалтывается/отвергается, а не аргументируется суть.

Где то упоминалось про велосипед/гусеницы/стиралку? Почему такая тяга оппененту сразу навешивать ярлык изобретателя невразумительного велосипеда?

Ну а если все же взять аналогию с велосипедом, то почему не предположить, что опонент вознамерился прикрутить, например электромоторчик с аккумулятором и, помимо "красивости" с 18-тью скоростями, еще и решил силенки свои поберечь, а может и скоростенки поприбавить?!

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

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

xStream: Дело не в использовании на всякий случай ... а в такой вещи, как проектирование и архитектура кода. Код сначала проектируется, учитываются все возможные зависимости, область применения и так далее.
Не хочется спорить с совершенно верным утверждением ... где-нибудь в серьезном проекте или на курсах по обучению, но не тут, в топике "Школа моддинга".

И невозможно учесть всевозможные зависимости и никто не отменял понятий универсальности и/или написания кода "на вырост".

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

 

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

Чем собственно таймер (его срабатывание) не событие, о чем уже ранее упоминал? Почему то, что выдает движек через свои внутренние коллбэки в праве называться событиями, а срабатывание таймера нет? Почему у таймера не может быть подписчиков? Даже тот коллбэк, которому собственно и устанавливается таймер в исходном варианте, уже подписчик, а ведь их может быть и не один.

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

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

 

Ну и т.д. и т.п. ИМХО, не следует свои шоры или наоборот ширту воззрений проецировать на нечто, что пока до конца то и не обусловлено и неограничено.

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

Вопрос пока для себя закрыл, дабы не размазывать по топику свои сомнения/недопонималки.

 

malandrinus, все же не согласен с эффемерностью роли события как объекта, даже в варианте, когда сам он ничем своим не располагает. Как минимум его удобство в транспортировке неких глобальных функций/данных другим, которые расположены в опциональных модулях. Если писать код заведомо законченым, то конечно весь транспорт подобных событий можно размазать по всем скриптам\модулям. Но если говорить о конструкторе, который может опционально видоизменяться, т.е. модули до(у)бавляться - то такой централизованный узел, принимающий сигналы и раздающий их по слотам, очень даже неплох и не эффимерен.

Иными словами, сварка и прочнее и компактнее болтового соединения, но и то и иное имеют свои места по применению и свои плюсы/минусы.

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Ссылка на комментарий

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

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

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

ЗЫ Всех с наступившим!

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

Ссылка на комментарий

Жаль, что диалоги постоянно скатываются с сути вопроса/темы на субъективности личностного характера. ;-(

Отбросив шелуху в ответной реплике, все же по сути выскажу:

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

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

Никогда не ставил в вину тем, кто не верит подобному, а пытается получить подтверждающие факты, перепроверить своим опытом/ошибками, и сам этому следую.

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

 

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Ссылка на комментарий

http://dl.dropbox.com/u/46539648/xs_scripts.rar - библиотека для работы с нетпакетами. Зеркальное отображение АСДС, только в скриптах и ООП.

 

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

 

ЗЫ Если это можно в какую-то другую тему запихать, то пожалуйста. Я не нашла ничего семантически подходящего, а в связи с отсутствием подробного мануала, новичкам сразу выдавать это не стоит :) В то же время, там внутри несколько приемов используются, как сакраментальное наследование и прочие интересности.

Изменено пользователем xStream

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

Ссылка на комментарий

Ммм, интересно, сейчас глянем.

Только вопрос - как будут сохраняться данные из самодельного pstor-а?

ЗЫ: Понимаю, что писать комментарии в коде на английском это что то вроде стандарта, но всё же их стоило написать на русском :D

 

___

 

-- The following assume type(value) == "number":

math.isnan = function(value)

return value ~= value

end

 

Проверяет что value число? Почему б тогда не использовать обычный type()? Просто вот тут как раз не понял значения функции.

 

Изменено пользователем *Shoker*

Можно просто Shoker, форум АМК съел моё старое имя и не хочет отдавать о_О

Мастер аномалий на свою заднюю точку.

Ссылка на комментарий

Когда выложу, тогда все и расскажу :)

Размер практически не ограничен, но злоупотреблять не стоит. Вот пример тестов:

local function test()
    log1(">>>>>>>>>>"..tostring(get('test_2000', nil)))
    local k
    for k = 1, 2000 do
        set('test_'..k, 'long_test_string_value #'..k)
    end
end

local function test2()
    log1(">>>>>>>>>>dumping test table")
    
    print_table_inlog_v2(get('test_table', {}))
    
    local tbl = { 
        a = 1,
        b = {1,3,5},
        c = "abracadabra"
    }

    set("test_table", tbl)
end

 

В первом сосздается 2000 переменных с именами 'test_xxx' и значениями 'long_test_string_value #xxx'. Очень легко подсчитать объем, занимаемой этой ватагой переменных :) (38 байт * 2000 = 76000, почти что 70 с гаком килобайт)

Во втором - записывается такой тип данных как таблица, что является бесспорным профитом - не надо создавать кучу переменных с суффиксами, например. (У меня как раз в виде таблиц сохраняются данные таймеров)

 

Добавлено через 13 мин.:

Проверяет что value число? Почему б тогда не использовать обычный type()? Просто вот тут как раз не понял значения функции.

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

Не видела ни разу, чтоб мат функции юзали со строками, например.

ЗЫ Писать комменты на инглише - привычка уже просто :) Пусть даже мой инглиш далеко не идеален.

 

Добавлено через 5 мин.:

Кстати, о функции isnan. Дело в том, что есть такое значение NaN (Not a number). В стандарте RFC определено, что если сравнивать, что угодно с этим значением, то всегда получим false. Движок ЛУА это реализует (стандарт этот). И эта функцию это и использует. Потому что при любых других значениях переменная БУДЕТ равна сама себе. При этом type(math.nan) == 'number'. Во как, такой вот финт ушами. С бесконечностями, имхо, проще.

Изменено пользователем xStream

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

Ссылка на комментарий

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

1. class "net_alife_item_weapon_shotgun"

ammo_ids - не u8. Это комплексный тип, представляющий собой таблицу из однобайтных значений. Таблице предшествует байт, в котором хранится размер этой таблицы. В терминах acdc это l8u8v или С/С (по data_packet.pm)

2. class "net_alife_item_weapon_magazined_w_gl"

параметр grenade_mode идет первым в пакете (если отбросить пакет cse_abstract, который в любом случае первый)

3. class "net_respawn"

параметр initialized называется spawned_obj и представляет собой сложный тип l8u16v (в терминах acdc)

 

Может, еще что-то есть, это бросилось в глаза.

P.S.: Да, это все минимум для второго патча ТЧ. В чистом ТЧ и на первом патче все немножко по-другому.

Изменено пользователем KD87
Ссылка на комментарий

Фидбек - это здорово.

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

Как написано в шапке - это своеобразный интерфейс (постаралась сделать простым для расширения) для "портирования" из АСДС. Так что если косяки, то все оттуда.

Я, конечно, еще проверю. Но что для ТЧ с версии 1.0004 и выше - точно.

 

Еще бы фидбек по использованию, но там, надеюсь, проблем не возникнет :)

 

Добавлено через 1 мин.:

Угу, что-то неактуальное, наверное, тот самый шотган:

use constant upd_properties_info => (
    { name => 'upd:ammo_ids', type => 'u8' },    # 0x1c0 (0x1e0)
);

Хорошо, что предусмотрен tail, по крайней мере данные не потеряются :)

Подкиньте ссылку на актуальную версию для ТЧ, 6-й патч. Универсальный не предлагать - я себе мозги сломаю заниматься переносом оттуда

Изменено пользователем xStream

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

Ссылка на комментарий

Дайте мне актуальную версию АСДС - будет :) А кто может сам исправить, спасибо скажу и исправления внесу.

да там и skeleton_flags в net_ph_skeleton хорошо бы разбирать по аналогии с num_items

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

 

ЗЫ Нету там таких констант, наврала. Опять же - все из АСДС, хехе.

 

Добавлено через 12 мин.:

Обновила архив, ссылка та же - http://dl.dropbox.com/u/46539648/xs_scripts.rar

Спасибо KD87, внесла изменения в соответствии с его замечаниями.

Изменено пользователем xStream

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

Ссылка на комментарий

xStream,

ну мне лично интересно посмотреть на изменение порядка чтения с grenade_mode у тебя :)

а со skeleton_flags в зависимости от значения флажка надо читать данные или нет. В all.spawn флажок всегда 0, а в живой игре не всегда.

 

Актуальная версия: или асдс универсальный или нет-пакеты Artos`а. Вроде только так.

Ссылка на комментарий

У Artos'a они далеко неактуальные. А универсальный АСДС - слом мозга.

 

Добавлено через 8 мин.:

Со skeleton_flags не нашла ветвления в универсальном АСДС. Так что...

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

Ссылка на комментарий

Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

Создать аккаунт

Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!

Зарегистрировать новый аккаунт

Войти

Есть аккаунт? Войти.

Войти
  • Недавно просматривали   0 пользователей

    • Ни один зарегистрированный пользователь не просматривает эту страницу.
×
×
  • Создать...