12.02.2013

Использование worldoflogs.com для анализа боя в World of Warcraft.

Возможности worldoflogs

Worldoflogs представляет широкий спектр возможностей по анализу рейдовых боев в World of Warcraft. Возможности эти следует разделить на 2 группы:
1) Использование стандартных имеющихся запросов
2) Написание своих запросов.
К сожалению стандартные запросы не позволяют глубоко анализировать выполнение игроками тактики на определенного босса или же соблюдение собственной ротации. Возможности их схожи с возможностями стандартных игровых аддонов, таких как Skada. В связи с этим основное внимание в статье я уделю именно написанию собственных запросов. Для их написания не нужно обладать каким-то специфическими знаниями, хотя знание SQL значительно упростит данный процесс, поскольку язык запросов в worldoflogs по синтаксису схож с SQL.

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

Обновлено 21.01.2014
К сожалению отчет все-таки удален.


Интерфейс worldlflogs


Главное меню






Главное меню состоит из следущих пунктов:
  • Ссылка на гильдию, к которой относится отчет;
  • Ссылка на собственно отчет и его дата/время;
  • Выпадающий список доступных видов анализа (например, нанесенный урон), по умолчанию dashboard - краткая информация обо всем;
  • Выбор сегментов - позволяет выбрать весь отчет, какой-то отдельный бой или группу боев (например, все бои связанные с определенным боссом, все успешные попытки или все неуспешные попытки);
  • Выбор игрока - позволяет выбрать отдельного игрока для анализа, игроки сгруппированы по классам;
  • Выбор неигрового персонажа - например боссы или адды - не так часто используется, но все же нужно знать;
  • Ссылка для покупки премиум учетки - нам это не нужно :)
Кратко пройдусь по основным видам анализа.
Самый популярный и самый простой - рекаунтоподобная таблица нанесенный и полученный урон, примененное и полученное исцеление и дружественный огонь.
Выглядит это следующим образом:


Видим здесь для каждого игрока величину нанесенного урона (в данном случае), среднее и эффективное среднее значение (эффективное среднее - это просто весь урон, поделенный на все время, среднее учитывает так называемое время активности, которое также имеется в таблице). Обратите внимание, что активное время не всегда вычисляется корректно, простейший пример - способность жреца послушания "щит души", во время действия этой способности реального исцеления не происходит, но не учитывать это как активное время - неправильно. Рекомендую пользоваться исключительно столбцом эффективного среднего или просто суммарного значения. Треугольник около имени означает наличие питомцев - можно открыть и посмотреть отдельно урон игрока и урон питомца. Справа от таблицы имеются переключатели, которые позволяют включить/отключить отображение питомцев, игроков, создания (т.н. NPC) и так далее, назначение их вполне очевидно.

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

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

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

Чуть более специфическая группа страниц для анализа - группа analyze - позволяет анализировать все то же самое, что отображают только что упомянутые таблицы, но более подробно. Расскажу на примере analyze - damage done, как наиболее часто используемой.
Открываем отчет, в котором я уже успел выбрать определенные параметры.





Мне было интересно, кто из назначенных бойцов бил Небесного защитника на Элегоне, поэтому я выбрал analyze by source и выбрал в выпадающем списке цель Небесный защитник. Видим, что воин нанес по нему урона на 50% больше рыцаря смерти и признается молодцом, в отличие от последнего.

Выбирая различные значения в переключателе analyze by и выбирая источник и цель можно получить множество интересных результатов.

Обратите внимание на поле expression - оно позволяет дополнительно фильтровать результаты. О нем я расскажу более подробно ниже, когда буду рассказывать про самостоятельное построение запросов.

Упражнение: попробуйте посмотреть, кто сколько получил урона от Цяна Безжалостного на призрачных королях (скорей всего люди, кто получал меньше всех урона не ловили клив). Пример немного надуманный и не дает точного результата, ниже я покажу, как можно это сделать точно.

Следующая страница, которую я рассмотрю, это страница "deaths overview" - позволяет отобразить смерти игроков в процессе боя. Часто там можно найти множество полезной информации. Перейдем к примеру. Видим, что отображаемая информация довольно аскетична. Для получения окна с подробной информации жмем на ссылку more. По умолчанию в списке событий отображаются только связанные с персонажем события - нанесенный и полученный урон, а также получаемое исцеления. Дополнительно можно включить получение ресурсов (например, мана или энергия света), касты заклинаний и полученные бафы. Если первое как правило не несет полезной информации, то два других несут. Они позволяют отследить, использовал ли танк какие-то защитные кулдауны. К сожалению смерть персонажа генерирует тучу событий SPELL_AURA_REMOVED (т.е. с персонажа снимаются все бафы и только потом он умирает). Было бы здорово, если бы весь этот мусор не отображался. В нашем случае включив бафы увидим, что несмотря на 13 стаков перезарядки танк не прожался под дыхание и взрыв защитника, что и привело к его гибели.

