Lumiere.4609:

Having spent a bit of time watching how most streams for pvp events in GW2 look, I think there is room for ArenaNet to provide some data that would simplify greatly the streams, as well as provide them with real data to generate stats on and work with.

API layout.

It makes sense to layout the API under /v2/ with the rest of the new APIs (and get use of their oauth/new codebase.)

This Document attempts to describe both how it might work specifically and why to do it.

/v2/spvp # open rooms/matches
/v2/spvp/room/{$room_id} # Room information and link to the match in progress
/v2/spvp/match/{$match_id} # Match information
/v2/spvp/stats/{$character/account?} # Stats for an account/character

GET /v2/spvp

Attributes:

  • search – Search for a room name
  • limit – limit count to a number of results
  • offset – start showing with the Nth result (use with limit to paginate)

No Request Body.

Response Example:


{
    "rooms": [
        { 
     	    "name": "Server 023",
            "room_uri": "http://api.guildwars2.com/v2/spvp/room/42",
            "match_uri": http://api.guildwars2.com/v2/spvp/match/47947"
        },
        { 
	    "name": "The Plunderdome",
            "room_uri": "http://api.guildwars2.com/v2/spvp/room/1004882",
            "match_uri": "http://api.guildwars2.com/v2/spvp/match/47948"
        }
    ]
}

(Continues Next Post… 5000 characters… lol)

This is a Cross-Post of https://forum-en.guildwars2.com/forum/game/pvp/Proposal-Structured-PvP-API to hopefully hit the right set of eyes (after talking to Colin last night )

Lumiere.4609:

GET /v2/spvp/room/{$room_id}

Attributes:

  • password: If the room is password protected, require that the password be provided.
    • Ignore the password if owner is logged in via oauth.

No Request Body

Response Example:


{
    "room": {
        "name": "The Plunderdome",
        "motd": "Plundering all day, yarr.",
        "team_size": 5,
        "score_limit": 500,
        "time_limit": 15,
        "respawn_time": 20,
        "respawn_type": "wave",
        "starting_players": 1,
        "reserved_slots": 0,
        "spectators": true,
        "ready_up": false,
        "autobalance": true,
        "lock_gear": false,
        "lock_skills": false,
        "progression": true,
        "force_respawn": false,
        "tournament_commands": false,
        "map_rotation": [
            "kyhlo",
            "forest",
            "legacy",
            "temple",
            "spirit_watch",
            "skyhammer",
            "courtyard
        ],
        "match_uri": "http://api.guildwars2.com/v2/spvp/match/47947"
    }
}

Optionally, show member players/guilds and banned players… but I don’t see it as a big deal.

Notes:

  • progression displays reverse of checkbox in the custom arena page (“No Progression” makes little sense)
  • match_uri is created as soon as the map loads and is active until a match begins and ends.

Lumiere.4609:

GET /v2/spvp/match/{$match_id}

Attributes:

  • password: If the room for this match is password protected, require that the password be provided.
    • Ignore the password if owner is logged in via oauth.

No Request Body

Response Example (pre-match):


{
    "match": {
        "id": 24727692,
        "room_uri": "http://api.guildwars2.com/v2/spvp/room/1004882",
        "score": {
            "red": 0,
            "blue": 0,
        },
        "map": "temple",
        "time_left": false,
        "players": {
            "red": [
                { 
                    "account_id": "somebody.2392",
                    "class", "Guardian",
                    "weapons": [
                        {
                            "main_hand": "mace",
                            "sigil1": "Sigil of Energy",
                            "off_hand": "focus",
                            "sigil2": "Sigil of Intelligence"
                        },
                        {
                            "main_hand": "staff",
                            "sigil1": "Sigil of Energy",
                            "sigil2": "Sigil of Battle"
                        }
                    ],
                    "rune": "Rune of the Soldier",
                    "amulet": "Cleric",
                    "utilities": [
                       "Shelter",
                       "Hold the Line",
                       "Sanctuary",
                       "Stand Your Ground",
                       "Renewed Focus"
                    ],
                    "trait": {
                        "distribution": [0,1,6,6,1],
                        "points": [
                            [],
                            [],
                            [4,5,11],
                            [2,5,11],
                            []
                        ]
                    }
                },
                ... // repeat these 4 more times
            ],
            "blue": [
                {
                    "account_id": "somebodyelse.2492",
                    "class", "Guardian",
                    "weapons": [
                        {
                            "main_hand": "mace",
                            "sigil1": "Sigil of Energy",
                            "off_hand": "focus",
                            "sigil2": "Sigil of Intelligence"
                        },
                        {
                            "main_hand": "staff",
                            "sigil1": "Sigil of Energy",
                            "sigil2": "Sigil of Battle"
                        }
                    ],
                    "rune": "Rune of the Soldier",
                    "amulet": "Cleric",
                    "utilities": [
                       "Shelter",
                       "Hold the Line",
                       "Sanctuary",
                       "Stand Your Ground",
                       "Renewed Focus"
                    ],
                    "trait": {
                        "distribution": [0,1,6,6,1],
                        "points": [
                            [],
                            [],
                            [4,5,11],
                            [2,5,11],
                            []
                        ]
                    }
                },
                ... // repeat these 4 more times
            ]
        }
    }
 }            

