Guild Wars 2 WvW API Usage in Android

Another android code sample that can get the active match-ups from the Guild Wars 2 WvWvW API, parse them all and store in a local cache.

First thing you need to do is create the helper class called Utils, create a file called Utils.java and insert the following code:

import java.text.DecimalFormat;

public class Utils {
    public enum eOwner {RED, BLUE, GREEN, NEUTRAL, UNKNOWN}
    public enum eMap {RED,BLUE, GREEN, CENTER, UNKNOWN}
    public enum eType {CAMP, TOWER, KEEP, CASTLE, RUIN, UNKNOWN}
    
    // Get the current structure owner and return it as a byte value
    // Method to speed up structer ownership lookups (Using a single byte is much faster than using strings and also saves around 80% memory)
    public static eOwner getStructureOwner (String owner)
    {
        if (owner.equalsIgnoreCase("red"))
            return eOwner.RED;
        else if (owner.equalsIgnoreCase("blue"))
            return eOwner.BLUE;
        else if (owner.equalsIgnoreCase("green"))
            return eOwner.GREEN;
        else if (owner.equalsIgnoreCase("neutral"))
            return eOwner.NEUTRAL;
        return eOwner.UNKNOWN;
    }
    
    // Get the current structure type and return it as a byte value
    // This is the same principle as StructreOwner with saving memory and increasing lookup speed
    public static eType getStructureType (String type)
    {
        if (type.equalsIgnoreCase("camp"))
            return eType.CAMP;
        else if (type.equalsIgnoreCase("tower"))
            return eType.TOWER;
        else if (type.equalsIgnoreCase("keep"))
            return eType.KEEP;
        else if (type.equalsIgnoreCase("castle"))
            return eType.CASTLE;
        else if (type.equalsIgnoreCase("ruin"))
            return eType.RUIN;
        return eType.UNKNOWN;
    }
    
    // Get the map type and return as a byte value
    public static eMap getMapType (String map)
    {
        if (map.equalsIgnoreCase("redhome"))
            return eMap.RED;
        else if (map.equalsIgnoreCase("bluehome"))
            return eMap.BLUE;
        else if (map.equalsIgnoreCase("greenhome"))
            return eMap.GREEN;
        else if (map.equalsIgnoreCase("center"))
            return eMap.CENTER;
        return eMap.UNKNOWN;
    }
    
    // Format a servers score from 123456 to 123,456
    public static String formatTotalScore (int value)
    {
        DecimalFormat df = new DecimalFormat("#,###,###");
        return df.format(value);
    }
}

Next you will needĀ to create the ObjectiveInfo class file – Create a class called ObjectiveInfo and paste in the following codeĀ :

//
// This handles all the individual objectives and their names, stores what tick will be given from owning, objective type, guild claimer etc..
//
class ObjectiveInfo {
    private int id; // Objective ID
    private int points; // Tick from owning objective
    private int coordX; // Map X coordinates
    private int coordY; // Map Y coordinates
    private Utils.eType type; // Objective Type
    private Utils.eOwner owner; // Server that owns objective
    private Utils.eMap map; // Which map the objective is in - Center(Eternal),Red,Blue,Green
    private String nameDE; // Objective name in Dutch
    private String nameEN; // " " English
    private String nameES; // " " Spanish
    private String nameFR; // " " French
    private String guildID; // Guild claimer ID
    private String guildName; // Guild claimer name

    // Create a copy of objectives from the previous tick, compare them against current and set a timer if the objective changed - which will give the duration on RI
    // Once that timer hits, set it to -1 for example to show the buff is inactive. Can also skip checking an objective for flipped state, if this is currently active
    // Even simpler method, store time the objective flipped and calculate if RI is active that way
    private int righteousIndignationCountdown;

    public ObjectiveInfo (int id, int points, int coordX, int coordY, String type, String nameDE, String nameEN, String nameES, String nameFR){
        this.id = id;
        this.points = points;
        this.coordX = coordX;
        this.coordY = coordY;
        this.type = Utils.getStructureType(type);
        this.owner = Utils.eOwner.UNKNOWN;
        this.nameDE = nameDE;
        this.nameEN = nameEN;
        this.nameES = nameES;
        this.nameFR = nameFR;
    }

