Jump to content
Korean Random
SoprachevAK

[WOT STAT] Мод для анализа подкруток

Recommended Posts

15 часов назад, SoprachevAK сказал:

Немного апдейтов

Спасибо за обнову, подпиской поддержал.

  • Upvote 1

Share this post


Link to post

Short link
Share on other sites
21 час назад, StranikS_Scan сказал:

Тебе нужно ввести разделение статистики по версии игры. Потому что в каждом новом патче игра по сути новая. И это кстати существенно снизить нагрузку на БД. Ибо все данные в кучу - это средний винегрет на Луне в вакууме.

Ну все эти данные собираются, надо просто на сайт фильтры добавить, займусь этим как нибудь. Тоже думал, что и по версии и по региону надо разделять, тк там вообще игры разные так то. 
Про оптимизацию БД оно не прокатит, у меня первичный ключ сортировки – никнейм игрока. Так что разбиение по версии игры ничего не изменит. А партицирование (физическое разделение файлов БД на диске) по месяцам, это на случай, если они начнут весить неприлично много, и надо будет отключать старые куски
 

 

21 час назад, StranikS_Scan сказал:

А по умолчанию графики не показывает. Нужно тыкать.

Спасибо исправил, не то значение поставил по умолчанию, а оно же сохраняется, вот и не заметил)

 

21 час назад, StranikS_Scan сказал:

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

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

 

21 час назад, StranikS_Scan сказал:

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

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

Как только мне не придётся ежедневно что то чинить в текущем сервисе, я тут же пойду извлекать из него пользу в виде динамических тепловых карт, благо PoC уже есть, и надо только умнее отфильтровать хорошие бои и отображать модом
 

 

21 час назад, StranikS_Scan сказал:

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

Огромное спасибо за исправления, это реально очень ценно! Честно сказать, я сомневался, что кто то читать будет) Завтра залью на сайт исправленную версию 


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




__________

Хотел найти полезность, что чем дольше стоишь в очереди, тем больше шанс попасть в низ списка, а оно наоборот. Чем дольше в очереди, тем больше шанс попасть в топ. 
От времени суток, на удивление, не зависит
image.thumb.png.ff9f9269eadbd079ba124a40fb96dde5.png
Средняя позиция по уровню в команде относительно времени в очереди в секундах

То есть когда кидает мгновенно, то в среднем, в бою будут танки на 0.9 уровня выше твоего. А если ждёшь хотя бы 30 секунд, то будут уже на 0.5 уровня выше твоего

with 8 as TARGET_LEVEL
select round(queue / 1000)              as period,
       avg(length(levelVisible))        as levelCount,
       avg(TARGET_LEVEL - arrayMax(levelVisible)) as position,
       count()
from (select arrayZip(playersResults.tankLevel, playersResults.team)                     as levelTeam,
             arraySort(arrayMap(t ->
                                    t.1, arrayFilter(t -> t.2 = playerTeam, levelTeam))) as myTeam,
             arrayDistinct(myTeam)                                                       as levelVisible,
             S.inQueueWaitTime                                                           as queue
      from Event_OnBattleResult
               join Event_OnBattleStart as S on S.id = onBattleStartId
      where battleMode = 'REGULAR'
        and queue < 1 * 60 * 1000
        and tankLevel = TARGET_LEVEL)
group by period
order by period;

 

Edited by SoprachevAK

Share this post


Link to post

Short link
Share on other sites
3 часа назад, SoprachevAK сказал:


Хотел найти полезность, что чем дольше стоишь в очереди, тем больше шанс попасть в низ списка, а оно наоборот. Чем дольше в очереди, тем больше шанс попасть в топ. 
От времени суток, на удивление, не зависит
image.thumb.png.ff9f9269eadbd079ba124a40fb96dde5.png
Средняя позиция по уровню в команде относительно времени в очереди в секундах

То есть когда кидает мгновенно, то в среднем, в бою будут танки на 0.9 уровня выше твоего. А если ждёшь хотя бы 30 секунд, то будут уже на 0.5 уровня выше твоего


with 8 as TARGET_LEVEL
select round(queue / 1000)              as period,
       avg(length(levelVisible))        as levelCount,
       avg(TARGET_LEVEL - arrayMax(levelVisible)) as position,
       count()
from (select arrayZip(playersResults.tankLevel, playersResults.team)                     as levelTeam,
             arraySort(arrayMap(t ->
                                    t.1, arrayFilter(t -> t.2 = playerTeam, levelTeam))) as myTeam,
             arrayDistinct(myTeam)                                                       as levelVisible,
             S.inQueueWaitTime                                                           as queue
      from Event_OnBattleResult
               join Event_OnBattleStart as S on S.id = onBattleStartId
      where battleMode = 'REGULAR'
        and queue < 1 * 60 * 1000
        and tankLevel = TARGET_LEVEL)
