...

суббота, 22 марта 2014 г.

Интерфейс с drag-and-drop: как не запутать пользователя?

Уверен, всем приходилось работать с интерфейсами drag-and-drop, а многим — разрабабтывать ПО с таковыми. В большинстве случаев факт drop'а объекта-draggable на объект-target устанавливается по факту попадания координат курсора мыши в bounding box объекта-target в обработчке событий типа mouseUp, dragStop и прочих.

Так работают почти все примеры, которые мне встречались. Но некоторое время назад, при реализации модуля интерактивного задания для образовательного ресурса, я столкнулся с тем, что такой подход не очень удобен. Основная причина — объекты-target существенно меньше объектов-draggable. Поэтому целится мышью неудбоно и утомительно. Таща крупный объектами-draggable, пользователь полностью перекрывает объект-target и не видит куда объект падает.


image


Соответственно было принято решение обрабатывать так:



  • если имеется контакт мыши с объектом-target, тогда drop будет строго на него

  • если нет, тогда ориентируемся на контакт bounding box объекта-draggable с bounding box объектов-target


    • если имеется контакт только с одним объектом-draggable — всё ясно, drop на него

    • а вот контакт с двумя и более — неоднозначная ситуация, куда делать drop — неясно






В такой ситуации можно было бы задавать пользователю вопрос — куда он хочет drop'нуть объект. Это удобно, если объекты-target на экране как-то именованы (например, пронумерованы). Однако в нашем случае это излишнее усложнение интерфейса. Поэтому решили запрещать drop в таком случае и реагировать так, словно пользователь отпустил объект при отсутствии контакта.

Подсветка.


Также мы решили в случае однозначности подсвечивать контактирующий объект-target (условно) зеленым цветом, а в случае неоднозначности все контактирующие объекты-target — желтым. Таким образом мы даем пользователю подсказку — почему у него в одном случае drop происходит нормально, а в другом — нет.


Однако! Напомню, что речь идет об учебном задании. Есть мнение, что такая подсветка может восприниматься как подсказка о правильности или неправильности попытки решения, а не о самом факте допустимости drop'а. При этом, позже к заданию добавили режим подсветки правильных ответов после вызова процедуры проверки. Если drop был на правильный объект-target, тогда объект-draggable подсвечивается зеленым, если нет — красным. И это стало сильно резонировать с подсветкой в процессе самого выполнения задания. Мы поменяли там цвета и стили подсветки, но насколько понятен такой интерфейс — неясно.


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


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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


хабрасуицид


сегодня в 20:33


Мы все надеялись, что ситуация в Крыму как-то образуется. Ещё недавно большинство на Украина считало Россию другом, братом, товарищем.В большинстве мы любили руссих, скорее вопреки чему-то чем за что-то.От любви до ненависти один шаг.И этот шаг уже сделан. Наш мир в котором Россия-друг разбился для того что бы из осколков склеить что-то другое. Я не знаю что это будет, но Россия точно перестанет быть для нас другом.Сейчас у нас ЕЩЁ НЕТ того, чем пугают по российским каналам. Пока ещё нет…Ведь российскую пропоганду можно воспринять так же как и ценные указания к действию.Но в появлении русофобов на Укриане виновата только сама Россия, которая сделала этот самый шаг к ненависти.Да и в Мире в целом «руссофобство» становится уже популярным.



Свежий взгляд

на бег


протестируй кроссовки

нового поколения




Стань

первоиспытателем!


Скачай Windows Server 2012 R2

и выиграй почетную футболку!


Скачать




Автоматизированное

продвижение сайтов




  • 50% экономии на ссылках

  • Запуск проекта за 10 минут

  • Вывод и удержание в ТОП 10



Подробнее




Новый 3G-планшет Login 2



2790 р.*


*Условия акции на www.megafon.ru

Подробнее




Разрабатываешь

приложения для бизнеса?


Участвуй в конкурсе



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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Команда Facebook выложила в открытый доступ язык Hack


сегодня в 19:57


Новый язык Hack является прямым потомком PHP, основанным на его синтаксисе. Hack создан специально для виртуальной машины HHVM (Hip Hop Virtual Machine). Команда Facebook полностью перевела разработку на новый язык и называет эту миграцию большим успехом. Однако Facebook не отказывается от поддержки PHP и планирует доработать поддержку PHP5 в HHVM.

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

Официальный сайт нового языка — http://hacklang.org/.

Новость о релизе языка.



Пример кода



<?hh
class MyClass {
public function alpha(): int {
return 1;
}

public function beta(): string {
return 'hi test';
}
}

function f(MyClass $my_inst): string {
// Fix me!
return $my_inst->alpha();
}


Возможности нового языка




Основной особенностью Hack стала реализация статической типизации. Это потребовало отключения некоторых возможностей языка, таких как переменная в переменной $$ и функция extract (неявное создание переменных в таблице символов из массива).

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

Типы проверяются специальным сервером (сервисом, демоном), который следит за изменением файлов исходного кода на диске.

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

Спасибо, Facebook, возможно это то, чего так долго не хватало PHP!





Свежий взгляд

на бег


протестируй кроссовки

нового поколения




Стань

первоиспытателем!


Скачай Windows Server 2012 R2

и выиграй почетную футболку!


Скачать




Автоматизированное

продвижение сайтов




  • 50% экономии на ссылках

  • Запуск проекта за 10 минут

  • Вывод и удержание в ТОП 10



Подробнее




Новый 3G-планшет Login 2



2790 р.*


*Условия акции на www.megafon.ru

Подробнее




Разрабатываешь

приложения для бизнеса?


Участвуй в конкурсе



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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


[Перевод] Как одним судебным преследованием Microsoft разрушила веру клиентов в неприкосновенность своих данных в «облачных» сервисах


На днях была опубликована новость о том, что бывший сотрудник Microsoft Алексей Кибкало (akibkalo) был арестован за хищение и разглашение торговых секретов Microsoft. Это событие, несомненно, стало новостью дня, и внезапно обнаружило то, что Microsoft занималась поиском «неопровержимых» доказательств против Алексея в чужих ящиках службы Hotmail/Outlook.


С одной стороны — ничего удивительного, ведь условия предоставления сервиса гласят:




«Вы соглашаетесь с тем, что корпорация Майкрософт вправе получать доступ, разглашать или хранить сведения, связанные с использованием вами служб, включая (без ограничения) ваши персональные данные и содержимое либо сведения, которые корпорация Майкрософт получает о вас вследствие использования вами служб (например, IP-адрес или другие сведения от третьих лиц), если у корпорации Майкрософт имеются основания полагать , что такие действия необходимы для

(а) соблюдения требований действующего законодательства или надлежащего реагирования на запросы судебных органов;

(б) обеспечения выполнения настоящего соглашения или защиты прав или собственности корпорации Майкрософт или ее пользователей либо (в) для обеспечения личной безопасности или предотвращения угрозы здоровью других людей.»

http://ift.tt/1ip06u2



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


