понедельник, 21 января 2013 г.

Action как замена копипаста

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

 Вот, например, есть у нас список игроков в MMO игре:

List<Player> players;

Теперь представим, где-нибудь в координатах x,y произошло некоторое событие, например, взорвалась граната и теперь надо пересчитать хиты игроков, находящихся в зоне поражения и послать сообщения об их полученом дамаге игрокам.

private void ExplodeGrenade(float x, float y, float range)
{
  //сохраним константу в локальную переменную, в реальности здесь может быть 
  //гораздо более сложный код
  float damage=Grenade.Damage;

  foreach(Player player in players)
  {
    //если игрок попадает в квадрат поражения, то пересчитаем ему
//уровень урона, чем дальше, тем меньше
if (player.X<x+range && player.X>x-range && player.Y<y+range && player.Y> y+ range) { //фунция Distance считает расстояние между двумя точками float playerDamage=damage/(Distance(player.X,player.Y,x,y)+1); //пошлем игроку сообщение о том, что он получил урон player.SendMessage(Message.TakeDamage,playerDamage); } } }
Теперь представим другую операцию, например игрок переместился на карте и нужно об этом сообщить другим игрокам, находящимся в зоне видимости:

private void MovePlayer(Player movingPlayer,float x, float y, float range)
{
  //Установим игроку новые координаты
  movingPlayer.X=x;
  movingPlayer.Y=y;

  foreach(Player player in players)
  {
    //сообщим всем игрокам в области видимости, что игрок меперестился
    if (player.X<x+range && player.X>x-range 
        && player.Y<y+range && player.Y> y+ range)
    {
      player.SendMessage(Message.Move,movingPlayer);
    }
  }
}

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

Общий код (цикл и условие) выносим в отдельную функцию

private void PlayerActionInRange(float x,float y,float range, 
                                 Action<Player> playerAction)
Как видно, в функции присутсвует параметр с типом Action<Player>. Класс Action является оберткой для делегата или просто говоря функции, которая будет вызываться из функции PlayerActionInRange. Параметром данной функции будет являться объект типа Player.



private void PlayerActionInRange(float x,float y,float range, 
              Action<Player> playerAction)
{
  foreach(Player player in players)
  {
    //для всех игроков в области видимости вызываем функцию 
    if (player.X<x+range && player.X>x-range 
        && player.Y<y+range && player.Y> y+ range)
    {
       //Параметром функции передаем текущего игрока в цикле
       playerAction(player); 
    }
  }
}
Теперь модифицируем функцию ExplodeGrenade

private void ExplodeGrenade(float x, float y, float range)
{
  //сохраним константу в локальную переменную, в реальности 
  //здесь может быть гораздо более сложный код
  float damage=Grenade.Damage;

  //В качестве параметра функции передаем ссылку на анонимный 
  //делегат, код которого будет находиться тут же
  PlayerActionInRange(x,y,range,player =>
  {
    //фунция Distance считает расстояние между двумя точками
    float playerDamage=damage/(Distance(player.X,player.Y,x,y)+1);
    //пошлем игроку сообщение о том, что он получил урон
    player.SendMessage(Message.TakeDamage,playerDamage);
  });
}
Теперь функция ExplodeGrenade стала гораздо более ясной для понимания. А представьте, если бы для организации выборки в реальном проекте пришлось бы писать не две, а с хотя бы десяток строчек кода, насколько упростился бы вид этой функции!

Функцию MovePlayer можно сделать еще проще, воспользовавшись лямбда-выражениями:
private void MovePlayer(Player movingPlayer, 
                        float x, float y, float range)
{
  //Установим игроку новые координаты:
  movingPlayer.X=x;
  movingPlayer.Y=y;

  //всего лишь одна строчка кода
  PlayerActionInRange(x,y,range,
                     p=>p.SendMessage(Message.Move,movingPlayer));
}
Вот таким нехитрым способом можно существенно упростить читаемость кода и избежать ошибок при копировании одинакового текста из одной фунции в другую

Реклама в приложении от креара медиа

Летом ко мне обращались многие рекламщики с предложением вставить в мою вконтактовскую игру рекламу. Я не собирался вставлять рекламу, которую не могу модерировать, т. к. велика вероятность того, что в приложении прорвется реклама, запрещенная правилами вконтакте и приложение будет забанено.
   Одной из обратившихся компаний была креара медиа. Т. к. компания является официальным партнером, то проблем с рекламным контентом не должно было быть, поэтому я у них запросил информацию о том, сколько я смогу зарабатывать на их рекламе. Они мне прислали усредненную статистику, я сделал расчеты и насчитал, что с каждых 10 тыс. уникальных пользователей я буду зарабатывать на рекламе аж целых 90 рублей в день.
  Возникло предположение, что я ошибся. Написал менеджеру креары вопрос, правильно ли я все посчитал, но ответа так и не получил, поэтому на креару забил.
  Через полгода решил проверить в реальности и подключить для теста прелоадеры рекламы. После трех дней тестов у меня получились очень интересные цифры:
  Прелоадер отображается примерно каждому десятому пользователю, т. е. на 1000 DAU имеем 100 показов.
  CTR около 3% (2.83)
  Оплата за каждый клик составляет примерно 1 руб. плюс за каждую 1000 показов - 6 рублей
 В итоге получаем, что на каждую 1000 DAU заработок на рекламе получается:
 1000*0.1*0.03*1+1000*0.1*6/1000=3 рубля 60 копеек. Соответсвенно со 100 тыс. DAU можно заработать 360 рублей в день. Просто фантастический доход!
  Для небольших приложений с DAU меньше 10 тыс. зайти самому один раз в день и прокликать ссылки оказывается гораздо выгоднее, чем получать деньги с рекламы, которая находится в приложении - это просто какой-то нонсенс.
  Я даже не говорю про то, что деньги от креары можно получить не ранее чем через 45 дней, после того, как они начислены и для получения этих копеек они требуют открыть публичную статистику приложения.

воскресенье, 20 января 2013 г.

MonoDevelop на Ubuntu 10.04

Что делать, если надо поставить на Ubuntu последнюю версию MonoDevelop, а в официальных репозиториях ее нет?

Для начала стоит глянуть в полуофициальный репозиторий badgerports - там, обычно, с небольшой задержкой после выпуска новых версий mono и monodevelop появляются пакеты для LTS версий Ubuntu, но, к сожалению, после выхода Ubuntu 12.04, автор прекратил обновлять пакеты для Lucid Lynx, а мне нужно было установить MonoDevelop именно на 10.04.

Поэтому, решил откомпилировать MonoDevelop из исходников. Если честно, то воспоминания о том сколько приходилось мучиться несколько лет назад, чтобы собрать MonoDevelop (а точнее, пререквизиты к нему), наводили на мысль, что данная затея может быть безуспешной. Но, как оказалось, все получилось довольно просто.

Сначала ставим Mono 2.10.8 из badgerports. Процесс подключения репозитория описан на сайте, поэтому тут повторять его не буду. Также установливаем MonoDevelop 2.8.

Устанавливаем пререквизиты:
sudo aptitude install intltool libmono-addins-cil-dev libmono-addins-gui-cil-dev gnome-sharp2
Хочу обратить внимание на пакеты libmono-addins-cil-dev libmono-addins-gui-cil-dev Если их не установить, то будут возникать ошибки на несоответствии версии mono-addins.
Дальше забираем исходники из последнего стабильного бранча:
git clone -b monodevelop-3.0-series https://github.com/mono/monodevelop.git
Собираем:
./configure 
make
Проверяем работоспособность:
make run
Если все нормально, можно проинсталлировать:
make install

Вот, собственно, и все.

пятница, 6 июля 2012 г.

VmWare Player could not open /dev/vmmon

Сегодня моргнул свет и девелоперская машина перезагрузилась. После этого VmWare Player перестал запускать виртуальную машину с svn, выдавая следующее сообщение:

Could not open /dev/vmmon: No such file or directory.
Помогла перекомпиляция модулей.

sudo rm /lib/modules/2.6.32-41-generic-pae/misc/*
sudo vmware-modconfig –console –install-all --appname="VMware Player" --icon="vmware-player"

четверг, 5 июля 2012 г.

VmWare Server умер...

Наверное, вчера был последний день, когда я использовал VmWare сервер. Он долго крутился, до тех пор пока после очередного обновления Ubuntu 10.04 до ядра 2.6.32-41 Web Access перестал запускаться. Все попытки реанимировать этого динозавра ни к чему не привели.
   С одной стороны обидно, что он умер на практически ровном месте, а с другой стороны столько долго работать с софтом, который уже несколько лет не поддерживается производителем - моветон.
  Радует хоть то, что почти все важные сервисы я давно перевел с vmware виртуалок в Xen, поэтому непредвиденной срочной миграции делать не пришлось.  Оперативно перетаскивать пришлось только один сервис, который раздавал файлы кроссдомена пользователям сайта.
   Из важного же остался svn, да девелоперские базы данных, но они, в принципе, смогут временно покрутиться и в VmWare Player на девелоперской машине, пока не перенесу их в другое место.
 
 Зато теперь столько оперативки освободится!

среда, 4 июля 2012 г.

Bash и кавычки

Довольно редко приходится писать что-то с использованием bash, но вот вчера возникла такая необходимость. И оказалось, что в bash совсем нетривиально передать в командную строку символ двойных кавычек ". Как ни пробовал (с экскейп-символами, с заворачиванием строку в одинарные кавычки и т. д.) - никак не получалось, только один способ помог:

agent="my user agent"
cmd="wget http://mysite.com -U $agent"
eval $cmd

вторник, 19 июня 2012 г.

МТС коннект

Зарекался я никогда не связываться с МТС, потому что хуже оператора по отношению к клиентам не найти... А тут пришлось покупать 3G модем, чтобы мониторить работоспособность сервисов, будучи за городом. Так как у Мегафона карта покрытия 3G вообще на сайте не отображается (сервер не работает) и это уже не в первый раз, а у МТС на карте как раз покрывается регион, куда я еду, то я решил взять модем МТСа.  Почему Билайн не стал смотреть - не знаю, наверное потому что мне сказали, что у МТС там все работает нормально.

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

Но самое главное, я купил модем на 14 мегабит, замерил скорость и оказалось, что до города, где я живу скорость скачки 1.5Мбит/с, а до Москвы - 2 Мбит/сек. И это при том, что я нахожусь в том месте, где покрытие 3G очень плотное. Какого хрена эти мудаки продают продукт, со свойствами в 10 раз хуже обещанных???

А ведь еще, в качестве опции, можно было доплатить 350 рублей и радоваться скоростью в полтора мегабита на модеме 21.6!