(This Section is split between 2 posts)

Lumiere.4609:

Response Example (in-match):


{
    "match": {
        "id": 24727692,
        "room_uri": "http://api.guildwars2.com/v2/spvp/room/1004882",
        "score": {
            "red": 0,
            "blue": 0,
        },
        "map": "temple",
        "time_left": false,
        "players": {
            "red": [
                { 
                    "account_id": "somebody.2392",
                    "name": "Some Name",
                    "class", "Guardian",
                    "weapons": [
                        {
                            "main_hand": "mace",
                            "sigil1": "Sigil of Energy",
                            "off_hand": "focus",
                            "sigil2": "Sigil of Intelligence"
                        },
                        {
                            "main_hand": "staff",
                            "sigil1": "Sigil of Energy",
                            "sigil2": "Sigil of Battle"
                        }
                    ],
                    "rune": "Rune of the Soldier",
                    "amulet": "Cleric",
                    "utilities": [
                       "Shelter",
                       "Hold the Line",
                       "Sanctuary",
                       "Stand Your Ground",
                       "Renewed Focus"
                    ],
                    "trait": {
                        "distribution": [0,1,6,6,1],
                        "points": [
                            [],
                            [],
                            [4,5,11],
                            [2,5,11],
                            []
                        ]
                    }
                    "events": [
                        {
                            "at": "00:30",
                            "type": "capture",
                            "point": "gate"
                        },
                        {
                            "at": "01:02",
                            "type" "kill",
                            "killed": "somebodyelse.2492"
                        },
                        {
                            "at": "06:51",
                            "type": "temple_buff",
                            "buff": "stillness"
                        },
                },
                ... // repeat these 4 more times
            ],
            "blue": [
                {
                    "account_id": "somebodyelse.2492",
                    "name": "Some Other Name",
                    "class", "Guardian",
                    "weapons": [
                        {
                            "main_hand": "mace",
                            "sigil1": "Sigil of Energy",
                            "off_hand": "focus",
                            "sigil2": "Sigil of Intelligence"
                        },
                        {
                            "main_hand": "staff",
                            "sigil1": "Sigil of Energy",
                            "sigil2": "Sigil of Battle"
                        }
                    ],
                    "rune": "Rune of the Soldier",
                    "amulet": "Cleric",
                    "utilities": [
                       "Shelter",
                       "Hold the Line",
                       "Sanctuary",
                       "Stand Your Ground",
                       "Renewed Focus"
                    ],
                    "trait": {
                        "distribution": [0,1,6,6,1],
                        "points": [
                            [],
                            [],
                            [4,5,11],
                            [2,5,11],
                            []
                        ]
                    }
                },
                ... // repeat these 4 more times
            ]
        }
    }
 }

Notes/Explaination:

  • Pre-Match the data provided is just the build itself, once the match starts it includes some event data to allow statistics to be generated, and to let someone post-match generate interesting tidbits to discuss like how quickly a team decapped behind their opponents.

Events:

  • Point Capture Neutralize Defend
  • Kills
  • Resses
  • Skirmisher
  • Map Special Events:
    • Temple Buffs
    • Kyhlo/Skyhammer Weapon Use/Hit
    • Forest Boss kills
    • Legacy Doors/Lord
    • Spirit Watch Orb pickup/drop/score (half/full)
  • Others?

Lumiere.4609:

GET /v2/spvp/stats/{$character_id}

Stats for a character/account over longer periods of time (season or all time)

Attributes:

  • season: Season number or “all” for all stats. M

No Request Body

Response Example:
[pre]
{
"character": "https://api.guildwars2.com/v2/characters/24597629",
"kills": 24.
"skirmisher": 4208,
"defends": 4,
"captures": 49,
"neutralizations": 48290
}

(etc etc)

GET /v2/spvp/match?since={$time}

Get all available matches that finished in a time period (default last ?? hours)

{
“matches”: [
“link1”,
“link2”,
“link3”,
“link4”,
“link5”
]}

This would allow a stats site to get a list of matches to pull data from without requiring it to look as frequently (lowering the load on the api)

Lumiere.4609:

(Holding an additional post just in case it needs it!)

Lawton Campbell.8517:

This is definitely something I’d like to see — there’s a huge amount of things that could be done with this. I’m thinking something along the lines of automated duel ladders via a password-protected/guild-only custom arena with an external web interface for match-making and gloating.

For ranked matches, I’m pretty sure we can’t provide data during the match — since you’d be able to inspect your opponent’s builds pre-match-start and other shenanigans. There may be other issues w.r.t. privacy/build secrecy. For hotjoins it’s no big deal since you can see everything in spectate mode; however, it’d be providing an advantage for ranked matches, which is probably something we don’t want. I’m not really sure.

Finally, there’s an issue of data retention — I’m not sure how much historical data we can feasibly store, but there’s a limit somewhere. It’d be on the application developer’s end to archive data, that’s just not something we can do.

Anyway, I don’t know if/when we’ll be able to implement it (or even which parts will come to light), but it’s an endpoint I really want, at least.

Lumiere.4609:

Hi Lawton,

Sorry about the delay in my reply! I agree with the during match for rated/unrated, maybe make the match data available after match (and for 24 hours after?)

I also added GET /v2/spvp/match?since={$time} as I realized there was no api-safe way to scrape the data. This would allow me to get a list once every 15..30..60 minutes of matches that I needed to record.

For ArenaNet Hot Join servers it should be active in real time, and for Custom Arenas it could be a flag given to the room owner (and maybe a separate password?) That said, Custom Arena’s is the most important place for it to exist as it will allow a streamer / broadcaster to pull all the builds and show them together instead of having to load each person individually. It also can serve as proof of match for tournament organizers.

From a data retention standpoint I totally agree about full historical data being difficult to feasibly search/manage while keeping any meaningful speed to the api. I would hope for the following to be in the API:

  • All Time data (totals, updated hourly)
  • Current Season/Ladder sum data (totals, updated hourly)
  • Last x matches/last x days (updated live, cached 15 minutes)

Beyond that, storing each match is actually trivial in that they’re all small json blobs that could be compressed and tossed into an object store (Amazon S3 or Rackspace Cloud Files) to be able to replay matches (it would also let us recreate MMR’s if at some point we wanted to tweak the glicko settings from back data)

If there’s anything I can help with to make this happen, please pm me or kick me a mail in game!

— Lumiere

Glyph.7805:

This is definitely something I’d like to see — there’s a huge amount of things that could be done with this. I’m thinking something along the lines of automated duel ladders via a password-protected/guild-only custom arena with an external web interface for match-making and gloating.

For ranked matches, I’m pretty sure we can’t provide data during the match — since you’d be able to inspect your opponent’s builds pre-match-start and other shenanigans. There may be other issues w.r.t. privacy/build secrecy. For hotjoins it’s no big deal since you can see everything in spectate mode; however, it’d be providing an advantage for ranked matches, which is probably something we don’t want. I’m not really sure.

Finally, there’s an issue of data retention — I’m not sure how much historical data we can feasibly store, but there’s a limit somewhere. It’d be on the application developer’s end to archive data, that’s just not something we can do.

Anyway, I don’t know if/when we’ll be able to implement it (or even which parts will come to light), but it’s an endpoint I really want, at least.

I really hope we can see something soon! I’m very interested in how this plays out, hopefully we can get some functionality that will allow us to also grab kills/deaths and some more specific information such as team damage dealt, skill use, and things of that nature to further track individual player stats in a log for developers and players. For instance tracking playstyle could help players run a better build. (do i deal alot of dps, or am i better at healing, or capping points, or team killing, etc.) it will really put a new level on pvp builds.

Now as for the applications developers or for the UI devs on guild wars 2 (whoever does the game programming), a tickbox that will be “disabled by default” for match data retention would be a good idea. and perhaps having a short window where that data can be grabbed by a server through the API. Say 7 days? I don’t know the specifics on how much data that will be for the servers personally, but I think it’s worth looking into, not everyone is going to have a use for data retention so I think it should be up to the players to manually enable the check box in order to cut down on data. If match data expired then return an error code that signifies that.

I don’t have a deep understanding of how match ID’s work (is each match incremented? or are the ID’s recycled?) but that’s my two cents. I really hope to see this up soon, i’m super excited to start taking advantage of this feature.

Lumiere.4609:

match id’s could simply be a hash, all I want is something short enough to type into an app to get live stats…

Lawton Campbell.8517:

The match IDs are GUIDs, actually.

In any case, as much as I want this API to exist today, it probably isn’t going to arrive any time soon

Glyph.7805:

Well short of having an API, is there a way to search for specific players rankings through this page: https://leaderboards.guildwars2.com/en/na/pvp ?

Perhaps something along the lines of :
https://leaderboards.guildwars2.com/en/na/pvp?player=Glyph.7805