Под давлением негодующей общественности, Microsoft пришлось объявить, что компания все же пересмотрит свою политику в отношении частных данных, хранимых в облачной почтовой службе Hotmail/Outlook. Джон Фрэнк, заместитель главы юридической службы и вице-президент по правовым вопросам, сделал официальное заявление в защиту действий компании, и конечно же пообещал «улучшить процессы и повысить прозрачность».



«Неприкосновенность данных наших клиентов невероятно важна для нас», — заявил Джон.



Конечно, уж куда проще извиниться, чем спросить разрешение.


Лично у меня нет претензий к тому, как Microsoft поступила в данной ситуации. Разумеется у них есть право шарить по своим собственным серверам. Ведь «Условия предоставления сервиса» дают им это разрешение.


ТЕМ НЕ МЕНЕЕ


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


Провайдеры облачных сервисов говорят что-то вроде «это ваши данные, мы просто храним их для вас» чтобы втереться в доверие и придать ложное чувство защищенности. На самом деле это просто чепуха.


Лучше всего на эту тему высказался в августе 2012 года Стив Возняк:



«В облаке вы уже не владеете ничем. И сами подписались на это. Чем больше данных мы передаём в веб, в облака, тем меньше контроля над ними остаётся у нас самих».



Так же Стив сказал:



«Я серьезно обеспокоен тем, как всё перемещается в облака. Мне кажется, что скоро это превратится в кошмар. Я опасаюсь самых ужасающих последствий уже в ближайшие пять лет».



Вот так одним, несомненно законным шагом, Microsoft сама доказала точку зрения, что компании, хранящие данные в облаках, невольно теряют свою собственность. Проблема касается не только службы Hotmail/Outlook. Это относится ко всем облачным службам Microsoft (Windows Azure, Xbox Lice, OneDrive, и т.д.). И это просто ужасно.


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


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


Доверие к политике Microsoft в отношении неприкосновенности данных, хранящихся в Hotmail/Outlook попало под серьезный удар, но отрадно слышать, что политика будет улучшена, и будет вызывать меньше вопросов, если в будущем придется прибегнуть к подобным действиям.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


[recovery mode] Концепция АТД



Доброго времени суток, хабравчане!

Следующий пост является изложением моих размышлений на тему природы классов и АТД. Эти размышления дополнены интересными цитатами из книг гуру разработки программного обеспечения.


Введение




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

наличие определенных доступных операций над элементами этого типа;

а также данные, относительно которых эти операции выполняются (диапазон значений).

Что же означает слово “абстрактный”? В первую очередь понятие “абстрактность” означет сосредоточение внимания на чем-то важном и, при этом, нам нужно отвлечься от неважных, на данный момент, деталей. Определение абстрактности хорошо раскрыто в книге Гради Буча (“Grady Booch”). Звучит определение так:

Абстракция – это выделение и придание совокупности объектов общих свойств, которые определяют их концепутальные границы и отличают от всех других видов объектов.

Иными словами, абстракция позволяет “пролить свет” на нужные нам данные объектов и, при этом, “затенить” те данные, которые нам не важны.


Итак, что же будет, если слить понятия “тип данных” и “абстракция” воедино? Мы получим тип данных, который предоставляет нам некий набор операций, обеспечивающих поведение объектов этого типа данных, а также этот тип данных будет скрывать те данные, с помощью которых реализовано данное поведение. Отсюда, приходим к понятию АТД:


АТД – это такой тип данных, который скрывает свою внутреннюю реализацию от клиентов.

Удивительно то, что путем применения абстракции АТД позволяет нам не задумываться над низкоуровневыми деталями реализации, а работать с высокоуровневой сущностью реального мира (Стив Макконнелл).


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


Преимущества АТД


Использование АТД имеет массу преимуществ (все описанные преимущества можно найти в книге Стива МакКоннела «Совершенный код”):



  • Инкапсуляция деталей реализации.

    Это означает, что единожды инкапсулировав детали реализации работы АТД мы предоставляем клиенту интерфейс, при помощи которого он может взаимодействовать с АТД. Изменив детали реализации, представление клиентов о работе АТД не изменится.



  • Снижение сложности.

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



  • Ограничение области использования данных.

    Используя АТД мы можем быть уверены, что данные, представляющие внутреннюю структуру АТД не будут зависеть от других участков кода. При этом реализуется “независимость” АТД.



  • Высокая информативность интерфейса.

    АТД позволяет представить весь интерфес в терминах сущностей предметной области, что, согласитесь, повышает удобочитаемость и информативность интерфейса.


Стив Макконнелл рекомендует представлять в виде АТД низкоуровнеые типы данных, такие как стек или список. Спросите себя, что представляет собой этот список. Если он представляет список сотрудников банка, то и рассматривайте АТД как список сотрудников банка.


Итак, мы разобрались, что такое АТД и назвали преимущества его применения. Теперь стоит отметить, что при разработке классов в ООП следует думать, в первую очередь, об АТД. При этом, как сказал Стив МакКоннелл, Вы программируете не на языке, а с помощью языка. Т.е Вы будете программировать выходя за рамки языка, не ограничиваясь мыслями в терминах массивов или простых типов данных. Вместо этого Вы будете думать на высоком уровне абстракции (например, в терминах электронных таблицах или списков сотрудников). Класс – это не что иное как дополнение и способ реализации концепции АТД. Мы можем даже представить класс в виде формулы:

Класс = АТД + Наследование + Полиморфизм.

Так почему же следут думать об АТД, при разработке классов. Потому что, сперва мы должны решить какие операции будут составлять интерфейс будущего класса, какие данные скрыть, а к каким предоставить открытый доступ. Мы должны подумать об обеспечении высокой информативности интерфейса, легкости оптимизации и проверки кода, а также о том, как бы нам предоставить правильную абстракцию, чтобы можно было думать о сущностях реального мира, а не о низкоуровнеых деталях реализации. Я считаю, что именно после определения АТД мы должны думать о вопросах наследования и полиморфизма.


Стоит отметить, что концепция АТД нашла гирокое применение в ООП, т.к именно эта концепция дополняет ООП и позволяет уменшить сложность программ в быстроменяющемся мире требований к ПО.


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


Использованные источники:


Стив Макконнелл – “Соверешенный код”;

Роберт Седжвик – «Алогритмы на Java».


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Mozilla изменяет дизайн Firefox: пробуем Firefox 29 beta


Компания Mozilla уже довольно давно работает над редизайном своего браузера, и теперь появились первые значительные результаты. Испробовать переосмысленный дизайн браузера Firefox можно на примере Firefox 29 beta.


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


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


Кроме всего прочего, у Firefox 29 beta теперь новый способ настройки интерфейса: можно использовать перетягивание для таких объектов, как журнал, синхронизация и все прочее. Кроме того, можно перетягивать иконки на тулбар.


Функция Firefox Sync по-прежнему позволяет синхронизировать браузеры на различных машинах пользователя, включая мобильные устройства. Что касается последних, то здесь появилась функция Firefox Accounts.


Тем не менее, наиболее заметным нововведением является все же интерфейс, который вскоре станет дефолтным для всех версий этого браузера.