group by period
order by period;

 

1. Льготы дольше ищут бой

2. Подкрутка. Тем кому нужен сниженный уровень боя, приходится ждать дольше, кому повышенный - быстрее

Edited by KPOT2338

Share this post


Link to post

Short link
Share on other sites
5 часов назад, SoprachevAK сказал:

А ещё я хотел снепшоты миникарты собирать раз в 5 секунд и отправлять после боя, для тепловых карт разъездов и попробовать скормить их нейроне.

WG уже подсуетилось и выдало:
Новое Лицензионное соглашение (с 10 апреля 2024 г.) пункт 5.3, е:

Цитата

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

https://legal.eu.wargaming.net/ru/eula-new/

Edited by Scharfhobel

Share this post


Link to post

Short link
Share on other sites
10 часов назад, SoprachevAK сказал:

То есть когда кидает мгновенно, то в среднем, в бою будут танки на 0.9 уровня выше твоего. А если ждёшь хотя бы 30 секунд, то будут уже на 0.5 уровня выше твоего

 

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

 

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

Edited by StranikS_Scan
  • Upvote 1

Share this post


Link to post

Short link
Share on other sites
7 часов назад, Scharfhobel сказал:

Новое Лицензионное соглашение

Да там вообще всё запрещено на самом деле если почитать)

Но на самом деле, чисто формально, я для обучения не планирую использовать контент танков, я буду брать просто координаты перемещения союзников/противников. Они под словом контент подразумевают то, что в той или иной степени защищается авторским правом, типа там если бы я визуально на модельках танков обучал, то да

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

  • Upvote 1

Share this post


Link to post

Short link
Share on other sites

Если расчёт идёт на сервере, то это лишняя нагрузка (и время)

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

т.е. до 1к пусть считает, а дальше для 1,5к, 2,2к, 3,3к, 4,7к, 6,8к, 10к, 15к...

Ну или округлить до более ровных значений 1,5/2,0/3,5/5,0/7,0/10/15

И рисовать ближайшие

погреш1.jpg

Edited by KPOT2338

Share this post


Link to post

Short link
Share on other sites
2 часа назад, KPOT2338 сказал:

Если расчёт идёт на сервере, то это лишняя нагрузка (и время)

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

т.е. до 1к пусть считает, а дальше для 1,5к, 2,2к, 3,3к, 4,7к, 6,8к, 10к, 15к...

Ну или округлить до более ровных значений 1,5/2,0/3,5/5,0/7,0/10/15

И рисовать ближайшие

погреш1.jpg

 

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

Share this post


Link to post

Short link
Share on other sites
15.03.2024 в 10:31, StranikS_Scan сказал:

Заходишь на сайт. А по умолчанию графики не показывает. Нужно тыкать.

 

image.thumb.png.521a562c9d2a55b9a608677683f309ae.png

 

Тебе нужно ввести разделение статистики по версии игры. Потому что в каждом новом патче игра по сути новая. И это кстати существенно снизить нагрузку на БД. Ибо все данные в кучу - это средний винегрет на Луне в вакууме.

 

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

 

-----------------------------------------------

 

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

 

Расширенное распределение урона

Выберите один вид урона
Учитываются выстрелы ББ, БП и КС снарядами по танкам с ХП, превышающим максимальный урон снаряда

 

Описание

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

 

Например, для базового урона в 11 единиц граничные значения

  • 11 - 25% = 8.25 => 8 (минимальное значение в игре)
  • 11 + 25% = 13.75 => 14 (максимальное значение в игре)

 

Это значит, что урон может принять любое значение из дискретного ряда [8, 9, 10, 11, 12, 13, 14], или же [-3, -2, -1, 0, +1, +2, +3] относительно базового урона. Если выполнить нормализацию значений относительно базового урона, то получим ряд [-27%, -18%, -9%, 0%, +9%, +18%, +27%], в котором всего 4 промежуточных значения -18%, -9%, +9%, +18%, а крайние значения -27%, +27% выходят за рамки исходного разброса в 25%. Слияние такого дискретного ряда с графиком, построенным для другого вида урона, например 390, приведет к возникновению аномальных пиков и провалов, что естественно для дискретных случайных величин, но совершенно не характерно для величин с непрерывным распределением. Поэтому для каждого вида урона строится индивидуальный график распределения вероятностей.

 

Граничные значения

 

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

 

Например, для базового урона в 390 единиц при 7 значениях на столбик

  • 390 - 25% => 293 (минимальное значение в игре)
  • 390 + 25% => 488 (максимальное значение в игре)

 

