Работа с dota2-api от версии 2.2.1 и старше

Запускаю цикл заметок о работе с частью Steam API используя dota2-api. Это не будет документацией в чистом виде, это будет своего рода сборник рецептов. Писать буду на русском и английском языках. В вики на гитхабе будут просто ссылки на эти записи.

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

Я не буду расписывать процесс установки dota2-api. Он один и тот же для любого пакета, который ставится через composer. Про то, как получить ключ к API, написано по ссылке — #. Единственным важным моментом является то, что если вы планируете хранить обычные игры (а не турнирные), то надо в БД убрать внешний ключ между таблицами leagues и matches (поле leagueid). Иначе матчи просто не сохранятся. Если же вы сохраняете какие-то турнирные матчи, то вначале должны сохранить список турниров в таблицу leagues.

Начнем с простого — загрузка данных о прошедшем матче. Тут нужен только match_id. Узнать его можно в клиенте игры. Возьмем какой-то случайный матч из «паблика» — 2472644644.

require_once 'vendor/autoload.php';
use Dota2Api\Api;
Api::init('YOUR_API_KEY', array('localhost', 'root', 'password', 'db_name', ''));
 
$mapper = new Dota2Api\Mappers\MatchMapperWeb(2472644644);
/* @var Dota2Api\Models\Match */
$match = $mapper->load();

Вроде бы все просто. Но, нет. API не всегда возвращает данные про игру. Может вернуть и пустой результат. В таком случае, надо попробовать загрузить еще раз или даже сделать несколько попыток:

Тут и далее фрагменты кода являются продолжением уже написанных фрагментов (то есть, $match уже определен ранее).

 

$retries = 3;
if (is_null($match) && $retries !== 0) {
    $match = $mapper->load();
    $retries--;
}
if (is_null($match)) {
    echo 'API is truly down'.
    exit 1;
}

В таком варианте, будет 3 попытки загрузить данные про игру. Если после них $match по-прежнему будет пустым, то, видимо, API упал всерьез и надолго. Посмотрим, как вывести информацию по игре, когда она уже доступна в $match (объект класса Dota2Api\Models\Match). Во всех моделях есть метод get для получения значений полей.

$durationInSeconds = $match->get('duration');
echo gmdate('H:i:s', $durationInSeconds); // '00:50:10'
echo $match->get('start_time'); // '2016-06-30 19:46:07'
echo $match->get('radiant_score').' - '.$match->get('dire_score'); // '57 - 47'
echo $match->get('game_mode');   // 2
echo $match->get('lobby_type');  // 7
echo $match->get('radiant_win'); // 1
echo $match->get('cluster');     // 181

game_mode, lobby_type и cluster выглядят не очень понятно — просто какие-то цифры. Для их «трактовки» есть классы из Dota2Api\Data:

lobbies = new Dota2Api\Data\Lobbies();
$lobbies->parse();
$matchLobbyType = $match->get('lobby_type');
echo $lobbies->getFieldById($matchLobbyType, 'name'); // 'Ranked'
 
$mods = new Dota2Api\Data\Mods();
$mods->parse();
$matchGameMode = $match->get('game_mode');
echo $mods->getFieldById($matchGameMode, 'name'); // 'Captain Mode'
 
$regions= new Dota2Api\Data\Regions();
$regions->parse();
$matchRegion = $match->get('cluster');
echo $regions->getFieldById($matchRegion, 'name'); // 'Russia'

Таким относительно не хитрым образом мы получили время начала матча, его продолжительность, счет, тип, ранг и регион, где он был проведен. Но, матч — это в первую очередь игроки. О них и поговорим. Как известно, на «классической» карте в одной игре 10 игроков (1х1 solomid — исключение, которое не рассматриваем):

/* @var Dota2Api\Models\Slot[] */
$slots = $match->getAllSlots();
foreach($slots as $player_slot => $slot) {
    // some code for $slot processing
}
print_r(array_keys($slots)); // [0, 1, 2, 3, 4, 128, 129, 130, 131, 132]
$firstSlot = $slots[0];
echo 'KDA: ' .
     $firstSlot->get('kills') . '/' .
     $firstSlot->get('deaths') . '/' .
     $firstSlot->get('assists') . "\n"; // 'KDA: 16/6/8'
echo $firstSlot->get('level');          // 25
echo $firstSlot->get('last_hits');      // 432
echo $firstSlot->get('denies');         // 14
echo $firstSlot->get('gold_per_min');   // 692
echo $firstSlot->get('xp_per_min');     // 646
echo $firstSlot->get('hero_id');        // 1