Бета доступна для платформ Windows, Mac, Linux, и Android.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Конечное и бесконечное в математике. Лекция Павла Кожевникова в Яндексе

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

Гильбертов отель




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

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


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


Попробуем формализовать рассмотренные нами только что задачи с точки зрения теории множеств. Что мы по сути сейчас делали? Мы нумеровали комнаты отеля. Такая ситуация в математике называется счетным множеством: бесконечное множество K, которое можено перенумеровать. Бесконечное множество называется счетным, если существует взаимно однозначное соответствие между элементами множества K и натуральными числами {1, 2, …}. Формальное представление рассмотренных нами задач выглядит следующим образом:



  • K – счетное множество, следовательно, K∪{a} также будет счетным множеством.

  • К1, K2 – счетные множества, следовательно, К1∪K2 будет счетным множеством.

  • K1, K2, K3, K4, … – счетные множества, следовательно, объединенное множество также будет счетным:




image

Счетные и несчетные множества




Но перейдем к более привычным для нас множествам – числовым. И начнем с натуральных чисел. Очевидно, что множество натуральных чисел (ℕ) счетное просто по определению. Если мы добавим к натуральным числам ноль и отрицательные числа, мы получим множество целых чисел. Счетное ли это множество? Мы уже говорили выше, что если объединить два счетных множества, результат также будет счетным множеством. От добавления 0 множество не потеряет свойства быть счетным.

Далее рассмотрим рациональные числа. Это множество чисел, представимых в виде m/n, где m – целое число, а n – натуральное:


image


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


Алгебраические числа – это множество корней многочленов с целыми коэффициентами. Будет ли это множество счетным? Множества корней многочленов разных степеней являются счетными множествами, а значит, результат их объединения также будет счетным множеством. Из этого следует, что множество алгебраических чисел (𝔸) – это счетное множество.


Существуют так называемые трансцендентные числа: в это множество (𝕀) входят действительные (ℝ), но не алгебраические (𝔸) числа. К этому множеству относятся числа e, π и т.д.


Раз уж мы упомянули действительные числа, остановимся на них подробнее, тем более, что как раз их множество счетным не является, что мы сейчас и докажем. И для этого нужно доказать что множество бесконечных десятичных дробей несчетно. Для простоты представим, что после запятой у нас могут быть только нули и единицы:


image


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
























0,a1, a2, a3, a4, a5
0,b1, b2, b3, b4, b5
0,c1, c2, c3, c4, c5
0,d1, d2, d3, d4, d5
...
...
...



Докажем, что такой таблицы существовать не может, сконструируем такую дробь, встретить которую в этой таблице невозможно:

0,x1, x2, x3, x4, x x5


Где x1≠a1, x2≠b2, x3≠c3 и т.д. Называется это канторовым диагональным процессом, он приводит нас к числу, которого нет в нашей таблице. А значит, множество у нас несчетное.


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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


[Из песочницы] Техники проработки персонажей в играх: Часть 1 — Таро

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

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


Первая часть статьи ознакомит вас с ассоциативным подходом с использованием карт Таро.


image



Зачем нужна разработка персонажей в играх?



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

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


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


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


Финальной причиной рассмотреть разработку характеров персонажей в процессе разработки самой игры, является то, что эти персонажи могут стать олицетворением бренда. Существует множество персонажей, чьи имена являются синонимами к их же медиафраншизам, например Master Chief и Samus Aran.


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


Что это за техники?



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

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


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


Методы, которые мы рассмотрим, следующие: колода Таро, затруднительное положение, беседа, сеть персонажей.


Колода Таро



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

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


Перед тем, как мешать колоду и сдавать карты, Вам стоит ознакомиться с символами и их значениями. К примеру, согласно некоторым, Башня означает падение гордости, или обреченности. Фокусник символизирует какое-то божественный проведение, Звезда — надежду и бессмертие, и т.д.


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

К примеру, вы работаете над игрой в жанре научной фантастики. Один из ваших персонажей ученый по имени Ленникс. Мы раздаем три карты на него и получаем Звезду (надежду\бессмертие), Фокусника (божественное проведение) и Башню (падение гордости\обреченность). Итак, если мы рассмотрим символическое значение этих карт, мы можем сойтись на следующих: надежде, божественном проведении, падении гордости.


Возможно Ленникс видит технологии, как последнюю надежду человечества, путь для людей, чтобы выйти за пределы их мелочности и фанатизма. Через науку, он надеется ускорить эволюцию людей, продвинув нас до уровня высшего разума. Эта затея оканчивается провалом и в результате страдает его гордость. Возможно, его эксперименты окончились смертью (или хуже), либо его раскрыли и изгнали. Итак Ленникс руководствуется чувством вины. Он знает, чего он хотел достичь, и возможно он чувствовал, что был не прав, и он выучил этот урок. Либо он чувствует, что был разбит, но в тайне продолжает свои эксперименты.


Чтение карт в другом порядке дает другие результаты. Скажем, мы раздали Башню, Фокусника и Звезду. В этом свете Ленникс, возможно, пережил какое-то личное горе — катастрофу, что причинила ему множество страданий. Он потерял свою веру в науку, но обрел другую веру — религию, магию или единение с природой, или что-то другое — и теперь он верит, что все же есть еще надежда. Глупый дурак, надежды на самом деле нет.


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


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


Продолжение следует...


Собственно ссылка на оригинал.

Буду благодарен за любые отзывы относительно перевода.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


[Перевод] Broccoli: первый бета-релиз

Broccoli является новой системой автоматической сборки. Её вполне можно сравнить с Rails asset pipeline, однако есть и некоторые различия: он запускается на Node.JS и не зависит от серверной части приложения.

После длинной вереницы 0.0.х альфа релизов, я только что выпустил первую бета версию, Broccoli 0.1.0.


Оглавление:




  1. Быстрый пример

  2. Мотивация / Особенности

  3. Архитектура

  4. За кулисами / Общий взгляд

  5. Сравнение с другими системами сборки

  6. Что дальше?






1. Быстрый пример




Ниже представлен пример конфигурационного файла для билда(Brocfile.js). Комментарии намеренно опущены, пример приведен только для того, чтобы проиллюстрировать синтаксис:

module.exports = function (broccoli) {
var filterCoffeeScript = require('broccoli-coffee');
var compileES6 = require('broccoli-es6-concatenator');

var sourceTree = broccoli.makeTree('lib');
sourceTree = filterCoffeeScript(sourceTree);

var appJs = compileES6(sourceTree, {
...
outputFile: '/assets/app.js'
});

var publicFiles = broccoli.makeTree('public');

return [appJs, publicFiles];
};




Запустите broccoli serve, чтобы начать отслеживать изменения исходных файлов. При каждом изменении любого файла из списка отслеживаемых, broccoli автоматически пересоберет его в целевой директории. Broccoli оптимизирован выполнять serve максимально быстро, поэтому вы не должны испытывать паузы между очередными сборками.

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


Для более подробного примера, взгляните на broccoli-sample-app