Меню персонажа

Меню персонажа нет смысл рассматривать подробно - тут большинство пунктов, которые схожи, с уже разбиравшимися, это разделы damage by spell, damage by actor, healing by spell, healing by actor. Их назначение вполне себе очевидно, можно посмотреть распределение по заклинаниям и целям для полученного/нанесенного урона и полученного/произведенного исцеления. Анализ здесь если и возможен, то весьма специфический и останавливаться на нем я не буду.

В меню персонажа имеет смысл уделить отдельное внимание пунктам buff gained/buff cast - они содержат в себе много полезной для анализа информации. Для начала взглянем на buff gained - здесь содержится список бафов и дебафов, скастованных (по мнению движка игры) игроком самостоятельно - как к себе, так и к другим целям. Здесь нужно обратить внимание на аптаймы важных бафов, например дознание у паладина в специальности воздаяние или гармония у друида-лекаря или же дебафов, то есть дот (порча, нестабильное колдовство и другие). При нажатии на # в самом правом столбике можно увидеть графическую визуализацию аптайма. Теперь обратим внимание на buff gained, здесь разумеется список аур значительно более длинный, однако тут можно найти немало полезного. Например, можно посмотреть, висел ли на боссе дебаф "слабые удары" и если да, то кто его накладывал.












Как видим доблестный танк успешно накладывал и поддерживал дебаф на боссе. Пару раз дебаф был перекрыт шаманом и воином.

Следует также обратить внимание на таблицу power gains (ее я рекомендую смотреть исключительно на вкладке buff gained, потому что например получение маны от собственного исчадия тьмы видно только там, а не в buffs cast).

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

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

16.08.2009

asp .net development сервер

Как сделать так, чтобы asp .net development сервер создал веб-приложение в папке / а не в папке /{имя проекта} ?

Наиболее простой способ:

Step 1: Select the “Tools->External Tools” menu option in VS or Visual Web Developer. This will allow you to configure and add new menu items to your Tools menu.

Step 2: Click the “Add” button to add a new external tool menu item. Name it “WebServer on Port 8080” (or anything else you want).

Step 3: For the “Command” textbox setting enter this value: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\WebDev.WebServer.EXE (note: this points to the web-server that VS usually automatically runs).

Step 4: For the “Arguments” textbox setting enter this value: /port:8080 /path:$(ProjectDir)

