function flt_flying_fleet_handler(&$config, $skip_fleet_update)
{
    $flt_update_mode = 0;
    // 0 - old
    // 1 - new
    global $time_now;
    /*
    if(($time_now - $GLOBALS['config']->flt_lastUpdate <= 8 ) || $GLOBALS['skip_fleet_update'])
    {
      return;
    }
    
    $GLOBALS['config']->db_saveItem('flt_lastUpdate', $time_now);
    doquery('LOCK TABLE {{table}}aks WRITE, {{table}}rw WRITE, {{table}}errors WRITE, {{table}}messages WRITE, {{table}}fleets WRITE, {{table}}planets WRITE, {{table}}users WRITE, {{table}}logs WRITE, {{table}}iraks WRITE, {{table}}statpoints WRITE, {{table}}referrals WRITE, {{table}}counter WRITE');
    */
    if ($skip_fleet_update) {
        return;
    }
    switch ($flt_update_mode) {
        case 0:
            if ($time_now - $config->flt_lastUpdate <= 4) {
                return;
            }
            break;
        case 1:
            if ($config->flt_lastUpdate) {
                if ($time_now - $config->flt_lastUpdate <= 15) {
                    return;
                } else {
                    $GLOBALS['debug']->error('Flying fleet handler is on timeout', 'FFH Error', 504);
                }
            }
            break;
    }
    $config->db_saveItem('flt_lastUpdate', $time_now);
    doquery('START TRANSACTION;');
    coe_o_missile_calculate();
    $flt_user_cache = array();
    $flt_fleet_cache = array();
    $flt_event_cache = array();
    $flt_planet_cache = array();
    $_fleets = doquery("SELECT *, fleet_start_time AS fleet_time FROM `{{fleets}}` WHERE `fleet_start_time` <= '{$time_now}' FOR UPDATE;");
    while ($fleet_row = mysql_fetch_assoc($_fleets)) {
        flt_cache_fleet($fleet_row, $flt_user_cache, $flt_planet_cache, $flt_fleet_cache, $flt_event_cache, CACHE_ALL);
    }
    $_fleets = doquery("SELECT *, fleet_end_stay AS fleet_time FROM `{{fleets}}` WHERE `fleet_end_stay` <= '{$time_now}' AND fleet_end_stay > 0 FOR UPDATE;");
    while ($fleet_row = mysql_fetch_assoc($_fleets)) {
        flt_cache_fleet($fleet_row, $flt_user_cache, $flt_planet_cache, $flt_fleet_cache, $flt_event_cache, CACHE_ALL);
    }
    $_fleets = doquery("SELECT *, fleet_end_time AS fleet_time FROM `{{fleets}}` WHERE `fleet_end_time` <= '{$time_now}' FOR UPDATE;");
    while ($fleet_row = mysql_fetch_assoc($_fleets)) {
        flt_cache_fleet($fleet_row, $flt_user_cache, $flt_planet_cache, $flt_fleet_cache, $flt_event_cache, CACHE_ALL);
    }
    uasort($flt_event_cache, 'flt_flyingFleetsSort');
    unset($_fleets);
    /*
    pdump(count($flt_user_cache), '$flt_user_row');
    pdump(count($flt_planet_cache), '$flt_planet_row');
    pdump(count($flt_fleet_cache), '$flt_fleet_cache');
    pdump(count($flt_event_cache), '$flt_event_cache');
    pdump($GLOBALS['time_now'], 'time_now');
    die();
    */
    foreach ($flt_event_cache as $fleet_event_id => $fleet_event) {
        //    pdump($fleet_event);
        // Checking data presence in cache
        flt_cache_fleet($fleet_event['fleet_id'], $flt_user_cache, $flt_planet_cache, $flt_fleet_cache, $flt_event_cache, CACHE_ALL ^ CACHE_EVENT);
        //pdump($flt_fleet_cache[$fleet_event['fleet_id']]['fleet_mission'], 'fleet_mission');
        //pdump($flt_fleet_cache[$fleet_event['fleet_id']]['fleet_start_time'], 'fleet_start_time');
        //pdump($flt_fleet_cache[$fleet_event['fleet_id']]['fleet_end_time'], 'fleet_end_time');
        //pdump($flt_fleet_cache[$fleet_event['fleet_id']]['fleet_mess'], 'fleet_mess');
        $fleet_row = $flt_fleet_cache[$fleet_event['fleet_id']];
        // There is no fleet for this event. It can be for fleets destroyed in battle, lost in expedition etc
        if (!$fleet_row) {
            continue;
        }
        $mission_data = array('fleet' => $flt_fleet_cache[$fleet_event['fleet_id']], 'src_user' => $flt_user_cache[$fleet_event['src_user_id']], 'src_planet' => $flt_planet_cache[$fleet_event['src_planet_hash']], 'dst_user' => $flt_user_cache[$fleet_event['dst_user_id']], 'dst_planet' => $flt_planet_cache[$fleet_event['dst_planet_hash']]);
        // Миссии должны возвращать измененные результаты, что бы второй раз не лезть в базу
        unset($mission_result);
        switch ($fleet_row['fleet_mission']) {
            // Для боевых атак нужно обновлять по САБу и по холду - таки надо возвращать данные из обработчика миссий!
            case MT_AKS:
            case MT_ATTACK:
                $attack_result = flt_mission_attack($mission_data);
                $mission_result = CACHE_COMBAT;
                break;
            case MT_DESTROY:
                $attack_result = flt_mission_destroy($mission_data);
                $mission_result = CACHE_COMBAT;
                break;
            case MT_COLONIZE:
                $mission_result = flt_mission_colonize($mission_data);
                break;
            case MT_EXPLORE:
                $mission_result = flt_mission_explore($mission_data);
                break;
            case MT_RELOCATE:
                $mission_result = flt_mission_relocate($mission_data);
                break;
            case MT_TRANSPORT:
                $mission_result = flt_mission_transport($mission_data);
                break;
            case MT_RECYCLE:
                $mission_result = flt_mission_recycle($mission_data);
                break;
            case MT_SPY:
                $mission_result = flt_mission_spy($mission_data);
                break;
            case MT_HOLD:
                $mission_result = flt_mission_hold($mission_data);
                break;
            case MT_MISSILE:
                // Missiles !!
                break;
            default:
                doquery("DELETE FROM `{{fleets}}` WHERE `fleet_id` = '{$fleet_row['fleet_id']}' LIMIT 1;");
                break;
        }
        if ($attack_result) {
            // Case for passed attack
            $attack_result = $attack_result['rw'][0];
            flt_unset_by_attack($attack_result['attackers'], $flt_user_cache, $flt_planet_cache, $flt_fleet_cache, $flt_event_cache);
            flt_unset_by_attack($attack_result['defenders'], $flt_user_cache, $flt_planet_cache, $flt_fleet_cache, $flt_event_cache);
            unset($attack_result);
            unset($flt_planet_cache[$fleet_event['dst_planet_hash']]);
        } else {
            // Unsetting data that we broken in mission handler
            if (($mission_result & CACHE_FLEET) == CACHE_FLEET) {
                unset($flt_fleet_cache[$fleet_event['fleet_id']]);
            }
            if (($mission_result & CACHE_USER_SRC) == CACHE_USER_SRC) {
                unset($flt_user_cache[$fleet_event['src_user_id']]);
            }
            if (($mission_result & CACHE_USER_DST) == CACHE_USER_DST) {
                unset($flt_user_cache[$fleet_event['dst_user_id']]);
            }
            if (($mission_result & CACHE_PLANET_SRC) == CACHE_PLANET_SRC) {
                unset($flt_planet_cache[$fleet_event['src_planet_hash']]);
            }
            if (($mission_result & CACHE_PLANET_DST) == CACHE_PLANET_DST) {
                unset($flt_planet_cache[$fleet_event['dst_planet_hash']]);
            }
        }
    }
    doquery('COMMIT;');
    if ($flt_update_mode == 1) {
        $config->db_saveItem('flt_lastUpdate', 0);
    }
}
function flt_flying_fleet_handler($skip_fleet_update = false)
{
    /*
    [*] Нужно ли заворачивать ВСЕ в одну транзакцию?
          С одной стороны - да, что бы данные были гарантированно на момент снапшота
          С другой стороны - нет, потому что при большой активности это все будет блокировать слишком много рядов, да и таймаут будет большой для ожидания всего разлоченного
          Стоит завернуть каждую миссию отдельно? Это сильно увеличит количество запросов, зато так же сильно снизит количество блокировок.
        Resume: НЕТ! Надо оставить все в одной транзакции! Так можно будет поддерживать consistency кэша. Там буквально сантисекунды блокировки
    [*] Убрать кэшированние данных о пользователях и планета. Офигенно освободит память - проследить!
          НЕТ! Считать, скольким флотам нужна будет инфа и кэшировать только то, что используется больше раза!
          Заодно можно будет исключить перересчет очередей/ресурсов - сильно ускорит дело!
          Особенно будет актуально, когда все бонусы будут в одной таблице
          Ну и никто не заставляет как сейчас брать ВСЕ из таблицы - только по полям. Гемор, но не сильный - сделать запрос по группам sn_data
          И писать в БД только один раз результат
    [*] Нужно ли на этом этапе знать полную информацию о флотах?
          Заблокировать флоты можно и неполным запросом. Блокировка флотов - это не страшно. Ну, не пройдет одна-две отмены - так никто и не гарантировал реалтайма!
          С одной стороны - да, уменьшит количество запросов
          С другой стооны - расход памяти
          Все равно надо будет знать полную инфу о флоте в момент обработки
    [*] Сделать тестовую БД для расчетов
    [*] Но не раньше, чем переписать все миссии
    */
    global $config, $debug;
    if ($config->game_disable != GAME_DISABLE_NONE || $skip_fleet_update) {
        return;
    }
    sn_db_transaction_start();
    if ($config->db_loadItem('game_disable') != GAME_DISABLE_NONE || SN_TIME_NOW - strtotime($config->db_loadItem('fleet_update_last')) <= $config->fleet_update_interval) {
        sn_db_transaction_rollback();
        return;
    }
    // Watchdog timer
    if ($config->db_loadItem('fleet_update_lock')) {
        if (SN_TIME_NOW - strtotime($config->fleet_update_lock) <= mt_rand(240, 300)) {
            sn_db_transaction_rollback();
            return;
        } else {
            $debug->warning('Flying fleet handler was locked too long - watchdog unlocked', 'FFH Error', 504);
        }
    }
    $config->db_saveItem('fleet_update_lock', SN_TIME_SQL);
    $config->db_saveItem('fleet_update_last', SN_TIME_SQL);
    sn_db_transaction_commit();
    //log_file('Начинаем обсчёт флотов');
    //log_file('Обсчёт ракет');
    sn_db_transaction_start();
    coe_o_missile_calculate();
    sn_db_transaction_commit();
    $fleet_list = array();
    $fleet_event_list = array();
    $missions_used = array();
    sn_db_transaction_start();
    //log_file('Запрос на флоты');
    $_fleets = doquery("SELECT * FROM `{{fleets}}` WHERE\n    (`fleet_start_time` <= " . SN_TIME_NOW . " AND `fleet_mess` = 0)\n    OR (`fleet_end_stay` <= " . SN_TIME_NOW . " AND fleet_end_stay > 0 AND `fleet_mess` = 0)\n    OR (`fleet_end_time` <= " . SN_TIME_NOW . ")\n  FOR UPDATE;");
    //log_file('Выборка флотов');
    while ($fleet_row = db_fetch($_fleets)) {
        set_time_limit(15);
        // Унифицировать код с темплейтным разбором эвентов на планете!
        $fleet_list[$fleet_row['fleet_id']] = $fleet_row;
        $missions_used[$fleet_row['fleet_mission']] = 1;
        if ($fleet_row['fleet_start_time'] <= SN_TIME_NOW && $fleet_row['fleet_mess'] == 0) {
            $fleet_event_list[] = array('fleet_row' => &$fleet_list[$fleet_row['fleet_id']], 'fleet_time' => $fleet_list[$fleet_row['fleet_id']]['fleet_start_time'], 'fleet_event' => EVENT_FLT_ARRIVE);
        }
        if ($fleet_row['fleet_end_stay'] > 0 && $fleet_row['fleet_end_stay'] <= SN_TIME_NOW && $fleet_row['fleet_mess'] == 0) {
            $fleet_event_list[] = array('fleet_row' => &$fleet_list[$fleet_row['fleet_id']], 'fleet_time' => $fleet_list[$fleet_row['fleet_id']]['fleet_end_stay'], 'fleet_event' => EVENT_FLT_ACOMPLISH);
        }
        if ($fleet_row['fleet_end_time'] <= SN_TIME_NOW) {
            $fleet_event_list[] = array('fleet_row' => &$fleet_list[$fleet_row['fleet_id']], 'fleet_time' => $fleet_list[$fleet_row['fleet_id']]['fleet_end_time'], 'fleet_event' => EVENT_FLT_RETURN);
        }
    }
    sn_db_transaction_commit();
    //log_file('Сортировка и подгрузка модулей');
    uasort($fleet_event_list, 'flt_flyingFleetsSort');
    unset($_fleets);
    // TODO: Грузить только используемые модули из $missions_used
    $mission_files = array(MT_ATTACK => 'flt_mission_attack', MT_AKS => 'flt_mission_attack', MT_DESTROY => 'flt_mission_attack', MT_TRANSPORT => 'flt_mission_transport', MT_RELOCATE => 'flt_mission_relocate', MT_HOLD => 'flt_mission_hold', MT_SPY => 'flt_mission_spy', MT_COLONIZE => 'flt_mission_colonize', MT_RECYCLE => 'flt_mission_recycle', MT_EXPLORE => 'flt_mission_explore');
    foreach ($missions_used as $mission_id => $cork) {
        require_once SN_ROOT_PHYSICAL . "includes/includes/{$mission_files[$mission_id]}" . DOT_PHP_EX;
    }
    //log_file('Обработка миссий');
    $sn_groups_mission = sn_get_groups('missions');
    foreach ($fleet_event_list as $fleet_event) {
        // TODO: Указатель тут потом сделать
        // TODO: СЕЙЧАС НАДО ПРОВЕРЯТЬ ПО БАЗЕ - А ЖИВОЙ ЛИ ФЛОТ?!
        $fleet_row = $fleet_event['fleet_row'];
        if (!$fleet_row) {
            // Fleet was destroyed in course of previous actions
            continue;
        }
        //log_file('Миссия');
        // TODO Обернуть всё в транзакции. Начинать надо заранее, блокируя все таблицы внутренним локом SELECT 1 FROM {{users}}
        sn_db_transaction_start();
        $config->db_saveItem('fleet_update_last', SN_TIME_SQL);
        $mission_data = $sn_groups_mission[$fleet_row['fleet_mission']];
        // Формируем запрос, блокирующий сразу все нужные записи
        db_flying_fleet_lock($mission_data, $fleet_row);
        $fleet_row = doquery("SELECT * FROM {{fleets}} WHERE fleet_id = {$fleet_row['fleet_id']} FOR UPDATE", true);
        if (!$fleet_row || empty($fleet_row)) {
            // Fleet was destroyed in course of previous actions
            sn_db_transaction_commit();
            continue;
        }
        if ($fleet_event['fleet_event'] == EVENT_FLT_RETURN) {
            // Fleet returns to planet
            RestoreFleetToPlanet($fleet_row, true, false, true);
            sn_db_transaction_commit();
            continue;
        }
        if ($fleet_event['fleet_event'] == EVENT_FLT_ARRIVE && $fleet_row['fleet_mess'] != 0) {
            // При событии EVENT_FLT_ARRIVE флот всегда должен иметь fleet_mess == 0
            // В противном случае это означает, что флот уже был обработан ранее - например, при САБе
            sn_db_transaction_commit();
            continue;
        }
        // TODO: Здесь тоже указатели
        // TODO: Кэширование
        // TODO: Выбирать только нужные поля
        // шпионаж не дает нормальный ID fleet_end_planet_id 'dst_planet'
        $mission_data = array('fleet' => &$fleet_row, 'dst_user' => $mission_data['dst_user'] || $mission_data['dst_planet'] ? db_user_by_id($fleet_row['fleet_target_owner'], true) : null, 'dst_planet' => $mission_data['dst_planet'] ? db_planet_by_vector($fleet_row, 'fleet_end_', true, '`id`, `id_owner`, `name`') : null, 'src_user' => $mission_data['src_user'] || $mission_data['src_planet'] ? db_user_by_id($fleet_row['fleet_owner'], true) : null, 'src_planet' => $mission_data['src_planet'] ? db_planet_by_vector($fleet_row, 'fleet_start_', true, '`id`, `id_owner`, `name`') : null, 'fleet_event' => $fleet_event['fleet_event']);
        if ($mission_data['dst_planet']) {
            // $mission_data['dst_planet'] = sys_o_get_updated($mission_data['dst_user'], $mission_data['dst_planet']['id'], $fleet_row['fleet_start_time']);
            if ($mission_data['dst_planet']['id_owner']) {
                $mission_data['dst_planet'] = sys_o_get_updated($mission_data['dst_planet']['id_owner'], $mission_data['dst_planet']['id'], $fleet_row['fleet_start_time']);
            }
            $mission_data['dst_user'] = $mission_data['dst_user'] ? $mission_data['dst_planet']['user'] : null;
            $mission_data['dst_planet'] = $mission_data['dst_planet']['planet'];
        }
        switch ($fleet_row['fleet_mission']) {
            // Для боевых атак нужно обновлять по САБу и по холду - таки надо возвращать данные из обработчика миссий!
            case MT_AKS:
            case MT_ATTACK:
            case MT_DESTROY:
                $attack_result = flt_mission_attack($mission_data);
                $mission_result = CACHE_COMBAT;
                break;
                /*
                case MT_DESTROY:
                  $attack_result = flt_mission_destroy($mission_data);
                  $mission_result = CACHE_COMBAT;
                break;
                */
            /*
            case MT_DESTROY:
              $attack_result = flt_mission_destroy($mission_data);
              $mission_result = CACHE_COMBAT;
            break;
            */
            case MT_TRANSPORT:
                $mission_result = flt_mission_transport($mission_data);
                break;
            case MT_HOLD:
                $mission_result = flt_mission_hold($mission_data);
                break;
            case MT_RELOCATE:
                $mission_result = flt_mission_relocate($mission_data);
                break;
            case MT_EXPLORE:
                $mission_result = flt_mission_explore($mission_data);
                break;
            case MT_RECYCLE:
                $mission_result = flt_mission_recycle($mission_data);
                break;
            case MT_COLONIZE:
                $mission_result = flt_mission_colonize($mission_data);
                break;
            case MT_SPY:
                $mission_result = flt_mission_spy($mission_data);
                break;
            case MT_MISSILE:
                // Missiles !!
                break;
                //      default:
                //        doquery("DELETE FROM `{{fleets}}` WHERE `fleet_id` = '{$fleet_row['fleet_id']}' LIMIT 1;");
                //      break;
        }
        sn_db_transaction_commit();
    }
    sn_db_transaction_start();
    $config->db_saveItem('fleet_update_last', SN_TIME_SQL);
    $config->db_saveItem('fleet_update_lock', '');
    sn_db_transaction_commit();
    //  log_file('Закончили обсчёт флотов');
}