2. Мотивация / Особенности




2.1. Быстрый ребилд



Главной задачей при проектировании Broccoli, была реализация быстрых инкрементальных сборок. И вот почему:

Например, вы используете Grunt для сборки приложения, написанного на CoffeeScript, SASS, и еще нескольких препроцессорах. Когда вы что-то разрабатываете, вы хотите редактировать файлы и сразу же видеть результат в браузере, без постоянных запусков билд системы. Так вот, для этой цели вы используете grunt watch, но по мере увеличения вашего приложения, сборка происходит все медленней и медленней. После нескольких месяцев работы над проектом, ваш цикл «отредактировал-обновил» превращается в «отредактировал-подождал-10-секунд-обновил».


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


С помощью Broccoli, вы можете просто запустить broccoli serve, и он сам поймет, какие файлы необходимо отслеживать, и будет пересобирать только те, которые в этом нуждаются.


В результате, это означает, что ребилд, как правило, должен иметь O(1) постоянное время выполнения, вне зависимости оттого, какое количество файлов используется в проекте, т.к. собирается всегда только один. Я стремлюсь к результату в 200ms на каждую сборку с типичным набором задач, а т.к. такое время задержки кажется почти мгновенным для человеческого мозга, то для меня приемлемы результаты до половины секунды.


2.2. Цепочки плагинов



Другая важная задача — возможность компановки вызова плагинов. Давате рассмотрим пример, чтобы показать как просто, используя Broccoli, вы можете компилировать CoffeeScript с последующей минифакцией:

var tree = broccoli.makeTree('lib')
tree = compileCoffeeScript(tree)
tree = uglifyJS(tree)
return tree




Используя Grunt, вам бы пришлось создавать временную директорию(помимо итоговой), чтобы сохранить туда вывод CoffeeScript. В результате всех этих манипуляций, Gruntfile обычно разрастается до довольно больших размеров. С помощью Broccoli, все подобные действия разруливаются автоматически.

3. Архитектура




Для особо любопытных, позвольте мне рассказать об архитектуре Broccoli.
3.1. Деревья, а не файлы



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

Если бы Broccoli работал с отдельными файлами, мы бы по-прежнему смогли компилировать CoffeeScript без проблем(т.к. файлы компилируются в соответствии 1 к 1), однако это вызвало бы проблемы при взаимодействии API с такими препроцессорами, как SASS(который позволяет использовать @import , что позволяет компилировать n файлов в 1).


Однако Broccoli спроектирован таким образом, что решение задач для препроцессоров типа SASS(n:1) не вызывает проблем, а задачи для препроцессоров типа CoffeeScript(1:1) легко решаются как частный случай n:1. А вообще, для таких(1:1) преобразований, у нас имеется класс Filter, который позволяет максимально просто использовать их в своих решениях.


3.2. Плагины просто возвращают новые деревья



Сперва, я спроектировал Broccoli с двумя примитивами: tree(далее «дерево»), которое представляют директории с файлами и transform(далее «преобразование»), которое берет на входе дерево и возвращает на выходе новое, скомпилированное дерево, после преобразований.

Это подразумевает, что мы преобразовываем деревья 1:1. Удивительно, но это не всегда является хорошей абстракцией. Например, в SASS есть «пути загрузки», которые используются для поиска файлов при использовании директивы @import. По схожему принципу работают конкатенаторы вроде r.js: у него существует опция «paths», которая отвечает за поиск импортируемых модулей. Лучший способ представления таких путей — сет(структура данных), состоящий из деревьев.


Как вы можете заметить, в реальном мире многие компиляторы/препроцессоры собирают n «деревьев» в одно. Простейший способ придерживаться подобного подхода — позволять плагинам разбираться со своими входными «деревьями» самим, тем самым позволяя им принимать 0, 1 или n «деревьев» на вход.


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



broccoli.makeTree('lib') // => a tree
compileCoffeeScript(tree) // => a tree
compileSass(tree, {
loadPaths: [moreTrees, ...]
}) // => a tree


3.3. Файловая система и есть наше API



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

Чтобы избежать этого, первое, что приходит на ум — вынести представление файловой системы в память, где наши деревья будут представлены в виде коллекции потоков. Gulp так и делает. Я тоже пытался реализовать такой подход в ранних версиях Broccoli, но это обернулось тем, что код стал достаточно сложным: с потоками, плагины должны были следить за очередями и взаимными блокировками. Также, к слову о потоках и путях: нам могут потребоваться атрибуты вроде времени последнего изменения или размера файла. Или, например, если нам будет необходима возможность считать файл заново или что-либо найти, отобразить файл в памяти, или, в конце концов, если нам потребуется передать входное дерево другому процессу через терминал — тут наше API для работы с потоками не сможет нам помочь, и нам придется сначала записать всё дерево в файловую систему. Слишком сложно!


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


Модуль fs Node.JS уже предоставляет необходимый нам API к файловой системе — все, чего мы можем только пожелать.


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


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


3.4. Кеширование вместо частичного ребилда



Когда я пробовал решить проблему инкрементальных сборок, я пытался разработать способ проверить, какие файлы нужно пересобирать, чтобы позволить Broccoli вызывать это событие только для подмножества исходных файлов. При инкрементальной сборке нам необходимо знать, от каких исходных файлов зависит результат, ведь зачастую мы сталкиваемся с отношениями n:1. «Частичная сборка» — это классический подход Make, так же как и Rails asset pipeline, Rake::Pipeline или Brunch, но лично я для себя решил, что это ненужные трудности.

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


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


Для плагинов, которые мапятся 1:1, таких, как CoffeeScript, мы можем использовать общий кеширующий механизм(представленный в пакете broccoli-filter), оставляя код плагина очень простым. Плагины, которые собираются n:1, такие, как SASS, требуют более тщательной заботы о кешировании, поэтому для них требуется реализовывать особенную логику для работы с кешем. Я полагаю, что в будущем мы все же сможем выделить какую-то общую часть логики кеширования.


3.5. «Нет» параллельности



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

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


С другой стороны, закон Амадаля останавливает нас от выигрыша большой производительности, при использовании параллельных запусков. Давайте приведем простой пример: наша сборка занмиает 16 секунд. Представим, что 50% мы можем запускать параллельно, а оставшаяся часть должна запускаться в порядке очереди(а-ля coffee->concate->uglify). Если мы запустим такую сборку на четырехядерной машине, то сборка будет занимать 10 секнуд: 8 секунд на синхронную часть, и 8 / 4 = 2 секунды на параллельную часть. В результате время сборки все равно целых 10 секнуд, а это всего лишь +40% производительности.


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


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


4. За кулисами / Общий взгляд



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

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


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


Вторая причина кроется в поддержке экосистемы front-end пакетов.


Я верю, что Bower и модульная система ES6 помогут нам построить прекрасную экосистему, но Bower сам по себе бесполезен, пока вы не надстроите над ним систему сборки. Вот почему Bower является абсолютно ни к чему не привязанным инструментом, позволяющим загружать все зависимости вашего проекта(рекурсивно, вместе с их зависимостями) в файловую систему — это все, что можно сделать с помощью него. Broccoli же нацелен стать именно тем недостающим звеном — надстройкой в виде системы сборки, под которой тот будет работать.