    public int getID () { return id; }
    public int getPoints () { return points; }
    public int getCoordX () { return coordX; }
    public int getCoordY () { return coordY; }
    public Utils.eType getType () { return type; }
    public void setOwner (Utils.eOwner owner) {this.owner = owner;}
    public Utils.eOwner getOwner () {return owner; }
    public void setMap (Utils.eMap borderName) {this.map = borderName; }
    public Utils.eMap getMap () {return this.map; }
    public String getNameDE () { return nameDE; }
    public String getNameEN () { return nameEN; }
    public String getNameES () { return nameES; }
    public String getNameFR () { return nameFR; }
    public void setGuildID (String guildID) {this.guildID = guildID; }
    public String getGuildID () { return this.guildID; }
}

Now we have the base ObjectiveInfo class done, let’s move on to the MatchupCache class which will store all the current matchups returned from the Guild Wars 2 API.

Create a class called MatchupCache and paste in the following code:

import android.content.Context;
import android.util.Log;
import android.util.SparseArray;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;

public class MatchupCache {
    private String matchID;

    private int redWorldID;
    private String redWorldNameDE;
    private String redWorldNameEN;
    private String redWorldNameES;
    private String redWorldNameFR;
    private int redTotalScore;
    private int redTick;

    private int blueWorldID;
    private String blueWorldNameDE;
    private String blueWorldNameEN;
    private String blueWorldNameES;
    private String blueWorldNameFR;
    private int blueTotalScore;
    private int blueTick;

    private int greenWorldID;
    private String greenWorldNameDE;
    private String greenWorldNameEN;
    private String greenWorldNameES;
    private String greenWorldNameFR;
    private int greenTotalScore;
    private int greenTick;

    private String startTime;
    private String endTime;

    private SparseArray<ObjectiveInfo> objectives;

    public MatchupCache (Context ctx, String matchID, int redWorldID, int blueWorldID, int greenWorldID, String startTime, String endTime)
    {
        this.matchID = matchID;
        this.redWorldID = redWorldID;
        this.blueWorldID = blueWorldID;
        this.greenWorldID = greenWorldID;
        this.startTime = startTime;
        this.endTime = endTime;

        try {
            // Fill the server names from gw2_worlds.json
            JSONArray jRoot = JSONFunctions.getJSONFromResource(ctx, "gw2_worlds.json");
            for (int iIndex=0; iIndex < jRoot.length(); iIndex++) {
                if (jRoot.getJSONObject(iIndex).getInt("world_id") == redWorldID) {
                    this.redWorldNameDE = jRoot.getJSONObject(iIndex).getString("name_de");
                    this.redWorldNameEN = jRoot.getJSONObject(iIndex).getString("name_en");
                    this.redWorldNameES = jRoot.getJSONObject(iIndex).getString("name_es");
                    this.redWorldNameFR = jRoot.getJSONObject(iIndex).getString("name_fr");
                }
                else if (jRoot.getJSONObject(iIndex).getInt("world_id") == blueWorldID) {
                    this.blueWorldNameDE = jRoot.getJSONObject(iIndex).getString("name_de");
                    this.blueWorldNameEN = jRoot.getJSONObject(iIndex).getString("name_en");
                    this.blueWorldNameES = jRoot.getJSONObject(iIndex).getString("name_es");
                    this.blueWorldNameFR = jRoot.getJSONObject(iIndex).getString("name_fr");
                }
                else if (jRoot.getJSONObject(iIndex).getInt("world_id") == greenWorldID) {
                    this.greenWorldNameDE = jRoot.getJSONObject(iIndex).getString("name_de");
                    this.greenWorldNameEN = jRoot.getJSONObject(iIndex).getString("name_en");
                    this.greenWorldNameES = jRoot.getJSONObject(iIndex).getString("name_es");
                    this.greenWorldNameFR = jRoot.getJSONObject(iIndex).getString("name_fr");
                }
            }

            // Fill the objective names from gw2_objectives.json
            jRoot = JSONFunctions.getJSONFromResource(ctx, "gw2_objectives.json");
            int iLen = jRoot.length();
            objectives = new SparseArray<>();

            for (int iIndex=0;iIndex < iLen;iIndex++) {
                String szCoords[] = jRoot.getJSONObject(iIndex).getString("coords").replace("[", "").replace("]","").split(",");

                ObjectiveInfo objectivename = new ObjectiveInfo(
                        jRoot.getJSONObject(iIndex).getInt("id"),
                        jRoot.getJSONObject(iIndex).getInt("score"),
                        Integer.valueOf(szCoords[0]),
                        Integer.valueOf(szCoords[1]),
                        jRoot.getJSONObject(iIndex).getString("type"),
                        jRoot.getJSONObject(iIndex).getJSONObject("name").getString("de"),
                        jRoot.getJSONObject(iIndex).getJSONObject("name").getString("en"),
                        jRoot.getJSONObject(iIndex).getJSONObject("name").getString("es"),
                        jRoot.getJSONObject(iIndex).getJSONObject("name").getString("fr"));
                objectives.put(iIndex, objectivename);
            }

            // Get the current matchup from the GW2 API URL
            JSONObject jsonmatchup = JSONFunctions.getJSONFromURL("https://api.guildwars2.com/v1/wvw/match_details.json?match_id=" + matchID);
            JSONArray jsonmaps = jsonmatchup.getJSONArray("maps");

            for (int i=0; i < jsonmaps.length(); i++) {
                Utils.eMap mapcolour = Utils.getMapType(jsonmaps.getJSONObject(i).getString("type"));
                JSONArray jsonobjectives = jsonmaps.getJSONObject(i).getJSONArray("objectives");
                for (int j=0; j < jsonobjectives.length(); j++) {
                    getObjectiveFromID(jsonobjectives.getJSONObject(j).getInt("id")).setMap(mapcolour);
                }
            }
        } catch (JSONException | IOException e) {
            Log.e("Error", e.getMessage());
            e.printStackTrace();
        }

    }


