/* * Copyright (C) 2023 NotEnoughUpdates contributors * * This file is part of NotEnoughUpdates. * * NotEnoughUpdates is free software: you can redistribute it * and/or modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * NotEnoughUpdates is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with NotEnoughUpdates. If not, see . */ package io.github.moulberry.notenoughupdates.profileviewer; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; import io.github.moulberry.notenoughupdates.events.ProfileDataLoadedEvent; import io.github.moulberry.notenoughupdates.miscfeatures.PetInfoOverlay; import io.github.moulberry.notenoughupdates.miscfeatures.profileviewer.bestiary.BestiaryData; import io.github.moulberry.notenoughupdates.profileviewer.data.APIDataJson; import io.github.moulberry.notenoughupdates.profileviewer.weight.senither.SenitherWeight; import io.github.moulberry.notenoughupdates.profileviewer.weight.weight.Weight; import io.github.moulberry.notenoughupdates.util.Constants; import io.github.moulberry.notenoughupdates.util.UrsaClient; import io.github.moulberry.notenoughupdates.util.Utils; import io.github.moulberry.notenoughupdates.util.hypixelapi.ProfileCollectionInfo; import io.github.moulberry.notenoughupdates.util.kotlin.KotlinTypeAdapterFactory; import lombok.Getter; import net.minecraft.client.Minecraft; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.util.EnumChatFormatting; import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; public class SkyblockProfiles { private static final String defaultNbtData = "Hz8IAAAAAAAAAD9iYD9kYD9kAAMAPwI/Gw0AAAA="; private static final String moulberryUuid = "d0e05de76067454dbeaec6d19d886191"; private static final List inventoryNames = Arrays.asList( "inv_armor", "fishing_bag", "quiver", "ender_chest_contents", "backpack_contents", "personal_vault_contents", "wardrobe_contents", "potion_bag", "inv_contents", "talisman_bag", "candy_inventory_contents", "equipment_contents" ); private static final List skills = Arrays.asList( "taming", "mining", "foraging", "enchanting", "carpentry", "farming", "combat", "fishing", "alchemy", "runecrafting", "social" ); private static final List tuningStats = Arrays.asList( "health", "defense", "walk_speed", "strength", "critical_damage", "critical_chance", "attack_speed", "intelligence" ); private final ProfileViewer profileViewer; // TODO: replace with UUID type private final String uuid; private final AtomicBoolean updatingSkyblockProfilesState = new AtomicBoolean(false); private final AtomicBoolean updatingGuildInfoState = new AtomicBoolean(false); private final AtomicBoolean updatingPlayerStatusState = new AtomicBoolean(false); private final AtomicBoolean updatingSoopyData = new AtomicBoolean(false); private final AtomicBoolean updatingBingoInfo = new AtomicBoolean(false); @Getter private Map nameToProfile = null; private JsonArray profilesArray; private List profileNames = new ArrayList<>(); private String latestProfileName; private long soopyNetworthLeaderboardPosition = -1; // -1 = default, -2 = loading, -3 = error private long soopyWeightLeaderboardPosition = -1; // -1 = default, -2 = loading, -3 = error private JsonObject guildInformation = null; // Assume the player is in a guild until proven otherwise private boolean isInGuild = true; private JsonObject playerStatus = null; private JsonObject bingoInformation = null; private long lastPlayerInfoState = 0; private long lastStatusInfoState = 0; private long lastGuildInfoState = 0; private long lastBingoInfoState = 0; public SkyblockProfiles(ProfileViewer profileViewer, String uuid) { this.profileViewer = profileViewer; this.uuid = uuid; } public JsonObject getPlayerStatus() { if (playerStatus != null) return playerStatus; if (updatingPlayerStatusState.get()) return null; long currentTime = System.currentTimeMillis(); if (currentTime - lastStatusInfoState < 15 * 1000) return null; lastStatusInfoState = currentTime; updatingPlayerStatusState.set(true); profileViewer.getManager().ursaClient .get(UrsaClient.status(Utils.parseDashlessUUID(uuid))) .handle((jsonObject, ex) -> { updatingPlayerStatusState.set(false); if (jsonObject != null && jsonObject.has("success") && jsonObject.get("success").getAsBoolean()) { playerStatus = jsonObject.get("session").getAsJsonObject(); } return null; }); return null; } public JsonObject getBingoInformation() { long currentTime = System.currentTimeMillis(); if (bingoInformation != null && currentTime - lastBingoInfoState < 15 * 1000) return bingoInformation; if (updatingBingoInfo.get() && bingoInformation != null) return bingoInformation; if (updatingBingoInfo.get() && bingoInformation == null) return null; lastBingoInfoState = currentTime; updatingBingoInfo.set(true); NotEnoughUpdates.INSTANCE.manager.ursaClient .get(UrsaClient.bingo(Utils.parseDashlessUUID(uuid))) .handle(((jsonObject, throwable) -> { updatingBingoInfo.set(false); if (jsonObject != null && jsonObject.has("success") && jsonObject.get("success").getAsBoolean()) { bingoInformation = jsonObject; } else { bingoInformation = null; } return null; })); return bingoInformation != null ? bingoInformation : null; } /** * -1 = default, -2 = loading, -3 = error * >= 0 = actual position */ public long getSoopyNetworthLeaderboardPosition() { if (soopyNetworthLeaderboardPosition == -1) { loadSoopyData(() -> {}); } return moulberryUuid.equals(uuid) ? 1 : soopyNetworthLeaderboardPosition; } public long getSoopyWeightLeaderboardPosition() { if (soopyWeightLeaderboardPosition == -1) { loadSoopyData(() -> {}); } return moulberryUuid.equals(uuid) ? 1 : soopyWeightLeaderboardPosition; } public boolean isProfileMaxSoopyWeight(String profileName) { String highestProfileName = ""; double largestProfileWeight = 0; for (Map.Entry profileEntry : nameToProfile.entrySet()) { if (!profileEntry.getValue().skillsApiEnabled()) { continue; } Map levelingInfo = profileEntry.getValue().getLevelingInfo(); if (levelingInfo == null) { continue; } double weightValue = new SenitherWeight(levelingInfo).getTotalWeight().getRaw(); if (weightValue > largestProfileWeight) { largestProfileWeight = weightValue; highestProfileName = profileEntry.getKey(); } } return highestProfileName.equals(profileName); } private long handleSoopyApiResponse(JsonObject response) { if (response == null || !response.has("success") || !response.get("success").getAsBoolean() || !response.has("data")) { return -3; // Error } else { return response.get("data").getAsLong(); } } private void loadSoopyData(Runnable callback) { if (updatingSoopyData.get()) { return; } updatingSoopyData.set(true); soopyNetworthLeaderboardPosition = -2; // Loading profileViewer.getManager().apiUtils .request() .url("https://api.soopy.dev/lb/lbpos/networth/" + this.uuid) .requestJson() .handle((jsonObject, throwable) -> { soopyNetworthLeaderboardPosition = handleSoopyApiResponse(jsonObject); return null; }); soopyWeightLeaderboardPosition = -2; // Loading profileViewer.getManager().apiUtils .request() .url("https://api.soopy.dev/lb/lbpos/weight/" + this.uuid) .requestJson() .handle((jsonObject, throwable) -> { soopyWeightLeaderboardPosition = handleSoopyApiResponse(jsonObject); return null; }); long currentTime = System.currentTimeMillis(); if (ProfileViewerUtils.lastSoopyRequestTime.containsKey(uuid)) { if (currentTime - ProfileViewerUtils.lastSoopyRequestTime.get(uuid) < 5 * 1000 * 60) { if (ProfileViewerUtils.soopyDataCache.containsKey(uuid)) { updateSoopyNetworth(ProfileViewerUtils.soopyDataCache.get(uuid)); callback.run(); } } } else { ProfileViewerUtils.lastSoopyRequestTime.put(uuid, currentTime); profileViewer.getManager().apiUtils .request() .url("https://soopy.dev/api/v2/player_networth2/" + this.uuid) .method("POST") .postData("application/json", profilesArray.toString()) .requestJson() .handle((jsonObject, throwable) -> { updateSoopyNetworth(jsonObject); ProfileViewerUtils.soopyDataCache.put(uuid, jsonObject); callback.run(); return null; }); } } private void updateSoopyNetworth(JsonObject jsonObject) { if (jsonObject == null || !jsonObject.has("success") || !jsonObject.get("success").getAsBoolean()) { for (Map.Entry entry : nameToProfile.entrySet()) { entry.getValue().soopyNetworth = new SoopyNetworth(null); } } else { for (Map.Entry entry : nameToProfile.entrySet()) { String profileId = entry.getValue().getOuterProfileJson().get("profile_id").getAsString().replace("-", ""); JsonElement curProfileData = jsonObject.getAsJsonObject("data").get(profileId); if (curProfileData == null) { entry.getValue().soopyNetworth = new SoopyNetworth(null); } else { entry.getValue().soopyNetworth = new SoopyNetworth(curProfileData.getAsJsonObject()); } } } updatingSoopyData.set(false); } public AtomicBoolean getUpdatingSkyblockProfilesState() { return updatingSkyblockProfilesState; } public @Nullable SkyblockProfile getProfile(String profileName) { return nameToProfile == null ? null : nameToProfile.get(profileName); } public SkyblockProfile getLatestProfile() { return nameToProfile.get(latestProfileName); } public String getLatestProfileName() { return latestProfileName; } public Map getOrLoadSkyblockProfiles(Runnable runnable) { if (nameToProfile != null) { return nameToProfile; } long currentTime = System.currentTimeMillis(); if (currentTime - lastPlayerInfoState < 15 * 1000 && updatingSkyblockProfilesState.get()) { return null; } lastPlayerInfoState = currentTime; updatingSkyblockProfilesState.set(true); profileViewer.getManager().ursaClient .get(UrsaClient.profiles(Utils.parseDashlessUUID(uuid))) .handle((profilesJson, throwable) -> { try { if (Utils.parseDashlessUUID(uuid).toString().equals(Minecraft.getMinecraft().thePlayer .getUniqueID() .toString())) { new ProfileDataLoadedEvent(uuid, profilesJson).post(); } } catch (Exception ignored) { } if (profilesJson != null && profilesJson.has("success") && profilesJson.get("success").getAsBoolean() && profilesJson.has("profiles")) { Map nameToProfile = new HashMap<>(); String latestProfileName = null; List profileNames = new ArrayList<>(); // player has no skyblock profiles if (profilesJson.get("profiles").isJsonNull()) { updatingSkyblockProfilesState.set(false); this.nameToProfile = new HashMap<>(); return null; } for (JsonElement profileEle : profilesJson.getAsJsonArray("profiles")) { JsonObject profile = profileEle.getAsJsonObject(); if (!profile.has("members")) { continue; } JsonObject members = profile.getAsJsonObject("members"); if (members.has(uuid)) { JsonObject member = members.getAsJsonObject(uuid); if (member.has("coop_invitation")) { if (!member.getAsJsonObject("coop_invitation").get("confirmed").getAsBoolean()) { continue; } } String profileName = profile.get("cute_name").getAsString(); if (profile.has("selected") && profile.get("selected").getAsBoolean()) { latestProfileName = profileName; } nameToProfile.put(profileName, new SkyblockProfile(profile)); profileNames.add(profileName); } } this.nameToProfile = nameToProfile; this.profilesArray = profilesJson.getAsJsonArray("profiles"); this.latestProfileName = latestProfileName; this.profileNames.addAll(profileNames); updatingSkyblockProfilesState.set(false); if (runnable != null) { runnable.run(); } } return null; }); return null; } public JsonObject getOrLoadGuildInformation(Runnable runnable) { if (guildInformation != null) { return guildInformation; } long currentTime = System.currentTimeMillis(); if (currentTime - lastGuildInfoState < 15 * 1000 && updatingGuildInfoState.get()) { return null; } lastGuildInfoState = currentTime; updatingGuildInfoState.set(true); profileViewer.getManager().ursaClient .get(UrsaClient.guild(Utils.parseDashlessUUID(uuid))) .handle((jsonObject, ex) -> { updatingGuildInfoState.set(false); if (jsonObject != null && jsonObject.has("success") && jsonObject.get("success").getAsBoolean()) { if (jsonObject.get("guild").isJsonNull()) { isInGuild = false; return null; } guildInformation = jsonObject.getAsJsonObject("guild"); if (runnable != null) { runnable.run(); } } return null; }); return null; } public boolean isPlayerInGuild() { return isInGuild; } public List getProfileNames() { return profileNames; } public void resetCache() { profilesArray = null; profileNames = new ArrayList<>(); guildInformation = null; playerStatus = null; nameToProfile = null; } public String getUuid() { return uuid; } public @Nullable JsonObject getHypixelProfile() { return profileViewer.getUuidToHypixelProfile().getOrDefault(uuid, null); } public static class SoopyNetworth { private final Map categoryToTotal = new LinkedHashMap<>(); private long networth; private SoopyNetworth(JsonObject nwData) { if (nwData == null || nwData.isJsonNull() || nwData.get("total").isJsonNull()) { networth = -2; return; } networth = nwData.get("total").getAsLong(); Map categoryToTotal = new HashMap<>(); for (Map.Entry entry : nwData.get("categories").getAsJsonObject().entrySet()) { if (!entry.getValue().isJsonNull()) { categoryToTotal.put(entry.getKey(), entry.getValue().getAsLong()); } } // Sort based on category value categoryToTotal.entrySet().stream() .sorted(Comparator.comparingLong(e -> -e.getValue())) .forEachOrdered(e -> this.categoryToTotal.put(e.getKey(), e.getValue())); } private SoopyNetworth asLoading() { networth = -1; return this; } /** * @return -2 = error, -1 = loading, >= 0 = success */ public long getNetworth() { return networth; } public Map getCategoryToTotal() { return categoryToTotal; } } private static final Gson gson = new GsonBuilder() .registerTypeAdapterFactory(KotlinTypeAdapterFactory.INSTANCE) .create(); public class SkyblockProfile { private final JsonObject outerProfileJson; private final String gamemode; private Integer magicPower = null; private LinkedHashMap tuningInfo = null; private Double skyblockLevel = null; private EnumChatFormatting skyBlockExperienceColour = null; private Map inventoryNameToInfo = null; private Map levelingInfo = null; private JsonObject petsInfo = null; private ProfileCollectionInfo collectionsInfo = null; private PlayerStats.Stats stats; private Long networth = null; private SoopyNetworth soopyNetworth = null; private MuseumData museumData = null; @Getter public @Nullable APIDataJson APIDataJson; private final AtomicBoolean updatingMuseumData = new AtomicBoolean(false); public class MuseumData { private long museumValue; private final Map weaponItems = new HashMap<>(); private final Map armorItems = new HashMap<>(); private final Map raritiesItems = new HashMap<>(); private final List specialItems = new ArrayList<>(); private final Map> savedItems = new HashMap<>(); private MuseumData(JsonObject museumJson) { JsonObject museum = Constants.MUSEUM; if (museum == null) { Utils.showOutdatedRepoNotification("museum.json"); museumValue = -3; return; } if (museumJson == null || museumJson.isJsonNull() || museumJson.get("members").isJsonNull()) { museumValue = -2; return; } JsonObject members = museumJson.get("members").getAsJsonObject(); if (members == null || members.isJsonNull() || !members.has(uuid)) { museumValue = -2; return; } JsonObject member = members.get(uuid).getAsJsonObject(); if (member == null || member.isJsonNull() || !member.has("value")) { museumValue = -2; return; } museumValue = member.get("value").getAsLong(); if (member.has("items")) { JsonObject museumItemsData = member.get("items").getAsJsonObject(); if (museumItemsData != null) { for (Map.Entry entry : museumItemsData.entrySet()) { JsonObject itemJson = entry.getValue().getAsJsonObject(); JsonArray contents = parseNbt(itemJson); String itemName = entry.getKey(); Long donationTime = itemJson.get("donated_time").getAsLong(); boolean borrowing = false; if (itemJson.has("borrowing")) { borrowing = itemJson.get("borrowing").getAsBoolean(); } getDataAndChildren(itemName, Pair.of(donationTime, borrowing)); processItems(itemName, contents); } } } if (member.has("special")) { JsonArray specialItemsData = member.get("special").getAsJsonArray(); if (specialItemsData != null) { for (JsonElement element : specialItemsData) { JsonObject itemData = element.getAsJsonObject(); JsonArray contents = parseNbt(itemData); long donationTime = itemData.get("donated_time").getAsLong(); JsonObject firstItem = contents.get(0).getAsJsonObject(); String itemID = firstItem.get("internalname").getAsString(); getDataAndChildren(itemID, Pair.of(donationTime, false)); specialItems.add(contents); } } } } private JsonArray parseNbt(JsonObject nbt) { JsonArray contents = new JsonArray(); JsonObject contentItems = nbt.get("items").getAsJsonObject(); String contentBytes = contentItems.get("data").getAsString(); try { NBTTagList items = CompressedStreamTools.readCompressed( new ByteArrayInputStream(Base64.getDecoder().decode(contentBytes)) ).getTagList("i", 10); for (int j = 0; j < items.tagCount(); j++) { JsonObject item = profileViewer.getManager().getJsonFromNBTEntry(items.getCompoundTagAt(j)); if (item == null) { continue; } contents.add(item); } } catch (IOException ignored) { } return contents; } private void processItems(String itemName, JsonArray contents) { JsonObject museum = Constants.MUSEUM; storeItem(itemName, contents, museum.get("weapons").getAsJsonArray(), weaponItems); storeItem(itemName, contents, museum.get("armor").getAsJsonArray(), armorItems); storeItem(itemName, contents, museum.get("rarities").getAsJsonArray(), raritiesItems); } private void storeItem(String itemName, JsonArray contents, JsonArray items, Map itemMap) { for (JsonElement item : items) { if (Objects.equals(item.getAsString(), itemName)) { itemMap.put(itemName, contents); return; } } } private void getDataAndChildren(String name, Pair itemData) { JsonObject children = Constants.MUSEUM.get("children").getAsJsonObject(); if (savedItems.containsKey(name)) return; savedItems.put(name, itemData); if (children.has(name)) { String childId = children.get(name).getAsString(); String childName = childId; JsonObject nameMappings = Constants.MUSEUM.get("armor_to_id").getAsJsonObject(); if (nameMappings.has(childId)) { childName = nameMappings.get(childId).getAsString(); } String displayName = NotEnoughUpdates.INSTANCE.manager.getDisplayName(childName); ItemStack stack = Utils.createItemStack(Items.dye, displayName, 10, "Donated as higher tier"); JsonObject item = profileViewer.getManager().getJsonForItem(stack); item.add("internalname", new JsonPrimitive("_")); JsonArray itemArray = new JsonArray(); itemArray.add(item); processItems(childId, itemArray); getDataAndChildren(childId, itemData); } } private MuseumData asLoading() { museumValue = -1; return this; } public long getValue() { return museumValue; } public Map getWeaponItems() { return weaponItems; } public Map getArmorItems() { return armorItems; } public Map getRaritiesItems() { return raritiesItems; } public List getSpecialItems() { return specialItems; } public Map> getSavedItems() { return savedItems; } } private void loadMuseumData() { if (updatingMuseumData.get()) { return; } updatingMuseumData.set(true); String profileId = getOuterProfileJson().get("profile_id").getAsString(); profileViewer.getManager().ursaClient.get(UrsaClient.museumForProfile(profileId)) .handle((museumJson, throwable) -> { if (museumJson != null && museumJson.has("success") && museumJson.get("success").getAsBoolean() && museumJson.has("members")) { museumData = new MuseumData(museumJson); return null; } return null; }); } public SkyblockProfile(JsonObject outerProfileJson) { this.outerProfileJson = outerProfileJson; this.gamemode = Utils.getElementAsString(outerProfileJson.get("game_mode"), null); try { APIDataJson = gson.fromJson(getProfileJson(), APIDataJson.class); } catch (Exception exception) { NotEnoughUpdates.LOGGER.error("Could not read data", exception); } } public @NotNull JsonObject getOuterProfileJson() { return outerProfileJson; } /** * @return Profile json with UUID of {@link SkyblockProfiles#uuid} */ public @NotNull JsonObject getProfileJson() { return Utils.getElement(outerProfileJson, "members." + SkyblockProfiles.this.uuid).getAsJsonObject(); } public @Nullable String getGamemode() { return gamemode; } /** * Calculates the amount of Magical Power the player has using the list of accessories * * @return the amount of Magical Power or -1 */ public int getMagicalPower() { if (magicPower != null) { return magicPower; } Map inventoryInfo = getInventoryInfo(); if (!inventoryInfo.containsKey("talisman_bag")) { return -1; } return magicPower = ProfileViewerUtils.getMagicalPower(inventoryInfo.get("talisman_bag"), getProfileJson()); } public LinkedHashMap getTuningInfo() { if (tuningInfo != null) { return tuningInfo; } JsonObject profileJson = getProfileJson(); if (Utils.getElement(profileJson, "accessory_bag_storage.tuning") == null) return null; JsonObject tuningData = Utils.getElementOrDefault( profileJson, "accessory_bag_storage.tuning.slot_0", new JsonObject() ).getAsJsonObject(); if (tuningData.entrySet().isEmpty()) return null; tuningInfo = new LinkedHashMap<>(); for (String stat : tuningStats) { int statData = tuningData.get(stat).getAsInt(); tuningInfo.put(stat, statData); } return tuningInfo; } public Map getInventoryInfo() { if (inventoryNameToInfo != null) { return inventoryNameToInfo; } JsonObject profileJson = getProfileJson(); inventoryNameToInfo = new HashMap<>(); for (String invName : inventoryNames) { JsonArray contents = new JsonArray(); if (invName.equals("backpack_contents")) { JsonObject backpackData = getBackpackData(Utils.getElement(profileJson, "inventory.backpack_contents")); inventoryNameToInfo.put("backpack_sizes", backpackData.getAsJsonArray("backpack_sizes")); contents = backpackData.getAsJsonArray("contents"); } else { String path = "inventory." + invName + ".data"; if (invName.endsWith("bag") || invName.equals("quiver")) { path = "inventory.bag_contents." + invName + ".data"; } else if (invName.equals("candy_inventory_contents")) { path = "shared_inventory.candy_inventory_contents"; //the mappings said that this is the new path but i cant verify that because the data doesnt exist. } String contentBytes = Utils.getElementAsString( Utils.getElement( profileJson, path ), defaultNbtData ); try { NBTTagList items = CompressedStreamTools.readCompressed( new ByteArrayInputStream(Base64.getDecoder().decode(contentBytes)) ).getTagList("i", 10); for (int j = 0; j < items.tagCount(); j++) { JsonObject item = profileViewer.getManager().getJsonFromNBTEntry(items.getCompoundTagAt(j)); contents.add(item); } } catch (IOException ignored) { } } inventoryNameToInfo.put(invName, contents); } return inventoryNameToInfo; } private JsonObject getBackpackData(JsonElement backpackContentsJson) { // JsonArray of JsonObjects JsonArray contents = new JsonArray(); JsonArray backpackSizes = new JsonArray(); JsonObject bundledReturn = new JsonObject(); bundledReturn.add("contents", contents); bundledReturn.add("backpack_sizes", backpackSizes); if (backpackContentsJson == null || !backpackContentsJson.isJsonObject()) { return bundledReturn; } for (Map.Entry backpack : backpackContentsJson.getAsJsonObject().entrySet()) { if (backpack.getValue().isJsonObject()) { try { String bytes = Utils.getElementAsString(backpack.getValue().getAsJsonObject().get("data"), defaultNbtData); NBTTagList items = CompressedStreamTools.readCompressed( new ByteArrayInputStream(Base64.getDecoder().decode(bytes)) ).getTagList("i", 10); backpackSizes.add(new JsonPrimitive(items.tagCount())); for (int j = 0; j < items.tagCount(); j++) { contents.add(profileViewer.getManager().getJsonFromNBTEntry(items.getCompoundTagAt(j))); } } catch (IOException ignored) { } } } return bundledReturn; } public EnumChatFormatting getSkyblockLevelColour() { if (Constants.SBLEVELS == null || !Constants.SBLEVELS.has("sblevel_colours")) { Utils.showOutdatedRepoNotification("sblevels.json or missing sblevel_colours"); return EnumChatFormatting.WHITE; } if (skyBlockExperienceColour != null) { return skyBlockExperienceColour; } double skyblockLevel = getSkyblockLevel(); EnumChatFormatting levelColour = EnumChatFormatting.WHITE; JsonObject sblevelColours = Constants.SBLEVELS.getAsJsonObject("sblevel_colours"); try { for (Map.Entry stringJsonElementEntry : sblevelColours.entrySet()) { int nextLevelBracket = Integer.parseInt(stringJsonElementEntry.getKey()); EnumChatFormatting valueByName = EnumChatFormatting.getValueByName(stringJsonElementEntry .getValue() .getAsString()); if (skyblockLevel >= nextLevelBracket) { levelColour = valueByName; } } } catch (RuntimeException ignored) { // catch both numberformat and getValueByName being wrong } return skyBlockExperienceColour = levelColour; } public double getSkyblockLevel() { if (skyblockLevel != null) { return skyblockLevel; } int element = Utils.getElementAsInt(Utils.getElement(getProfileJson(), "leveling.experience"), 0); return skyblockLevel = (element / 100.0); } public boolean skillsApiEnabled() { return Utils.getElementAsLong(Utils.getElement(getProfileJson(), "player_data.experience.SKILL_COMBAT"), -1) != -1; } /** * NOTE: will NOT return null if skills api is disabled, use {@link SkyblockProfile#skillsApiEnabled()} instead * This can still return null if the leveling constant is not up-to-date * * @return Map containing skills, slayers, HOTM, dungeons & dungeon classes */ public Map getLevelingInfo() { if (levelingInfo != null) { return levelingInfo; } JsonObject leveling = Constants.LEVELING; if (leveling == null || !leveling.has("social")) { Utils.showOutdatedRepoNotification("leveling.json or missing social"); return null; } JsonObject profileJson = getProfileJson(); Map out = new HashMap<>(); for (String skillName : skills) { float skillExperience = 0; if (skillName.equals("social")) { // Get the coop's social skill experience since social is a shared skill for (Map.Entry memberProfileJson : Utils .getElement(outerProfileJson, "members") .getAsJsonObject() .entrySet()) { skillExperience += Utils.getElementAsFloat( Utils.getElement(memberProfileJson.getValue(), "player_data.experience.SKILL_SOCIAL"), 0 ); } } else { skillExperience += Utils.getElementAsFloat( Utils.getElement(profileJson, "player_data.experience.SKILL_" + skillName.toUpperCase(Locale.ROOT)), 0 ); } JsonArray levelingArray = Utils.getElement(leveling, "leveling_xp").getAsJsonArray(); if (skillName.equals("runecrafting")) { levelingArray = Utils.getElement(leveling, "runecrafting_xp").getAsJsonArray(); } else if (skillName.equals("social")) { levelingArray = Utils.getElement(leveling, "social").getAsJsonArray(); } int maxLevel = ProfileViewerUtils.getLevelingCap(leveling, skillName); if (skillName.equals("farming")) { maxLevel += Utils.getElementAsInt(Utils.getElement(profileJson, "jacobs_contest.perks.farming_level_cap"), 0); } if (skillName.equals("taming")) { maxLevel += Utils.getElementOrDefault(profileJson, "pets_data.pet_care.pet_types_sacrificed", new JsonArray()).getAsJsonArray().size(); } out.put(skillName, ProfileViewerUtils.getLevel(levelingArray, skillExperience, maxLevel, false)); } out.put( "hotm", ProfileViewerUtils.getLevel( Utils.getElement(leveling, "HOTM").getAsJsonArray(), Utils.getElementAsFloat(Utils.getElement(profileJson, "mining_core.experience"), 0), ProfileViewerUtils.getLevelingCap(leveling, "HOTM"), false ) ); out.put( "catacombs", ProfileViewerUtils.getLevel( Utils.getElement(leveling, "catacombs").getAsJsonArray(), Utils.getElementAsFloat(Utils.getElement(profileJson, "dungeons.dungeon_types.catacombs.experience"), 0), ProfileViewerUtils.getLevelingCap(leveling, "catacombs"), false ) ); out.put( "cosmetic_catacombs", ProfileViewerUtils.getLevel( Utils.getElement(leveling, "catacombs").getAsJsonArray(), Utils.getElementAsFloat(Utils.getElement(profileJson, "dungeons.dungeon_types.catacombs.experience"), 0), 99, false ) ); for (String className : Weight.DUNGEON_CLASS_NAMES) { float classExperience = Utils.getElementAsFloat( Utils.getElement(profileJson, "dungeons.player_classes." + className + ".experience"), 0 ); out.put( className, ProfileViewerUtils.getLevel( Utils.getElement(leveling, "catacombs").getAsJsonArray(), classExperience, ProfileViewerUtils.getLevelingCap(leveling, "catacombs"), false ) ); out.put( "cosmetic_" + className, ProfileViewerUtils.getLevel( Utils.getElement(leveling, "catacombs").getAsJsonArray(), classExperience, 99, false ) ); } for (String slayerName : Weight.SLAYER_NAMES) { float slayerExperience = Utils.getElementAsFloat(Utils.getElement( profileJson, "slayer.slayer_bosses." + slayerName + ".xp" ), 0); out.put( slayerName, ProfileViewerUtils.getLevel( Utils.getElement(leveling, "slayer_xp." + slayerName).getAsJsonArray(), slayerExperience, slayerName.equals("vampire") ? 5 : 9, true ) ); } return levelingInfo = out; } /** * Get the Skyblock XP provided by the bestiary progress for this profile * * @return skyblock xp */ public int getBestiaryXp() { return BestiaryData.calculateBestiarySkyblockXp(getProfileJson()); } public JsonObject getPetsInfo() { if (petsInfo != null) { return petsInfo; } JsonArray petsArray = Utils .getElementOrDefault(getProfileJson(), "pets_data.pets", new JsonArray()) .getAsJsonArray(); if (petsArray.size() > 0) { JsonObject activePet = null; for (JsonElement petEle : petsArray.getAsJsonArray()) { JsonObject petObj = petEle.getAsJsonObject(); if (petObj.has("active") && petObj.get("active").getAsBoolean()) { activePet = petObj; break; } } // TODO: STOP DOING THIS AAAAA petsInfo = new JsonObject(); petsInfo.add("active_pet", activePet); petsInfo.add("pets", petsArray); return petsInfo; } return null; } public ProfileCollectionInfo getCollectionInfo() { if (collectionsInfo != null) { return collectionsInfo; } // TODO: Is this supposed to be async? return collectionsInfo = ProfileCollectionInfo.getCollectionData(outerProfileJson, uuid).getNow(null); } public PlayerStats.Stats getStats() { if (stats != null) { return stats; } if (skillsApiEnabled()) { return null; } return stats = PlayerStats.getStats( getLevelingInfo(), getInventoryInfo(), getPetsInfo(), getProfileJson() ); } public long getNetworth() { if (networth != null) { return networth; } Map inventoryInfo = getInventoryInfo(); JsonObject profileInfo = getProfileJson(); HashMap mostExpensiveInternal = new HashMap<>(); long networth = 0; for (Map.Entry entry : inventoryInfo.entrySet()) { for (JsonElement element : entry.getValue()) { if (element != null && element.isJsonObject()) { JsonObject item = element.getAsJsonObject(); String internalName = item.get("internalname").getAsString(); if (profileViewer.getManager().auctionManager.isVanillaItem(internalName)) { continue; } JsonObject bzInfo = profileViewer.getManager().auctionManager.getBazaarInfo(internalName); long auctionPrice; if (bzInfo != null && bzInfo.has("curr_sell")) { auctionPrice = (int) bzInfo.get("curr_sell").getAsFloat(); } else { auctionPrice = (long) profileViewer.getManager().auctionManager.getItemAvgBin(internalName); if (auctionPrice <= 0) { auctionPrice = profileViewer.getManager().auctionManager.getLowestBin(internalName); } } // Backpack, cake bag, etc try { if (item.has("item_contents")) { JsonArray bytesArr = item.get("item_contents").getAsJsonArray(); byte[] bytes = new byte[bytesArr.size()]; for (int bytesArrI = 0; bytesArrI < bytesArr.size(); bytesArrI++) { bytes[bytesArrI] = bytesArr.get(bytesArrI).getAsByte(); } NBTTagCompound contents_nbt = CompressedStreamTools.readCompressed(new ByteArrayInputStream(bytes)); NBTTagList items = contents_nbt.getTagList("i", 10); for (int j = 0; j < items.tagCount(); j++) { if (items.getCompoundTagAt(j).getKeySet().size() > 0) { NBTTagCompound nbt = items.getCompoundTagAt(j).getCompoundTag("tag"); String internalname2 = profileViewer.getManager().createItemResolutionQuery().withItemNBT(nbt).resolveInternalName(); if (internalname2 != null) { if (profileViewer.getManager().auctionManager.isVanillaItem(internalname2)) continue; JsonObject bzInfo2 = profileViewer.getManager().auctionManager.getBazaarInfo(internalname2); long auctionPrice2; if (bzInfo2 != null && bzInfo2.has("curr_sell")) { auctionPrice2 = (int) bzInfo2.get("curr_sell").getAsFloat(); } else { auctionPrice2 = (long) profileViewer.getManager().auctionManager.getItemAvgBin(internalname2); if (auctionPrice2 <= 0) { auctionPrice2 = profileViewer.getManager().auctionManager.getLowestBin(internalname2); } } int count2 = items.getCompoundTagAt(j).getByte("Count"); mostExpensiveInternal.put( internalname2, auctionPrice2 * count2 + mostExpensiveInternal.getOrDefault(internalname2, 0L) ); networth += auctionPrice2 * count2; } } } } } catch (IOException ignored) { } int count = 1; if (element.getAsJsonObject().has("count")) { count = element.getAsJsonObject().get("count").getAsInt(); } mostExpensiveInternal.put( internalName, auctionPrice * count + mostExpensiveInternal.getOrDefault(internalName, 0L) ); networth += auctionPrice * count; } } } if (networth == 0) return -1; networth = (int) (networth * 1.3f); JsonObject petsInfo = getPetsInfo(); if (petsInfo != null && petsInfo.has("pets")) { if (petsInfo.get("pets").isJsonArray()) { JsonArray pets = petsInfo.get("pets").getAsJsonArray(); for (JsonElement element : pets) { if (element.isJsonObject()) { JsonObject pet = element.getAsJsonObject(); String petname = pet.get("type").getAsString(); String tier = pet.get("tier").getAsString(); String tierNum = GuiProfileViewer.RARITY_TO_NUM.get(tier); if (tierNum != null) { String internalname2 = petname + ";" + tierNum; JsonObject info2 = profileViewer.getManager().auctionManager.getItemAuctionInfo(internalname2); if (info2 == null || !info2.has("price") || !info2.has("count")) continue; int auctionPrice2 = (int) (info2.get("price").getAsFloat() / info2.get("count").getAsFloat()); networth += auctionPrice2; } } } } } float bankBalance = Utils.getElementAsFloat(Utils.getElement(profileInfo, "banking.balance"), 0); float purseBalance = Utils.getElementAsFloat(Utils.getElement(profileInfo, "currencies.coin_purse"), 0); networth += bankBalance + purseBalance; return this.networth = networth; } public SoopyNetworth getSoopyNetworth(Runnable callback) { if (soopyNetworth != null) { return soopyNetworth; } loadSoopyData(callback); return new SoopyNetworth(null).asLoading(); } public MuseumData getMuseumData() { if (museumData != null) { return museumData; } loadMuseumData(); return new MuseumData(null).asLoading(); } public void updateBeastMasterMultiplier() { if (!getUuid().equals(Minecraft.getMinecraft().thePlayer.getUniqueID().toString().replace("-", ""))) return; boolean hasBeastmasterCrest = false; PetInfoOverlay.Rarity currentBeastRarity = PetInfoOverlay.Rarity.COMMON; for (JsonElement talisman : getInventoryInfo().get("talisman_bag")) { if (talisman.isJsonNull()) continue; String internalName = talisman.getAsJsonObject().get("internalname").getAsString(); if (internalName.startsWith("BEASTMASTER_CREST")) { hasBeastmasterCrest = true; try { PetInfoOverlay.Rarity talismanRarity = PetInfoOverlay.Rarity.valueOf(internalName.replace( "BEASTMASTER_CREST_", "" )); if (talismanRarity.beastcreatMultiplyer > currentBeastRarity.beastcreatMultiplyer) currentBeastRarity = talismanRarity; } catch (Exception ignored) { } } } if (hasBeastmasterCrest) { JsonObject stats = getProfileJson().get("player_stats").getAsJsonObject(); int mk = Utils.getElementAsInt(Utils.getElement(stats, "mythos.kills"), 0); float petXpBoost = mk > 10000 ? 1f : mk > 7500 ? 0.9f : mk > 5000 ? 0.8f : mk > 2500 ? 0.7f : mk > 1000 ? 0.6f : mk > 500 ? 0.5f : mk > 250 ? 0.4f : mk > 100 ? 0.3f : mk > 25 ? 0.2f : 0.1f; PetInfoOverlay.getConfig().beastMultiplier = (petXpBoost == 0 ? 0.1f : petXpBoost) * currentBeastRarity.beastcreatMultiplyer; } } public void updateTamingLevel() { if (!getUuid().equals(Minecraft.getMinecraft().thePlayer.getUniqueID().toString().replace("-", ""))) return; if (!getLatestProfile().skillsApiEnabled()) return; if (getLevelingInfo() != null && getLevelingInfo().get("taming") != null) { //idfk what else could cause an NPE here PetInfoOverlay.getConfig().tamingLevel = (int) getLevelingInfo().get("taming").level; } } } }