Кстати говоря, Broccoli сам по себе никак не связан с Bower или модулями ES6 — вы можете использовать его с тем, с чем захотите. (я знаю, что есть и другие связки, например npm + browserify, или npm + r.js.) Я затрону их в одном из следующих своих постов.


5. Сравнение с другими системами сборки



Если я вас почти убедил, но вам все еще интересно, как другие системы сборки ведут себя в сравнении с Broccoli, то позвольте мне объяснить, почему я написал Broccoli вместо того, чтобы использовать одну из нижеперечисленных:

Grunt — это инструмент для запуска задач, и он никогда не позиционировался, как система для сборки. Если вы попробуете использовать его как систему сборки, вы быстро разочаруетесь, т.к. он не предоставляет возможности использовать цепные вызовы(композицию), и вам быстро надоест разбираться со временными директориями, а тем временем файл конфигурации будет все разрастаться и разрастаться. Так же он не может обеспечить надежные инкриментальные сборки, поэтому ваши повторные сборки будут выполняться медленно и/или будут ненадежны, см. «Быстрый ребилд» выше.


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


Gulp пытается решить проблему с последовательным вызовом плагинов, но, как по мне, он страдает определенными ошибками в архитектуре: несмотря на то, что вся его работа крутится вокруг «деревьев», они реализованы последовательностью файлов. Это отлично работает, когда один исходный файл преобразоввывается в один конечный файл. Но когда плагину требуется следовать import директивам, а это требует обращение к файлам вне очереди, работа усложняется. Сейчас, плагины, которые используют import директивы, приостанавливают выполнение сборки и считывают необходимые файлы прямо из файловой системы. В будущем, я слышал, будут использованы библиотеки, которые позволят запускать все потоки на виртуальной файловой системе и передавать их компилятору. Я считаю, что все эти усложнения — симптом полного несоответствия между системой сборки и компилятором. Вы можете еще раз прочесть раздел «Деревья, а не файлы», там я более подробно останавливался на этом вопросе. Я так же совсем не уверен, что абстрагируясь от файлов к потокам или буферу, мы получим более удобное API; см. «Файловая система и есть наше API».


Brunch, как и Gulp, использует API, основанное на файлах(а не на деревьях). Так же как и в Gulp, плагины, в конце концов, идут в обход системы сборки, когда им необходимо получить более одного файла. Brunch так же пытается выполнять частичный ребилд вместо кеширования, см. «Кеширование вместо частичного ребилда» выше.


Rake::Pipeline написан на Ruby, который менее вездесущ, чем Node в мире front-end разработки, и он так же пытается выполнять частичные сборки. Иегуда Кац сказал, что система не очень активно поддерживается, и он ставит на Broccoli.


Rails asset pipeline так же использует частичные сборки, и, более того, использует весьма различные подходы для разработки и продакшена, что может привести к весьма неожиданным ошибкам в момент деплоя. Но, что важнее, он требует ROR на бэкэнде.


6. Что дальше




Список плагинов все еще невелик. Но если этого достаточно для ваших задач, я бы настоятельно рекомендовал дать Broccoli шанс: http://ift.tt/1cShDFT

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


В ближайшие пару недель в мои планы входит улучшение документации и вычищение кода ядра Broccoli и плагинов. Мы так же хотим добавить тесты для ядра Broccoli и предоставить элегантное решение интеграционных тестов для плагинов. Так же, в наших существующих плагинах отсутствует поддержка source map'ов. Это весьма накладно с точки зрения производительности, т.к. плагинам, при последовательном вызове, приходится брать Source Maps предыдущего плагина и правильно их интерполировать, но я пока еще не нашел времени заняться этим.


Скоро вы сможете увидеть активное использование Broccoli в экосистеме фреймворка Ember, который будет обеспечивать дефолтный стек ember-cli(скоро появится, по функциональности похожа на rails command line). Мы так же надеемся заменить Rake::Pipeline и Grunt при процессе сборки ядра Ember.


Я бы так же очень хотел увидеть Broccoli адаптированным под проекты вне Ember-сообщества. JS MVC приложения, написанные с помощью таких фреймворков, как Angular или Backbone, различные JS и CSS библиотеки, требующие сборок — главные кандидаты, чтобы быть собранными с помощью Broccoli. Используя Broccoli для реальных сценариев сборки, мы должны удостовериться в надежности его API, и я надеюсь, что в ближайшие несколько месяцев мы сможем выпустить первую стабильную(1.0.0) версию.


Этот пост является первым постом, детально рассматривающим архитектуру Broccoli, так что справочной информации/документации все еще мало. Я буду рад помочь вам начать работу, и исправить любые баги, с которыми вы столкнетесь. Вы можете найти меня на #broccolijs на Freenode, или написав мне на почту/позвонив в Google Talk: joliss42@gmail.com. Я так же буду рад ответить на любые интересующие вас вопросы в соответствующем разделе на Github.


Особые благодарности Jonas Nicklas, Josef Brandl, Paul Miller, Erik Bryn, Yehuda Katz, Jeff Felchner, Chris Willard, Joe Fiorini, Luke Melia, Andrew Davey, and Alex Matchneer за чтение и критику черновиков этой статьи.


От переводчика



Я бы хотел поблагодарить z6Dabrata и Марию Гилёву за помощь в корректировании этого перевода.

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


[Из песочницы] Long Polling для Android

Прочитав статью, стал внедрять в web проекты Long Polling. На nginx крутится серверная часть, на javascript клиенты слушают каналы. Прежде всего это было очень полезно для личных сообщений на сайте.

Потом в поддержку web проектов стали разрабатываться приложения под Android. Встал вопрос: как реализовать многопользовательский проект, в котором равнозначно участвовали бы как браузерные клиенты, так и мобильные приложения. Так как Long Polling уже был внедрён в браузерные версии, решено было написать java модуль и для Android.



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

Серверная часть




Итак начну с сервера.
Nginx



Используется nginx-push-stream-module

В настройках nginx:



# Директива для сбора статистики
location /channels-stats {
push_stream_channels_statistics;
push_stream_channels_path $arg_id;
}

# Директива для публикации сообщений
location /pub {
push_stream_publisher admin;
push_stream_channels_path $arg_id;
push_stream_store_messages on; # Сохранять пропущенные сообщения, чтобы доставить их, когда клиент начнёт слушать канал
}

# Директива для прослушивания сообщений
location ~ ^/lp/(.*) {
push_stream_subscriber long-polling;
push_stream_channels_path $1;
push_stream_message_template "{\"id\":~id~,\"channel\":\"~channel~\",\"tag\":\"~tag~\",\"time\":\"~time~\",\"text\":~text~}";
push_stream_longpolling_connection_ttl 30s;
}




В конфиге описаны три директивы: для отправки, приёма сообщений, а так же для получения статистики.
PHP



