From 8d655a4008d8fba0ca6f477be0e051924dc67ec6 Mon Sep 17 00:00:00 2001 From: Linnea Gräf Date: Thu, 31 Jul 2025 02:33:01 +0200 Subject: Add some basic models for the profile viewer --- .../skyblock/profileviewer/model/ApiProfile.java | 17 +++ .../profileviewer/model/ApiProfileResponse.java | 10 ++ .../profileviewer/model/CommunityUpgrades.java | 38 ++++++ .../skyblock/profileviewer/model/CoopBanking.java | 28 +++++ .../skyblock/profileviewer/model/Currencies.java | 17 +++ .../profileviewer/model/DefaultCatacombs.java | 21 ++++ .../skyblock/profileviewer/model/Dungeons.java | 58 +++++++++ .../skyblock/profileviewer/model/FairySouls.java | 12 ++ .../profileviewer/model/GenericCatacombs.java | 133 +++++++++++++++++++++ .../profileviewer/model/ProfileMember.java | 21 ++++ .../profileviewer/model/ProfileMemberProfile.java | 16 +++ .../skyblock/profileviewer/model/Slayer.java | 10 ++ .../skyblock/profileviewer/model/SlayerBoss.java | 32 +++++ .../skyblock/profileviewer/model/Treasures.java | 80 +++++++++++++ .../skyblock/profileviewer/model/package-info.java | 11 ++ 15 files changed, 504 insertions(+) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ApiProfile.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ApiProfileResponse.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/CommunityUpgrades.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/CoopBanking.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/Currencies.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/DefaultCatacombs.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/Dungeons.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/FairySouls.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/GenericCatacombs.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ProfileMember.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ProfileMemberProfile.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/Slayer.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/SlayerBoss.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/Treasures.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/package-info.java (limited to 'src') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ApiProfile.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ApiProfile.java new file mode 100644 index 00000000..4864ca52 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ApiProfile.java @@ -0,0 +1,17 @@ +package de.hysky.skyblocker.skyblock.profileviewer.model; + +import com.google.gson.annotations.SerializedName; + +import java.util.Map; +import java.util.UUID; + +public class ApiProfile { + @SerializedName("profile_id") + public UUID profileId = UUID.randomUUID(); + public CommunityUpgrades communityUpgrades = new CommunityUpgrades(); + public Map members = Map.of(); + public CoopBanking banking = new CoopBanking(); + @SerializedName("cute_name") + public String cuteName; + public boolean selected = false; +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ApiProfileResponse.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ApiProfileResponse.java new file mode 100644 index 00000000..18adf3ef --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ApiProfileResponse.java @@ -0,0 +1,10 @@ +package de.hysky.skyblocker.skyblock.profileviewer.model; + +import java.util.List; + +/** + * Object mapping for the success response of {@code /v2/skyblock/profiles}. + */ +public class ApiProfileResponse { + public List profiles = List.of(); +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/CommunityUpgrades.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/CommunityUpgrades.java new file mode 100644 index 00000000..8fe83902 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/CommunityUpgrades.java @@ -0,0 +1,38 @@ +package de.hysky.skyblocker.skyblock.profileviewer.model; + +import com.google.gson.annotations.SerializedName; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +public class CommunityUpgrades { +// @Nullable +// @SerializedName("currently_upgrading") +// public String currentlyUpgrading = null; + @SerializedName("upgrade_states") + public List upgradeStates = List.of(); + + public int getUpgradeTier(String upgradeId) { + int tier = 0; + for (var upgradeState : upgradeStates) { + if (!Objects.equals(upgradeId, upgradeState.upgradeId)) + continue; + tier = Math.max(tier, upgradeState.tier); + } + return tier; + } + + public static class UpgradeState { + @SerializedName("upgrade") + public String upgradeId; + public int tier; + @SerializedName("started_ms") + public long startedMs; + @SerializedName("started_by") + public UUID startedBy; + @SerializedName("claimed_by") + public UUID claimedBy; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/CoopBanking.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/CoopBanking.java new file mode 100644 index 00000000..2d331099 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/CoopBanking.java @@ -0,0 +1,28 @@ +package de.hysky.skyblocker.skyblock.profileviewer.model; + +import com.google.gson.annotations.SerializedName; + +import java.util.List; + +public class CoopBanking { + /** + * @see ProfileMemberProfile#personalBankAccount + */ + public double balance; + public List transactions = List.of(); + + public static class Transaction { + public double amount; + public long timestamp; + public BankAction action; + @SerializedName("initiator_name") + public String initiatorName; + } + + public enum BankAction { + DEPOSIT, + WITHDRAW, + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/Currencies.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/Currencies.java new file mode 100644 index 00000000..f682ff4e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/Currencies.java @@ -0,0 +1,17 @@ +package de.hysky.skyblocker.skyblock.profileviewer.model; + +import com.google.gson.annotations.SerializedName; + +import java.util.Map; + +public class Currencies { + @SerializedName("coin_purse") + public double coinsInPurse; + @SerializedName("motes_purse") + public double riftMotes; + public Map essence = Map.of(); + + public static class EssenceCurrency { + public int current; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/DefaultCatacombs.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/DefaultCatacombs.java new file mode 100644 index 00000000..663b2947 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/DefaultCatacombs.java @@ -0,0 +1,21 @@ +package de.hysky.skyblocker.skyblock.profileviewer.model; + +import com.google.gson.annotations.SerializedName; + +/** + * The default catacombs info contains some extra stats that are shared across master mode and normal mode. + * I am however not entirely sure everything in here is fully shared between the two modes. + */ +public class DefaultCatacombs extends GenericCatacombs { + public double experience; + /** + * Aggregate attempts (including failures) across master mode and regular mode. + */ + @SerializedName("times_played") + public AggregateStat timesPlayed; + + @SerializedName("watcher_kills") + public AggregateStat watcherKills; + + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/Dungeons.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/Dungeons.java new file mode 100644 index 00000000..5b6c16d8 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/Dungeons.java @@ -0,0 +1,58 @@ +package de.hysky.skyblocker.skyblock.profileviewer.model; + +import com.google.gson.annotations.SerializedName; +import de.hysky.skyblocker.skyblock.profileviewer.utils.LevelFinder; +import org.jetbrains.annotations.Nullable; + +import java.time.Instant; +import java.time.LocalDate; +import java.util.Map; + +public class Dungeons { + @SerializedName("last_dungeon_run") + public String lastDungeonRun; + public int secrets; + @SerializedName("selected_dungeon_class") + public String selectedDungeonClass; + @SerializedName("daily_runs") + public DailyRuns dailyRuns = new DailyRuns(); + /** + * Croesus storage data + */ + public Treasures treasures = new Treasures(); + @SerializedName("player_classes") + public Map classStats = Map.of(); + @SerializedName("dungeon_types") + public PerDungeonType dungeonInfo; + + public static class PerDungeonType { + @SerializedName("master_catacombs") + public GenericCatacombs masterModeCatacombs = new GenericCatacombs(); + public DefaultCatacombs catacombs = new DefaultCatacombs(); + } + + public static class ClassStats { + public double experience; + + public LevelFinder.LevelInfo getLevelInfo() { + return LevelFinder.getLevelInfo("Catacombs", (long) experience); + } + } + + public static class DailyRuns { + /** + * This is days since UNIX epoch. + */ + @SerializedName("current_day_stamp") + public int currentDayStamp; + + public @Nullable LocalDate getLastDailyRunDate() { + if (currentDayStamp == 0) + return null; + return LocalDate.ofEpochDay(currentDayStamp); + } + + @SerializedName("completed_runs_count") + public int completedRunsCount; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/FairySouls.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/FairySouls.java new file mode 100644 index 00000000..5efb97e8 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/FairySouls.java @@ -0,0 +1,12 @@ +package de.hysky.skyblocker.skyblock.profileviewer.model; + +import com.google.gson.annotations.SerializedName; + +public class FairySouls { + @SerializedName("fairy_exchanges") + public int fairyExchanges; + @SerializedName("total_collected") + public int totalCollected; + @SerializedName("unspent_souls") + public int unspentSouls; +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/GenericCatacombs.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/GenericCatacombs.java new file mode 100644 index 00000000..90e625de --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/GenericCatacombs.java @@ -0,0 +1,133 @@ +package de.hysky.skyblocker.skyblock.profileviewer.model; + +import com.google.gson.annotations.SerializedName; +import net.fabricmc.loader.api.metadata.Person; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +public class GenericCatacombs { + @SerializedName("best_score") + public PersonalBest bestScore = new PersonalBest(); + @SerializedName("fastest_time") + public PersonalBest fastestTime = new PersonalBest(); + @SerializedName("mobs_killed") + public AggregateStat mobsKilled = new AggregateStat(); + @SerializedName("most_mobs_killed") + public PersonalBest mobsKilledInOneRun = new PersonalBest(); + @SerializedName("most_damage_mage") + public PersonalBest mostMageDamage = new PersonalBest(); + /** + * Is this only for healer role or in general? + */ + @SerializedName("most_healing") + public PersonalBest mostHealing = new PersonalBest(); + + @SerializedName("tier_completions") + public AggregateStat tierCompletions = new AggregateStat(); + /** + * Is this adding the milestones achieved across runs? Definitely a weird metric to track, and then not include any recent update in the API. + */ + @SerializedName("milestone_completions") + public AggregateStat milestoneCompletions = new AggregateStat(); + + @SerializedName("most_damage_tank") + public PersonalBest mostDamageTank = new PersonalBest(); + @SerializedName("fastest_time_s") + public PersonalBest fastestTimeS = new PersonalBest(); + @SerializedName("fastest_time_s_plus") + public PersonalBest fastestTimeSPlus = new PersonalBest(); + @SerializedName("most_damage_archer") + public PersonalBest mostDamageArcher = new PersonalBest(); + @SerializedName("most_damage_berserk") + public PersonalBest mostDamageBeserk = new PersonalBest(); + @SerializedName("most_damage_healer") + public PersonalBest mostDamageHealer = new PersonalBest(); + + @SerializedName("highest_tier_completed") + public int highestTierCompleted; + + /** + * Mapping of 0 indexed floor to a list of "best runs". This might be the run in which one of the {@link PersonalBest personal bests} was achieved, but i am not entirely sure. + */ + @SerializedName("best_runs") + public Map> bestRuns = Map.of(); + + public static class BestRun { + public long timestamp; + @SerializedName("score_exploration") + public int scoreExploration; + @SerializedName("score_speed") + public int scoreSpeed; + @SerializedName("score_skill") + public int scoreSkill; + @SerializedName("score_bonus") + public int scoreBonus; + /** + * One of {@code tank}, {@code healer}, etc. + */ + @SerializedName("dungeon_class") + public String dungeonClass; + public List teammates = List.of(); + @SerializedName("elapsed_time") + public int elapsedTime; + @SerializedName("damage_dealt") + public double damageDealt; + public int deaths; + @SerializedName("mobs_killed") + public int mobsKilled; + @SerializedName("secrets_found") + public int secretsFound; + @SerializedName("damage_mitigated") + public double damageMitigated; + @SerializedName("ally_healing") + public double allyHealing; + } + + public static class PerFloorDisambiguation { + @SerializedName("0") + public @Nullable Double entrance; + @SerializedName("1") + public @Nullable Double one; + @SerializedName("2") + public @Nullable Double two; + @SerializedName("3") + public @Nullable Double three; + @SerializedName("4") + public @Nullable Double four; + @SerializedName("5") + public @Nullable Double five; + @SerializedName("6") + public @Nullable Double six; + @SerializedName("7") + public @Nullable Double seven; + + /** + * @param oneIndexedFloor one indexed floor (F1 = 1), with Entrance = 0. + */ + public @Nullable Double getBest(int oneIndexedFloor) { + return switch (oneIndexedFloor) { + case 0 -> entrance; + case 1 -> one; + case 2 -> two; + case 3 -> three; + case 4 -> four; + case 5 -> five; + case 6 -> six; + case 7 -> seven; + + default -> throw new IllegalStateException("Unexpected floor: " + oneIndexedFloor); + }; + } + } + + public static class PersonalBest extends PerFloorDisambiguation { + public @Nullable Double best; + } + + public static class AggregateStat extends PerFloorDisambiguation { + public @Nullable Double total; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ProfileMember.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ProfileMember.java new file mode 100644 index 00000000..8eb0b41c --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ProfileMember.java @@ -0,0 +1,21 @@ +package de.hysky.skyblocker.skyblock.profileviewer.model; + +import com.google.gson.annotations.SerializedName; + +import java.util.Map; +import java.util.UUID; + +public class ProfileMember { + @SerializedName("player_id") + public UUID playerId; + /** + * Nota bene: this is for item collections, for boss collections you need to manually add up the boss kill counts. + */ + public Map collection = Map.of(); + public Slayer slayer = new Slayer(); + @SerializedName("fairy_soul") + public FairySouls fairySouls = new FairySouls(); + public ProfileMemberProfile profile = new ProfileMemberProfile(); + public Currencies currencies = new Currencies(); + public Dungeons dungeons = new Dungeons(); +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ProfileMemberProfile.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ProfileMemberProfile.java new file mode 100644 index 00000000..51c0cc8b --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/ProfileMemberProfile.java @@ -0,0 +1,16 @@ +package de.hysky.skyblocker.skyblock.profileviewer.model; + +import com.google.gson.annotations.SerializedName; + +public class ProfileMemberProfile { + @SerializedName("first_join") + public long firstJoin; + public int personal_bank_upgrade; + public boolean cookie_buff_active; + // TODO: deletion_notice, coop_invitation + /** + * @see ApiProfile#banking + */ + @SerializedName("bank_account") + public double personalBankAccount; +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/Slayer.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/Slayer.java new file mode 100644 index 00000000..0adb9054 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/Slayer.java @@ -0,0 +1,10 @@ +package de.hysky.skyblocker.skyblock.profileviewer.model; + +import com.google.gson.annotations.SerializedName; + +import java.util.Map; + +public class Slayer { + @SerializedName("slayer_bosses") + public Map slayerBosses = Map.of(); +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/SlayerBoss.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/SlayerBoss.java new file mode 100644 index 00000000..215b6b0e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/SlayerBoss.java @@ -0,0 +1,32 @@ +package de.hysky.skyblocker.skyblock.profileviewer.model; + +import com.google.gson.annotations.SerializedName; + +import java.util.Map; + +public class SlayerBoss { + @SerializedName("claimed_levels") + public Map claimedLevels = Map.of(); + @SerializedName("boss_kills_tier_0") + int bossKills0; + @SerializedName("boss_kills_tier_1") + int bossKills1; + @SerializedName("boss_kills_tier_2") + int bossKills2; + @SerializedName("boss_kills_tier_3") + int bossKills3; + @SerializedName("boss_kills_tier_4") + int bossKills4; + public int xp; + + public int getBossKillsByZeroIndexedTier(int tier) { + return switch (tier) { + case 0 -> bossKills0; + case 1 -> bossKills1; + case 2 -> bossKills2; + case 3 -> bossKills3; + case 4 -> bossKills4; + default -> 0; + }; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/Treasures.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/Treasures.java new file mode 100644 index 00000000..47eb433f --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/Treasures.java @@ -0,0 +1,80 @@ +package de.hysky.skyblocker.skyblock.profileviewer.model; + +import com.google.gson.annotations.SerializedName; + +import java.util.List; +import java.util.UUID; + +public class Treasures { + public List runs = List.of(); + /** + * This is a list of chests. Nota bene: the chests are not grouped by dungeon run, but have a run id that can be used to group them. + */ + public List chests = List.of(); + // TODO: add a method to collate runs and chests + + public static class Chest { + @SerializedName("run_id") + public UUID runId; + @SerializedName("chest_id") + public UUID chestId; + + /** + * The chest type, one of {@code wood}, {@code gold}, {@code diamond}, {@code emerald}, {@code obsidian}, {@code bedrock}. + */ + @SerializedName("treasure_type") + public String treasureType; + + public Rewards rewards = new Rewards(); + + public int quality; + @SerializedName("shiny_eligible") + public boolean shinyEligible; + public boolean paid; + public int rerolls; + } + + public static class Rewards { + /** + * List of rewards found in a chest. These are not item ids directly: + *
    + *
  • Enchanted books in the form of {@code combo_1}, {@code feather_falling_6}
  • + *
  • Enchanted ultimate books in the form of {@code wise_1} (without {@code ultimate})
  • + *
  • Essence in the form of {@code ESSENCE:UNDEAD:22}, {@code ESSENCE:WITHER:30}
  • + *
  • Items also have a completely arbitrary form sometimes: {@code master_jerry} {@code dark_orb_1} (with stack size) {@code shadow_boots} (unstackable without stack size, but with differing id from the nbt id)
  • + *
  • Realistically the only way to maintain a mapping for this is a repo file.
  • + *
+ */ + public List rewards = List.of(); + + @SerializedName("rolled_rng_meter_randomly") + public boolean rolledRngMeterRandomly; + } + + public static class Run { + @SerializedName("run_id") + public UUID runId; + @SerializedName("completion_ts") + public long completionTimestamp; + /** + * {@code "master_catacombs"} or {@link "catacombs"} TODO: CITATION REQUIRED + */ + @SerializedName("dungeon_type") + public String dungeonType; + @SerializedName("dungeon_tier") + public int dungeonTier; + public List participants = List.of(); + } + + public static class Participant { + @SerializedName("player_uuid") + public UUID playerUUID; + /** + * This is formatted as {@code [name]: [role] ([class level])} + some colour codes (probably depending on level). Notably the class level is not a roman numeral, but instead an arabic one. + */ + @SerializedName("display_name") + public String display_name; + @SerializedName("class_milestone") + public int classMilestone; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/package-info.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/package-info.java new file mode 100644 index 00000000..de5cc978 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/model/package-info.java @@ -0,0 +1,11 @@ +/** + * Package containing models for the JSON responses used by the profile viewer. + * {@code class}es are intentionally used instead of records, since specifying defaults for records is more cumbersome. + * This package is {@link org.jetbrains.annotations.NotNullByDefault not null by default}, meaning warnings are emitted + * if a field is not explicitly marked as nullable or has a default value provided. This should be a sufficient safeguard + * against most API deviations (such as missing fields). + */ +@NotNullByDefault +package de.hysky.skyblocker.skyblock.profileviewer.model; + +import org.jetbrains.annotations.NotNullByDefault; -- cgit