function flt_mission_destroy($mission_data) { $fleet_row = $mission_data['fleet']; $destination_planet = $mission_data['dst_planet']; if (!$destination_planet || !is_array($destination_planet) || $destination_planet['planet_type'] != PT_MOON) { flt_send_back($fleet_row); return CACHE_FLEET; } $combat_data = flt_mission_attack($mission_data); return $combat_data; /* $combat_data = flt_mission_attack($mission_data, false); if(empty($combat_data) || $combat_data[UBE_OUTCOME][UBE_COMBAT_RESULT] != UBE_COMBAT_RESULT_WIN) { return $combat_data; } $fleet_row = $mission_data['fleet']; $destination_planet = $mission_data['dst_planet']; if(!$destination_planet || !is_array($destination_planet) || $destination_planet['planet_type'] != PT_MOON) { doquery("UPDATE {{fleets}} SET `fleet_mess` = 1 WHERE `fleet_id` = {$fleet_row['fleet_id']} LIMIT 1;"); return $combat_data; } // sn_ube_combat_analyze_moon_destroy($combat_data); sn_ube_report_save($combat_data); $combat_data = sn_ube_report_load($combat_data[UBE_REPORT_CYPHER]); sn_db_transaction_commit(); //debug($combat_data); sn_db_transaction_start(); ube_combat_result_apply($combat_data); sn_db_transaction_rollback(); sn_ube_message_send($combat_data); // $combat_data[UBE_OUTCOME][UBE_MOON] = UBE_MOON_DESTROY_SUCCESS; global $template_result; sn_ube_report_generate($combat_data, $template_result); $template = gettemplate('ube_combat_report', true); $template->assign_recursive($template_result); display($template, '', false, '', false, false, true); die(); // $combat_data[UBE_OUTCOME][UBE_MOON] = UBE_MOON_DESTROY_FAILED; // $combat_data[UBE_OUTCOME][UBE_MOON_REAPERS] = UBE_MOON_REAPERS_NONE; // $combat_data[UBE_OUTCOME][UBE_MOON_REAPERS] = UBE_MOON_REAPERS_DIED; // $combat_data[UBE_OUTCOME][UBE_MOON_REAPERS] = UBE_MOON_REAPERS_RETURNED; // $combat_data[UBE_OUTCOME][UBE_MOON_DESTROY_CHANCE] = min(99, round((100 - sqrt($moon_size)) * sqrt($reapers))); // $combat_data[UBE_OUTCOME][UBE_MOON_REAPERS_DIE_CHANCE] = round(sqrt($moon_size) / 2); // $combat_data[UBE_OUTCOME][UBE_PLANET][PLANET_SIZE] return $combat_data; */ }
function flt_mission_destroy($mission_data) { $fleet_row = $mission_data['fleet']; $destination_planet = $mission_data['dst_planet']; if (!$destination_planet || !is_array($destination_planet) || $destination_planet['planet_type'] != PT_MOON) { flt_send_back($fleet_row); return CACHE_FLEET; } $combat_data = flt_mission_attack($mission_data); return $combat_data; }
function flt_mission_destroy($mission_data) { $result = flt_mission_attack($mission_data); if (empty($result) || $result['won'] != 1) { return $result; } $fleet_row = $mission_data['fleet']; $destination_planet = $mission_data['dst_planet']; if (!$destination_planet || !is_array($destination_planet)) { doquery("UPDATE {{fleets}} SET `fleet_mess` = 1 WHERE `fleet_id` = {$fleet_row['fleet_id']} LIMIT 1;"); return; } global $lang; $MoonSize = $destination_planet['diameter']; $MoonName = $destination_planet['name']; $RipsKilled = 0; $MoonDestroyed = 0; foreach ($result['rw'][count($result['rw']) - 1]['attackers'] as $fleet) { foreach ($fleet['detail'] as $shipID => $shipNum) { $Rips += $shipID == SHIP_DEATH_STAR ? $shipNum : 0; } } if ($Rips > 0) { $MoonDestChance = min(99, round((100 - sqrt($MoonSize)) * sqrt($Rips))); $RipDestChance = round(sqrt($MoonSize) / 2); $UserChance = mt_rand(1, 100); if ($UserChance > 0 and $UserChance <= $MoonDestChance) { $RipsKilled = 0; $MoonDestroyed = 1; } elseif ($UserChance > 0 and $UserChance <= $RipDestChance) { $RipsKilled = 1; $MoonDestroyed = 0; } } if ($MoonDestroyed == 1) { doquery("DELETE FROM {{planets}} WHERE `id` ='{$destination_planet['id']}';"); $message = $lang['sys_moon_destroyed']; } elseif ($RipsKilled == 1) { //TODO: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! А нужно удалять все флоты !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! doquery("DELETE FROM {{fleets}} WHERE `fleet_id` = '{$fleet_row["fleet_id"]}';"); $message = $lang['sys_rips_destroyed']; } else { $message = $lang['sys_rips_come_back']; } $message .= "<br><br>"; $message .= $lang['sys_chance_moon_destroy'] . intval($MoonDestChance) . "%. <br>" . $lang['sys_chance_rips_destroy'] . intval($RipDestChance) . "%"; msg_send_simple_message($fleet_row['fleet_owner'], '', $fleet_row['fleet_start_time'], MSG_TYPE_COMBAT, $lang['sys_mess_tower'], $lang['sys_moon_destruction_report'], $message); msg_send_simple_message($destination_planet['id_owner'], '', $fleet_row['fleet_start_time'], MSG_TYPE_COMBAT, $lang['sys_mess_tower'], $lang['sys_moon_destruction_report'], $message); return $result; }
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('Закончили обсчёт флотов'); }