Сообщения публикуются сервером. Вот пример функции на php:

/*
* $cids - ID канала, либо массив, у которого каждый элемент - ID канала
* $text - сообщение, которое необходимо отправить
*/
public static function push($cids, $text)
{
$text = json_encode($text);
$c = curl_init();
$url = 'http://ift.tt/NCgfkn';

curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
curl_setopt($c, CURLOPT_POST, true);
$results = array();
if (!is_array($cids)) {
$cids = array($cids);
}
$cids = array_unique($cids);
foreach ($cids as $v) {
curl_setopt($c, CURLOPT_URL, $url . $v);
curl_setopt($c, CURLOPT_POSTFIELDS, $text);
$results[] = curl_exec($c);
}
curl_close($c);
}




Здесь тоже всё просто: передаём канал(ы) и сообщение, которое хотим отправить. Ввиду того, что обычный plain текст никому не интересен, и существует замечательный формат json, отправлять можно сразу объекты.

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


В итоге был разработан следующий формат, состоящий из трёх параметров:



[id пользователя]_[название сервиса]_[id сервиса]




Если мы хотим отправить сообщение всем пользователям, то используем канал:

0_main_0




если пользователю с id=777:

777_main_0




если изменилась стоимость заказа с id=777 в общем для всех списке, то:

0_orderPriceChanged_777




Получилось очень гибко.

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

А для разделения типов сообщения можно использовать параметр, например act:

const ACT_NEW_MESSAGE = 1;
LongPolling::push($uid."_main_0", array(
"act" => ACT_NEW_MESSAGE,
"content" => "Hello, user ".$uid."!",
));


Клиентская часть




Про серверную часть вроде всё. Приступим к java!

Класс я разместил на gihub.

В своей библиотеке я использовал библиотеку android-async-http, осуществляющую удобные асинхронные http запросы. В примере я добавил скомпилированный jar файл.

Интерфейс у класса достаточно простой.

Для начала надо создать callback объект, в методы которого будут приходить ответы. Так как мы используем в сообщениях преимущественно объекты, то в качестве callback класса был выбран JsonHttpResponseHandler:



private final static int ACT_NEW_ORDER = 1;
private final static int ACT_DEL_ORDER = 2;
private final static int ACT_ATTRIBUTES_CHANGED = 3;
private final static int ACT_MESSAGE = 4;

private final JsonHttpResponseHandler handler = new JsonHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
try {
JSONObject json = response.getJSONObject("text");
switch (json.getInt("act")) {
case ACT_NEW_ORDER:
...
break;
case ACT_DEL_ORDER:
...
break;
case ACT_ATTRIBUTES_CHANGED:
...
break;
case ACT_MESSAGE:
...
break;
default:
break;
}
} catch (JSONException e) {
e.printStackTrace();
}
}
};




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

Дальше инициализируем LongPolling объект (допустим мы это делаем в Activity):



private LongPolling lp;
private int uid = 1;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_balance);
lp = new LongPolling(getApplicationContext(), "http://example.com/lp/", Integer.toString(uid) + "_main_0", handler);
}




Если Long Polling нам нужен только в Activity, то необходимо прописать:

public void onResume() {
super.onResume();
lp.connect();
}

public void onPause() {
super.onPause();
lp.disconnect();
}




Если же сообщения нужно принимать во всём приложении (а это как правило), то объект можно инициализировать в классе приложения (Application) или в сервисе (Service).

Тогда сразу после инициализации нужно начать прослушивание

lp = new LongPolling(getApplicationContext(), "http://example.com/lp/", Integer.toString(uid) + "_main_0", handler);
lp.connect();




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

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://ift.tt/nIICcg">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
...
<application>
...
<receiver android:name="com.app.example.receivers.InternetStateReceiver" >
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<action android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" />
</intent-filter>
</receiver>
</application>
</manifest>




и InternetStateReceiver

public class InternetStateReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
final ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
final android.net.NetworkInfo wifi = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
final android.net.NetworkInfo mobile = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
if (wifi != null && wifi.isAvailable() || mobile != null && mobile.isAvailable()) {
application.getInstance().lp.connect();
} else {
application.getInstance().lp.connect();
}
}
}


Статистика




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

Допустим, нас интересует количество пользователей онлайн.

Для этого мы получаем XML/json информацию по url:

http://ift.tt/1iNidKK



и видим следующее:

<?xml version="1.0" encoding="UTF-8" ?>
<root>
<hostname>example.com</hostname>
<time>2014-03-22T00:03:37</time>
<channels>2</channels>
<wildcard_channels>0</wildcard_channels>
<uptime>818530</uptime>
<infos>
<channel>
<name>4_main_0</name>
<published_messages>0</published_messages>
<stored_messages>0</stored_messages>
<subscribers>1</subscribers>
</channel>
<channel>
<name>23_main_0</name>
<published_messages>0</published_messages>
<stored_messages>0</stored_messages>
<subscribers>1</subscribers>
</channel>
</infos>
</root>




В теге subscribers мы видим количество слушателей каждого канала. Но так как в данном случае у каждого пользователя свой канал, на PHP составим список пользователей онлайн:

const STATISTICS_URL = 'http://ift.tt/1iNidKK';
public static function getOnlineIds()
{
$str = file_get_contents(self::STATISTICS_URL);
if (!$str)
return;
$json = json_decode($str);
if (empty($json -> infos))
return;
$ids = array();
foreach ($json->infos as $v) {
if ($v -> subscribers > 0 && substr_count($v -> channel, '_main_0') > 0) {
$ids[] = str_replace('_main_0', '', $v -> channel);
}
}
return $ids;
}




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

Заключение




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

Буду рад услышать критику и предложения.


Ссылки




  1. http://ift.tt/1iNi89S — собственно результат работы

  2. http://ift.tt/x5d3SE — nxing модуль

  3. http://ift.tt/1bGDy1L — библиотека асинхронных http запросов для Android


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


IntelliJ IDEA 13.1: Весеннее обновление

Привет, Хабр!

Вместе с хорошим известием для Северного полушария, что продолжительность светлого времени суток увеличилась, мы хотим рассказать о релизе IntelliJ IDEA 13.1, случившемся на этой неделе. Но перед рассказом о новинках хотелось бы отметить еще одно, весьма ожидаемое всем Java-cообществом, событие…



Конечно же релиз Java 8. В версии 13.1 мы обеспечили полную поддержку Java 8, поэтому вы можете полноценно работать с последней редакцией языка уже сейчас, в знакомой для вас IDE.


JAVA 8




Внимательный читатель мог заметить, что уже в предыдущих версиях IntelliJ IDEA осуществлялась “предварительная” поддержка Java 8. Например, уже в 13 версии была добавлена поддержка отладки проектов, в которых используются анонимные классы и лямбда-функции.

Теперь новые инспекции и автодополнение кода позволят в полной мере эффективно работать с Java 8. Рассмотрим на примере, как с помощью новых инспекций можно быстро перевести ваш код на новый Stream API:


image


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