Тут стоит обратить внимание на индексы массива слотов — они идут не подряд. То есть, обходить этот массив имеет смысл только циклом foreach. В объектах класса Dota2Api\Models\Slot почти все поля с данными самодостаточны. Отличаются только поля hero_id, account_id и item_*. Первое — это идентификатор героя, на котором играл участник матча. Второе — идентификатор игрока. Третья группа (item_0 - item_5) — это инвентарь. Ниже показано, как получить человекопонятные данные на их основе:

$heroes = new Dota2Api\Data\Heroes();
$heroes->parse();
 
$heroId = $firstSlot->get('hero_id');
echo $heroes->getFieldById($heroId, 'localized_name'); // 'Anti-Mage'
echo $heroes->getImgUrlById($heroId); // 'http://media.steampowered.com/apps/dota2/images/heroes/antimage_eg.png'
echo $heroes->getImgUrlById($heroId, false); // 'http://media.steampowered.com/apps/dota2/images/heroes/antimage_lg.png'
 
$items = new Dota2Api\Data\Items();
$items->parse();
 
$firstItem = $firstSlot->get('item_0');
$items->getFieldById($firstItem, 'name'); // 'bfury'
echo $items->getImgUrlById($firstItem); // 'http://media.steampowered.com/apps/dota2/images/items/bfury_eg.png' - small image
echo $items->getImgUrlById($firstItem, false); // 'http://media.steampowered.com/apps/dota2/images/items/bfury_lg.png' - big image

С инвентарем и героем все ясно, а вот как быть с игроком? Увы, данные по игроку (его ник, steamid, profile_url) доступны в другом API. Один важный момент — в настройках профиля игрока можно скрыть свою «историю матчей» от других. API тогда вместо реального account_id будет возвращать значение 4294967295. Оно доступно как константа в Dota2Api\Models\Player::ANONYMOUS:

if ($firstSlot->get('account_id') == Dota2Api\Models\Player::ANONYMOUS) {
    echo 'Anonymous player';
}

У всех героев есть набор уникальных способностей. И у каждого игрока своя логика при выборе раскачки героя. Эти данные (на каком уровне какая способность была прокачана) тоже есть в объектах класса Slot:

$abilities = new Dota2Api\Data\Abilities();
$abilities->parse();
 
$buildDetails = $firstSlot->getAbilitiesUpgrade();
foreach($buildDetails as $level) {
    echo $level['level'] . ' ' . $abilities->getFieldById($level['ability'], 'name') . ' ' . $level['time'] . "\n";
}

С $level['time'] надо быть осторожным. Отсчет ведется от начала матча. То есть, время пиков и банов тоже считалось. Вот и получается, что способность на первом уровне игрок выбрал на 12-й минуте (по версии API).

Рассматриваемый матч игрался в CM-режиме и в нем была фаза пико-банов. Как можно узнать, кого же выбирали игроки и в какой очередности?

$picksBans = $match->getAllPicksBans();
foreach($picksBans as $order) {
    $team = $order['team'] == 0 ? 'radiant' : 'dire';
    $state = $order['is_pick'] == 1 ? 'pick' : 'ban';
    echo $team .' '.$state.' '.$heroes->getFieldById($order['hero_id'], 'localized_name')."\n";
}
/* Output:
    dire ban Lifestealer
    radiant ban Nature's Prophet
    dire ban Invoker
    radiant ban Sven
    dire pick Elder Titan
    radiant pick Lion
    radiant pick Axe
    dire pick Disruptor
    radiant ban Windranger
    dire ban Warlock
    radiant ban Slark
    dire ban Silencer
    radiant pick Anti-Mage
    dire pick Beastmaster
    radiant pick Death Prophet
    dire pick Weaver
    radiant ban Queen of Pain
    dire ban Zeus
    dire pick Viper
    radiant pick Winter Wyvern
 */

Мы разобрали, какие данные про игру можно получить из API и как их вывести пользователю в читаемом виде. Каждый раз обращаться к API за данными слишком долго. Лучше сохранять их в БД и брать уже оттуда:

$dbMapper = new Dota2Api\Mappers\MatchMapperDb();
$dbMapper->save($match); // save to db
/* @var Dota2Api\Models\Match */
$matchFromDb = $dbMapper->load($match->get('match_id')); // load from db

Из $matchFromDb данные берутся точно так же, как и из матчей, полученных из API.

Формат вывода данных ограничивается только вашей фантазией (в крайнем случае, всегда можно подсмотреть, как сделано у dota2statistic.com или dotabuff.com).

, ,

Оставить комментарий

Top ↑ | Main page | Back