From 1bccbee1beb2683530edc1ae4a48e21de6f24279 Mon Sep 17 00:00:00 2001 From: Sammers21 Date: Wed, 11 Sep 2024 10:40:05 +0300 Subject: [PATCH] releaseb fix blitz bracket formation --- .../sammers/pla/blizzard/BlizzardAPI.java | 164 +++++++----------- .../sammers/pla/blizzard/PvpBracket.java | 5 +- .../sammers/pla/blizzard/WowAPICharacter.java | 4 +- .../github/sammers/pla/logic/CharUpdater.java | 7 - 4 files changed, 67 insertions(+), 113 deletions(-) diff --git a/src/io/github/sammers/pla/blizzard/BlizzardAPI.java b/src/io/github/sammers/pla/blizzard/BlizzardAPI.java index a6fadcd5..6ddd0198 100644 --- a/src/io/github/sammers/pla/blizzard/BlizzardAPI.java +++ b/src/io/github/sammers/pla/blizzard/BlizzardAPI.java @@ -99,84 +99,61 @@ public Maybe character(String region, String realm, String name String absoluteURI = "https://" + realRegion + ".api.blizzard.com/profile/wow/character/" + realmSearch + "/" + nameSearch; return token().flatMapMaybe(blizzardAuthToken -> { long tick = System.nanoTime(); - return maybeResponse(realNamespace, absoluteURI) - .flatMap(json -> { - Maybe res; - if (json.getInteger("code") != null && json.getInteger("code") == 404) { - log.debug("Character not found: " + name + " on " + realm + " in " + realRegion); - res = Maybe.empty(); - } else { - log.debug("Found Character: " + name + " on " + realm + " in " + realRegion); - res = maybeResponse(realNamespace, json.getJsonObject("pvp_summary").getString("href")) - .flatMap(pvp -> { - JsonArray bracketFromJson = pvp.getJsonArray("brackets"); - if (bracketFromJson == null) { - bracketFromJson = new JsonArray(); - } - Single> bracketList = Maybe.concatEager( - bracketFromJson.stream() - .map(o -> ((JsonObject) o).getString("href")) - .map(ref -> maybeResponse(realNamespace, ref) - ).toList() - ).toList(); - Maybe achievementsRx = maybeResponse(realNamespace, absoluteURI + "/achievements"); - Maybe mediaRx = maybeResponse(realNamespace, absoluteURI + "/character-media").onErrorReturnItem(new JsonObject()); - Maybe petsRx = maybeResponse(realNamespace, absoluteURI + "/collections/pets"); - Maybe specsRx = maybeResponse(realNamespace, absoluteURI + "/specializations"); - return Single.zip( - bracketList, - Maybe.concatEager(List.of(achievementsRx, mediaRx, petsRx, specsRx)).toList(), - Pair::new).flatMapMaybe(pair -> { - List brackets = pair.getValue0(); - List otherStuff = pair.getValue1(); - Optional prev = Optional.ofNullable(characterCache.getByFullName(Character.fullNameByRealmAndName(name, realm))); - Optional ctfs = Optional.ofNullable(cutoffs.get(realRegion)); - WowAPICharacter parsed = WowAPICharacter.parse(characterCache, prev, refs, ctfs, json, pvp, - brackets, otherStuff.get(0), otherStuff.get(1), otherStuff.get(3), otherStuff.get(2), realRegion); - return Maybe.just(parsed); - }).doOnSuccess(wowAPICharacter -> { - long elapsed = System.nanoTime() - tick; - log.debug("Parsed character {} in {} ms", wowAPICharacter.fullName(), elapsed / 1000000); - }); - }) - .doOnError(e -> log.error("Error parsing character: " + name + " on " + realm + " in " + realRegion, e)) - .onErrorResumeNext(Maybe.empty()); - } - return res; - }); + return maybeResponse(realNamespace, absoluteURI).flatMap(json -> { + Maybe res; + if (json.getInteger("code") != null && json.getInteger("code") == 404) { + log.debug("Character not found: " + name + " on " + realm + " in " + realRegion); + res = Maybe.empty(); + } else { + log.debug("Found Character: " + name + " on " + realm + " in " + realRegion); + res = maybeResponse(realNamespace, json.getJsonObject("pvp_summary").getString("href")).flatMap(pvp -> { + JsonArray bracketFromJson = pvp.getJsonArray("brackets"); + if (bracketFromJson == null) { + bracketFromJson = new JsonArray(); + } + Single> bracketList = Maybe.concatEager(bracketFromJson.stream().map(o -> ((JsonObject) o).getString("href")).map(ref -> maybeResponse(realNamespace, ref)).toList()).toList(); + Maybe achievementsRx = maybeResponse(realNamespace, absoluteURI + "/achievements"); + Maybe mediaRx = maybeResponse(realNamespace, absoluteURI + "/character-media").onErrorReturnItem(new JsonObject()); + Maybe petsRx = maybeResponse(realNamespace, absoluteURI + "/collections/pets"); + Maybe specsRx = maybeResponse(realNamespace, absoluteURI + "/specializations"); + return Single.zip(bracketList, Maybe.concatEager(List.of(achievementsRx, mediaRx, petsRx, specsRx)).toList(), Pair::new).flatMapMaybe(pair -> { + List brackets = pair.getValue0(); + List otherStuff = pair.getValue1(); + Optional prev = Optional.ofNullable(characterCache.getByFullName(Character.fullNameByRealmAndName(name, realm))); + Optional ctfs = Optional.ofNullable(cutoffs.get(realRegion)); + WowAPICharacter parsed = WowAPICharacter.parse(characterCache, prev, refs, ctfs, json, pvp, + brackets, otherStuff.get(0), otherStuff.get(1), otherStuff.get(3), otherStuff.get(2), realRegion); + return Maybe.just(parsed); + }).doOnSuccess(wowAPICharacter -> { + long elapsed = System.nanoTime() - tick; + log.debug("Parsed character {} in {} ms", wowAPICharacter.fullName(), elapsed / 1000000); + }); + }).doOnError(e -> log.error("Error parsing character: " + name + " on " + realm + " in " + realRegion, e)).onErrorResumeNext(Maybe.empty()); + } + return res; + }); }); } Maybe maybeResponse(String namespace, String url) { - return token().flatMapMaybe(blizzardAuthToken -> - rpsToken().andThen( - Maybe.defer(() -> { - log.debug("Getting " + url); - return webClient.getAbs(url) - .addQueryParam("namespace", namespace) - .addQueryParam("locale", LOCALE) - .bearerTokenAuthentication(blizzardAuthToken.accessToken()) - .timeout(TimeUnit.MINUTES.toMillis(10)) - .rxSend() - .onErrorResumeNext(er -> { - log.error("Error getting " + url, er); - return Single.error(er); - }) - .flatMapMaybe(resp -> { - log.debug("Got response to" + url + " " + resp.statusCode()); - if (resp.statusCode() == 200) { - return Maybe.just(resp.bodyAsJsonObject()); - } else if (resp.statusCode() == 429 || resp.statusCode() / 100 == 5) { - int code = resp.statusCode(); - log.info(code + " Retrying " + url + " " + resp.statusMessage()); - return rpsToken().andThen(rpsToken()).andThen(maybeResponse(namespace, url)); - } else { - return Maybe.error(new IllegalStateException("Error getting " + url + " " + resp.statusCode() + " " + resp.statusMessage() + " " + resp.bodyAsString())); - } - }); - }) - ) - ); + return token().flatMapMaybe(blizzardAuthToken -> rpsToken().andThen(Maybe.defer(() -> { + log.debug("Getting " + url); + return webClient.getAbs(url).addQueryParam("namespace", namespace).addQueryParam("locale", LOCALE).bearerTokenAuthentication(blizzardAuthToken.accessToken()).timeout(TimeUnit.MINUTES.toMillis(10)).rxSend().onErrorResumeNext(er -> { + log.error("Error getting " + url, er); + return Single.error(er); + }).flatMapMaybe(resp -> { + log.debug("Got response to" + url + " " + resp.statusCode()); + if (resp.statusCode() == 200) { + return Maybe.just(resp.bodyAsJsonObject()); + } else if (resp.statusCode() == 429 || resp.statusCode() / 100 == 5) { + int code = resp.statusCode(); + log.info(code + " Retrying " + url + " " + resp.statusMessage()); + return rpsToken().andThen(rpsToken()).andThen(maybeResponse(namespace, url)); + } else { + return Maybe.error(new IllegalStateException("Error getting " + url + " " + resp.statusCode() + " " + resp.statusMessage() + " " + resp.bodyAsString())); + } + }); + }))); } public static String realRegion(String region) { @@ -223,23 +200,13 @@ public Maybe pvpLeaderboard(String region, Integer pvpSeasonId, realPvpBracket = pvpBracket; } String url = "https://" + realRegion + ".api.blizzard.com/data/wow/pvp-season/" + pvpSeasonId + "/pvp-leaderboard/" + realPvpBracket; - return Maybe.defer(() -> - token().flatMapMaybe(blizzardAuthToken -> maybeResponse(realNamespace, url)).map(PvpLeaderBoard::fromJson) - ) - .doOnSubscribe(disposable -> log.info("Getting leaderboard for region={} ssn={} bracket={}", realRegion, pvpSeasonId, realPvpBracket)) - .doOnError(er -> log.error("Error fetching Blizzard PVP leaderboard", er)) - .onErrorResumeNext(Maybe.empty()); + return Maybe.defer(() -> token().flatMapMaybe(blizzardAuthToken -> maybeResponse(realNamespace, url)).map(PvpLeaderBoard::fromJson)).doOnSubscribe(disposable -> log.info("Getting leaderboard for region={} ssn={} bracket={}", realRegion, pvpSeasonId, realPvpBracket)).doOnError(er -> log.error("Error fetching Blizzard PVP leaderboard", er)).onErrorResumeNext(Maybe.empty()); } private Single authorize() { MultiMap form = MultiMap.caseInsensitiveMultiMap(); form.set("grant_type", "client_credentials"); - return webClient.postAbs(AUTH_URL) - .addQueryParam("grant_type", "client_credentials") - .basicAuthentication(clientId, clientSecret) - .rxSendForm(form) - .map(response -> BlizzardAuthToken.fromJson(response.bodyAsJsonObject())) - .doOnSuccess(token -> log.info("Got token: {}", token)); + return webClient.postAbs(AUTH_URL).addQueryParam("grant_type", "client_credentials").basicAuthentication(clientId, clientSecret).rxSendForm(form).map(response -> BlizzardAuthToken.fromJson(response.bodyAsJsonObject())).doOnSuccess(token -> log.info("Got token: {}", token)); } public Single realms(String region) { @@ -248,15 +215,14 @@ public Single realms(String region) { String realRegion = realRegion(region); String namespace = "dynamic-" + realRegion; String url = "https://" + realRegion + ".api.blizzard.com/data/wow/connected-realm/index"; - return maybeResponse(namespace, url) - .flatMapSingle(index -> { - List hrefs = index.getJsonArray("connected_realms").stream().map(o -> ((JsonObject) o).getString("href")).toList(); - List> list = hrefs.stream().map(href -> maybeResponse(namespace, href)).toList(); - return Maybe.merge(list).toList().map(responses -> { - log.info("Realms for {} fetched in {}ms", realRegion, System.currentTimeMillis() - tick); - return Realms.fromBlizzardJson(realRegion, index, responses); - }); + return maybeResponse(namespace, url).flatMapSingle(index -> { + List hrefs = index.getJsonArray("connected_realms").stream().map(o -> ((JsonObject) o).getString("href")).toList(); + List> list = hrefs.stream().map(href -> maybeResponse(namespace, href)).toList(); + return Maybe.merge(list).toList().map(responses -> { + log.info("Realms for {} fetched in {}ms", realRegion, System.currentTimeMillis() - tick); + return Realms.fromBlizzardJson(realRegion, index, responses); }); + }); }); } @@ -266,14 +232,6 @@ public Single cutoffs(String region) { public Single cutoffs(String region, Integer pvpSsnId) { String realRegion = realRegion(region); - return token().flatMap(blizzardAuthToken -> - webClient.getAbs("https://" + realRegion + ".api.blizzard.com/data/wow/pvp-season/" + pvpSsnId + "/pvp-reward/index") - .addQueryParam("namespace", "dynamic-" + realRegion) - .addQueryParam("locale", LOCALE) - .bearerTokenAuthentication(blizzardAuthToken.accessToken()) - .rxSend() - .map(HttpResponse::bodyAsJsonObject) - .map(res -> Cutoffs.fromBlizzardJson(realRegion, res)) - ); + return token().flatMap(blizzardAuthToken -> webClient.getAbs("https://" + realRegion + ".api.blizzard.com/data/wow/pvp-season/" + pvpSsnId + "/pvp-reward/index").addQueryParam("namespace", "dynamic-" + realRegion).addQueryParam("locale", LOCALE).bearerTokenAuthentication(blizzardAuthToken.accessToken()).rxSend().map(HttpResponse::bodyAsJsonObject).map(res -> Cutoffs.fromBlizzardJson(realRegion, res))); } } diff --git a/src/io/github/sammers/pla/blizzard/PvpBracket.java b/src/io/github/sammers/pla/blizzard/PvpBracket.java index 6bd8b535..de2e6026 100644 --- a/src/io/github/sammers/pla/blizzard/PvpBracket.java +++ b/src/io/github/sammers/pla/blizzard/PvpBracket.java @@ -25,9 +25,12 @@ public static PvpBracket parse(JsonObject wowApiBracket, Optional pr String type = wowApiBracket.getJsonObject("bracket").getString("type"); Long rating = wowApiBracket.getLong("rating"); Optional stats; - if (type.equals("SHUFFLE")) { + if (type.equals("SHUFFLE") || type.equals("BLITZ")) { type = type + "-" + wowApiBracket.getJsonObject("specialization").getString("name"); stats = Optional.ofNullable(wowApiBracket.getJsonObject("season_round_statistics")); + if (stats.isEmpty()) { + stats = Optional.ofNullable(wowApiBracket.getJsonObject("season_match_statistics")); + } } else { stats = Optional.ofNullable(wowApiBracket.getJsonObject("season_match_statistics")); } diff --git a/src/io/github/sammers/pla/blizzard/WowAPICharacter.java b/src/io/github/sammers/pla/blizzard/WowAPICharacter.java index 9315a176..4af2c3bb 100644 --- a/src/io/github/sammers/pla/blizzard/WowAPICharacter.java +++ b/src/io/github/sammers/pla/blizzard/WowAPICharacter.java @@ -198,7 +198,7 @@ public static WowAPICharacter parse( return foundChars.get(0).pos(); } else { Long finalPos = -1L; - if (btype.equals("SHUFFLE")) { + if (btype.equals("SHUFFLE") || btype.equals("BLITZ")) { String spec = wowApiBracket.getJsonObject("specialization").getString("name"); finalPos = foundChars.stream().filter(c -> c.fullSpec().contains(spec)).findFirst().map(Character::pos).orElse(-1L); } @@ -207,7 +207,7 @@ public static WowAPICharacter parse( }).orElse(-1L); Long cutoffByBracketType; Optional prevBracket; - if (btype.equals("SHUFFLE")) { + if (btype.equals("SHUFFLE") || btype.equals("BLITZ")) { String spec = wowApiBracket.getJsonObject("specialization").getString("name"); Long id = wowApiBracket.getJsonObject("specialization").getLong("id"); String specCode = Cutoffs.specCodeNameById(spec, id); diff --git a/src/io/github/sammers/pla/logic/CharUpdater.java b/src/io/github/sammers/pla/logic/CharUpdater.java index ba3822ac..9698b181 100644 --- a/src/io/github/sammers/pla/logic/CharUpdater.java +++ b/src/io/github/sammers/pla/logic/CharUpdater.java @@ -2,29 +2,22 @@ import io.github.sammers.pla.Main; import io.github.sammers.pla.blizzard.BlizzardAPI; -import io.github.sammers.pla.blizzard.PvpBracket; import io.github.sammers.pla.blizzard.WowAPICharacter; import io.github.sammers.pla.db.Character; import io.github.sammers.pla.db.DB; import io.github.sammers.pla.db.Snapshot; import io.reactivex.Completable; import io.reactivex.Flowable; -import io.reactivex.Maybe; import io.reactivex.Single; - import org.javatuples.Pair; import org.javatuples.Triplet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.time.Instant; -import java.time.temporal.ChronoUnit; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Stream; import static io.github.sammers.pla.logic.Conts.*;