Если вы хотите сделать соответствующие правки в более, чем одном месте вашего проекта, то можно запустить соответствующую инспекцию (“foreach loop can be collapsed with stream api”) на всем проекте (или в другом заданном контексте) через Analyze → Run inspection by Name.


image


MULTIPLE SELECTIONS




Одной из важных фич, которую мы добавили в последние версии IntelliJ-based IDE, стала функция множественного выделения (известная как Multiple Selection, Multi Carets и Multiple Cursors). Multiple Selection позволяет вносить правки в код в нескольких местах одновременно, похожая функция на сегодняшний день есть в Submlime Text и некоторых других редакторах.

Как это работает:



  • Добавить/удалить выделение: Alt + Shift + Mouse Click

  • Выделить/отменить выделение следующего совпадения: Alt + J / Shift + Alt + J (Ctrl + G / Shift + Ctrl + G) для Mac OS X)

  • Выбрать все совпадения: Shift + Ctrl + Alt + J (Ctrl + Cmd + G для Mac OS X)

  • Клонировать курсор выше/ниже (горячие клавиши еще не назначены)

  • Удалить все выделения: Esc




Multiple selection отлично работает с другими возможностями редактирования кода IntelliJ IDEA: Code completion, Select word at caret, Join lines, Copy/paste, и другими:

image


Именно эта фича набрала наибольшее количество голосов в нашем треккере.


POSTFIX CODE COMPLETION




Другой важной возможностью, появившейся в версии 13.1, является постфиксное автодополнение кода или postfix code completion. С его помощью вы можете заменять одно выражение (которое вы уже ввели) на другое в зависимости от добавляемого вами постфикса. Функция полезна именно в том случае, если вы уже что-то ввели и теперь хотели бы изменить это выражение, не возвращаясь и не изменяя код.

Например, если к логическому выражению добавить постфикс “.if”, то оно автоматически будет обернуто оператором ветвления if, и курсор переместится в тело оператора. Аналогичным образом “.notnull” оборачивается конструкцией с проверкой на значение null.


image


Таким образом postfix completion позволяет в меньших случаях перемещать курсор в обратном направлении, сохраняя ваше время.


NASHORN DEBUGGER




Для тех, кто планирует использовать runtime Nashorn (о том, что это такое можно почитать в других хабростатьях) для запуска JavaScript в своих приложениях, будет полезным узнать о новых возможностях отладчика IntelliJ IDEA. Теперь можно ставить брейкпоинты и перемещаться между ними, даже если вы отметили строки в JavaScript. Кроме того, если JavaScript ссылается на объект из Java, отладчик будет перемещаться в Java-код и обратно в скрипт в зависимости от последовательности кода и выставленных брейкпоинтов:

image


Более подробно про отладку проектов с JDK8’s Nashorn JavaScript можно прочитать в нашем блогпосте.


CHRONON “BACK-IN-TIME” JAVA DEBUGGER




IntelliJ IDEA 13.1 Ultimate edition обзавелась еще одним очень полезным плагином для интеграции с Сhronon. С его помощью можно записывать историю выполнения программы, и затем очень удобно ее просматривать, перемещаясь по коду и просматривая значения полей в каждый момент вызова конкретного метода. Это делает процесс поиска “сложных” багов более эффективным:

image


Chronon является коммерческим продуктом. Однако пользователи IntelliJ IDEA Ultimate могут пользоваться им совершенно бесплатно в IDE. Как добавить плагин и о подробностях его использования можно прочитать здесь.


ANGULAR-JS, SPY-JS, LIVE CONSOLE


AngularJS



В последней версии IntelliJ IDEA мы также расширили поддержку фреймворка AngularJS.

  • Была добавлена поддержка автодополнения имен директив (directives), контроллеров (controller) и приложений, а также code insight для данных, размещенных внутри фигурных скобок.

  • Angular-specific navigation позволяет легко перемещаться от имени контроллера в HTML к его определению в Javascript, или от ngView или $routeProvider к его темплейтам.

  • Теперь можно вызвать AngularJS документацию для директив прямо из редактора.


image


Spy-JS



Spy-JS — это мощный инструмент для трассировки JavaScript. С помощью него можно просматривать полный список событий, открывать дерево вызовов и подсвеченный путь выполнения кода. Вы также можете просматривать stack trace для выделенного участка кода, фильтровать скрипты и события, следить за производительностью кода. Spy-JS приспособлен для работы на любых стационарных и мобильных браузерах, позволяет эффективно профилировать и отлаживать код.

image


Более подробно про SPY-JS можно почитать в хабростатье.


Live Console



С сonsole читатель скорее всего знаком по работе с отладкой в браузере, однако теперь эта фича доступна и в IDE во время отладки веб-страницы. В консоли вы можете исполнять команд и запускать JavaScript.

image


WHAT ELSE?





  • Улучшилась производительность для проектов на фреймворке Spring, которые используют большие объемы XML. (Ultimate Edition)

  • Добавлены улучшения в системы контроля версий: Amend commit для Merсurial, 2х этапная аутентификация в GitHub, улучшенные Log и Diff viewer.

  • Отладка приложений, запущенных на OpenShift (Ultimate Edition).




Более подробно со списком новых возможностей можно ознакомиться на странице http://ift.tt/o67zU5

Скачать новую версию можно — сами знаете где: http://ift.tt/1h2gLT7


Программируйте с удовольствием!


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


15 проблем, возникающих при интеграции протектора в Windows-приложение

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

Данная статья скорее всего будет малополезна матерым системным програмистам под Windows, а также тем, кто уже съел не одну собаку при создании дистрибутивов ПО.


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


Также данная статья только ставит вопросы, не давая ответов, так что не ищите их в ней.


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



В один прекрасные день я проснулся, и понял, что так жить больше нельзя. Мое приложение качают, устанавливают, и… И все! Не покупают! Я не слишком долго размышлял над причиной — я давно предполагал, что когда-нибудь так и будет, потому что у меня фактически не было защиты — как многие начинающие шароварщики, я решил, что все сделаю сам, и сделал систему защиты на основе ассиметричного шифрования; но под конец, когда первые «защищенные» моей защитой версии стали покупать, мне стало лень доделывать проверку подписи в программе — это надо было еще разбираться с какой-либо криптографической библиотекой для C++, и я оставил, как есть. Покупают же!

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


Протектор — наше всё




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

Пару слов о «фарше»




Под «фаршем» я подразумеваю дополнительные функции, которые облегчают использование протектора и продажу ПО, защищенного им: консольные утилиты для пакетной обработки приложений (в т.ч. чтобы можно было настроить и post-build step); сервер активации, который желательно ставится на любой хостинг; генератор лицензий, который также можно захостить на любом хостинге и отдавать регистритору ссылку на него. Также к «фаршу» можно отнести и поддержку x64-приложений — я был свято уверен, что наравне с x86-версиями я буду предоставлять и x64-версии.

Выбор сделан




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

Еще 30 тыщ ведер и золотой ключик у нас в кармане




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

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


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


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


