andsfonseca / dota2-rpc-client

Allows you to observe the status of your DotA 2 (Defense of the Ancients 2) match within the Discord, through the Rich Presence service. 🎮
https://andsfonseca.github.io/dota2-rpc-client/
MIT License
41 stars 8 forks source link

[Feature]: Botmatch detection #6

Closed leamare closed 2 years ago

leamare commented 2 years ago

Are you on the latest version?

Is there a fix in progress related to your proposal?

Is your feature request related to a problem? Please describe the problem.

Steam RPC has separate status for botmatches (#DOTA_lobby_type_name_bot_match), but dota2-rpc-client doesn't really detect it as a botmatch (just as a regular one).

Describe the solution you'd like

Would be nice to add botmatch support.

It would also be nice to have a robot icon in the corner of hero portrait (similar to smoke status) for botmatches as an additional indicator.

Dota 2 Patch

No response

Anything else?

No response

andsfonseca commented 2 years ago

This one is a little more difficult. But I'll research if I can get the information directly from Steam RPC.

Currently I use Dota's Game Integration service, which is nothing more than a big JSON with lots of information. I still haven't found a way to detect a game with bots from this data. I believe that seeing this information directly from Steam RPC could be a solution.

andsfonseca commented 2 years ago

Do you have any links about this? I never looked to see if it's possible to get this information.

leamare commented 2 years ago

This one is a little more difficult. But I'll research if I can get the information directly from Steam RPC.

I was working on a project similar to yours and researching the topic.

Advantage of using GSI: you don't need to log into Steam every time. With Steam RPC you either have to enter Steam Guard code every time or use another account to monitor status. Also, GSI has much more information.

But Steam RPC translates directly into Discord RPC (and it also has the "Main Menu", "Main Menu (Idle)", "In lobby" states).

I can't link any additional information on this though. ApexRPC was like a reference project to me, and the rest of stuff came from manually testing and logging results from different manipulations.

I still haven't found a way to detect a game with bots from this data

I suspected this might be a problem.

I believe that seeing this information directly from Steam RPC could be a solution.

This would require user to log into Steam, I think, and wouldn't get any advantages. Unless Steam SDK can provide this functionality, of course. If this is the case, I don't think it's worth it then.

Although IIRC you can get Steam RPC status from a player even if you are not on the player's friends list. So in theory it should be possible to make a bot/proxy which would check a person's status.

Another potential solution would be looking at the players list in the match (similar to what 9kmmr bot does). I didn't have prior experience with GSI, but I would assume that bots would have something suspicious in their data, e.g. Player->SteamID. I think this should be the best solution.

andsfonseca commented 2 years ago

You're right, having to save steam login information might be a little out of scope.

This is the output I get when I'm playing with bots:

OUTPUT ```json { "auth" : { "token" : "518quwZuBDWr" }, "draft" : {}, "hero" : { "aghanims_scepter" : false, "aghanims_shard" : false, "alive" : true, "break" : false, "buyback_cooldown" : 0, "buyback_cost" : 278, "disarmed" : false, "has_debuff" : false, "health" : 640, "health_percent" : 100, "hexed" : false, "id" : 7, "level" : 1, "magicimmune" : false, "mana" : 291, "mana_percent" : 100, "max_health" : 640, "max_mana" : 291, "muted" : false, "name" : "npc_dota_hero_earthshaker", "respawn_seconds" : 0, "silenced" : false, "smoked" : false, "stunned" : false, "talent_1" : false, "talent_2" : false, "talent_3" : false, "talent_4" : false, "talent_5" : false, "talent_6" : false, "talent_7" : false, "talent_8" : false, "xp" : 0, "xpos" : 4968, "ypos" : 5616 }, "map" : { "clock_time" : 39, "customgamename" : "", "daytime" : true, "game_state" : "DOTA_GAMERULES_STATE_GAME_IN_PROGRESS", "game_time" : 125, "matchid" : "0", "name" : "start", "nightstalker_night" : false, "paused" : false, "ward_purchase_cooldown" : 0, "win_team" : "none" }, "player" : { "activity" : "playing", "assists" : 0, "commands_issued" : 44, "deaths" : 0, "denies" : 0, "gold" : 786, "gold_from_creep_kills" : 0, "gold_from_hero_kills" : 0, "gold_from_income" : 66, "gold_from_shared" : 0, "gold_reliable" : 186, "gold_unreliable" : 600, "gpm" : 284, "kill_list" : {}, "kill_streak" : 0, "kills" : 0, "last_hits" : 0, "name" : "mysteamuser", "steamid" : "mysteamid", "team_name" : "dire", "xpm" : 0 }, "provider" : { "appid" : 570, "name" : "Dota 2", "timestamp" : 1654870840, "version" : 47 }, "wearables" : { "style0" : 1, "style1" : 1, "style6" : 1, "wearable0" : 12969, "wearable1" : 12692, "wearable10" : 14912, "wearable2" : 7293, "wearable3" : 8579, "wearable4" : 8366, "wearable5" : 12935, "wearable6" : 12969, "wearable7" : 7293, "wearable8" : 792, "wearable9" : 793 } } ````
leamare commented 2 years ago

Could "map.matchid": "0" pop up in any other game aside from a botmatch or a local lobby? I think this could be a decent endicator, if it only shows up for botmatches.

andsfonseca commented 2 years ago

I believe this is just an indication that I created a local room. Let me check if this value changes when I host a game with bots on the network

andsfonseca commented 2 years ago
OUTPUT ```json { "auth" : { "token" : "518quwZuBDWr" }, "draft" : {}, "hero" : { "aghanims_scepter" : false, "aghanims_shard" : false, "alive" : true, "break" : false, "buyback_cooldown" : 0, "buyback_cost" : 260, "disarmed" : false, "has_debuff" : false, "health" : 520, "health_percent" : 100, "hexed" : false, "id" : 109, "level" : 1, "magicimmune" : false, "mana" : 303, "mana_percent" : 100, "max_health" : 520, "max_mana" : 303, "muted" : false, "name" : "npc_dota_hero_terrorblade", "respawn_seconds" : 0, "silenced" : false, "smoked" : false, "stunned" : false, "talent_1" : false, "talent_2" : false, "talent_3" : false, "talent_4" : false, "talent_5" : false, "talent_6" : false, "talent_7" : false, "talent_8" : false, "xp" : 0, "xpos" : -5322, "ypos" : -6322 }, "map" : { "clock_time" : -76, "customgamename" : "", "daytime" : false, "game_state" : "DOTA_GAMERULES_STATE_PRE_GAME", "game_time" : 88, "matchid" : "6611000909", "name" : "start", "nightstalker_night" : false, "paused" : false, "ward_purchase_cooldown" : 122, "win_team" : "none" }, "player" : { "activity" : "playing", "assists" : 0, "commands_issued" : 37, "deaths" : 0, "denies" : 0, "gold" : 160, "gold_from_creep_kills" : 0, "gold_from_hero_kills" : 0, "gold_from_income" : 0, "gold_from_shared" : 0, "gold_reliable" : 0, "gold_unreliable" : 160, "gpm" : 0, "kill_list" : {}, "kill_streak" : 0, "kills" : 0, "last_hits" : 0, "name" : "name", "steamid" : "id", "team_name" : "radiant", "xpm" : 0 }, "provider" : { "appid" : 570, "name" : "Dota 2", "timestamp" : 1654874448, "version" : 47 }, "wearables" : { "wearable0" : 12917, "wearable1" : 306, "wearable2" : 476, "wearable3" : 478, "wearable4" : 8650, "wearable5" : 8632, "wearable6" : 540, "wearable7" : 793, "wearable8" : 14912 } } ````
OUTPUT ```json { "auth" : { "token" : "518quwZuBDWr" }, "draft" : {}, "hero" : { "aghanims_scepter" : false, "aghanims_shard" : false, "alive" : true, "break" : false, "buyback_cooldown" : 0, "buyback_cost" : 264, "disarmed" : false, "has_debuff" : false, "health" : 700, "health_percent" : 100, "hexed" : false, "id" : 2, "level" : 1, "magicimmune" : false, "mana" : 291, "mana_percent" : 100, "max_health" : 700, "max_mana" : 291, "muted" : false, "name" : "npc_dota_hero_axe", "respawn_seconds" : 0, "silenced" : false, "smoked" : false, "stunned" : false, "talent_1" : false, "talent_2" : false, "talent_3" : false, "talent_4" : false, "talent_5" : false, "talent_6" : false, "talent_7" : false, "talent_8" : false, "xp" : 0, "xpos" : -5294, "ypos" : -6242 }, "map" : { "clock_time" : -50, "customgamename" : "", "daytime" : false, "game_state" : "DOTA_GAMERULES_STATE_PRE_GAME", "game_time" : 109, "matchid" : "6611018105", "name" : "start", "nightstalker_night" : false, "paused" : false, "ward_purchase_cooldown" : 96, "win_team" : "none" }, "player" : { "activity" : "playing", "assists" : 0, "commands_issued" : 20, "deaths" : 0, "denies" : 0, "gold" : 600, "gold_from_creep_kills" : 0, "gold_from_hero_kills" : 0, "gold_from_income" : 0, "gold_from_shared" : 0, "gold_reliable" : 0, "gold_unreliable" : 600, "gpm" : 0, "kill_list" : {}, "kill_streak" : 0, "kills" : 0, "last_hits" : 0, "name" : "name", "steamid" : "id", "team_name" : "radiant", "xpm" : 0 }, "provider" : { "appid" : 570, "name" : "Dota 2", "timestamp" : 1654874989, "version" : 47 }, "wearables" : { "wearable0" : 12964, "wearable1" : 13930, "wearable10" : 12964, "wearable11" : 12964, "wearable12" : 14912, "wearable2" : 13928, "wearable3" : 13929, "wearable4" : 8632, "wearable5" : 13872, "wearable6" : 8366, "wearable7" : 726, "wearable8" : 12954, "wearable9" : 791 } } ````
OUTPUT ```json { "auth" : { "token" : "518quwZuBDWr" }, "draft" : {}, "hero" : { "aghanims_scepter" : false, "aghanims_shard" : false, "alive" : true, "break" : false, "buyback_cooldown" : 0, "buyback_cost" : 253, "disarmed" : false, "has_debuff" : false, "health" : 580, "health_percent" : 100, "hexed" : false, "id" : 105, "level" : 1, "magicimmune" : false, "mana" : 375, "mana_percent" : 100, "max_health" : 580, "max_mana" : 375, "muted" : false, "name" : "npc_dota_hero_techies", "respawn_seconds" : 0, "silenced" : false, "smoked" : false, "stunned" : false, "talent_1" : false, "talent_2" : false, "talent_3" : false, "talent_4" : false, "talent_5" : false, "talent_6" : false, "talent_7" : false, "talent_8" : false, "xp" : 0, "xpos" : -5250, "ypos" : -6135 }, "map" : { "clock_time" : -79, "customgamename" : "", "daytime" : false, "game_state" : "DOTA_GAMERULES_STATE_PRE_GAME", "game_time" : 17, "matchid" : "0", "name" : "dota", "nightstalker_night" : false, "paused" : false, "ward_purchase_cooldown" : 0, "win_team" : "none" }, "player" : { "activity" : "playing", "assists" : 0, "commands_issued" : 22, "deaths" : 0, "denies" : 0, "gold" : 600, "gold_from_creep_kills" : 0, "gold_from_hero_kills" : 0, "gold_from_income" : 0, "gold_from_shared" : 0, "gold_reliable" : 0, "gold_unreliable" : 600, "gpm" : 0, "kill_list" : {}, "kill_streak" : 0, "kills" : 0, "last_hits" : 0, "name" : "name", "steamid" : "id", "team_name" : "radiant", "xpm" : 0 }, "provider" : { "appid" : 570, "name" : "Dota 2", "timestamp" : 1654875275, "version" : 47 }, "wearables" : { "wearable0" : 489, "wearable1" : 491, "wearable10" : 793, "wearable11" : 14912, "wearable2" : 487, "wearable3" : 492, "wearable4" : 394, "wearable5" : 490, "wearable6" : 499, "wearable7" : 9166, "wearable8" : 791, "wearable9" : 792 } } ````

From what I understand, every local match that is not a private room displays "BOT: {HERO_NAME}" (match id: 0) and every match with an id, even if it's with bots, displays "Private Room".

I think it will be possible to create a bot detection with this information. I'll implement later and validate.