Следовательно, на графике, слева от базового урона будет 390-293=97 возможных значений, а справа 488-390=98, в сумме же будет всего 196 значений. Построить симметричный график относительного базового урона в этом случае невозможно, поэтому на графике центральный столбик будет состоять из 7 значений [387, 388, 389, 390, 391, 392, 393], крайний левый столбик из 3 значений [293, 294, 295], а крайний правый из 4 значений [485, 486, 487, 488].

 

Пример для урона в 360 единиц

  • 360 - 25% => 270
  • 360 + 25% => 450

 

Диапазон возможных значений урона [270; 450] и состоит из 181 значения, 90 из которых находится слева от среднего, а еще 90 справа от него, что позволяет построить зеркально симметричный график с шагом в 1 значение на каждый столбик. При распределении урона по столбикам с шагом в 5 значений, получается 17+1+17=35 центральных столбиков по 5 значений в каждом и два крайних столбика по 3 значения в каждом. В этом случае график сохраняет симметрию относительно центральной оси. При этом следует учитывать, что крайние столбики могут содержать меньше значений, чем остальные.

 

Расчет погрешности

Погрешность графика распределения зависит от:

  • базового урона BASE_DMG (разная величина ошибки округления)
  • количества выстрелов N с уроном (влияет на достоверность распределения)
  • числа уронов M на столбик (меняется форма дискретного графика)

 

Для определения погрешности искусственно генерируется серия из X «экспериментов» распределения при фиксированных значениях указанных выше параметров, и с их помощью определяется возможный диапазон отклонения значений для исходного графика.

 

Алгоритм расчета погрешности:

1.     Первоначально с помощью стандартного генератора псевдослучайных чисел math.random() в JavaScript генерируется N вещественных значений в диапазоне от 0 до 1, включительно.

2.     Сгенерированный ряд преобразуется в танковое распределение урона подобное Гауссовскому распределению с диапазоном значений от -1 до +1, включительно.

 

С помощью сплайн интерполяции по 100 значений к функции численно обратной функции распределения урона (CDF) для канонического нормального распределения с коэффициентами a = 0.02399886787b = -0.006796346129c = 0.4711312389 в формуле a*exp(-((x-b)/c)^2)

 

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

 

DMG = round((invCDF(RND) * 0.25 + 1) * BASE_DMG) - формула для расчета урона

 

4.     Значения, полученного ряда, размещаются по столбикам из расчета M значений урона на столбик.

5.     Алгоритм выше повторяется X раз, где X - количество «экспериментов», необходимых для расчета погрешности (параметр задается пользователем до начала расчета).

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

 

Доверительный интервал

 

Зная погрешность графика распределения урона, можно построить доверительный интервал с указанием уровня доверия P. Интервал отображается на графике в виде двух огибающих линий, обозначающих нижнюю и верхнюю границы доверительного интервала, между которыми находится P% «экспериментов» из X симуляций.

 

Карточки инфографики

Средний урон

Арифметическое среднее всех уронов в базе данных (сумма делить на количество)

 

Медианный урон

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

 

Выстрелов с уроном ниже среднего

Рассчитывается как процент выстрелов с уроном ниже среднего по сравнению с выстрелами с уроном выше среднего. Выстрелы с базовым уроном не учитываются. В идеальном распределении этот показатель должен составлять 50%.

 

 

 

 

Ещё раз огромное спасибо за исправления. Текст действительно стал сильно лучше. Обновил сайт 

  • Upvote 1

Share this post


Link to post

Short link
Share on other sites
17 часов назад, KPOT2338 сказал:

Если расчёт идёт на сервере, то это лишняя нагрузка (и время)

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

т.е. до 1к пусть считает, а дальше для 1,5к, 2,2к, 3,3к, 4,7к, 6,8к, 10к, 15к...

Ну или округлить до более ровных значений 1,5/2,0/3,5/5,0/7,0/10/15

И рисовать ближайшие

погреш1.jpg

Ну стоит отдать должно, у меня на компе сильно быстрее, но я понимаю, это не аргумент)) 
Вообще этот расчёт имеет смысл для малого числа выстрелов, когда распределение выглядит "кривым", и хочется понять насколько сильно это плохо

А так расчёт на клиенте, просто в отдельном потоке. Его можно сделать многопоточным и вообще сильно оптимизировать, но сейчас явно не до этого
Сейчас сделал кнопочку "отменить", на случай если расчёт слишком долгий и возможность самостоятельно задать число выстрелов
image.thumb.png.fbb2aa772f3f1133212b1c9f974f104e.png

  • Upvote 1

Share this post


Link to post

