I’m starting a series of posts about the work with a part of the Steam API using dota2-api. It will be some kind of cookbook rather than pure documentation (rus/eng). Wiki on the github will contain links to this posts.
As a developer, I think my code is simple, clear and “easy to understand” for others. These “others” often feel exactly the opposite. These contradictions cause the creation of all sorts of “guides”, “manuals” and “recipes”.
I won’t describe dota2-api install process. It’s the same for any composer-package. Just follow the link to get your API-key – #. The only important point is, if you plan to save some public matches (not league’s matches), you should remove the foreign key for tables leagues
and matches
(field called leagueid
). If you don’t, the matches won’t be saved then. You should save the tournaments list to table leagues
if you plan to store leagues matches.
Let’s start with a simple thing, i.e. loading data for a match. We need only a match_id
. It can be got in the game’s client. We take some random public match – 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(); |
It looks pretty simple. However it is not. API not always returns game’s data. It can return even an empty result. We should try to reload it several times in this case:
Hereinafter, the code snippets continue already written segments (that is,
$match
is already defined earlier).
$retries = 3; if (is_null($match) && $retries !== 0) { $match = $mapper->load(); $retries--; } if (is_null($match)) { echo 'API is truly down'. exit 1; } |
We try to load match three times. If $match
remains empty, it means that API is truly down. Let’s see how to output the match data, when it’s already available via $match
(instance of Dota2Api\Models\Match
). All models have method get
to access to the object’s properties.
$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
and cluster
look not so clear – just unknown numbers. Dota2Api\Data
contains classes to their interpretation.
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' |
So, we got match’s start time, duration, result, type, rank and region, it was played in. But, first of all, match means it’s players. As we know, 10 players are in the “classic” map (1×1 solomid – exception, which does not consider):
/* @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 |
You should keep in mind, that the array with slots has indexes that are not consecutive. So, foreach
should be used to traverse it. Almost all data fields in the Dota2Api\Models\Slot
are self-sufficient. Only hero_id
, account_id
and item_*
differ. First is a hero identifier that player used. Second is a player identifier. Third group (item_0 - item_5
) is player’s inventory. The code below shows how to transform this in human-readable format:
$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 |
Inventory and hero data is now clear, but what should we do with a player? Unfortunately, player’s data (like nickname, steamid, profile_url) is available in another API-endpoint. One important moment – user may config it’s client to hide “matches history” from another players. API will return 4294967295
instead of the real account_id
. This value is available as a constant in the Dota2Api\Models\Player::ANONYMOUS
:
if ($firstSlot->get('account_id') == Dota2Api\Models\Player::ANONYMOUS) { echo 'Anonymous player'; } |
Each hero has its unique list of abilities. Each player has its own vision how to build it’s hero. Slot
instances also contain this data (hero level and used ability):
$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"; } |
You should be very careful with $level['time']
. Time count starts together with the game and picks/bans time is included. This means that 1st level ability is learned on the 12th minute (according to API).
Viewed match was played with Captains Mode and it has a picks/bans phase. How can we get this information?
$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 */ |
We understand what game data can be received from the API and how to display it to the user in readable form. It is to long to address the API each time we need data. The better idea is to store it in the local DB and to use it while needed:
$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 |
The way to get data from $matchFromDb
is the same to any match received from API.
An output format can be limited with your imagination only (and you may always look how it’s done in the dota2statistic.com or dotabuff.com).
dota2-api cookbook
- Working with dota2-api version 2.2.1 and higher
- Loading many matches using dota2-api
- Working with teams in the dota2-api
- Working with tournaments using dota2-api
- Working with players using dota2-api
Add comment