Step 5: Select the “Use Output Window” checkbox (this will prevent the command-shell window from popping up.


Перевод:

Выбираем в меню visual studio "tools" - "external toools"

Создаем новый элемент "add", выбираем любое имя

Command: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\WebDev.WebServer.EXE (это путь к серверу в .net framework 2.0, для четвертого фреймворка он будет другой)

Arguments: /port:{выбранный нами порт} /path:{путь к каталогу с сайтом}

Выбрать use output window

Все, теперь можно выбирать в меню tools - наше выбранное имя

04.02.2009

Context.RewritePath и атрибут action тега form

Как известно для того чтобы использовать friendly-urls нужно в обработчике события BeginRequest производить вызов Context.RewritePath и соответственно переписывать url.
Например /help/default.aspx можно заменить на default.aspx?action=help
Но при этом возникает неприятность. При рендеринге формы в атрибут action будет записан уже новый url ( default.aspx?action=help в нашем случае ) Таким образом, при нажатии на любую кнопку пользователю будет возвращена страница уже без дружественного url

Существует множество способов изменить такое поведение.
Наиболее простой способ - перекрывать метод Render и в нем сначала переписывать значение action а потом вызывать метод базового класса.


protected override void Render(HtmlTextWriter writer)
{
Page.Form.Action = Request.RawUrl;
base.Render(writer);
}



Однако этот метод не очень гибкий, поскольку нужно либо перекрывать метод Render на каждой странице либо создавать наследника класса Page и использовать его.
Еще один подводный камень - свойство Action класса HtmlForm доступно только в .NET Framework 2.0 SP1. Без сервиспака оно недоступно.

Поэтому лучше использовать технологию Adapters.
Для этого нужно создать папку App_Browsers и в ней файл name.browser со следующим содержимым

<browsers>
<browser refid="Default">
<controladapters>
<adapter controltype="System.Web.UI.HtmlControls.HtmlForm" adaptertype="HtmlFormAdapter">
</adapter>
</controladapters>
</browser>
</browsers>

Остается написать класс HtmlFormAdapter


public class HtmlFormAdapter : ControlAdapter
{
protected override void Render(HtmlTextWriter writer)
{
base.Render(new HtmlFormWriter(writer));
}
private class HtmlFormWriter : HtmlTextWriter
{
public HtmlFormWriter(HtmlTextWriter writer)
: base(writer)
{
InnerWriter = writer.InnerWriter;
}
public HtmlFormWriter(TextWriter writer)
: base(writer)
{
InnerWriter = writer;
}
public override void WriteAttribute(string key, string value, bool fEncode)
{
if ( string.Equals(key, "action", StringComparison.CurrentCultureIgnoreCase) )
{
value = HttpContext.Current.Request.RawUrl;
}
base.WriteAttribute(key, value, fEncode);
}
}
}


Перекрываем метод WriteAttribute и подменяем аттрибут action

P.S. существуют и другие методы, о них возможно напишу позже

14.01.2009

Пейджинг в GridView с использованием get параметров

Пейджинг в GridView, как известно, производится с помощью параметров, сохраняемых во ViewState, таким образом при каждом обновлении делается POST-запрос.

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

Естественное решение проблемы - использование GET-параметров. В них можно передавать номер страницы и количество элементов на странице.

Создаем класс-наследник GridView


public class ContentList : GridView
{
}


Сделаем 2 публичных свойства чтоб манипулировать GET-параметрами (чтоб избежать возможных конфликтов)


public string PageParamName
{
get { return pageParamName; }
set
{
if ( string.Equals(pageSizeParamName, value,
StringComparison.CurrentCultureIgnoreCase) )
throw new ArgumentException("parameter names are equal");
pageParamName = value;
}
}
public string PageSizeParamName
{
get { return pageSizeParamName; }
set
{
if (string.Equals(pageParamName, value,
StringComparison.CurrentCultureIgnoreCase))
throw new ArgumentException("parameter names are equal");
pageSizeParamName = value;
}
}

private string pageParamName = "page"; //default values
private string pageSizeParamName = "pagesize";


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

Теперь перекрываем метод OnInit, в котором устанавливаем значение размера страницы и номера страницы. Здесь нужно учесть что в C# нумерация массивов начинается с нуля а нам желательно начинать нумерацию с единицы.

В итоге получим


protected override void OnInit(EventArgs e)
{
base.OnInit(e);

if (! string.IsNullOrEmpty(Context.Request.QueryString[PageSizeParamName]))
{
PageSize = Int32.Parse(Context.Request.QueryString[PageSizeParamName]);
}

if (!string.IsNullOrEmpty(Context.Request.QueryString[PageParamName]))
PageIndex = Int32.Parse(Context.Request.QueryString[PageParamName]) - 1;
}


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

Остается последний шаг - перекрыть метод InitializePager и там создать ссылки с нужными GET-параметрами. Подробно описывать его не буду, приведу лишь код функции создающей GET-URL с учетом параметров пейджинга.


private string createPagedPath(int page, int pagesize)
{
string param = string.Empty;

foreach ( string s in Context.Request.QueryString.AllKeys )
{
if (string.Equals(s, PageParamName,
StringComparison.CurrentCultureIgnoreCase) )
continue;
if (string.Equals(s, PageSizeParamName,
StringComparison.CurrentCultureIgnoreCase))
continue;
param += string.Format("&{0}={1}", s, Context.Request.QueryString[s]);
}

return string.Format("{0}?{1}={2}&{3}={4}{5}",
Context.Request.Path, PageParamName, page, PageSizeParamName, pagesize, param);
}

Как видно из кода, проверяем все GET-параметры и копируем из них только те, которые не относятся к нашим параметрам с именами PageParamName и PageSizeParamName. Затем берем Path запроса, добавляем к нему параметры пейджинга и исходные параметры, если таковые имеются в запросе.

P.S. Стоит обратить внимание на изменение свойств PageParamName и PageSizeParamName, желательно прописывать их прямо на странице .aspx и не изменять в коде, поскольку это может привести к труднолокализуемым ошибкам (либо вообще не менять, а оставить значения по умолчанию).

09.01.2009

sql management studio 2008

Установил себе наконец sql server 2008 (пока что express with advanced services).
Обнаружил, что наконец-то появился более-менее приличный intellisence в редактировании хранимых процедур.
Как и следовало в общем ожидать, радость продлилась недолго. Например, в нем не поддерживаются временные таблицы.
То есть код

INSERT * FROM table INTO #temp

и далее

SELECT * FROM #temp

вызывает ошибку unknown identifier #temp. Естественно, процедура компилируется. Так что хорошее начало но работать им видимо придется еще.