    public void UpdateScores ()
    {
        try {
            JSONObject jsonmatchup = JSONFunctions.getJSONFromURL("https://api.guildwars2.com/v1/wvw/match_details.json?match_id=" + matchID);

            // Hard-coded indexes, because of the array returning 3 sets of scores which is Red, Blue and Green
            redTotalScore = jsonmatchup.getJSONArray("scores").getInt(0);
            blueTotalScore = jsonmatchup.getJSONArray("scores").getInt(1);
            greenTotalScore = jsonmatchup.getJSONArray("scores").getInt(2);
            JSONArray jsonmaps = jsonmatchup.getJSONArray("maps");
            redTick = 0;
            blueTick = 0;
            greenTick = 0;

            // Calculate the servers current ticks, cycling through owned objectives and set the current guild ownership (if set)

            for (int i=0; i < jsonmaps.length(); i++)
            {
                JSONArray jsonobjectives = jsonmaps.getJSONObject(i).getJSONArray("objectives");
                for (int j=0; j < jsonobjectives.length(); j++)
                {
                    Utils.eOwner owner = Utils.getStructureOwner(jsonobjectives.getJSONObject(j).getString("owner"));
                    ObjectiveInfo objective = getObjectiveFromID(jsonobjectives.getJSONObject(j).getInt("id"));
                    objective.setOwner(owner);

                    if (jsonobjectives.getJSONObject(j).has("owner_guild"))
                        objective.setGuildID(jsonobjectives.getJSONObject(j).getString("owner_guild"));

                    if (owner == Utils.eOwner.RED)
                        redTick += objective.getPoints();
                    else if (owner == Utils.eOwner.BLUE)
                        blueTick += objective.getPoints();
                    else if (owner == Utils.eOwner.GREEN)
                        greenTick += objective.getPoints();
                }
            }

        } catch (JSONException | IOException e) {
            Log.e("Error", e.getMessage());
            e.printStackTrace();
        }
    }

    public String getMatchID () {return matchID;}

    public int getRedWorld () {return redWorldID;}
    public String getRedWorldNameDE () { return redWorldNameDE; }
    public String getRedWorldNameEN () { return redWorldNameEN; }
    public String getRedWorldNameES () { return redWorldNameES; }
    public String getRedWorldNameFR () { return redWorldNameFR; }
    public int getRedTotalScore () { return redTotalScore; }
    public int getRedTick (){ return redTick; }