Short link
Share on other sites
14 часов назад, StranikS_Scan сказал:

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

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

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

Наверное надо будет заняться, но потом

  • Upvote 1

Share this post


Link to post

Short link
Share on other sites

Тем временем уже 43771 пользователей, и я уже третий день трачу всё свободное время на борьбу с Grafana Loki (это общепринятая штука для сбора логов), и я в неё пишу все логи мода. И внезапно, сервер который спокойно выдерживает сложные аналитические запросы по 20М+ строк, не тянет выборку логов за прошлый час. Скорее всего она не совсем предназначена для логов с фронта (хотя её точно так используют), а может, просто для проекта на 40к пользователей я слишком мало плачу за сервера.

А ещё общий охват достиг 2.5 миллиона игроков, для тех кто думает, что игра вот вот умрёт) 

  • Upvote 2

Share this post


Link to post

Short link
Share on other sites

@SoprachevAK а зачем бороться с Loki, если уже есть Clickhouse? Он очень круто подходит для логов)

Даже мегакорпорации на переезжают на Clickhouse для хранения логов и понаписали гайдов об этом)

Share this post


Link to post

Short link
Share on other sites
4 часа назад, yepev сказал:

@SoprachevAK а зачем бороться с Loki, если уже есть Clickhouse? Он очень круто подходит для логов)

Даже мегакорпорации на переезжают на Clickhouse для хранения логов и понаписали гайдов об этом)

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

Вчера вот уже переписал на кликхаус, пока что полёт нормальный. По 5к строк логов в минуту

image.thumb.png.972ba263f5caa04fe55b2a2816e9fa58.png

  • Upvote 1

Share this post


Link to post

Short link
Share on other sites

Интересная ситуация

Как будто бы надо завтра попробовать нарисовать распределение ХП при стрельбе по танкам с ХП от 0.75 до 1.25 альфы. Но на такой большой выборке, как будто бы должно быть симметричным

 

IMG_0849.jpeg

Share this post


Link to post

Short link
Share on other sites
1 час назад, SoprachevAK сказал:

симметричным

 

Нет. Есть же еще модули. Взрыв БК например, потому нечестно добитых больше.

  • Upvote 1

Share this post


Link to post

Short link
Share on other sites
10 часов назад, StranikS_Scan сказал:

Взрыв БК например

В точку!
Исключил взрывы БК, стало лучше, но не совсем 69/64. Откуда то лишние 3% фрагов
Уничтожение по модулям у меня не учитывается, у меня фраг это когда осталось 0 хп после выстрела

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

Share this post


Link to post

Short link
Share on other sites
16 минут назад, KPOT2338 сказал:

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

Но ситуация забавная)

Share this post


Link to post

Short link
Share on other sites

Апдейтики 25.03.2024

С одной стороны ничего интересного, а с другой тотальная оптимизация всего.

Последнее время, каждый день, общая инфографика грузилась всё медленнее и медленнее. В общем то пофиксил. Добавил materialized view и projection в базу данных. Это такие отдельные таблицы, в которых хранятся предрассчитанные метрики и инкрементально обновляются на каждую вставку. (что это значит? – все данные всё ещё актуальным секунда в секунду по любым фильтрам).

Единственная проблема – это запросы стали немного странными специфичными, тк projection'ы выбираются автоматически, запросы должны по выбираемым выражениям соответствовать заранее подготовленным projection'ам. 


Заодно пока переписывал запросы, исправил в них несколько неточностией. Из явно видного – графики мест в команде теперь строго детерминированны и считают сколько человек было ЛУЧШЕ игрока (одинаковые показатели трактуются в пользу игрока). Так что теперь выгдядят интереснее, особенно на арте
 image.thumb.png.4194c8da17f050d2006972eab450f6e2.png

 

Про нечестно добитых/спасённых. Во первых убрал из выборки взрывы БК, во вторых мне подсказали как её полностью передалать на относительные значения, но пока что думать тяжело. Сейчас нечестно добитых всегда будет больше, потому что противников с ХП > 1.0 альфы встречается чаще чем с ХП < 1.0 альфы. А это связано с наличием арты, которая респится с 450-490 хп 
Если исключать арту из целей, всё равно есть небольшой перекос, но сильно меньше, пока не знаю почему
 

Время загрузки До/После на одним и том же сервере, на одной и той же БД. Страница результатов для случайных боёв

 

ДО:

1385467490_2024-03-2504_00_33.thumb.png.79368037fe0e5d714f29b55578832794.png
 

ПОСЛЕ:
1316895246_2024-03-2503_57_39.thumb.png.58f345e4f495c135c908e65fa08792e1.png
 

 

Edited by SoprachevAK

Share this post


Link to post

Short link
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...