Дальнейшая повесть, собственно, и рассказывает о некоторых проблемах, с которыми и я столкнулся в процессе этого прикручивания. Как оказалось, есть нюансы, особенно если вы хотите поддерживать все ОС от Windows 2000 до Windows 8.1.


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

Проблема 1 — хранение регистрационной информации на компьютере пользователя




Как оказалось, правильно хранить регистрационные данные пользователя на компьютерах под управлением современных систем Windows не так и просто. Данные можно сохранять без проблем лишь в том случае, если активация производится только для текущего пользователя. Если пытаться активировать программу для всех пользователей (что более чем логично для per computer лицензии), то возникают некоторые проблемы.

Проблема 2 — файлы или реестр




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

Проблема 3 — виртуализация для бесправных




Во времена W2K/WinXP очень многие пользователи грешили тем, что всегда работали на компьютере с правами администратора. В системах Windows, начиная с Windows Vista, Microsoft многое сделала для того, чтобы уменьшить проблемы от такого использования системы.

В современных системах Windows, если приложение с правами обычного пользователя записывает какие-то данные в ветку реестра под HKEY_LOCAL_MACHINE, то срабатывает виртуализация реестра и реально данные пишутся куда-то в ветку HKEY_CURRENT_USER. Все довольны — программа довольна, что ей не запретили запись в реестр, и что она поменяла данные для всех пользователей системы. Пользователи системы также довольны тем, что программы одного пользователя системы делают меньше гадостей другим пользователям. В итоге все довольны, кроме разработчиков ПО, у которых прибавилось головной боли.


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


Проблема 4 — виртуализация для имеющих права




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

Проблема 5 — отключить виртуализацию




В принципе, виртуализацию отключить не сложно, нужно лишь немного погуглить. Немного сложнее не забыть, что программа должна работать и под Windows 2000/Windows XP, в которых новых Vista API нет и не будет.

Проблема 6 — отключили таки виртуализацию?




Если вы неплохой програмист, и умеете пользоваться гуглом, с виртуализацией реестра и файловой системы вы наверно разобрались. Но есть нюанс. Вы забыли, что ваше приложение выпускается в версиях x86 и x64, а у вас продаются сразу оба варианта под одной лицензией. Это другая ось виртуализации, ее надо обрабатывать отдельно. Опять же, не стоит забывать о поддержке W2K/WXP.

Проблема 7 — дайте прав




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

Перезапуск приложения с расширеными правами реализуется для всех систем одинаково, отличие есть лишь для старых систем W2K/WXP — запрашивается пароль администратора системы, в системах Windows Vista и выше появится обычное окно UAC.


Проблема 8 — много прав у президента




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

Хорошее приложение работать максимально удобно для пользователя. К сожалению, для систем W2K/WXP нет более или менее документированного способа для приложения перезапуститься с пониженным правами, и единственное, что остается — выдать предупреждение пользователю о необходимости запустить программу обычным способом и завершить свою работу.


Для систем Windows Vista+ у приложения есть возможность перезапуститься с правами Windows Explorer'а. Эта возможность, а также действия, необходимые для её реализации, известна google'у.


Проблема 9 — что-то пошло не так




Хотя я и старался все делать хорошо и правильно, и оттестировал свои методы и диалоги активации на W2K, WinXP, Windows 7 и Windows 8.1, проблем с активацией в первом же приложении, в которое я добавил свои новые функции, избежать не удалось. Поначалу я каждый раз что-то дописывал, перезаливал дистрибутив, и просил терпеливых пользователей попробовать еще раз. Потом мне это надоело, и я решил, что надо сделать мини-систему логгирования, и раскидать запись сообщений во всех контрольных точках процесса активации. Надо сказать, что это сильно помогло в отладке активации, да и получилась простая, но довольно удобная система протоколирования для несложных однопоточных приложений, которую можно использовать и для нужд приложения. Тут я вкратце опишу, что мне хотелось получить, в основном в плане GUI для активации, и чего не было у протектора.

Проблема 10 — кто показал GUI?




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

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


Производитель моего протектора предпочитает сам соединяться с сервером активации и получать ключ для заданного HWID-fingerprint'а и короткого серийника (кода активации), и он просто немного облегчил мне задачу — мне не нужно самому разбираться с WinInet.


В этом случае нужно показать пользователю GUI. Некоторые протекторы умеют это делать (но возможно не всегда так и не всегда то, что точно нужно). Мой протектор не имел никакого встроенного GUI активации, и все пришлось делать самому.


Я выявил набор опций пользовательского интерфейса активации, который хотелось по разному предлагать в разных программах/в разных версиях одной программы. Вот мой джентельменский набор:



  • Активировать серийный номер

  • Офлайн активация серийного номера

  • Активировать при помощи ключевого файла лицензии

  • Активировать пробную версию




Проблема 11 — весь гемор только при установке




Я не люблю такие программы, которые вроде устанавливаются без вопросов, а потом при каждом запуске еще что-то выпрашивают. Мне нравится, когда обо всем сразу спрашивают — например, при установке програмы: триал, или купил? Если триал — отметил для себя: возможны запросы от программы. Купил: жду, что будет работать без вопросов.

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


Проблема 12 — интеграция с инсталлятором




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

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


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


Проблема 13 — пожалейте сисадмина, сделайте silent/batch режим установки




После того, как в инсталлятор были встроены странички активации, я задумался над тем, каково это будет сисадмину организации, которая купит у меня 50 копий ПО? Решил, что явно не сладко, если он все 50 копий будет ставить и активировать руками. Поэтому было решено сделать поддержку Silent-установки; для этого, как минимум, нужно научить инсталлятор принимать код активации из параметров командной строки программы установки. Также я добавил поддержку возможности активировать Trial-лицензию, используя опции командной строки. Еще пара хотелок, которые не слишком уж относятся, собственно, к протектору и активации приложения, но лежат где-то рядом, и я решил запихать их в один модуль с остальным.

Проблема 14 — диалог «О Программе»




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

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


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


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


Само-собой, что все это должно быть оформлено в виде вызова одной единственной функции, пусть и с большим количеством параметров, чтобы таскать «About» из проекта в проект было просто.


Проблема 15 — shell-диалоги




Shell-диалоги — это диалоги выбора файла для сохранения, выбора файла для открытия, а также диалог выбора каталога. Для систем W2k/WinXP и для систем Windows Vista+ эти диалоги выглядят по разному, и что более печально, используют абсолютно разные API. Как минимум, мне необходимо было использовать такие диалоги в процессе активации, да и в своих программах хотелось, не теряя совместимости со старыми системами, отображать их в наиболее современном виде. Также хотелось каждый такой диалог вызывать максимально просто — иметь по одной функции для каждого типа диалога, не слишком теряя в функциональности по сравнению с системными функциями. На самом деле, решение почти каждой проблемы не слишком сложно, и находится гуглом без особых проблем. Немного больше времени занимает создание более или менее красивых и универсальные диалогов. В целом же, на все это, как ни крути, уходит время, и я решил выпустить свое решение отдельным продуктом, о котором раскажу в следующем посте (в хабе «Я пиарюсь»).

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.