    public int getBlueWorld () {return blueWorldID;}
    public String getBlueWorldNameDE () { return blueWorldNameDE; }
    public String getBlueWorldNameEN () { return blueWorldNameEN; }
    public String getBlueWorldNameES () { return blueWorldNameES; }
    public String getBlueWorldNameFR () { return blueWorldNameFR; }
    public int getBlueTotalScore () { return blueTotalScore; }
    public int getBlueTick () { return blueTick; }

    public int getGreenWorld () {return greenWorldID;}
    public String getGreenWorldNameDE () { return greenWorldNameDE; }
    public String getGreenWorldNameEN () { return greenWorldNameEN; }
    public String getGreenWorldNameES () { return greenWorldNameES; }
    public String getGreenWorldNameFR () { return greenWorldNameFR; }
    public int getGreenTotalScore () { return greenTotalScore; }
    public int getGreenTick () { return greenTick; }

    public String getStartTime () {return startTime;}
    public String getEndTime () {return endTime; }

    public ObjectiveInfo getObjectiveFromID (int id)
    {
        for (int i=0; i < objectives.size(); i++)
        {
            if (objectives.get(i).getID() == id)
                return objectives.get(i);
        }

        return null;
    }
}

This class stores ALL the match-ups from the GW2 API. You can easily add a filter for EU or US servers – Check if the WorldIDs are above 2,000 for EU, or below for US.

Last thing we need is the JSONFunctions class which is the same as the previous post without the API key code. Create a new class called JSONFunctions and paste in the following code:

import android.content.Context;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class JSONFunctions {

    static JSONObject jObj;
    static JSONArray jArray;

    protected static String readResource (Context ctx, String filename) throws IOException {

        StringBuilder sb = new StringBuilder();
        BufferedReader input = new BufferedReader(new InputStreamReader(ctx.getAssets().open(filename), "utf-8"), 8192);

        String currLine;
        while ((currLine = input.readLine()) != null) {
            sb.append(currLine);
        }
        input.close();

        return sb.toString();
    }

    public static JSONArray getJSONFromResource (Context ctx, String filename) throws IOException {
        try {
            String json = readResource(ctx, filename);
            jArray = new JSONArray(json);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return jArray;
    }

    protected static String readURL (String url) throws IOException {
        StringBuilder sb = new StringBuilder();

        URL requestUrl = new URL(url);
        HttpURLConnection conn = (HttpURLConnection) requestUrl.openConnection();
        if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
            BufferedReader input = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"), 8192);

            String line;
            while ((line = input.readLine()) != null) {
                sb.append(line);
            }

            input.close();
        }

        return sb.toString();
    }

    public static JSONObject getJSONFromURL (String url) throws IOException {
        try {
            String json = readURL(url);
            jObj = new JSONObject(json);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return jObj;
    }
}

Now to establish the cache we need to do:

    private SparseArray<MatchupCache> mMatchupsCache;

// Cache the active matchups
    try {
        JSONObject jRoot = JSONFunctions.getJSONFromURL("https://api.guildwars2.com/v1/wvw/matches.json");
        // Locate the array name in JSON
        JSONArray jArray = jRoot.getJSONArray("wvw_matches");
        mMatchupsCache = new SparseArray<>();
        for (int iIndex =0; iIndex < jArray.length(); iIndex++) {
            MatchupCache matchup = new MatchupCache(MainActivity.this,
                    jArray.getJSONObject(iIndex).getString("wvw_match_id"),
                    jArray.getJSONObject(iIndex).getInt("red_world_id"),
                    jArray.getJSONObject(iIndex).getInt("blue_world_id"),
                    jArray.getJSONObject(iIndex).getInt("green_world_id"),
                    jArray.getJSONObject(iIndex).getString("start_time"),
                    jArray.getJSONObject(iIndex).getString("end_time"));

            matchup.UpdateScores();
            mMatchupsCache.put(iIndex, matchup);
        }
    } catch (JSONException | IOException e) {
        Log.e("Error", e.getMessage());
        e.printStackTrace();
    }

You’ll need the last two files which can be downloaded here. This zip contains the assets (gw2_objectives.json + gw2_worlds.json) that go inside your projects assets folder.

Make sure to run this in a AsyncTask for example and not inside the main OnCreate else your app will freeze for a while without anything happening on screen – Causing users to believe your application has frozen.

Final Note: Read up about the JSON structures returned on the official Guild Wars 2 API here. Specifically under Version 1 Endpoints – World vs World

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.