├── .github └── workflows │ └── maven.yml ├── .gitignore ├── LICENSE ├── README.md ├── hypixel-api-core ├── pom.xml └── src │ ├── main │ └── java │ │ └── net │ │ └── hypixel │ │ └── api │ │ ├── HypixelAPI.java │ │ ├── adapters │ │ ├── BoostersTypeAdapterFactory.java │ │ ├── CustomizedTypeAdapterFactory.java │ │ ├── DateTimeTypeAdapter.java │ │ ├── GameTypeTypeAdapter.java │ │ ├── PlayerTypeAdapter.java │ │ ├── ServerTypeTypeAdapter.java │ │ └── UUIDTypeAdapter.java │ │ ├── data │ │ └── type │ │ │ ├── GameType.java │ │ │ ├── GuildAchievement.java │ │ │ ├── LobbyType.java │ │ │ └── ServerType.java │ │ ├── exceptions │ │ ├── BadResponseException.java │ │ ├── BadStatusCodeException.java │ │ └── HypixelAPIException.java │ │ ├── http │ │ ├── HTTPQueryParams.java │ │ ├── HypixelHttpClient.java │ │ ├── HypixelHttpResponse.java │ │ └── RateLimit.java │ │ ├── pets │ │ ├── IPetRarity.java │ │ ├── IPetRepository.java │ │ ├── IPetType.java │ │ ├── Pet.java │ │ ├── PetAttribute.java │ │ ├── PetStats.java │ │ ├── PetType.java │ │ └── impl │ │ │ ├── AbstractPetRepositoryImpl.java │ │ │ ├── PetRarityImpl.java │ │ │ ├── PetRepositoryImpl.java │ │ │ ├── PetTypeImpl.java │ │ │ └── compatibility │ │ │ └── BackwardsCompatibilityPetRepositoryImpl.java │ │ ├── reply │ │ ├── AbstractReply.java │ │ ├── BoostersReply.java │ │ ├── CountsReply.java │ │ ├── FindGuildReply.java │ │ ├── GuildReply.java │ │ ├── KeyReply.java │ │ ├── LeaderboardsReply.java │ │ ├── PlayerReply.java │ │ ├── PunishmentStatsReply.java │ │ ├── RateLimitedReply.java │ │ ├── RecentGamesReply.java │ │ ├── ResourceReply.java │ │ ├── StatusReply.java │ │ └── skyblock │ │ │ ├── SkyBlockAuctionsReply.java │ │ │ ├── SkyBlockBazaarReply.java │ │ │ ├── SkyBlockNewsReply.java │ │ │ ├── SkyBlockProfileReply.java │ │ │ ├── SkyBlockProfilesReply.java │ │ │ ├── bingo │ │ │ ├── BingoEventData.java │ │ │ └── SkyBlockBingoDataReply.java │ │ │ └── firesales │ │ │ ├── FireSaleItem.java │ │ │ └── SkyBlockFireSalesReply.java │ │ └── util │ │ ├── Banner.java │ │ ├── IGuildLeveling.java │ │ ├── ILeveling.java │ │ ├── PropertyFilter.java │ │ ├── Rarity.java │ │ ├── ResourceType.java │ │ ├── UnstableHypixelObject.java │ │ └── Utilities.java │ └── test │ └── java │ └── net │ └── hypixel │ └── api │ └── TestResources.java ├── hypixel-api-example ├── README.md ├── pom.xml └── src │ ├── main │ └── java │ │ └── net │ │ └── hypixel │ │ └── api │ │ └── example │ │ ├── ExampleUtil.java │ │ ├── GetBoostersExample.java │ │ ├── GetCountsExample.java │ │ ├── GetGuildExample.java │ │ ├── GetLeaderboardsExample.java │ │ ├── GetPetsExample.java │ │ ├── GetPlayerExample.java │ │ ├── GetPunishmentStatsExample.java │ │ ├── GetRecentGamesExample.java │ │ ├── GetResourceExample.java │ │ ├── GetStatusExample.java │ │ └── skyblock │ │ ├── GetAuctionsExample.java │ │ ├── GetBazaarExample.java │ │ ├── GetBingoDataExample.java │ │ ├── GetFireSalesExample.java │ │ ├── GetNewsExample.java │ │ ├── GetProfileExample.java │ │ └── GetProfilesExample.java │ └── test │ ├── java │ └── net │ │ └── hypixel │ │ └── api │ │ └── example │ │ └── TestAuthenticatedEndpoints.java │ └── resources │ └── junit-platform.properties ├── hypixel-api-transport-apache ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── net │ └── hypixel │ └── api │ └── apache │ └── ApacheHttpClient.java ├── hypixel-api-transport-reactor ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── net │ └── hypixel │ └── api │ └── reactor │ └── ReactorHttpClient.java ├── hypixel-api-transport-unirest ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── net │ └── hypixel │ └── api │ └── unirest │ └── UnirestHttpClient.java └── pom.xml /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: Maven Verify 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Set up JDK 8 15 | uses: actions/setup-java@v4 16 | with: 17 | java-version: '8' 18 | distribution: 'adopt' 19 | - name: Build with Maven 20 | run: mvn -B verify --file pom.xml 21 | env: 22 | HYPIXEL_API_KEY: ${{ secrets.HYPIXEL_API_KEY }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | /API/ 4 | .DS_Store 5 | target 6 | Example/target 7 | Website/javadocs 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Hypixel Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Hypixel Public API (Java) 2 | ====== 3 | [![Maven Package](https://github.com/HypixelDev/PublicAPI/actions/workflows/maven.yml/badge.svg)](https://github.com/HypixelDev/PublicAPI/actions/workflows/maven.yml) 4 | 5 | This is a Java implementation of the Hypixel API. For discussing the API, requesting help or suggestions you can use the 6 | GitHub [Discussions](https://github.com/HypixelDev/PublicAPI/discussions). 7 | 8 | ### Documentation 9 | 10 | Hypixel Public API documentation can be found at [https://api.hypixel.net/](https://api.hypixel.net/). Java 11 | documentation can be found in the code. 12 | 13 | ### GitHub Issues 14 | 15 | GitHub issues should only be used to report bugs. Everything else should either be in GitHub discussions or use the 16 | Hypixel [Code Creations](https://hypixel.net/forums/code-creations.65/) forum. 17 | 18 | ### Usage 19 | 20 | You can use this API as a dependency via the public Hypixel maven repo. You can also use 21 | the [Example Code](https://github.com/HypixelDev/PublicAPI/tree/master/hypixel-api-example) as a good starting point. 22 | 23 | #### Hypixel Maven Repo 24 | 25 | ```xml 26 | 27 | 28 | Hypixel 29 | https://repo.hypixel.net/repository/Hypixel/ 30 | 31 | ``` 32 | 33 | This repo can also be used with Gradle. 34 | 35 | ```gradle 36 | repositories { 37 | maven { url 'https://repo.hypixel.net/repository/Hypixel/' } 38 | } 39 | ``` 40 | 41 | #### Transports 42 | 43 | We include three built-in options for communicating with the Hypixel API, you can include either of these or even 44 | include the core API directly and create your own instance of HypixelHTTPClient. 45 | 46 | * [Apache HttpClient Transport](hypixel-api-transport-apache/README.md) 47 | * [Unirest Java Transport](hypixel-api-transport-unirest/README.md) 48 | * [Project Reactor Transport](hypixel-api-transport-reactor/README.md) (automatic rate-limiting by default) 49 | 50 | ### Dependencies 51 | 52 | The Hypixel API Core implementation has the following dependencies: 53 | 54 | * [Google Gson library - 2.10.1](https://mvnrepository.com/artifact/com.google.code.gson/gson) 55 | 56 | Transports will also have dependencies where required. 57 | 58 | ### Contributing 59 | 60 | When contributing changes to the Java API please provide as much detail on the changes and the reasons for them. We will 61 | not accept changes that have no meaningful contribution to the project. -------------------------------------------------------------------------------- /hypixel-api-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | hypixel-api 7 | net.hypixel 8 | 4.4 9 | 10 | 4.0.0 11 | 12 | hypixel-api-core 13 | 14 | 15 | 1.8 16 | 1.8 17 | 18 | 19 | 20 | 21 | com.google.code.gson 22 | gson 23 | 2.10.1 24 | 25 | 26 | org.junit.jupiter 27 | junit-jupiter 28 | 5.9.3 29 | test 30 | 31 | 32 | com.konghq 33 | unirest-java 34 | 3.14.4 35 | test 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/adapters/BoostersTypeAdapterFactory.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.adapters; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | import net.hypixel.api.reply.BoostersReply; 6 | 7 | public class BoostersTypeAdapterFactory extends CustomizedTypeAdapterFactory { 8 | 9 | public BoostersTypeAdapterFactory(Class customizedClass) { 10 | super(customizedClass); 11 | } 12 | 13 | @Override 14 | protected void afterRead(JsonElement json) { 15 | JsonObject obj = json.getAsJsonObject(); 16 | 17 | JsonElement stackedElement = obj.get("stacked"); 18 | if (stackedElement != null) { 19 | if (stackedElement.isJsonPrimitive()) { 20 | if (stackedElement.getAsJsonPrimitive().isBoolean()) { 21 | obj.addProperty("queuedToStack", stackedElement.getAsJsonPrimitive().getAsBoolean()); 22 | obj.remove("stacked"); 23 | } 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/adapters/CustomizedTypeAdapterFactory.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.adapters; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.TypeAdapter; 6 | import com.google.gson.TypeAdapterFactory; 7 | import com.google.gson.reflect.TypeToken; 8 | import com.google.gson.stream.JsonReader; 9 | import com.google.gson.stream.JsonWriter; 10 | 11 | import java.io.IOException; 12 | 13 | /** 14 | * Credits: https://gist.github.com/dustin-graham/6469150#file-customizedtypeadapterfactory-java 15 | */ 16 | public abstract class CustomizedTypeAdapterFactory implements TypeAdapterFactory { 17 | private final Class customizedClass; 18 | 19 | public CustomizedTypeAdapterFactory(Class customizedClass) { 20 | this.customizedClass = customizedClass; 21 | } 22 | 23 | @SuppressWarnings("unchecked") // we use a runtime check to guarantee that 'C' and 'T' are equal 24 | public final TypeAdapter create(Gson gson, TypeToken type) { 25 | return type.getRawType() == customizedClass ? (TypeAdapter) customizeMyClassAdapter(gson, (TypeToken) type) : null; 26 | } 27 | 28 | private TypeAdapter customizeMyClassAdapter(Gson gson, TypeToken type) { 29 | final TypeAdapter delegate = gson.getDelegateAdapter(this, type); 30 | final TypeAdapter elementAdapter = gson.getAdapter(JsonElement.class); 31 | return new TypeAdapter() { 32 | @Override 33 | public void write(JsonWriter out, C value) throws IOException { 34 | JsonElement tree = delegate.toJsonTree(value); 35 | beforeWrite(value, tree); 36 | elementAdapter.write(out, tree); 37 | } 38 | 39 | @Override 40 | public C read(JsonReader in) throws IOException { 41 | JsonElement tree = elementAdapter.read(in); 42 | afterRead(tree); 43 | return delegate.fromJsonTree(tree); 44 | } 45 | }; 46 | } 47 | 48 | /** 49 | * Override this to muck with {@code toSerialize} before it is written to 50 | * the outgoing JSON stream. 51 | */ 52 | protected void beforeWrite(C source, JsonElement toSerialize) { 53 | } 54 | 55 | /** 56 | * Override this to muck with {@code deserialized} before it parsed into 57 | * the application type. 58 | */ 59 | protected void afterRead(JsonElement deserialized) { 60 | } 61 | } -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/adapters/DateTimeTypeAdapter.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.adapters; 2 | 3 | import com.google.gson.*; 4 | import net.hypixel.api.util.Utilities; 5 | 6 | import java.lang.reflect.Type; 7 | import java.time.ZonedDateTime; 8 | 9 | /** 10 | * Our dates are always saved as a timestamp 11 | * if we diverge from that path we can adapt 12 | * it in here as well by just using some more 13 | * parsing. 14 | */ 15 | public class DateTimeTypeAdapter implements JsonDeserializer, JsonSerializer { 16 | 17 | @Override 18 | public JsonElement serialize(ZonedDateTime src, Type typeOfSrc, JsonSerializationContext context) { 19 | return new JsonPrimitive(src.toInstant().toEpochMilli()); 20 | } 21 | 22 | @Override 23 | public ZonedDateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 24 | return Utilities.getDateTime(Long.parseLong(json.getAsString())); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/adapters/GameTypeTypeAdapter.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.adapters; 2 | 3 | import com.google.gson.*; 4 | import net.hypixel.api.data.type.GameType; 5 | 6 | import java.lang.reflect.Type; 7 | 8 | /** 9 | * We need this adapter because we note GameTypes 10 | * as both the id and as it's enum name 11 | */ 12 | public class GameTypeTypeAdapter implements JsonDeserializer, JsonSerializer { 13 | 14 | @Override 15 | public JsonElement serialize(GameType src, Type typeOfSrc, JsonSerializationContext context) { 16 | return new JsonPrimitive(src.toString()); 17 | } 18 | 19 | @Override 20 | public GameType deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 21 | if (json.isJsonPrimitive() && json.getAsJsonPrimitive().isNumber()) { 22 | return GameType.fromId(json.getAsInt()); 23 | } 24 | 25 | String raw = json.getAsString(); 26 | try { 27 | return GameType.fromId(Integer.parseInt(raw)); 28 | } catch (NumberFormatException ignored) { 29 | } 30 | 31 | try { 32 | return GameType.valueOf(raw); 33 | } catch (IllegalArgumentException ignored) { 34 | } 35 | 36 | return null; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/adapters/PlayerTypeAdapter.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.adapters; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.TypeAdapter; 6 | import com.google.gson.stream.JsonReader; 7 | import com.google.gson.stream.JsonToken; 8 | import com.google.gson.stream.JsonWriter; 9 | import net.hypixel.api.reply.PlayerReply.Player; 10 | 11 | import java.io.IOException; 12 | 13 | public class PlayerTypeAdapter extends TypeAdapter { 14 | 15 | private final TypeAdapter defaultAdapter; 16 | 17 | public PlayerTypeAdapter() { 18 | defaultAdapter = new Gson().getAdapter(JsonElement.class); 19 | } 20 | 21 | @Override 22 | public void write(JsonWriter out, Player value) throws IOException { 23 | defaultAdapter.write(out, value.getRaw()); 24 | } 25 | 26 | @Override 27 | public Player read(JsonReader in) throws IOException { 28 | JsonToken type = in.peek(); 29 | if (type == JsonToken.NULL) { 30 | in.nextNull(); 31 | return new Player(null); 32 | } 33 | return new Player(defaultAdapter.read(in)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/adapters/ServerTypeTypeAdapter.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.adapters; 2 | 3 | import com.google.gson.*; 4 | import net.hypixel.api.data.type.GameType; 5 | import net.hypixel.api.data.type.ServerType; 6 | 7 | import java.lang.reflect.Type; 8 | 9 | public class ServerTypeTypeAdapter implements JsonDeserializer, JsonSerializer { 10 | 11 | @Override 12 | public JsonElement serialize(ServerType src, Type typeOfSrc, JsonSerializationContext context) { 13 | return new JsonPrimitive(src.name()); 14 | } 15 | 16 | @Override 17 | public ServerType deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 18 | String raw = json.getAsString(); 19 | try { 20 | return GameType.fromId(Integer.parseInt(raw)); 21 | } catch (NumberFormatException ignored) { 22 | } 23 | return ServerType.valueOf(raw); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/adapters/UUIDTypeAdapter.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.adapters; 2 | 3 | import com.google.gson.*; 4 | import net.hypixel.api.util.Utilities; 5 | 6 | import java.lang.reflect.Type; 7 | import java.util.UUID; 8 | 9 | public class UUIDTypeAdapter implements JsonDeserializer, JsonSerializer { 10 | 11 | @Override 12 | public JsonElement serialize(UUID src, Type typeOfSrc, JsonSerializationContext context) { 13 | return new JsonPrimitive(src.toString()); 14 | } 15 | 16 | @Override 17 | public UUID deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 18 | return Utilities.uuidFromString(json.getAsString()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/data/type/GameType.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.data.type; 2 | 3 | public enum GameType implements ServerType { 4 | QUAKECRAFT("Quakecraft", "Quake", 2), 5 | WALLS("Walls", "Walls", 3), 6 | PAINTBALL("Paintball", "Paintball", 4), 7 | SURVIVAL_GAMES("Blitz Survival Games", "HungerGames", 5), 8 | TNTGAMES("The TNT Games", "TNTGames", 6), 9 | VAMPIREZ("VampireZ", "VampireZ", 7), 10 | WALLS3("Mega Walls", "Walls3", 13), 11 | ARCADE("Arcade", "Arcade", 14), 12 | ARENA("Arena Brawl", "Arena", 17), 13 | MCGO("Cops and Crims", "MCGO", 21), 14 | UHC("UHC Champions", "UHC", 20), 15 | BATTLEGROUND("Warlords", "Battleground", 23), 16 | SUPER_SMASH("Smash Heroes", "SuperSmash", 24), 17 | GINGERBREAD("Turbo Kart Racers", "GingerBread", 25), 18 | HOUSING("Housing", "Housing", 26), 19 | SKYWARS("SkyWars", "SkyWars", 51), 20 | TRUE_COMBAT("Crazy Walls", "TrueCombat", 52), 21 | SPEED_UHC("Speed UHC", "SpeedUHC", 54), 22 | SKYCLASH("SkyClash", "SkyClash", 55), 23 | LEGACY("Classic Games", "Legacy", 56), 24 | PROTOTYPE("Prototype", "Prototype", 57), 25 | BEDWARS("Bed Wars", "Bedwars", 58), 26 | MURDER_MYSTERY("Murder Mystery", "MurderMystery", 59), 27 | BUILD_BATTLE("Build Battle", "BuildBattle", 60), 28 | DUELS("Duels", "Duels", 61), 29 | SKYBLOCK("SkyBlock", "SkyBlock", 63), 30 | PIT("Pit", "Pit", 64), 31 | REPLAY("Replay", "Replay", 65), 32 | SMP("SMP", "SMP", 67), 33 | WOOL_GAMES("Wool Wars", "WoolGames", 68), 34 | ; 35 | 36 | private static final GameType[] VALUES = values(); 37 | 38 | private final String name, dbName; 39 | private final int id; 40 | 41 | GameType(String name, String dbName, int id) { 42 | this.name = name; 43 | this.dbName = dbName; 44 | this.id = id; 45 | } 46 | 47 | /** 48 | * @param id The internal id 49 | * @return The GameType associated with that id, or null if there isn't one. 50 | */ 51 | public static GameType fromId(int id) { 52 | for (GameType gameType : VALUES) { 53 | if (gameType.id == id) { 54 | return gameType; 55 | } 56 | } 57 | return null; 58 | } 59 | 60 | /** 61 | * @param dbName The key used in the database 62 | * @return The GameType associated with that key, or null if there isn't one. 63 | */ 64 | public static GameType fromDatabase(String dbName) { 65 | for (GameType gameType : VALUES) { 66 | if (gameType.dbName.equals(dbName)) { 67 | return gameType; 68 | } 69 | } 70 | return null; 71 | } 72 | 73 | /** 74 | * Exposing this method allows people to use the array without cloning. 75 | * Slightly faster but not as safe since the array could be modified. 76 | */ 77 | public static GameType[] getValues() { 78 | return VALUES; 79 | } 80 | 81 | /** 82 | * @return The official name of the GameType 83 | */ 84 | @Override 85 | public String getName() { 86 | return name; 87 | } 88 | 89 | /** 90 | * @return The internal ID that is occasionally used in various database schemas 91 | */ 92 | public int getId() { 93 | return id; 94 | } 95 | 96 | public String getDbName() { 97 | return dbName; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/data/type/GuildAchievement.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.data.type; 2 | 3 | /** 4 | * Types of achievements that can be earned by guilds on Hypixel. 5 | *

6 | * Created using slothpixel/hypixelconstants 7 | * as a reference. 8 | */ 9 | public enum GuildAchievement { 10 | /** 11 | * A tiered achievement based on the highest amount of experience earned by a guild in a single 12 | * day. 13 | */ 14 | EXPERIENCE_KINGS, 15 | 16 | /** 17 | * A tiered achievement based on the highest number of members a guild has had online at the 18 | * same time. 19 | */ 20 | ONLINE_PLAYERS, 21 | 22 | /** 23 | * A tiered achievement based on a guild's highest level. 24 | */ 25 | PRESTIGE, 26 | 27 | /** 28 | * A tiered achievement based on the highest number of combined wins (in mini-games) between 29 | * members of a guild on the same day. 30 | */ 31 | WINNERS 32 | } 33 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/data/type/LobbyType.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.data.type; 2 | 3 | /** 4 | * A LobbyType is used for lobbies which do not have a gametype linked. 5 | */ 6 | public enum LobbyType implements ServerType { 7 | MAIN("Main Lobby"), 8 | TOURNAMENT("Tournament Hall"), 9 | ; 10 | 11 | private static final LobbyType[] VALUES = values(); 12 | 13 | private final String name; 14 | 15 | LobbyType(String name) { 16 | this.name = name; 17 | } 18 | 19 | /** 20 | * Exposing this method allows people to use the array without cloning. 21 | * Slightly faster but not as safe since the array could be modified. 22 | */ 23 | public static LobbyType[] getValues() { 24 | return VALUES; 25 | } 26 | 27 | @Override 28 | public String getName() { 29 | return name; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/data/type/ServerType.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.data.type; 2 | 3 | public interface ServerType { 4 | 5 | String name(); 6 | 7 | String getName(); 8 | 9 | static ServerType valueOf(String value) { 10 | try { 11 | return GameType.valueOf(value); 12 | } catch (IllegalArgumentException e) { 13 | // ignored 14 | } 15 | 16 | try { 17 | return LobbyType.valueOf(value); 18 | } catch (IllegalArgumentException e) { 19 | // ignored 20 | } 21 | 22 | return null; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/exceptions/BadResponseException.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.exceptions; 2 | 3 | public class BadResponseException extends HypixelAPIException { 4 | 5 | public BadResponseException(String responseCause) { 6 | super(responseCause); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/exceptions/BadStatusCodeException.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.exceptions; 2 | 3 | public class BadStatusCodeException extends HypixelAPIException { 4 | private final int statusCode; 5 | private final String responseCause; 6 | 7 | public BadStatusCodeException(int statusCode, String responseCause) { 8 | super("Got a bad status code " + statusCode + ", cause \"" + responseCause + "\""); 9 | this.statusCode = statusCode; 10 | this.responseCause = responseCause; 11 | } 12 | 13 | public int getStatusCode() { 14 | return statusCode; 15 | } 16 | 17 | public String getResponseCause() { 18 | return responseCause; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/exceptions/HypixelAPIException.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.exceptions; 2 | 3 | public abstract class HypixelAPIException extends RuntimeException { 4 | 5 | protected HypixelAPIException(String message) { 6 | super(message); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/http/HTTPQueryParams.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.http; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class HTTPQueryParams { 7 | 8 | public static HTTPQueryParams create() { 9 | return new HTTPQueryParams(); 10 | } 11 | 12 | private final Map params = new HashMap<>(); 13 | 14 | private HTTPQueryParams() { 15 | 16 | } 17 | 18 | public HTTPQueryParams add(String key, Object value) { 19 | this.params.put(key, value); 20 | return this; 21 | } 22 | 23 | public String getAsQueryString(String base) { 24 | StringBuilder url = new StringBuilder(base); 25 | boolean startedQuery = false; 26 | 27 | for (Map.Entry entry : params.entrySet()) { 28 | if (!startedQuery) { 29 | startedQuery = true; 30 | url.append("?"); 31 | } else { 32 | url.append("&"); 33 | } 34 | 35 | url.append(entry.getKey()).append("=").append(entry.getValue()); 36 | } 37 | 38 | return url.toString(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/http/HypixelHttpClient.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.http; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | 5 | public interface HypixelHttpClient { 6 | 7 | String DEFAULT_USER_AGENT = "Hypixel PublicAPI/4.0"; 8 | 9 | CompletableFuture makeRequest(String url); 10 | 11 | CompletableFuture makeAuthenticatedRequest(String url); 12 | 13 | void shutdown(); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/http/HypixelHttpResponse.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.http; 2 | 3 | public class HypixelHttpResponse { 4 | 5 | private final int statusCode; 6 | private final String body; 7 | private final RateLimit rateLimit; 8 | 9 | @Deprecated 10 | public HypixelHttpResponse(int statusCode, String body) { 11 | this(statusCode, body, null); 12 | } 13 | 14 | public HypixelHttpResponse(int statusCode, String body, RateLimit rateLimit) { 15 | this.statusCode = statusCode; 16 | this.body = body; 17 | this.rateLimit = rateLimit; 18 | } 19 | 20 | public int getStatusCode() { 21 | return statusCode; 22 | } 23 | 24 | public String getBody() { 25 | return body; 26 | } 27 | 28 | public RateLimit getRateLimit() { 29 | return rateLimit; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/http/RateLimit.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.http; 2 | 3 | import java.util.Date; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | public class RateLimit { 7 | private final int limit; 8 | private final int remaining; 9 | private final int reset; 10 | private final Date resetAt; 11 | 12 | public RateLimit(int limit, int remaining, int reset) { 13 | this.limit = limit; 14 | this.remaining = remaining; 15 | this.reset = reset; 16 | this.resetAt = new Date(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(reset)); 17 | } 18 | 19 | /** 20 | * @return the total limit allowed for the used API key per interval 21 | */ 22 | public int getLimit() { 23 | return limit; 24 | } 25 | 26 | /** 27 | * @return the remaining amount of requests for the used API key during this interval 28 | */ 29 | public int getRemaining() { 30 | return remaining; 31 | } 32 | 33 | /** 34 | * @return the time in seconds until the limit interval resets 35 | */ 36 | public int getReset() { 37 | return reset; 38 | } 39 | 40 | /** 41 | * @return the date at which time the limit interval resets, this date won't be accurate to the millisecond due to 42 | * the only context being in seconds 43 | */ 44 | public Date getResetAt() { 45 | return resetAt; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "RateLimit{" + 51 | "limit=" + limit + 52 | ", remaining=" + remaining + 53 | ", reset=" + reset + 54 | ", resetAt=" + resetAt + 55 | '}'; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/pets/IPetRarity.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.pets; 2 | 3 | public interface IPetRarity { 4 | 5 | /** 6 | * Retrieves the name of this pet rarity. 7 | * This is only for pets and should not be mistaken with {@link net.hypixel.api.util.Rarity}. 8 | * Even though they currently are the same values, this might change in the future and should be used accordingly. 9 | * 10 | * @return the name of this rarity 11 | */ 12 | String getName(); 13 | 14 | /** 15 | * Retrieves the color of this pet rarity 16 | * This is only for pets and should not be mistaken with {@link net.hypixel.api.util.Rarity}. 17 | * Even though they currently are the same values, this might change in the future and should be used accordingly. 18 | * 19 | * @return the color of this rarity 20 | */ 21 | String getColor(); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/pets/IPetRepository.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.pets; 2 | 3 | import net.hypixel.api.reply.PlayerReply.Player; 4 | 5 | import java.util.Collection; 6 | 7 | public interface IPetRepository { 8 | 9 | /** 10 | * Gets the pet type associated with the provided key parameter 11 | * A pet type is an object holding the pet's key, name, rarity and package. 12 | * 13 | * @param key the key of the pet 14 | * @return {@code null} if no pet type matches the key, otherwise the pet type associated with that key 15 | */ 16 | IPetType getTypeByKey(String key); 17 | 18 | /** 19 | * Gets the pet type associated with the provided package parameter 20 | * A pet type is an object holding the pet's key, name, rarity and package. 21 | * 22 | * @param typePackage the package of the pet 23 | * @return {@code null} if using 24 | * {@link net.hypixel.api.pets.impl.compatibility.BackwardsCompatibilityPetRepositoryImpl} or if no pet type 25 | * matches the package, otherwise the pet type associated with that package 26 | */ 27 | IPetType getTypeByPackage(String typePackage); 28 | 29 | /** 30 | * Lists all pets matching the given {@link IPetRarity} 31 | * 32 | * @param rarity The rarity of the pets 33 | * @return A collection (usually a {@link java.util.Set}) that contains all matched pets. If no pets are found, 34 | * this returns an empty collection. 35 | */ 36 | Collection getTypesForRarity(IPetRarity rarity); 37 | 38 | /** 39 | * Gets the pet rarity matching the provided name 40 | * 41 | * @param name the name of the rarity 42 | * @return {@code null} if no rarity matches the provided name, otherwise returns the matched rarity 43 | */ 44 | IPetRarity getRarityByName(String name); 45 | 46 | /** 47 | * Gets if a player has unlocked the specified {@link IPetType} 48 | * 49 | * @param type the pet type the player must have 50 | * @param player the player to check against 51 | * @return {@code true} if the player has unlocked the pet, otherwise {@code false} 52 | */ 53 | boolean hasPlayerUnlocked(IPetType type, Player player); 54 | 55 | /** 56 | * @return a collection of all the cached rarities 57 | */ 58 | Collection getRarities(); 59 | 60 | /** 61 | * @return a collection of all the cached pet types 62 | */ 63 | Collection getTypes(); 64 | 65 | } 66 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/pets/IPetType.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.pets; 2 | 3 | public interface IPetType { 4 | 5 | /** 6 | * @return the key of this pet type 7 | */ 8 | String getKey(); 9 | 10 | /** 11 | * @return the name of this pet type 12 | */ 13 | String getName(); 14 | 15 | /** 16 | * Note that the rarity can be {@code null} 17 | * 18 | * @return the rarity of this pet type 19 | */ 20 | IPetRarity getRarity(); 21 | 22 | /** 23 | * Note that the package is always {@code null} when using the backwards-compatible repository. 24 | * 25 | * @return the package of this pet type 26 | */ 27 | String getPackage(); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/pets/Pet.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.pets; 2 | 3 | import java.util.Map; 4 | 5 | public class Pet { 6 | 7 | private static final int[] EXPERIENCE_LEVELS = { 8 | 200, 210, 230, 250, 280, 310, 350, 390, 450, 500, 570, 640, 710, 800, 880, 980, 1080, 1190, 1300, 1420, 9 | 1540, 1670, 1810, 1950, 2100, 2260, 2420, 2580, 2760, 2940, 3120, 3310, 3510, 3710, 3920, 4140, 4360, 4590, 10 | 4820, 5060, 5310, 5560, 5820, 6090, 6360, 6630, 6920, 7210, 7500, 7800, 8110, 8420, 8740, 9070, 9400, 9740, 11 | 10080, 10430, 10780, 11150, 11510, 11890, 12270, 12650, 13050, 13440, 13850, 14260, 14680, 15100, 15530, 12 | 15960, 16400, 16850, 17300, 17760, 18230, 18700, 19180, 19660, 20150, 20640, 21150, 21650, 22170, 22690, 13 | 23210, 23750, 24280, 24830, 25380, 25930, 26500, 27070, 27640, 28220, 28810, 29400, 30000 14 | }; 15 | 16 | private final Map stats; 17 | private int level; 18 | private int experience; 19 | private String name; 20 | 21 | public Pet(Map stats) { 22 | this.stats = stats; 23 | 24 | if (stats != null) { 25 | if (stats.get("experience") != null) { 26 | experience = ((Number) stats.get("experience")).intValue(); 27 | } 28 | if (stats.get("name") != null) { 29 | name = (String) stats.get("name"); 30 | } 31 | } 32 | 33 | updateLevel(); 34 | } 35 | 36 | /** 37 | * Gets the custom name of the pet, if present 38 | * 39 | * @return {@code null} if no custom name has been set for the pet, otherwise returns the custom name 40 | */ 41 | public String getName() { 42 | return name; 43 | } 44 | 45 | /** 46 | * Gets the average happiness, what we mean by "average happiness" is the mean value of all the {@link PetAttribute} 47 | * 48 | * @return the average happiness, with a min value of {@code 0.0} 49 | */ 50 | public double getAverageHappiness() { 51 | double attributeAverage = 0; 52 | for (PetAttribute attribute : PetAttribute.values()) { 53 | attributeAverage += getAttribute(attribute); 54 | } 55 | 56 | return attributeAverage / PetAttribute.values().length; 57 | } 58 | 59 | /** 60 | * Gets the value associated with the attribute 61 | * 62 | * @param attribute the attribute 63 | * @return the value associated with the value, or {@code 1} if not found or not a number 64 | */ 65 | public int getAttribute(PetAttribute attribute) { 66 | @SuppressWarnings("unchecked") 67 | Map attributeObject = (Map) stats.get(attribute.name()); 68 | 69 | if (attributeObject == null) { 70 | return 1; 71 | } 72 | 73 | Object timestampObj = attributeObject.get("timestamp"); 74 | Object valueObj = attributeObject.get("value"); 75 | if (!(timestampObj instanceof Number) || !(valueObj instanceof Number)) { 76 | return 1; 77 | } 78 | 79 | long currentTime = System.currentTimeMillis(); 80 | long timestamp = ((Number) timestampObj).longValue(); 81 | int value = ((Number) valueObj).intValue(); 82 | 83 | long timeElapsed = currentTime - timestamp; 84 | long minutesPassed = timeElapsed / (1000 * 60); 85 | long iterations = (long) Math.floor(minutesPassed / 5); 86 | 87 | return Math.max(0, Math.round(value - iterations * attribute.getDecay())); 88 | } 89 | 90 | /** 91 | * Updates the cached level based on the experience 92 | * 93 | * @return {@code false} no matter what 94 | */ 95 | public boolean updateLevel() { 96 | int xp = experience; 97 | int level = 1; 98 | for (int xpLevel : EXPERIENCE_LEVELS) { 99 | if (xp < xpLevel) { 100 | break; 101 | } else { 102 | xp -= xpLevel; 103 | level++; 104 | } 105 | } 106 | this.level = level; 107 | return false; 108 | } 109 | 110 | /** 111 | * Gets the level of this pet 112 | * 113 | * @return the level 114 | */ 115 | public int getLevel() { 116 | return level; 117 | } 118 | 119 | /** 120 | * Gets the experience required to level up to the provided level 121 | * 122 | * @param level the target level 123 | * @return the experience amount required to reach the provided level 124 | */ 125 | public int getExperienceUntilLevel(int level) { 126 | int xp = 0; 127 | for (int i = 0; i < Math.min(level - 1, 100); i++) { 128 | xp += EXPERIENCE_LEVELS[i]; 129 | } 130 | return xp; 131 | } 132 | 133 | /** 134 | * Gets the experience amount starting from the current level 135 | * 136 | * @return the current experience starting from the current level 137 | */ 138 | public int getLevelProgress() { 139 | return experience - getExperienceUntilLevel(level); 140 | } 141 | 142 | @Override 143 | public String toString() { 144 | return "Pet{" + 145 | "stats=" + stats + 146 | ", level=" + level + 147 | ", experience=" + experience + 148 | ", name='" + name + '\'' + 149 | '}'; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/pets/PetAttribute.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.pets; 2 | 3 | public enum PetAttribute { 4 | 5 | HUNGER, 6 | THIRST, 7 | EXERCISE; 8 | 9 | public int getDecay() { 10 | return 1; 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/pets/PetStats.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.pets; 2 | 3 | import net.hypixel.api.pets.impl.compatibility.BackwardsCompatibilityPetRepositoryImpl; 4 | import net.hypixel.api.reply.PlayerReply; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public class PetStats { 10 | 11 | private final Map petMap = new HashMap<>(); 12 | 13 | @Deprecated 14 | public PetStats(Map> petStats) { 15 | this(BackwardsCompatibilityPetRepositoryImpl.INSTANCE, petStats); 16 | } 17 | 18 | public PetStats(IPetRepository petRepository, Map> petStats) { 19 | for (Map.Entry> stringMapEntry : petStats.entrySet()) { 20 | try { 21 | petMap.put(petRepository.getTypeByKey(stringMapEntry.getKey()), new Pet(stringMapEntry.getValue())); 22 | } catch (IllegalArgumentException e) { 23 | System.out.println("Invalid pet! " + stringMapEntry.getKey()); 24 | } 25 | } 26 | } 27 | 28 | /** 29 | * Gets a specific pet based on its pet type. Each player can only have one pet per type 30 | * 31 | * Note: If this returns {@code null}, it doesn't mean that the player hasn't unlocked the pet. 32 | * What it does mean though, is that the player hasn't given any attributes nor a name to the pet 33 | * 34 | * To check if a player has unlocked a pet, use {@link IPetRepository#hasPlayerUnlocked(IPetType, PlayerReply.Player)} 35 | * 36 | * @param type the pet type to retrieve 37 | * @return {@code null} if the player hasn't given a name and hasn't given any {@link PetAttribute}, 38 | * otherwise the {@link Pet} instance 39 | */ 40 | public Pet getPet(IPetType type) { 41 | return petMap.get(type); 42 | } 43 | 44 | /** 45 | * Lists all the pets the player have 46 | * 47 | * @return a map filled with all the pets the player have 48 | * @deprecated Use {@link #listAllPets()} instead 49 | */ 50 | @Deprecated 51 | public Map getAllPets() { 52 | Map oldPets = new HashMap<>(); 53 | 54 | for (Map.Entry entry : petMap.entrySet()) { 55 | if (!(entry.getKey() instanceof PetType)) { 56 | oldPets.clear(); 57 | throw new IllegalStateException("Cannot use #getAllPets when using the new pet repository. Please use #listAllPets"); 58 | } 59 | 60 | oldPets.put((PetType) entry.getKey(), entry.getValue()); 61 | } 62 | 63 | return oldPets; 64 | } 65 | 66 | /** 67 | * Lists all the pets the player have 68 | * 69 | * @return a map filled with all the pets the player have 70 | */ 71 | public Map listAllPets() { 72 | return petMap; 73 | } 74 | 75 | @Override 76 | public String toString() { 77 | return "PetStats{" + 78 | "petMap=" + petMap + 79 | '}'; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/pets/PetType.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.pets; 2 | 3 | import net.hypixel.api.util.Rarity; 4 | 5 | /** 6 | * The old and deprecated enum for the pet types 7 | * @deprecated Consider using the {@link IPetRepository} 8 | */ 9 | @Deprecated 10 | public enum PetType implements IPetType { 11 | 12 | CAT_BLACK("Cat: Black", Rarity.COMMON), 13 | CAT_RED("Cat: Red", Rarity.COMMON), 14 | CAT_SIAMESE("Cat: Siamese", Rarity.COMMON), 15 | SILVERFISH("Silverfish", Rarity.COMMON), 16 | ZOMBIE("Zombie", Rarity.COMMON), 17 | PIG("Pig", Rarity.COMMON), 18 | COW("Cow", Rarity.COMMON), 19 | WOLF("Wolf", Rarity.COMMON), 20 | CHICKEN("Chicken", Rarity.COMMON), 21 | HORSE_BROWN("Horse: Brown", Rarity.COMMON), 22 | SHEEP_WHITE("Sheep: White", Rarity.COMMON), 23 | SHEEP_GRAY("Sheep: Gray", Rarity.COMMON), 24 | SHEEP_BROWN("Sheep: Brown", Rarity.COMMON), 25 | SHEEP_SILVER("Sheep: Silver", Rarity.COMMON), 26 | 27 | HORSE_CREAMY("Horse: Creamy", Rarity.RARE), 28 | HORSE_CHESTNUT("Horse: Chestnut", Rarity.RARE), 29 | HORSE_DARK_BROWN("Horse: Dark Brown", Rarity.RARE), 30 | HORSE_GREY("Horse: Gray", Rarity.RARE), 31 | DONKEY("Donkey", Rarity.RARE), 32 | MULE("Mule", Rarity.RARE), 33 | VILLAGER_FARMER("Villager: Farmer", Rarity.RARE), 34 | VILLAGER_LIBRARIAN("Villager: Librarian", Rarity.RARE), 35 | VILLAGER_PRIEST("Villager: Priest", Rarity.RARE), 36 | VILLAGER_BLACKSMITH("Villager: Blacksmith", Rarity.RARE), 37 | VILLAGER_BUTCHER("Villager: Butcher", Rarity.RARE), 38 | SHEEP_ORANGE("Sheep: Orange", Rarity.RARE), 39 | SHEEP_MAGENTA("Sheep: Magenta", Rarity.RARE), 40 | SHEEP_LIGHT_BLUE("Sheep: Light Blue", Rarity.RARE), 41 | SHEEP_YELLOW("Sheep: Yellow", Rarity.RARE), 42 | SHEEP_LIME("Sheep: Lime", Rarity.RARE), 43 | SHEEP_CYAN("Sheep: Cyan", Rarity.RARE), 44 | SHEEP_PURPLE("Sheep: Purple", Rarity.RARE), 45 | SHEEP_BLUE("Sheep: Blue", Rarity.RARE), 46 | SHEEP_GREEN("Sheep: Green", Rarity.RARE), 47 | SHEEP_RED("Sheep: Red", Rarity.RARE), 48 | CAVE_SPIDER("Cave Spider", Rarity.RARE), 49 | SLIME_TINY("Slime (Tiny)", Rarity.RARE), 50 | MAGMA_CUBE_TINY("Magma Cube (Tiny)", Rarity.EPIC), 51 | ZOMBIE_BABY("Zombie (Baby)", Rarity.RARE), 52 | PIG_BABY("Pig (Baby)", Rarity.RARE), 53 | COW_BABY("Cow (Baby)", Rarity.RARE), 54 | WOLF_BABY("Wolf (Baby)", Rarity.RARE), 55 | CHICKEN_BABY("Chicken (Baby)", Rarity.RARE), 56 | CAT_BLACK_BABY("Cat: Black (Baby)", Rarity.RARE), 57 | CAT_RED_BABY("Cat: Red (Baby)", Rarity.RARE), 58 | CAT_SIAMESE_BABY("Cat: Siamese (Baby)", Rarity.RARE), 59 | BROWN_HORSE_BABY("Horse: Brown (Baby)", Rarity.RARE), 60 | SHEEP_WHITE_BABY("Sheep: White (Baby)", Rarity.RARE), 61 | SHEEP_GRAY_BABY("Sheep: Gray (Baby)", Rarity.RARE), 62 | SHEEP_BROWN_BABY("Sheep: Brown (Baby)", Rarity.RARE), 63 | SHEEP_SILVER_BABY("Sheep: Silver (Baby)", Rarity.RARE), 64 | 65 | SPIDER("Spider", Rarity.EPIC), 66 | SKELETON("Skeleton", Rarity.EPIC), 67 | HORSE_WHITE("Horse: White", Rarity.EPIC), 68 | HORSE_BLACK("Horse: Black", Rarity.EPIC), 69 | HORSE_UNDEAD("Undead Horse", Rarity.EPIC), 70 | ZOMBIE_VILLAGER("Villager: Zombie", Rarity.EPIC), 71 | PIG_ZOMBIE("Pig Zombie", Rarity.EPIC), 72 | SHEEP_BLACK("Sheep: Black", Rarity.EPIC), 73 | SHEEP_PINK("Sheep: Pink", Rarity.EPIC), 74 | BAT("Bat", Rarity.EPIC), 75 | SLIME_SMALL("Slime (Small)", Rarity.EPIC), 76 | MAGMA_CUBE_SMALL("Magma Cube (Small)", Rarity.EPIC), 77 | MOOSHROOM("Mooshroom", Rarity.EPIC), 78 | CREEPER("Creeper", Rarity.EPIC), 79 | HORSE_CREAMY_BABY("Horse: Creamy (Baby)", Rarity.EPIC), 80 | HORSE_CHESTNUT_BABY("Horse: Chestnut (Baby)", Rarity.EPIC), 81 | HORSE_DARK_BROWN_BABY("Horse: Dark Brown (Baby)", Rarity.EPIC), 82 | HORSE_GRAY_BABY("Horse: Gray (Baby)", Rarity.EPIC), 83 | VILLAGER_FARMER_BABY("Villager: Farmer (Baby)", Rarity.EPIC), 84 | VILLAGER_LIBRARIAN_BABY("Villager: Librarian (Baby)", Rarity.EPIC), 85 | VILLAGER_PRIEST_BABY("Villager: Priest (Baby)", Rarity.EPIC), 86 | VILLAGER_BLACKSMITH_BABY("Villager: Blacksmith (Baby)", Rarity.EPIC), 87 | VILLAGER_BUTCHER_BABY("Villager: Butcher (Baby)", Rarity.EPIC), 88 | SHEEP_ORANGE_BABY("Sheep: Orange (Baby)", Rarity.EPIC), 89 | SHEEP_MAGENTA_BABY("Sheep: Magenta (Baby)", Rarity.EPIC), 90 | SHEEP_LIGHT_BLUE_BABY("Sheep: Light Blue (Baby)", Rarity.EPIC), 91 | SHEEP_YELLOW_BABY("Sheep: Yellow (Baby)", Rarity.EPIC), 92 | SHEEP_LIME_BABY("Sheep: Lime (Baby)", Rarity.EPIC), 93 | SHEEP_CYAN_BABY("Sheep: Cyan (Baby)", Rarity.EPIC), 94 | SHEEP_PURPLE_BABY("Sheep: Purple (Baby)", Rarity.EPIC), 95 | SHEEP_BLUE_BABY("Sheep: Blue (Baby)", Rarity.EPIC), 96 | SHEEP_GREEN_BABY("Sheep: Green (Baby)", Rarity.EPIC), 97 | SHEEP_RED_BABY("Sheep: Red (Baby)", Rarity.EPIC), 98 | SHEEP_RAINBOW("Sheep: Rainbow", Rarity.LEGENDARY), 99 | 100 | IRON_GOLEM("Golem", Rarity.LEGENDARY), 101 | ENDERMAN("Enderman", Rarity.LEGENDARY), 102 | MOOSHROOM_BABY("Mooshroom (Baby)", Rarity.LEGENDARY), 103 | PIG_ZOMBIE_BABY("Pig Zombie (Baby)", Rarity.LEGENDARY), 104 | SHEEP_PINK_BABY("Sheep: Pink (Baby)", Rarity.LEGENDARY), 105 | SHEEP_BLACK_BABY("Sheep: Black (Baby)", Rarity.LEGENDARY), 106 | BLAZE("Blaze", Rarity.LEGENDARY), 107 | WITCH("Witch", Rarity.EPIC), 108 | HORSE_SKELETON("Skeleton Horse", Rarity.LEGENDARY), 109 | SNOWMAN("Snowman", Rarity.EPIC), 110 | SLIME_BIG("Slime (Big)", Rarity.LEGENDARY), 111 | MAGMA_CUBE_BIG("Magma Cube (Big)", Rarity.LEGENDARY), 112 | WITHER_SKELETON("Wither Skeleton", Rarity.LEGENDARY), 113 | 114 | GREEN_HELPER("Green Little Helper", Rarity.EPIC), 115 | RED_HELPER("Red Little Helper", Rarity.EPIC), 116 | 117 | WILD_OCELOT("Wild Ocelot"), 118 | WILD_OCELOT_BABY("Wild Ocelot (Baby)"), 119 | 120 | SQUID("Squid"), 121 | 122 | BROWN_RABBIT("Rabbit: Brown", Rarity.RARE), 123 | WHITE_RABBIT("Rabbit: White", Rarity.RARE), 124 | BLACK_RABBIT("Rabbit: Black", Rarity.RARE), 125 | BLACK_WHITE_RABBIT("Rabbit: Black & White", Rarity.EPIC), 126 | GOLD_RABBIT("Rabbit: Gold", Rarity.EPIC), 127 | SALT_PEPPER_RABBIT("Rabbit: Salt & Pepper", Rarity.EPIC), 128 | 129 | HEROBRINE("Herobrine", Rarity.LEGENDARY), 130 | CREEPER_POWERED("Powered Creeper", Rarity.LEGENDARY), 131 | ENDERMITE("Endermite", Rarity.LEGENDARY); 132 | 133 | public static final PetType[] VALUES = values(); 134 | 135 | private final String key; 136 | private final String name; 137 | private final Rarity rarity; 138 | 139 | PetType(String name) { 140 | this(name, null); 141 | } 142 | 143 | PetType(String name, Rarity rarity) { 144 | this.key = name(); 145 | this.name = name; 146 | this.rarity = rarity; 147 | } 148 | 149 | @Override 150 | public String getKey() { 151 | return key; 152 | } 153 | 154 | @Override 155 | public String getName() { 156 | return name; 157 | } 158 | 159 | @Override 160 | public Rarity getRarity() { 161 | return rarity; 162 | } 163 | 164 | @Override 165 | public String getPackage() { 166 | return null; 167 | } 168 | } 169 | 170 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/pets/impl/AbstractPetRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.pets.impl; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonObject; 6 | import com.google.gson.JsonPrimitive; 7 | import net.hypixel.api.pets.IPetRepository; 8 | import net.hypixel.api.pets.IPetType; 9 | import net.hypixel.api.pets.PetType; 10 | import net.hypixel.api.reply.PlayerReply; 11 | 12 | public abstract class AbstractPetRepositoryImpl implements IPetRepository { 13 | 14 | @Override 15 | public boolean hasPlayerUnlocked(IPetType type, PlayerReply.Player player) { 16 | if (type instanceof PetType) { 17 | throw new IllegalArgumentException("Old PetType enum doesn't include packages, which are required to use this method. Please use the new pet repository"); 18 | } 19 | 20 | String packageName = type.getPackage(); 21 | 22 | if (packageName == null) { 23 | throw new IllegalArgumentException("The provided pet type doesn't have a package, which is required to use this method"); 24 | } 25 | 26 | JsonObject vanityMeta = player.getObjectProperty("vanityMeta"); 27 | 28 | // Make sure vanityMeta is present as well as the inner packages array 29 | if (vanityMeta == null || !vanityMeta.has("packages")) { 30 | return false; 31 | } 32 | 33 | JsonElement packages = vanityMeta.get("packages"); 34 | 35 | // Check if packages is an array 36 | if (!(packages instanceof JsonArray)) { 37 | return false; 38 | } 39 | 40 | JsonArray packagesArray = packages.getAsJsonArray(); 41 | 42 | // Loop through packages until we find the pet type, if one matches 43 | for (JsonElement element : packagesArray) { 44 | // Make sure the element is a json primitive, so we can #getAsString without worry 45 | if (element instanceof JsonPrimitive) { 46 | if (element.getAsString().equalsIgnoreCase(packageName)) { 47 | return true; 48 | } 49 | } 50 | } 51 | 52 | return false; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/pets/impl/PetRarityImpl.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.pets.impl; 2 | 3 | import com.google.gson.JsonObject; 4 | import net.hypixel.api.pets.IPetRarity; 5 | 6 | public class PetRarityImpl implements IPetRarity { 7 | 8 | private final String name; 9 | private final String color; 10 | 11 | public PetRarityImpl(JsonObject jsonObject) { 12 | this.name = jsonObject.get("name").getAsString(); 13 | this.color = jsonObject.get("color").getAsString(); 14 | } 15 | 16 | @Override 17 | public String getName() { 18 | return name; 19 | } 20 | 21 | @Override 22 | public String getColor() { 23 | return color; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return "PetRarityImpl{" + 29 | "name='" + name + '\'' + 30 | ", color='" + color + '\'' + 31 | '}'; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/pets/impl/PetRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.pets.impl; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonObject; 6 | import net.hypixel.api.pets.IPetRarity; 7 | import net.hypixel.api.pets.IPetType; 8 | import net.hypixel.api.reply.ResourceReply; 9 | 10 | import java.util.Collection; 11 | import java.util.Collections; 12 | import java.util.HashSet; 13 | import java.util.Set; 14 | import java.util.function.Function; 15 | 16 | public class PetRepositoryImpl extends AbstractPetRepositoryImpl { 17 | 18 | private final ResourceReply reply; 19 | 20 | private final Collection rarities; 21 | private final Collection types; 22 | 23 | public PetRepositoryImpl(ResourceReply reply) { 24 | this.reply = reply; 25 | 26 | if (!reply.isSuccess()) { 27 | throw new IllegalStateException("Cannot transform unsuccessful resource reply to pet repository"); 28 | } 29 | 30 | this.rarities = parseCollection("rarities", PetRarityImpl::new); 31 | this.types = parseCollection("types", jsonObject -> new PetTypeImpl(this, jsonObject)); 32 | } 33 | 34 | private Collection parseCollection(String key, Function factory) { 35 | Set set = new HashSet<>(); 36 | 37 | JsonArray jsonArray = reply.getResponse().get(key).getAsJsonArray(); 38 | 39 | for (JsonElement element : jsonArray) { 40 | if (!element.isJsonObject()) { 41 | throw new IllegalStateException("Invalid element in " + key + ": expected json object but got " + element); 42 | } 43 | 44 | set.add(factory.apply(element.getAsJsonObject())); 45 | } 46 | 47 | return Collections.unmodifiableSet(set); 48 | } 49 | 50 | @Override 51 | public IPetType getTypeByKey(String type) { 52 | for (IPetType petType : types) { 53 | if (petType.getKey().equals(type)) { 54 | return petType; 55 | } 56 | } 57 | 58 | return null; 59 | } 60 | 61 | @Override 62 | public IPetType getTypeByPackage(String typePackage) { 63 | for (IPetType petType : types) { 64 | if (petType.getPackage().equals(typePackage)) { 65 | return petType; 66 | } 67 | } 68 | 69 | return null; 70 | } 71 | 72 | @Override 73 | public Collection getTypesForRarity(IPetRarity rarity) { 74 | Set petTypes = new HashSet<>(); 75 | 76 | for (IPetType petType : types) { 77 | if (petType.getRarity().equals(rarity)) { 78 | petTypes.add(petType); 79 | } 80 | } 81 | 82 | return petTypes; 83 | } 84 | 85 | @Override 86 | public IPetRarity getRarityByName(String name) { 87 | for (IPetRarity rarity : rarities) { 88 | if (rarity.getName().equals(name)) { 89 | return rarity; 90 | } 91 | } 92 | 93 | return null; 94 | } 95 | 96 | @Override 97 | public Collection getRarities() { 98 | return rarities; 99 | } 100 | 101 | @Override 102 | public Collection getTypes() { 103 | return types; 104 | } 105 | 106 | @Override 107 | public String toString() { 108 | return "PetRepositoryImpl{" + 109 | "rarities=" + rarities + 110 | ", types=" + types + 111 | '}'; 112 | } 113 | 114 | public ResourceReply getReply() { 115 | return reply; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/pets/impl/PetTypeImpl.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.pets.impl; 2 | 3 | import com.google.gson.JsonObject; 4 | import net.hypixel.api.pets.IPetRarity; 5 | import net.hypixel.api.pets.IPetType; 6 | 7 | public class PetTypeImpl implements IPetType { 8 | 9 | private final String key; 10 | private final String name; 11 | private final IPetRarity rarity; 12 | private final String typePackage; 13 | 14 | public PetTypeImpl(PetRepositoryImpl repository, JsonObject jsonObject) { 15 | this.key = jsonObject.get("key").getAsString(); 16 | this.name = jsonObject.get("name").getAsString(); 17 | this.rarity = jsonObject.get("rarity").isJsonNull() ? null : repository.getRarityByName(jsonObject.get("rarity").getAsString()); 18 | this.typePackage = jsonObject.get("package").getAsString(); 19 | } 20 | 21 | @Override 22 | public String getKey() { 23 | return key; 24 | } 25 | 26 | @Override 27 | public String getName() { 28 | return name; 29 | } 30 | 31 | @Override 32 | public IPetRarity getRarity() { 33 | return rarity; 34 | } 35 | 36 | @Override 37 | public String getPackage() { 38 | return typePackage; 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "PetTypeImpl{" + 44 | "key='" + key + '\'' + 45 | ", name='" + name + '\'' + 46 | ", rarity=" + rarity + 47 | ", typePackage='" + typePackage + '\'' + 48 | '}'; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/pets/impl/compatibility/BackwardsCompatibilityPetRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.pets.impl.compatibility; 2 | 3 | import net.hypixel.api.pets.IPetRarity; 4 | import net.hypixel.api.pets.IPetType; 5 | import net.hypixel.api.pets.PetType; 6 | import net.hypixel.api.pets.impl.AbstractPetRepositoryImpl; 7 | import net.hypixel.api.util.Rarity; 8 | 9 | import java.util.Arrays; 10 | import java.util.Collection; 11 | import java.util.HashSet; 12 | import java.util.Set; 13 | 14 | public class BackwardsCompatibilityPetRepositoryImpl extends AbstractPetRepositoryImpl { 15 | 16 | public static final BackwardsCompatibilityPetRepositoryImpl INSTANCE = new BackwardsCompatibilityPetRepositoryImpl(); 17 | 18 | private final Collection rarities; 19 | private final Collection types; 20 | 21 | public BackwardsCompatibilityPetRepositoryImpl() { 22 | this.rarities = Arrays.asList(Rarity.values()); 23 | this.types = Arrays.asList(PetType.VALUES); 24 | } 25 | 26 | @Override 27 | public IPetType getTypeByKey(String key) { 28 | for (IPetType petType : types) { 29 | if (petType.getKey().equals(key)) { 30 | return petType; 31 | } 32 | } 33 | 34 | return null; 35 | } 36 | 37 | @Override 38 | public IPetType getTypeByPackage(String typePackage) { 39 | return null; // Always return null, deprecated PetType enum doesn't include packages 40 | } 41 | 42 | @Override 43 | public Collection getTypesForRarity(IPetRarity rarity) { 44 | Set petTypes = new HashSet<>(); 45 | 46 | for (IPetType petType : types) { 47 | if (petType.getRarity().equals(rarity)) { 48 | petTypes.add(petType); 49 | } 50 | } 51 | 52 | return petTypes; 53 | } 54 | 55 | @Override 56 | public IPetRarity getRarityByName(String name) { 57 | try { 58 | return Rarity.valueOf(name); 59 | } catch (IllegalArgumentException ignored) { 60 | return null; 61 | } 62 | } 63 | 64 | @Override 65 | public Collection getRarities() { 66 | return rarities; 67 | } 68 | 69 | @Override 70 | public Collection getTypes() { 71 | return types; 72 | } 73 | 74 | @Override 75 | public String toString() { 76 | return "BackwardsCompatibilityPetRepositoryImpl{" + 77 | "rarities=" + rarities + 78 | ", types=" + types + 79 | '}'; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/AbstractReply.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply; 2 | 3 | public abstract class AbstractReply { 4 | 5 | protected boolean success; 6 | protected String cause; 7 | 8 | public boolean isSuccess() { 9 | return success; 10 | } 11 | 12 | public String getCause() { 13 | return cause; 14 | } 15 | 16 | @Override 17 | public String toString() { 18 | return "AbstractReply{" + 19 | "success=" + success + 20 | ", cause='" + cause + '\'' + 21 | '}'; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/BoostersReply.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply; 2 | 3 | import net.hypixel.api.data.type.GameType; 4 | 5 | import java.time.ZonedDateTime; 6 | import java.util.List; 7 | import java.util.UUID; 8 | 9 | public class BoostersReply extends RateLimitedReply { 10 | private List boosters; 11 | private BoosterState boosterState; 12 | 13 | public List getBoosters() { 14 | return boosters; 15 | } 16 | 17 | public BoosterState getBoosterState() { 18 | return boosterState; 19 | } 20 | 21 | @Override 22 | public String toString() { 23 | return "BoostersReply{" + 24 | "boosters=" + boosters + 25 | ", boosterState=" + boosterState + 26 | "} " + super.toString(); 27 | } 28 | 29 | public static class BoosterState { 30 | private boolean decrementing; 31 | 32 | public boolean isDecrementing() { 33 | return decrementing; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "BoosterState{" + 39 | "decrementing=" + decrementing + 40 | '}'; 41 | } 42 | } 43 | 44 | public static class Booster { 45 | private UUID purchaserUuid; 46 | private double amount; 47 | private int originalLength; 48 | private int length; 49 | private GameType gameType; 50 | private ZonedDateTime dateActivated; 51 | private List stacked; 52 | private boolean queuedToStack; 53 | 54 | public UUID getPurchaserUuid() { 55 | return purchaserUuid; 56 | } 57 | 58 | public double getAmount() { 59 | return amount; 60 | } 61 | 62 | public int getOriginalLength() { 63 | return originalLength; 64 | } 65 | 66 | public int getLength() { 67 | return length; 68 | } 69 | 70 | public GameType getGameType() { 71 | return gameType; 72 | } 73 | 74 | public ZonedDateTime getDateActivated() { 75 | return dateActivated; 76 | } 77 | 78 | public List getStacked() { 79 | return stacked; 80 | } 81 | 82 | public boolean isQueuedToStack() { 83 | return queuedToStack; 84 | } 85 | 86 | @Override 87 | public String toString() { 88 | return "Booster{" + 89 | "purchaserUuid=" + purchaserUuid + 90 | ", amount=" + amount + 91 | ", originalLength=" + originalLength + 92 | ", length=" + length + 93 | ", gameType=" + gameType + 94 | ", dateActivated=" + dateActivated + 95 | ", stacked=" + stacked + 96 | ", queuedToStack=" + queuedToStack + 97 | '}'; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/CountsReply.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply; 2 | 3 | import java.util.Map; 4 | 5 | public class CountsReply extends RateLimitedReply { 6 | private Map games; 7 | private int playerCount; 8 | 9 | public Map getGames() { 10 | return games; 11 | } 12 | 13 | public int getPlayerCount() { 14 | return playerCount; 15 | } 16 | 17 | public static class GameCount { 18 | private Map modes; 19 | private int players; 20 | 21 | public Map getModes() { 22 | return modes; 23 | } 24 | 25 | public int getPlayers() { 26 | return players; 27 | } 28 | 29 | @Override 30 | public String toString() { 31 | return "GameCount{" + 32 | "modes=" + modes + 33 | ", players=" + players + 34 | '}'; 35 | } 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return "GameCountsReply{" + 41 | "games=" + games + 42 | ", playerCount=" + playerCount + 43 | "} " + super.toString(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/FindGuildReply.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply; 2 | 3 | public class FindGuildReply extends RateLimitedReply { 4 | private String guild; 5 | 6 | /** 7 | * @return The ID of the guild that was found, or null if there was no guild by that name 8 | */ 9 | public String getGuild() { 10 | return guild; 11 | } 12 | 13 | @Override 14 | public String toString() { 15 | return "FindGuildReply{" + 16 | "guild='" + guild + '\'' + 17 | "} " + super.toString(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/KeyReply.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | import java.util.UUID; 6 | 7 | public class KeyReply extends RateLimitedReply { 8 | private Key record; 9 | 10 | public Key getRecord() { 11 | return record; 12 | } 13 | 14 | @Override 15 | public String toString() { 16 | return "KeyReply{" + 17 | "record=" + record + 18 | "} " + super.toString(); 19 | } 20 | 21 | public class Key { 22 | private UUID key; 23 | @SerializedName("owner") 24 | private UUID ownerUuid; 25 | private int totalQueries; 26 | private int queriesInPastMin; 27 | 28 | public UUID getKey() { 29 | return key; 30 | } 31 | 32 | public UUID getOwnerUuid() { 33 | return ownerUuid; 34 | } 35 | 36 | public int getTotalQueries() { 37 | return totalQueries; 38 | } 39 | 40 | public int getQueriesInPastMin() { 41 | return queriesInPastMin; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "Key{" + 47 | "key=" + key + 48 | ", ownerUuid=" + ownerUuid + 49 | ", totalQueries=" + totalQueries + 50 | ", queriesInPastMin=" + queriesInPastMin + 51 | '}'; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/LeaderboardsReply.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply; 2 | 3 | import net.hypixel.api.data.type.GameType; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.UUID; 8 | 9 | public class LeaderboardsReply extends RateLimitedReply { 10 | private Map> leaderboards; 11 | 12 | public Map> getLeaderboards() { 13 | return leaderboards; 14 | } 15 | 16 | @Override 17 | public String toString() { 18 | return "LeaderboardsReply{" + 19 | "leaderboards=" + leaderboards + 20 | "} " + super.toString(); 21 | } 22 | 23 | public class Leaderboard { 24 | 25 | private String path; 26 | private String prefix; 27 | private int count; 28 | private List leaders; 29 | private String title; 30 | 31 | public Leaderboard() { 32 | } 33 | 34 | public String getPath() { 35 | return path; 36 | } 37 | 38 | public String getPrefix() { 39 | return prefix; 40 | } 41 | 42 | public int getCount() { 43 | return count; 44 | } 45 | 46 | public List getLeaders() { 47 | return leaders; 48 | } 49 | 50 | public String getTitle() { 51 | return title; 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return "Leaderboard{" + 57 | "path='" + path + '\'' + 58 | ", prefix='" + prefix + '\'' + 59 | ", count=" + count + 60 | ", leaders=" + leaders + 61 | ", title='" + title + '\'' + 62 | '}'; 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/PlayerReply.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonObject; 6 | import com.google.gson.reflect.TypeToken; 7 | import net.hypixel.api.HypixelAPI; 8 | import net.hypixel.api.data.type.GameType; 9 | import net.hypixel.api.pets.IPetRepository; 10 | import net.hypixel.api.pets.PetStats; 11 | import net.hypixel.api.pets.impl.compatibility.BackwardsCompatibilityPetRepositoryImpl; 12 | import net.hypixel.api.util.ILeveling; 13 | import net.hypixel.api.util.UnstableHypixelObject; 14 | import net.hypixel.api.util.Utilities; 15 | 16 | import java.lang.reflect.Type; 17 | import java.time.ZonedDateTime; 18 | import java.util.Map; 19 | import java.util.UUID; 20 | 21 | public class PlayerReply extends RateLimitedReply { 22 | 23 | // Suppressed because this field is dynamically assigned by Gson using reflection. 24 | @SuppressWarnings({"unused", "RedundantSuppression"}) 25 | private Player player; 26 | 27 | public Player getPlayer() { 28 | return player; 29 | } 30 | 31 | @Override 32 | public String toString() { 33 | return "PlayerReply{" + 34 | "player=" + player + 35 | "} " + super.toString(); 36 | } 37 | 38 | /** 39 | * Information and statistics for a player on the Hypixel network. 40 | *


41 | * If the player does not {@link #exists() exist}, methods may return unexpected results. 42 | */ 43 | public static class Player extends UnstableHypixelObject { 44 | 45 | private static final String DEFAULT_RANK = "NONE"; 46 | 47 | /** 48 | * @param raw A JSON object representing a Hypixel player, as returned from the API. If this 49 | * object is valid, it can be retrieved later via {@link #getRaw()}. 50 | */ 51 | public Player(JsonElement raw) { 52 | super(raw); 53 | } 54 | 55 | /** 56 | * @return The player's Minecraft UUID, or {@code null} if the player does not {@link 57 | * #exists() exist}. 58 | */ 59 | public UUID getUuid() { 60 | String uuidStr = getStringProperty("uuid", null); 61 | return uuidStr != null ? Utilities.uuidFromString(uuidStr) : null; 62 | } 63 | 64 | /** 65 | * @return The Minecraft username that the player had when they last connected to Hypixel. 66 | * {@code null} if the player's name is unknown. 67 | */ 68 | public String getName() { 69 | // Attempt to get their display name 70 | String displayName = getStringProperty("displayname", null); 71 | if (displayName != null) { 72 | return displayName; 73 | } 74 | 75 | // Fallback to their most recently-known alias 76 | JsonArray knownAliases = getArrayProperty("knownAliases"); 77 | if (knownAliases != null && knownAliases.size() > 0) { 78 | return knownAliases.get(knownAliases.size() - 1).getAsString(); 79 | } 80 | 81 | // Fallback to lowercase variants of their name 82 | return getStringProperty("playername", getStringProperty("username", null)); 83 | } 84 | 85 | /** 86 | * @return The total amount of network experience earned by the player. 87 | */ 88 | public long getNetworkExp() { 89 | long exp = getLongProperty("networkExp", 0); 90 | exp += ILeveling.getTotalExpToFullLevel(getLongProperty("networkLevel", 0) + 1); 91 | return exp; 92 | } 93 | 94 | /** 95 | * @return The player's precise network level, including their progress to the next level. 96 | */ 97 | public double getNetworkLevel() { 98 | return ILeveling.getExactLevel(getNetworkExp()); 99 | } 100 | 101 | /** 102 | * @return The total amount of karma points earned by the player. 103 | */ 104 | public long getKarma() { 105 | return getLongProperty("karma", 0); 106 | } 107 | 108 | /** 109 | * @return The date when the player first connected to Hypixel. Defaults to the unix epoch 110 | * when unknown. 111 | */ 112 | public ZonedDateTime getFirstLoginDate() { 113 | return getTimestamp("firstLogin"); 114 | } 115 | 116 | /** 117 | * @return The last known time when the player connected to the main Hypixel network. 118 | * Defaults to the unix epoch when unknown. 119 | */ 120 | public ZonedDateTime getLastLoginDate() { 121 | return getTimestamp("lastLogin"); 122 | } 123 | 124 | /** 125 | * @return The last known time when the player disconnected from the main Hypixel network. 126 | * Defaults to the unix epoch when unknown. 127 | */ 128 | public ZonedDateTime getLastLogoutDate() { 129 | return getTimestamp("lastLogout"); 130 | } 131 | 132 | /** 133 | * @return {@code true} if the player is currently connected to the Hypixel network. {@code 134 | * false} otherwise, or if the player's online status is hidden in the API. 135 | * @see HypixelAPI#getStatus(UUID) 136 | * @deprecated The status endpoint ({@link HypixelAPI#getStatus(UUID)}) is 137 | * recommended for checking a player's online status. 138 | */ 139 | @Deprecated 140 | public boolean isOnline() { 141 | return getLongProperty("lastLogin", 0) > getLongProperty("lastLogout", 0); 142 | } 143 | 144 | /** 145 | * @return The color of the player's "+"s if they have MVP+ or MVP++. If they do not have 146 | * either rank, or if they have not selected a color, {@code RED} is returned as the 147 | * default. 148 | */ 149 | public String getSelectedPlusColor() { 150 | return getStringProperty("rankPlusColor", "RED"); 151 | } 152 | 153 | /** 154 | * Note, returned colors use the names seen in this 155 | * table, in all uppercase. For example, {@code DARK_BLUE} and {@code GRAY}. 156 | * 157 | * @return The color of the player's name tag if they have MVP++. Defaults to {@code GOLD}. 158 | */ 159 | public String getSuperstarTagColor() { 160 | return getStringProperty("monthlyRankColor", "GOLD"); 161 | } 162 | 163 | /** 164 | * Returns the most privileged network rank that the player has. 165 | *


166 | * Example: If...

    167 | *
  • A player's base rank is MVP+ ({@code MVP_PLUS})
  • 168 | *
  • They have a subscription for MVP++ ({@code SUPERSTAR})
  • 169 | *
  • They are a staff member with the moderator rank ({@code MODERATOR})
  • 170 | *
171 | * ...then this method will return {@code MODERATOR}, because it has the highest permission 172 | * level of the three ranks. 173 | * 174 | * @return The most privileged network rank that the player has, or {@code NONE} if they do 175 | * not have any. 176 | * @apiNote Display prefixes are not considered, as they have no effect on permissions. 177 | * Examples include "OWNER" and "MOJANG". 178 | * @see "How 179 | * do I get a player's rank prefix?" 180 | */ 181 | public String getHighestRank() { 182 | if (hasRankInField("rank")) { 183 | return getStringProperty("rank", DEFAULT_RANK); 184 | 185 | } else if (hasRankInField("monthlyPackageRank")) { 186 | return getStringProperty("monthlyPackageRank", DEFAULT_RANK); 187 | 188 | } else if (hasRankInField("newPackageRank")) { 189 | return getStringProperty("newPackageRank", DEFAULT_RANK); 190 | 191 | } else if (hasRankInField("packageRank")) { 192 | return getStringProperty("packageRank", DEFAULT_RANK); 193 | } 194 | 195 | return DEFAULT_RANK; 196 | } 197 | 198 | /** 199 | * @return {@code true} if the player has a network rank (e.g. {@code VIP}, {@code MVP++}, 200 | * {@code MODERATOR}, etc). 201 | * @apiNote Display prefixes are not considered, as they are technically not ranks. Examples 202 | * include "OWNER" and "MOJANG". 203 | */ 204 | public boolean hasRank() { 205 | return !getHighestRank().equals(DEFAULT_RANK); 206 | } 207 | 208 | /** 209 | * @return {@code true} if the player is a member of the Hypixel 210 | * Build Team. Otherwise {@code false}. 211 | */ 212 | public boolean isOnBuildTeam() { 213 | return getBooleanProperty("buildTeam", false) 214 | || getBooleanProperty("buildTeamAdmin", false); 215 | } 216 | 217 | /** 218 | * @return The player's most recently played {@link GameType}, or {@code null} if it is 219 | * unknown. 220 | */ 221 | public GameType getMostRecentGameType() { 222 | try { 223 | return GameType.valueOf(getStringProperty("mostRecentGameType", "")); 224 | } catch (IllegalArgumentException ignored) { 225 | return null; 226 | } 227 | } 228 | 229 | /** 230 | * @return Information about the player's lobby pets, or {@code null} if they have none. 231 | * @deprecated Use {@link #getPetStats(IPetRepository)} instead 232 | */ 233 | @Deprecated 234 | public PetStats getPetStats() { 235 | return getPetStats(BackwardsCompatibilityPetRepositoryImpl.INSTANCE); 236 | } 237 | 238 | /** 239 | * @return Information about the player's lobby pets, or {@code null} if they have none. 240 | */ 241 | public PetStats getPetStats(IPetRepository petRepository) { 242 | JsonObject petStats = getObjectProperty("petStats"); 243 | if (petStats == null) { 244 | return null; 245 | } 246 | 247 | Type statsObjectType = new TypeToken>>() { 248 | }.getType(); 249 | return new PetStats(petRepository, Utilities.GSON.fromJson(petStats, statsObjectType)); 250 | } 251 | 252 | /** 253 | * @return {@code true} if the player could be identified by the API. Otherwise {@code 254 | * false}. 255 | */ 256 | public boolean exists() { 257 | return getUuid() != null; 258 | } 259 | 260 | @Override 261 | public String toString() { 262 | return exists() 263 | ? "Player" + raw 264 | : "Player{exists=false}"; 265 | } 266 | 267 | /** 268 | * Helper method for checking if a rank-related field contains a non-default rank. 269 | * 270 | * @param name The name/json-path of the field to check. 271 | * @return Whether or not the field contains a non-default rank value. 272 | * @implNote {@code false} if {@code null}, {@code NONE}, or {@code NORMAL} 273 | */ 274 | protected boolean hasRankInField(String name) { 275 | String value = getStringProperty(name, DEFAULT_RANK); 276 | return !value.isEmpty() && !value.equals("NONE") && !value.equals("NORMAL"); 277 | } 278 | 279 | /** 280 | * Helper method for deserializing unix timestamp fields, in milliseconds. 281 | * 282 | * @param name The name/json-path of the field to check. 283 | * @return The date represented by the timestamp, or the unix epoch if the field cannot be 284 | * found. 285 | */ 286 | protected ZonedDateTime getTimestamp(String name) { 287 | long timestamp = getLongProperty(name, 0); 288 | return Utilities.getDateTime(timestamp); 289 | } 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/PunishmentStatsReply.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | public class PunishmentStatsReply extends RateLimitedReply { 6 | @SerializedName("staff_rollingDaily") 7 | private int staffRollingDaily; 8 | @SerializedName("staff_total") 9 | private int staffTotal; 10 | @SerializedName("watchdog_total") 11 | private int watchdogTotal; 12 | @SerializedName("watchdog_lastMinute") 13 | private int watchdogLastMinute; 14 | @SerializedName("watchdog_rollingDaily") 15 | private int watchdogRollingDaily; 16 | 17 | public int getStaffRollingDaily() { 18 | return staffRollingDaily; 19 | } 20 | 21 | public int getStaffTotal() { 22 | return staffTotal; 23 | } 24 | 25 | public int getWatchdogTotal() { 26 | return watchdogTotal; 27 | } 28 | 29 | public int getWatchdogLastMinute() { 30 | return watchdogLastMinute; 31 | } 32 | 33 | public int getWatchdogRollingDaily() { 34 | return watchdogRollingDaily; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return "WatchdogStatsReply{" + 40 | "staffRollingDaily=" + staffRollingDaily + 41 | ", staffTotal=" + staffTotal + 42 | ", watchdogTotal=" + watchdogTotal + 43 | ", watchdogLastMinute=" + watchdogLastMinute + 44 | ", watchdogRollingDaily=" + watchdogRollingDaily + 45 | "} " + super.toString(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/RateLimitedReply.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply; 2 | 3 | import net.hypixel.api.http.RateLimit; 4 | 5 | public abstract class RateLimitedReply extends AbstractReply { 6 | private RateLimit rateLimit; 7 | 8 | public RateLimit getRateLimit() { 9 | return rateLimit; 10 | } 11 | 12 | public void setRateLimit(RateLimit rateLimit) { 13 | this.rateLimit = rateLimit; 14 | } 15 | 16 | @Override 17 | public String toString() { 18 | return "RateLimitedReply{" + 19 | "rateLimit=" + rateLimit + 20 | "} " + super.toString(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/RecentGamesReply.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply; 2 | 3 | import net.hypixel.api.data.type.GameType; 4 | 5 | import java.time.ZonedDateTime; 6 | import java.util.List; 7 | 8 | public class RecentGamesReply extends RateLimitedReply { 9 | 10 | private List games; 11 | 12 | /** 13 | * @return Up to 100 of the player's most recently played games 14 | * @see GameSession 15 | */ 16 | public List getGames() { 17 | return games; 18 | } 19 | 20 | @Override 21 | public String toString() { 22 | return "RecentGamesReply{" + 23 | "games=" + games + 24 | '}'; 25 | } 26 | 27 | public static class GameSession { 28 | 29 | private ZonedDateTime date; 30 | private GameType gameType; 31 | private String mode; 32 | private String map; 33 | private ZonedDateTime ended; 34 | 35 | /** 36 | * @return When the game started 37 | */ 38 | public ZonedDateTime getStartDate() { 39 | return date; 40 | } 41 | 42 | /** 43 | * @return Game played during this session 44 | * @see GameType 45 | */ 46 | public GameType getGameType() { 47 | return gameType; 48 | } 49 | 50 | /** 51 | * @return Subtype of the game played (e.g. "solo_insane_lucky" for {@link GameType#SKYWARS}) 52 | */ 53 | public String getMode() { 54 | return mode; 55 | } 56 | 57 | /** 58 | * @return Map that was played on 59 | */ 60 | public String getMap() { 61 | return map; 62 | } 63 | 64 | /** 65 | * @return When the game ended. If null, the game is ongoing 66 | */ 67 | public ZonedDateTime getEndDate() { 68 | return ended; 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | return "GameSession{" + 74 | "date=" + date + 75 | ", gameType=" + gameType + 76 | ", mode='" + mode + '\'' + 77 | ", map='" + map + '\'' + 78 | ", ended=" + ended + 79 | '}'; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/ResourceReply.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply; 2 | 3 | import com.google.gson.JsonObject; 4 | import net.hypixel.api.reply.AbstractReply; 5 | 6 | public class ResourceReply extends AbstractReply { 7 | 8 | private final long lastUpdated; 9 | private final JsonObject response; 10 | 11 | public ResourceReply(JsonObject response) { 12 | this.response = response; 13 | this.success = response.has("success") && response.get("success").getAsBoolean(); 14 | this.cause = response.has("cause") ? response.get("cause").getAsString() : null; 15 | this.lastUpdated = response.has("lastUpdated") ? response.get("lastUpdated").getAsLong() : -1; 16 | } 17 | 18 | public JsonObject getResponse() { 19 | if (response == null || response.isJsonNull()) { 20 | return null; 21 | } else { 22 | return response.getAsJsonObject(); 23 | } 24 | } 25 | 26 | /** 27 | * Gets unix time when the resource was updated. 28 | * Will return -1 if last updated was not included in response 29 | * 30 | * @return long unix time 31 | */ 32 | public long getLastUpdated() { 33 | return lastUpdated; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "ResourceReply{" + 39 | "lastUpdated=" + lastUpdated + 40 | ", response=" + response + 41 | "} " + super.toString(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/StatusReply.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import net.hypixel.api.data.type.ServerType; 5 | 6 | public class StatusReply extends RateLimitedReply { 7 | 8 | /** 9 | * {@link StatusReply.Session} instance of player 10 | */ 11 | private Session session; 12 | 13 | public Session getSession() { 14 | return session; 15 | } 16 | 17 | @Override 18 | public String toString() { 19 | 20 | return "StatusReply{" + 21 | "session=" + session + 22 | "} " + super.toString(); 23 | } 24 | 25 | public static class Session { 26 | 27 | /** 28 | * Boolean if player is online. 29 | * May be disabled in the player their settings, so may vary from player to player 30 | */ 31 | private boolean online; 32 | 33 | /** 34 | * ServerType could be null if a new game/lobby has been released and type is not yet added. 35 | *

36 | * This will NOT throw an exception. 37 | */ 38 | @SerializedName("gameType") 39 | private ServerType serverType; 40 | 41 | /** 42 | * Mode of game being played 43 | * Will be "lobby" if player is in lobby 44 | */ 45 | private String mode; 46 | 47 | /** 48 | * Map being played 49 | * Will be null if player is not in game 50 | **/ 51 | private String map; 52 | 53 | public boolean isOnline() { 54 | return online; 55 | } 56 | 57 | public ServerType getServerType() { 58 | return serverType; 59 | } 60 | 61 | public String getMode() { 62 | return mode; 63 | } 64 | 65 | public String getMap() { 66 | return map; 67 | } 68 | 69 | @Override 70 | public String toString() { 71 | return "Session{" + 72 | "online=" + online + 73 | ", serverType=" + serverType + 74 | ", mode=" + mode + 75 | ", map=" + map + 76 | "}"; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/skyblock/SkyBlockAuctionsReply.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply.skyblock; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | import net.hypixel.api.reply.AbstractReply; 6 | 7 | public class SkyBlockAuctionsReply extends AbstractReply { 8 | private int page; 9 | private int totalPages; 10 | private int totalAuctions; 11 | private long lastUpdated; 12 | private JsonElement auctions; 13 | 14 | public int getPage() { 15 | return page; 16 | } 17 | 18 | public int getTotalPages() { 19 | return totalPages; 20 | } 21 | 22 | public int getTotalAuctions() { 23 | return totalAuctions; 24 | } 25 | 26 | public long getLastUpdated() { 27 | return lastUpdated; 28 | } 29 | 30 | public JsonArray getAuctions() { 31 | if (auctions == null || auctions.isJsonNull()) { 32 | return null; 33 | } else { 34 | return auctions.getAsJsonArray(); 35 | } 36 | } 37 | 38 | public boolean hasNextPage() { 39 | return page < totalPages - 1; 40 | } 41 | 42 | public boolean hasPrevPage() { 43 | return page > 0; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return "SkyBlockAuctionsReply{" + 49 | "page=" + page + 50 | ", totalPages=" + totalPages + 51 | ", totalAuctions=" + totalAuctions + 52 | ", lastUpdated=" + lastUpdated + 53 | ", auctions=" + auctions + 54 | "} " + super.toString(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/skyblock/SkyBlockBazaarReply.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply.skyblock; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import net.hypixel.api.reply.AbstractReply; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | public class SkyBlockBazaarReply extends AbstractReply { 10 | 11 | private long lastUpdated; 12 | private Map products; 13 | 14 | public Map getProducts() { 15 | return products; 16 | } 17 | 18 | /** 19 | * Returns {@link Product} from bazaar reply. 20 | * Returns null if product does not exist 21 | * 22 | * @param productId product in bazaar 23 | * @return instance of Product 24 | */ 25 | public Product getProduct(String productId) { 26 | return products.get(productId); 27 | } 28 | 29 | @Override 30 | public String toString() { 31 | return "BazaarReply{" + 32 | "lastUpdated=" + lastUpdated + 33 | ", products=" + products + 34 | "} " + super.toString(); 35 | } 36 | 37 | public class Product { 38 | 39 | @SerializedName("product_id") 40 | private String productId; 41 | 42 | @SerializedName("sell_summary") 43 | private List

sellSummary; 44 | 45 | @SerializedName("buy_summary") 46 | private List buySummary; 47 | 48 | @SerializedName("quick_status") 49 | private Status quickStatus; 50 | 51 | public String getProductId() { 52 | return productId; 53 | } 54 | 55 | public List getSellSummary() { 56 | return sellSummary; 57 | } 58 | 59 | public List getBuySummary() { 60 | return buySummary; 61 | } 62 | 63 | public Status getQuickStatus() { 64 | return quickStatus; 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | return "Product{" + 70 | "productId='" + productId + 71 | ", sellSummary=" + sellSummary + 72 | ", buySummary=" + buySummary + 73 | ", quickStatus=" + quickStatus + 74 | "}"; 75 | } 76 | 77 | public class Summary { 78 | 79 | private long amount; 80 | private double pricePerUnit; 81 | private long orders; 82 | 83 | public long getAmount() { 84 | return amount; 85 | } 86 | 87 | public double getPricePerUnit() { 88 | return pricePerUnit; 89 | } 90 | 91 | public long getOrders() { 92 | return orders; 93 | } 94 | 95 | @Override 96 | public String toString() { 97 | return "Summary{" + 98 | "amount=" + amount + 99 | ", pricePerUnit=" + pricePerUnit + 100 | ", orders=" + orders + 101 | "}"; 102 | } 103 | } 104 | 105 | public class Status { 106 | 107 | private String productId; 108 | private double sellPrice; 109 | private long sellVolume; 110 | private long sellMovingWeek; 111 | private long sellOrders; 112 | private double buyPrice; 113 | private long buyVolume; 114 | private long buyMovingWeek; 115 | private long buyOrders; 116 | 117 | public String getProductId() { 118 | return productId; 119 | } 120 | 121 | public double getSellPrice() { 122 | return sellPrice; 123 | } 124 | 125 | public long getSellVolume() { 126 | return sellVolume; 127 | } 128 | 129 | public long getSellMovingWeek() { 130 | return sellMovingWeek; 131 | } 132 | 133 | public long getSellOrders() { 134 | return sellOrders; 135 | } 136 | 137 | public double getBuyPrice() { 138 | return buyPrice; 139 | } 140 | 141 | public long getBuyVolume() { 142 | return buyVolume; 143 | } 144 | 145 | public long getBuyMovingWeek() { 146 | return buyMovingWeek; 147 | } 148 | 149 | public long getBuyOrders() { 150 | return buyOrders; 151 | } 152 | 153 | @Override 154 | public String toString() { 155 | return "Status{" + 156 | "productId='" + productId + 157 | ", sellPrice=" + sellPrice + 158 | ", sellVolume=" + sellVolume + 159 | ", sellMovingWeek=" + sellMovingWeek + 160 | ", sellOrders=" + sellOrders + 161 | ", buyPrice=" + buyPrice + 162 | ", buyVolume=" + buyVolume + 163 | ", buyMovingWeek=" + buyMovingWeek + 164 | ", buyOrders=" + buyOrders + 165 | "}"; 166 | } 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/skyblock/SkyBlockNewsReply.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply.skyblock; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | import net.hypixel.api.reply.RateLimitedReply; 6 | 7 | public class SkyBlockNewsReply extends RateLimitedReply { 8 | private JsonElement items; 9 | 10 | public JsonArray getItems() { 11 | if (items == null || items.isJsonNull()) { 12 | return null; 13 | } else { 14 | return items.getAsJsonArray(); 15 | } 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return "SkyBlockNewsReply{" + 21 | "items=" + items + 22 | "} " + super.toString(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/skyblock/SkyBlockProfileReply.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply.skyblock; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | import net.hypixel.api.reply.RateLimitedReply; 6 | 7 | public class SkyBlockProfileReply extends RateLimitedReply { 8 | private JsonElement profile; 9 | 10 | public JsonObject getProfile() { 11 | if (profile == null || profile.isJsonNull()) { 12 | return null; 13 | } else { 14 | return profile.getAsJsonObject(); 15 | } 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return "SkyBlockProfileReply{" + 21 | "profile=" + profile + 22 | "} " + super.toString(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/skyblock/SkyBlockProfilesReply.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply.skyblock; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | import net.hypixel.api.reply.RateLimitedReply; 6 | 7 | public class SkyBlockProfilesReply extends RateLimitedReply { 8 | private JsonElement profiles; 9 | 10 | public JsonArray getProfiles() { 11 | if (profiles == null || profiles.isJsonNull()) { 12 | return null; 13 | } else { 14 | return profiles.getAsJsonArray(); 15 | } 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return "SkyBlockProfilesReply{" + 21 | "profiles=" + profiles + 22 | "} " + super.toString(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/skyblock/bingo/BingoEventData.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply.skyblock.bingo; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | import java.util.List; 6 | 7 | public class BingoEventData { 8 | private int key; 9 | private int points; 10 | @SerializedName("completed_goals") 11 | private List completedGoals; 12 | 13 | public int getKey() { 14 | return key; 15 | } 16 | 17 | public int getPoints() { 18 | return points; 19 | } 20 | 21 | public List getCompletedGoals() { 22 | return completedGoals; 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return "BingoEventData{" + 28 | "key=" + key + 29 | ", points=" + points + 30 | ", completedGoals=" + completedGoals + 31 | '}'; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/skyblock/bingo/SkyBlockBingoDataReply.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply.skyblock.bingo; 2 | 3 | import net.hypixel.api.reply.RateLimitedReply; 4 | 5 | import java.util.List; 6 | 7 | public class SkyBlockBingoDataReply extends RateLimitedReply { 8 | private List events; 9 | 10 | public List getEvents() { 11 | return events; 12 | } 13 | 14 | @Override 15 | public String toString() { 16 | return "SkyBlockBingoPlayerDataReply{" + 17 | "events=" + events + 18 | "} " + super.toString(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/skyblock/firesales/FireSaleItem.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply.skyblock.firesales; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | import java.time.ZonedDateTime; 6 | 7 | public class FireSaleItem { 8 | @SerializedName("item_id") 9 | private String itemId; 10 | private ZonedDateTime start; 11 | private ZonedDateTime end; 12 | private int amount; 13 | private int price; 14 | 15 | public String getItemId() { 16 | return itemId; 17 | } 18 | 19 | public ZonedDateTime getStart() { 20 | return start; 21 | } 22 | 23 | public ZonedDateTime getEnd() { 24 | return end; 25 | } 26 | 27 | public int getAmount() { 28 | return amount; 29 | } 30 | 31 | public int getPrice() { 32 | return price; 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "FireSaleItem{" + 38 | "itemId='" + itemId + '\'' + 39 | ", start=" + start + 40 | ", end=" + end + 41 | ", amount=" + amount + 42 | ", price=" + price + 43 | '}'; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/reply/skyblock/firesales/SkyBlockFireSalesReply.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reply.skyblock.firesales; 2 | 3 | import net.hypixel.api.reply.AbstractReply; 4 | 5 | import java.util.List; 6 | 7 | public class SkyBlockFireSalesReply extends AbstractReply { 8 | private List sales; 9 | 10 | public List getSales() { 11 | return sales; 12 | } 13 | 14 | @Override 15 | public String toString() { 16 | return "SkyBlockFireSalesReply{" + 17 | "sales=" + sales + 18 | "} " + super.toString(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/util/Banner.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.util; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import java.util.Collections; 5 | import java.util.List; 6 | import java.util.Objects; 7 | 8 | /** 9 | * A Minecraft-style banner design. 10 | *


11 | * 12 | * Color Code Reference 13 | *

    14 | *
  • {@code 0}: Black
  • 15 | *
  • {@code 1}: Red
  • 16 | *
  • {@code 2}: Green
  • 17 | *
  • {@code 3}: Brown
  • 18 | *
  • {@code 4}: Blue
  • 19 | *
  • {@code 5}: Purple
  • 20 | *
  • {@code 6}: Cyan
  • 21 | *
  • {@code 7}: Silver (Light Grey)
  • 22 | *
  • {@code 8}: Grey (Dark Grey)
  • 23 | *
  • {@code 9}: Pink
  • 24 | *
  • {@code 10}: Lime
  • 25 | *
  • {@code 11}: Yellow
  • 26 | *
  • {@code 12}: Light Blue
  • 27 | *
  • {@code 13}: Magenta
  • 28 | *
  • {@code 14}: Orange
  • 29 | *
  • {@code 15}: White
  • 30 | *
31 | * These numeric color codes are returned by the following methods: 32 | *
    33 | *
  • {@link #getBase()}
  • 34 | *
  • {@link Pattern#getColor()}
  • 35 | *
36 | * 37 | * @see Banner (Minecraft Wiki) 38 | */ 39 | public class Banner { 40 | 41 | @SerializedName("Base") 42 | private String baseColor; 43 | @SerializedName("Patterns") 44 | private List patterns; 45 | 46 | /** 47 | * An integer (wrapped in a string) indicating the background/base color of the banner. See the 48 | * linked cheat-sheet for a list of possible values. 49 | * 50 | * @return the banner's background color. 51 | * @see Color code cheat-sheet 52 | */ 53 | public String getBaseColor() { 54 | return baseColor; 55 | } 56 | 57 | /** 58 | * @deprecated Renamed to {@link #getBaseColor()}. 59 | */ 60 | @Deprecated 61 | public String getBase() { 62 | return getBaseColor(); 63 | } 64 | 65 | /** 66 | * The shapes that compose the banner, minus the {@link #getBaseColor() base/background layer}. 67 | * Patterns in the list are ordered from background to foreground, meaning that the last pattern 68 | * in the list will always be displayed on top. 69 | * 70 | * @return an immutable list of the banner's layers. 71 | */ 72 | public List getPatterns() { 73 | return patterns == null 74 | ? Collections.emptyList() 75 | : Collections.unmodifiableList(patterns); 76 | } 77 | 78 | @Override 79 | public String toString() { 80 | return "Banner{" + 81 | "baseColor='" + baseColor + '\'' + 82 | ", patterns=" + patterns + 83 | '}'; 84 | } 85 | 86 | @Override 87 | public boolean equals(Object o) { 88 | if (this == o) { 89 | return true; 90 | } 91 | if (o == null || getClass() != o.getClass()) { 92 | return false; 93 | } 94 | Banner banner = (Banner) o; 95 | return Objects.equals(baseColor, banner.baseColor) && 96 | Objects.equals(patterns, banner.patterns); 97 | } 98 | 99 | @Override 100 | public int hashCode() { 101 | return Objects.hash(baseColor, patterns); 102 | } 103 | 104 | /** 105 | * A colored shape that makes up a layer of a {@link Banner} design. 106 | */ 107 | public static class Pattern { 108 | 109 | @SerializedName("Pattern") 110 | private String type; 111 | @SerializedName("Color") 112 | private String color; 113 | 114 | /** 115 | * A short identifier indicating the shape to be used for the layer. See the link below for 116 | * each type's identifier. 117 | * 118 | * @return the pattern's type identifier. 119 | * @see Pattern identifiers 120 | */ 121 | public String getType() { 122 | return type; 123 | } 124 | 125 | /** 126 | * @deprecated Renamed to {@link #getType()}. 127 | */ 128 | @Deprecated 129 | public String getPattern() { 130 | return getType(); 131 | } 132 | 133 | /** 134 | * An integer (wrapped in a string) indicating the color used to draw the pattern's shape. 135 | * See the linked cheat-sheet for a list of possible values. 136 | * 137 | * @return the pattern's color. 138 | * @see Banner Color code cheat-sheet 139 | */ 140 | public String getColor() { 141 | return color; 142 | } 143 | 144 | @Override 145 | public String toString() { 146 | return "Pattern{" + 147 | "type='" + type + '\'' + 148 | ", color='" + color + '\'' + 149 | '}'; 150 | } 151 | 152 | @Override 153 | public boolean equals(Object o) { 154 | if (this == o) { 155 | return true; 156 | } 157 | if (o == null || getClass() != o.getClass()) { 158 | return false; 159 | } 160 | Pattern pattern = (Pattern) o; 161 | return Objects.equals(type, pattern.type) && 162 | Objects.equals(color, pattern.color); 163 | } 164 | 165 | @Override 166 | public int hashCode() { 167 | return Objects.hash(type, color); 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/util/IGuildLeveling.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.util; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | public interface IGuildLeveling { 8 | 9 | // TODO: 6/6/20 Finish javadocs and add examples 10 | 11 | /** 12 | * An unmodifiable list containing the experience needed to get from each level to the next. For 13 | * example, the value at index 0 is the amount of guild experience needed to progress from guild 14 | * level 0 to 1. The value at index 7 is the guild exp needed to progress from level 7 -> 8. 15 | * Etc. 16 | *

17 | * The last element in this list is the value used for any levels beyond the size of this list. 18 | * For example, if this list has 15 values, then the last value is used for levels 14 -> 15 and 19 | * any levels after that. 20 | */ 21 | List EXP_NEEDED = Collections.unmodifiableList(Arrays.asList( 22 | 100000, // Lvl 0 -> Lvl 1 23 | 150000, // Lvl 1 -> Lvl 2 24 | 250000, // Lvl 2 -> Lvl 3 25 | 500000, // Etc 26 | 750000, 27 | 1000000, 28 | 1250000, 29 | 1500000, 30 | 2000000, 31 | 2500000, 32 | 2500000, 33 | 2500000, 34 | 2500000, 35 | 2500000, 36 | 3000000 37 | )); 38 | 39 | /** 40 | * The last value in {@link #EXP_NEEDED}. This represents exp difference between any two levels 41 | * >= {@link #EXP_NEEDED}.size() - 1. 42 | * 43 | * @see #EXP_NEEDED 44 | */ 45 | int MAX_EXP_NEEDED = EXP_NEEDED.get(EXP_NEEDED.size() - 1); 46 | 47 | /** 48 | * This method returns the full level of a guild with that amount of guild experience. This 49 | * method does not take into account the guild's progress to the next level, but will return an 50 | * integer representing the last whole guild level reached by the guild. If the experience 51 | * parameter is less than 0, an {@link IllegalArgumentException} will be thrown. 52 | * 53 | * @param exp The total experience gathered by a guild; should be >= 0 54 | * @return An integer representing the guild's current whole level 55 | */ 56 | static double getLevel(double exp) { 57 | if (exp < 0) { 58 | throw new IllegalArgumentException("Experience value must be >= 0"); 59 | } 60 | 61 | for (int level = 0; ; level++) { 62 | double needed = getExpFromLevelToNext(level); 63 | exp -= needed; 64 | 65 | if (exp < 0) { 66 | return level; 67 | } 68 | } 69 | } 70 | 71 | /** 72 | * This method returns the precise guild level for that amount of guild experience. This is the 73 | * equivalent of adding up the result of {@link #getLevel(double)} and {@link 74 | * #getPercentageToNextLevel(double)}. The value returned by this method is a floating point 75 | * number greater than or equal to 0, representing the guild's previse level. If the experience 76 | * parameter is less than 0, an {@link IllegalArgumentException} may be thrown. 77 | * 78 | * @param exp The total experience gathered by a guild; should be >= 0 79 | * @return Exact level of a guild with that much experience 80 | */ 81 | static double getExactLevel(double exp) { 82 | return getLevel(exp) + getPercentageToNextLevel(exp); 83 | } 84 | 85 | /** 86 | * This method returns the amount of experience needed to go from that level to the next. If the 87 | * level parameter is less than 0, an {@link IllegalArgumentException} will be thrown. 88 | * 89 | * @param level The starting level 90 | * @return The amount of guild exp needed to progress from that level to the next level 91 | * @see #EXP_NEEDED 92 | * @see #MAX_EXP_NEEDED 93 | */ 94 | static double getExpFromLevelToNext(double level) { 95 | if (level < 0) { 96 | throw new IllegalArgumentException("Level value must be >= 0"); 97 | } 98 | 99 | return level >= EXP_NEEDED.size() ? MAX_EXP_NEEDED : EXP_NEEDED.get((int) level); 100 | } 101 | 102 | /** 103 | * This method returns the amount of guild experience needed to reach a precise level. For 104 | * example, passing in a level of 10.5 will return the amount of exp needed for level 10 plus 105 | * half the amount of exp needed between levels 10 and 11. If the level parameter is less than 106 | * 0, an {@link IllegalArgumentException} may be thrown. 107 | * 108 | * @param level The precise level reached with the returned amount of experience; should be >= 109 | * 0 110 | * @return The total experience needed to reach that precise level 111 | */ 112 | static double getTotalExpToLevel(double level) { 113 | double progress = level - (int) level; 114 | return getTotalExpToFullLevel(level) + (progress * getExpFromLevelToNext(level)); 115 | } 116 | 117 | /** 118 | * This method returns the total amount of exp needed for a guild to reach a whole level 119 | * (integer). For example, if a guild had 0 experience, this method would return how much 120 | * experience they would need before they reached level 5.0. If the level parameter is less than 121 | * 0, an {@link IllegalArgumentException} may be thrown. 122 | * 123 | * @param level The level reached with the returned amount of exp; should be an integer 124 | * @return The total amount of experience needed to reach that level 125 | */ 126 | static double getTotalExpToFullLevel(double level) { 127 | double expNeeded = 0; 128 | 129 | for (int i = 0; i < (int) level; i++) { 130 | expNeeded += getExpFromLevelToNext(i); 131 | } 132 | 133 | return expNeeded; 134 | } 135 | 136 | /** 137 | * This method returns a guild's current progress to the next level as a floating-point number 138 | * between 0 (inclusively) and 1 (exclusively). For example, if a guild has an experience value 139 | * exactly halfway between the exp needed for their current level (floored) and the exp needed 140 | * for the next level (floored), this method will return 0.5. If the experience parameter is 141 | * less than 0, an {@link IllegalArgumentException} will be thrown. 142 | * 143 | * @param exp The total experience gathered by a guild; should be >= 0 144 | * @return The guild's progress to the next level as a percentage between 0 and 1 145 | */ 146 | static double getPercentageToNextLevel(double exp) { 147 | if (exp < 0) { 148 | throw new IllegalArgumentException("Experience value must be >= 0"); 149 | } 150 | 151 | double currentLvl = getLevel(exp); 152 | // Exp needed for the current whole level (excluding progress) 153 | double totalExpForCurrentLvl = getTotalExpToFullLevel(currentLvl); 154 | // Exp diff between current whole level and next whole level 155 | double expToNextLvl = getExpFromLevelToNext(currentLvl); 156 | 157 | return (exp - (totalExpForCurrentLvl)) / expToNextLvl; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/util/ILeveling.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.util; 2 | 3 | public interface ILeveling { 4 | 5 | String EXP_FIELD = "networkExp"; 6 | String LVL_FIELD = "networkLevel"; 7 | 8 | double BASE = 10_000; 9 | double GROWTH = 2_500; 10 | 11 | /* Constants to generate the total amount of XP to complete a level */ 12 | double HALF_GROWTH = 0.5 * GROWTH; 13 | 14 | /* Constants to look up the level from the total amount of XP */ 15 | double REVERSE_PQ_PREFIX = -(BASE - 0.5 * GROWTH) / GROWTH; 16 | double REVERSE_CONST = REVERSE_PQ_PREFIX * REVERSE_PQ_PREFIX; 17 | double GROWTH_DIVIDES_2 = 2 / GROWTH; 18 | 19 | /** 20 | * This method returns the level of a player calculated by the current experience gathered. The result is 21 | * a precise level of the player. The value is not zero-indexed and represents the absolute visible level 22 | * for the player. 23 | * The result can't be smaller than 1 and negative experience results in level 1. 24 | *

25 | * Examples: 26 | * - 0 XP = 1.0 27 | * - 5000 XP = 1.0 28 | * - 10000 XP = 2.0 29 | * - 50000 XP = 4.0 30 | * - 79342431 XP = 249.0 31 | * 32 | * @param exp Total experience gathered by the player. 33 | * @return Absolute level of player (Smallest value is 1.0) 34 | */ 35 | static double getLevel(double exp) { 36 | return exp < 0 ? 1 : Math.floor(1 + REVERSE_PQ_PREFIX + Math.sqrt(REVERSE_CONST + GROWTH_DIVIDES_2 * exp)); 37 | } 38 | 39 | /** 40 | * This method returns the level of a player calculated by the current experience gathered. The result is 41 | * a precise level of the player. The value is not zero-indexed and represents the visible level 42 | * for the player. 43 | * The result can't be smaller than 1 and negative experience results in level 1. 44 | *

45 | * Examples: 46 | * - 0 XP = 1.0 47 | * - 5000 XP = 1.5 48 | * - 10000 XP = 2.0 49 | * - 50000 XP = 4.71... 50 | * - 79342431 XP = 249.46... 51 | * 52 | * @param exp Total experience gathered by the player. 53 | * @return Exact level of player (Smallest value is 1.0) 54 | */ 55 | static double getExactLevel(double exp) { 56 | return ILeveling.getLevel(exp) + ILeveling.getPercentageToNextLevel(exp); 57 | } 58 | 59 | /** 60 | * This method returns the amount of experience that is needed to progress from level to level + 1. (5 to 6) 61 | * The levels passed must absolute levels with the smallest level being 1. Smaller values always return 62 | * the BASE constant. The calculation is precise and if a decimal is passed it returns the XP from the 63 | * progress of this level to the next level with the same progress. (5.5 to 6.5) 64 | *

65 | * Examples: 66 | * - 1 (to 2) = 10000.0 XP 67 | * - 2 (to 3) = 12500.0 XP 68 | * - 3 (to 4) = 15000.0 XP 69 | * - 5 (to 6) = 20000.0 XP 70 | * - 5.5 (to 6.5) = 21250.0 XP 71 | * - 130 (to 131) = 332500.0 XP 72 | * - 250 (to 251) = 632500.0 XP 73 | * 74 | * @param level Level from which you want to get the next level with the same level progress 75 | * @return Experience to reach the next level with same progress 76 | */ 77 | static double getExpFromLevelToNext(double level) { 78 | return level < 1 ? BASE : GROWTH * (level - 1) + BASE; 79 | } 80 | 81 | /** 82 | * This method returns the experience it needs to reach that level. If you want to reach the given level 83 | * you have to gather the amount of experience returned by this method. This method is precise, that means 84 | * you can pass any progress of a level to receive the experience to reach that progress. (5.764 to get 85 | * the experience to reach level 5 with 76.4% of level 6. 86 | *

87 | * Examples: 88 | * - 1.0 = 0.0 XP 89 | * - 2.0 = 10000.0 XP 90 | * - 3.0 = 22500.0 XP 91 | * - 5.0 = 55000.0 XP 92 | * - 5.764 = 70280.0 XP 93 | * - 130.0 = 21930000.0 XP 94 | * - 250.43 = 79951975.0 XP 95 | * 96 | * @param level The level and progress of the level to reach 97 | * @return The experience required to reach that level and progress 98 | */ 99 | static double getTotalExpToLevel(double level) { 100 | double lv = Math.floor(level), x0 = ILeveling.getTotalExpToFullLevel(lv); 101 | if (level == lv) return x0; 102 | return (ILeveling.getTotalExpToFullLevel(lv + 1) - x0) * (level % 1) + x0; 103 | } 104 | 105 | /** 106 | * Helper method that may only be called by full levels and has the same functionality as getTotalExpToLevel() 107 | * but doesn't support progress and returns wrong values for progress due to perfect curve shape. 108 | * 109 | * @param level Level to receive the amount of experience to 110 | * @return Experience to reach the given level 111 | */ 112 | static double getTotalExpToFullLevel(double level) { 113 | return (HALF_GROWTH * (level - 2) + BASE) * (level - 1); 114 | } 115 | 116 | /** 117 | * This method returns the current progress of this level to reach the next level. This method is as 118 | * precise as possible due to rounding errors on the mantissa. The first 10 decimals are totally 119 | * accurate. 120 | *

121 | * Examples: 122 | * - 5000.0 XP (Lv. 1) = 0.5 (50 %) 123 | * - 22499.0 XP (Lv. 2) = 0.99992 (99.992 %) 124 | * - 5324224.0 XP (Lv. 62) = 0.856763076923077 (85.6763076923077 %) 125 | * - 23422443.0 XP (Lv. 134) = 0.4304905109489051 (43.04905109489051 %) 126 | * 127 | * @param exp Current experience gathered by the player 128 | * @return Current progress to the next level 129 | */ 130 | static double getPercentageToNextLevel(double exp) { 131 | double lv = ILeveling.getLevel(exp), x0 = getTotalExpToLevel(lv); 132 | return (exp - x0) / (ILeveling.getTotalExpToLevel(lv + 1) - x0); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/util/PropertyFilter.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.util; 2 | 3 | import java.util.HashSet; 4 | import java.util.Iterator; 5 | import java.util.Objects; 6 | import java.util.Set; 7 | import java.util.stream.Collectors; 8 | 9 | /** 10 | * A tool for trimming unneeded properties from data, especially to minimize their memory and 11 | * storage consumption. Based on MongoDB projections. 12 | *


13 | * To use an inclusion filter, property names (or "keys") can be added via {@link 14 | * #include(String...) include(...)} or the {@link #including(String...) including(...) 15 | * constructor}. When an object is passed through the filter, any properties not explicitly named 16 | * using the aforementioned methods will be removed from the object. If the object did not have an 17 | * included property to begin with, it will not be created. 18 | *


19 | * Property names are referenced using dot-notation. See the documentation for {@link 20 | * #include(String...) include(...)} for more details. 21 | */ 22 | public class PropertyFilter { 23 | 24 | /** 25 | * Shorthand for constructing a new filter that only allows the {@code includedKeys} to pass 26 | * through. See {@link #include(String...)} for the key syntax. 27 | */ 28 | public static PropertyFilter including(String... includedKeys) { 29 | PropertyFilter filter = new PropertyFilter(); 30 | filter.include(includedKeys); 31 | return filter; 32 | } 33 | 34 | // Only these keys are allowed in objects passed through. 35 | protected final Set allowedKeys; 36 | 37 | public PropertyFilter() { 38 | allowedKeys = new HashSet<>(); 39 | } 40 | 41 | /** 42 | * Allows properties with any of the provided {@code keys} to pass through the filter. To 43 | * include nested properties, use dots ({@code .}) to separate each parent property from its 44 | * child. If a property's name contains a dot literally, use a double-backslash to escape the 45 | * dot. (e.g. {@code "key_with_literal_dot\\.in_it"} instead of {@code 46 | * "key_with_literal_dot.in_it"}) 47 | *

 48 |      * Examples:
 49 |      *     •{@code uuid}                - Keep the player's UUID when filtering.
 50 |      *     •{@code stats}               - Keep all of the player's stats when filtering.
 51 |      *     •{@code stats.SkyWars}       - Keep all of the player's SkyWars stats when filtering.
 52 |      *     •{@code stats.SkyWars.coins} - Keep just the player's SkyWars coins when filtering.
 53 |      * 
54 | * If an added key conflicts with an existing one, the newer key takes precedence. 55 | * 56 | * @param keys Names of properties that will be allowed to pass through the filter (in 57 | * dot-notation). 58 | */ 59 | public void include(String... keys) { 60 | if (keys == null) { 61 | throw new IllegalArgumentException("Cannot include null property keys"); 62 | } 63 | 64 | // Check for key collisions. 65 | for (String rawKey : keys) { 66 | if (rawKey == null) { 67 | throw new IllegalArgumentException("Cannot include null property keys"); 68 | } 69 | 70 | PropertyKey key = new PropertyKey(rawKey); 71 | boolean shouldAddKey = true; 72 | Iterator existingKeys = allowedKeys.iterator(); 73 | 74 | while (existingKeys.hasNext()) { 75 | PropertyKey existingKey = existingKeys.next(); 76 | 77 | // Ignore duplicate keys. 78 | if (existingKey.equals(key)) { 79 | shouldAddKey = false; 80 | break; 81 | } 82 | 83 | // Check if the new key collides with the existing key's scope. 84 | if (key.isExtendedBy(existingKey)) { 85 | // Replace & continue, since there can be multiple keys with narrower scopes. 86 | existingKeys.remove(); 87 | } else if (existingKey.isExtendedBy(key)) { 88 | // Replace & break, since only 1 key should possibly have a wider scope. 89 | existingKeys.remove(); 90 | break; 91 | } 92 | } 93 | 94 | if (shouldAddKey) { 95 | allowedKeys.add(key); 96 | } 97 | } 98 | } 99 | 100 | /** 101 | * Removes all of the provided keys from the filter, such that objects passed through the filter 102 | * will not include properties with those keys. 103 | *


104 | * Attempting to remove a key that was already removed, or never {@link #include(String...) 105 | * included} to begin with, will have no effect. 106 | */ 107 | public void remove(String... keys) { 108 | if (keys == null) { 109 | throw new IllegalArgumentException("Cannot remove null keys"); 110 | } 111 | 112 | for (String key : keys) { 113 | if (key == null) { 114 | throw new IllegalArgumentException("Cannot remove null keys"); 115 | } 116 | allowedKeys.removeIf(existingKey -> existingKey.toString().equals(key)); 117 | } 118 | } 119 | 120 | /** 121 | * @return A new set containing all property keys that can pass through the filter. 122 | * @see #include(String...) 123 | */ 124 | public Set getIncluded() { 125 | return allowedKeys.stream() 126 | .map(PropertyKey::toString) 127 | .collect(Collectors.toSet()); 128 | } 129 | 130 | /** 131 | * The key a property in an object, potentially one nested inside multiple other objects. 132 | */ 133 | protected static final class PropertyKey { 134 | 135 | // The key's full stringified form. Literal dots (.) should still have escape characters. 136 | final String full; 137 | 138 | // Each "part" of the key, delimited by un-escaped dots in the `full` key. 139 | final String[] tokens; 140 | 141 | PropertyKey(String full) { 142 | if (full == null) { 143 | throw new IllegalArgumentException("Property key cannot be null"); 144 | } 145 | this.full = full; 146 | tokens = Utilities.tokenizeKey(full); 147 | } 148 | 149 | /** 150 | * @return {@code true} if the {@code other} key starts with all of this key's {@link 151 | * #tokens} & has additional tokens at the end, otherwise {@code false}. 152 | */ 153 | boolean isExtendedBy(PropertyKey other) { 154 | String otherFull = other.full; 155 | int extensionIndex = full.length(); 156 | 157 | // (1) `other` cannot possibly be an extension if it has a shorter or equal length. 158 | // (2) Check that the key continues immediately after extensionIndex. 159 | // (3) Check that the dot (.) we found in (2) wasn't escaped. 160 | // (4) Check that the other key starts with this entire key. 161 | return otherFull.length() > full.length() 162 | && otherFull.charAt(extensionIndex) == '.' 163 | && otherFull.charAt(extensionIndex - 1) != '\\' 164 | && otherFull.startsWith(full); 165 | } 166 | 167 | @Override 168 | public boolean equals(Object o) { 169 | if (this == o) { 170 | return true; 171 | } 172 | if (o == null) { 173 | return false; 174 | } 175 | if (o instanceof PropertyKey) { 176 | return full.equals(((PropertyKey) o).full); 177 | } 178 | if (o instanceof String) { 179 | return full.equals(o); 180 | } 181 | return false; 182 | } 183 | 184 | @Override 185 | public int hashCode() { 186 | return Objects.hash(full); 187 | } 188 | 189 | @Override 190 | public String toString() { 191 | return full; 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/util/Rarity.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.util; 2 | 3 | import net.hypixel.api.pets.IPetRarity; 4 | 5 | public enum Rarity implements IPetRarity { 6 | 7 | COMMON("GREEN"), 8 | RARE("BLUE"), 9 | EPIC("DARK_PURPLE"), 10 | LEGENDARY("GOLD"), 11 | ; 12 | 13 | private final String name; 14 | private final String color; 15 | 16 | Rarity(String color) { 17 | this.name = name(); 18 | this.color = color; 19 | } 20 | 21 | @Override 22 | public String getName() { 23 | return name; 24 | } 25 | 26 | @Override 27 | public String getColor() { 28 | return color; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/util/ResourceType.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.util; 2 | 3 | public enum ResourceType { 4 | 5 | ACHIEVEMENTS("achievements"), 6 | CHALLENGES("challenges"), 7 | QUEST("quests"), 8 | GUILDS_ACHIEVEMENTS("guilds/achievements"), 9 | VANITY_PETS("vanity/pets"), 10 | VANITY_COMPANIONS("vanity/companions"), 11 | SKYBLOCK_COLLECTIONS("skyblock/collections"), 12 | SKYBLOCK_SKILLS("skyblock/skills"), 13 | SKYBLOCK_ITEMS("skyblock/items"), 14 | SKYBLOCK_ELECTION("skyblock/election"), 15 | SKYBLOCK_BINGO("skyblock/bingo"), 16 | ; 17 | 18 | /** 19 | * Path to resource 20 | */ 21 | private final String path; 22 | 23 | ResourceType(String path) { 24 | this.path = path; 25 | } 26 | 27 | public String getPath() { 28 | return path; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/util/UnstableHypixelObject.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.util; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonNull; 6 | import com.google.gson.JsonObject; 7 | import net.hypixel.api.util.PropertyFilter.PropertyKey; 8 | 9 | import java.util.Map.Entry; 10 | 11 | /** 12 | * An object returned from the Hypixel API that lacks a defined structure. 13 | */ 14 | public abstract class UnstableHypixelObject { 15 | 16 | protected final JsonObject raw; 17 | 18 | protected UnstableHypixelObject(JsonElement raw) { 19 | this.raw = raw instanceof JsonObject 20 | ? (JsonObject) raw 21 | : new JsonObject(); 22 | } 23 | 24 | /** 25 | * @return The raw object returned by the Hypixel API; the source of any properties for the 26 | * object 27 | */ 28 | public JsonObject getRaw() { 29 | return raw; 30 | } 31 | 32 | /** 33 | * @param key Dot-notation path to the desired field 34 | * @return {@code true} if the object has a value associated with the {@code key}, including if 35 | * that value is {@link JsonNull}. Otherwise {@code false}. 36 | * @see #getProperty(String) 37 | */ 38 | public boolean hasProperty(String key) { 39 | return getProperty(key) != null; 40 | } 41 | 42 | /** 43 | * Strips the object of any properties that haven't explicitly been allowed via the filter's 44 | * {@link PropertyFilter#include(String...) include()} method or the {@link 45 | * PropertyFilter#including(String...) including()} constructor. 46 | *


47 | * The resulting object will (at most) only contain the properties returned by {@link 48 | * PropertyFilter#getIncluded() filter#getIncluded()}. If the object does not already have any 49 | * of the included keys, they will not be added. 50 | * 51 | * @throws IllegalArgumentException If the {@code filter} is {@code null}. 52 | */ 53 | public void filter(PropertyFilter filter) { 54 | if (filter == null) { 55 | throw new IllegalArgumentException("Cannot use a null filter"); 56 | } else if (raw.entrySet().isEmpty()) { 57 | // Ignore empty objects. 58 | return; 59 | } 60 | 61 | JsonObject temp = new JsonObject(); 62 | for (PropertyKey key : filter.allowedKeys) { 63 | JsonElement value = getProperty(key.toString()); 64 | if (value == null) { 65 | // Ignore null properties. 66 | continue; 67 | } 68 | 69 | // Create any required parents for the property, similar to File#mkdirs(). 70 | JsonObject parent = temp; 71 | String[] tokens = key.tokens; 72 | for (int i = 0; i < tokens.length; i++) { 73 | String token = tokens[i]; 74 | String escapedToken = token.replace("\\.", "."); 75 | 76 | if (i < tokens.length - 1) { 77 | 78 | // Use the existing child object (if one exists). 79 | JsonElement existingChild = parent.get(token); 80 | if (existingChild instanceof JsonObject) { 81 | parent = (JsonObject) existingChild; 82 | continue; 83 | } 84 | 85 | // Create a new child object if one doesn't exist. 86 | JsonObject child = new JsonObject(); 87 | parent.add(escapedToken, child); 88 | parent = child; 89 | } else { 90 | // Set the final value of the property. 91 | parent.add(escapedToken, value); 92 | } 93 | } 94 | } 95 | 96 | // Replace the contents of the original object. 97 | raw.entrySet().clear(); 98 | for (Entry property : temp.entrySet()) { 99 | raw.add(property.getKey(), property.getValue()); 100 | } 101 | } 102 | 103 | /** 104 | * Get a String from the object 105 | * 106 | * @return The string value associated with the {@code key}, or {@code defaultValue} if the 107 | * value does not exist or isn't a string 108 | * @see #getProperty(String) 109 | */ 110 | public String getStringProperty(String key, String defaultValue) { 111 | JsonElement value = getProperty(key); 112 | if (value == null 113 | || !value.isJsonPrimitive() 114 | || !value.getAsJsonPrimitive().isString()) { 115 | return defaultValue; 116 | } 117 | return value.getAsJsonPrimitive().getAsString(); 118 | } 119 | 120 | /** 121 | * Get a float from the object 122 | * 123 | * @return The float value associated with the {@code key}, or {@code defaultValue} if the value 124 | * does not exist or isn't a float 125 | * @see #getProperty(String) 126 | */ 127 | public float getFloatProperty(String key, float defaultValue) { 128 | return getNumberProperty(key, defaultValue).floatValue(); 129 | } 130 | 131 | /** 132 | * Get a double from the object 133 | * 134 | * @return The double value associated with the {@code key}, or {@code defaultValue} if the 135 | * value does not exist or isn't a double 136 | * @see #getProperty(String) 137 | */ 138 | public double getDoubleProperty(String key, double defaultValue) { 139 | return getNumberProperty(key, defaultValue).doubleValue(); 140 | } 141 | 142 | /** 143 | * Get a long from the object 144 | * 145 | * @return The long value associated with the {@code key}, or {@code defaultValue} if the value 146 | * does not exist or isn't a long 147 | * @see #getProperty(String) 148 | */ 149 | public long getLongProperty(String key, long defaultValue) { 150 | return getNumberProperty(key, defaultValue).longValue(); 151 | } 152 | 153 | /** 154 | * Get an integer from the object 155 | * 156 | * @return The int value associated with the {@code key}, or {@code defaultValue} if the value 157 | * does not exist or isn't an int 158 | * @see #getProperty(String) 159 | */ 160 | public int getIntProperty(String key, int defaultValue) { 161 | return getNumberProperty(key, defaultValue).intValue(); 162 | } 163 | 164 | /** 165 | * Get a Number property from the object 166 | * 167 | * @return The numeric value associated with the {@code key}, or {@code defaultValue} if the 168 | * value does not exist or isn't a number 169 | * @see #getProperty(String) 170 | */ 171 | public Number getNumberProperty(String key, Number defaultValue) { 172 | JsonElement value = getProperty(key); 173 | if (value == null 174 | || !value.isJsonPrimitive() 175 | || !value.getAsJsonPrimitive().isNumber()) { 176 | return defaultValue; 177 | } 178 | return value.getAsJsonPrimitive().getAsNumber(); 179 | } 180 | 181 | /** 182 | * Get a boolean from the object 183 | * 184 | * @return The boolean value associated with the {@code key}, or {@code defaultValue} if the 185 | * value does not exist or isn't a boolean 186 | * @see #getProperty(String) 187 | */ 188 | public boolean getBooleanProperty(String key, boolean defaultValue) { 189 | JsonElement value = getProperty(key); 190 | if (value == null 191 | || !value.isJsonPrimitive() 192 | || !value.getAsJsonPrimitive().isBoolean()) { 193 | return defaultValue; 194 | } 195 | return value.getAsJsonPrimitive().getAsBoolean(); 196 | } 197 | 198 | /** 199 | * Get a JsonArray property from the object 200 | * 201 | * @return The JSON array associated with the {@code key}, or an empty array if the value does 202 | * not exist or isn't an array 203 | * @see #getProperty(String) 204 | */ 205 | public JsonArray getArrayProperty(String key) { 206 | JsonElement result = getProperty(key); 207 | if (result == null || !result.isJsonArray()) { 208 | return new JsonArray(); 209 | } 210 | return result.getAsJsonArray(); 211 | } 212 | 213 | /** 214 | * Get a JsonObject property from the object 215 | * 216 | * @return The JSON object associated with the {@code key}, or {@code null} if the value does 217 | * not exist or isn't a JSON object 218 | * @see #getProperty(String) 219 | */ 220 | public JsonObject getObjectProperty(String key) { 221 | JsonElement result = getProperty(key); 222 | if (result == null || !result.isJsonObject()) { 223 | return null; 224 | } 225 | return result.getAsJsonObject(); 226 | } 227 | 228 | /** 229 | * Read a property from the object returned by the API 230 | * 231 | * @param key Dot-notation path to the desired field (e.g. {@code "stats.SkyWars.deaths"}) 232 | * @return The value associated with the specified property, or {@code null} if no value is set 233 | * for that property. 234 | */ 235 | public JsonElement getProperty(String key) { 236 | if (key == null) { 237 | throw new IllegalArgumentException("Property key cannot be null"); 238 | } else if (key.isEmpty()) { 239 | // Return root object if path is empty. 240 | return raw; 241 | } 242 | 243 | String[] tokens = Utilities.tokenizeKey(key); 244 | 245 | // Navigate the raw object until the end of the provided token list. 246 | JsonObject parent = getRaw(); 247 | for (int i = 0; i < tokens.length; i++) { 248 | 249 | JsonElement child = parent.get(tokens[i].replace("\\.", ".")); 250 | if (i + 1 == tokens.length) { 251 | // No more tokens; current child must be the output. 252 | return child; 253 | } 254 | 255 | // More tokens follow; child must be an object to continue. 256 | if (child instanceof JsonObject) { 257 | parent = child.getAsJsonObject(); 258 | continue; 259 | } 260 | break; 261 | } 262 | 263 | return null; 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /hypixel-api-core/src/main/java/net/hypixel/api/util/Utilities.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.util; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import net.hypixel.api.adapters.*; 6 | import net.hypixel.api.data.type.GameType; 7 | import net.hypixel.api.data.type.ServerType; 8 | import net.hypixel.api.reply.BoostersReply; 9 | import net.hypixel.api.reply.PlayerReply.Player; 10 | 11 | import java.time.Instant; 12 | import java.time.ZoneId; 13 | import java.time.ZonedDateTime; 14 | import java.util.UUID; 15 | import java.util.regex.Pattern; 16 | 17 | public final class Utilities { 18 | 19 | private static final Pattern TOKEN_SPLITTER = Pattern.compile("(?(BoostersReply.Booster.class)) 27 | .create(); 28 | 29 | public static ZonedDateTime getDateTime(long timeStamp) { 30 | return Instant.ofEpochMilli(timeStamp).atZone(ZoneId.of("America/New_York")); 31 | } 32 | 33 | /** 34 | * Splits the input {@code key} into tokens, which are delimited by dots ({@code .}) that aren't 35 | * preceded by a backslash ({@code \}). 36 | */ 37 | public static String[] tokenizeKey(String key) { 38 | return TOKEN_SPLITTER.split(key); 39 | } 40 | 41 | public static UUID uuidFromString(String uuidStr) { 42 | if (!uuidStr.contains("-")) { 43 | uuidStr = uuidStr.substring(0, 8) + "-" 44 | + uuidStr.substring(8, 12) + "-" 45 | + uuidStr.substring(12, 16) + "-" 46 | + uuidStr.substring(16, 20) + "-" 47 | + uuidStr.substring(20, 32); 48 | 49 | } 50 | return UUID.fromString(uuidStr); 51 | } 52 | 53 | private Utilities() { 54 | throw new UnsupportedOperationException("Helper class should not be instantiated"); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /hypixel-api-core/src/test/java/net/hypixel/api/TestResources.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api; 2 | 3 | import kong.unirest.HttpResponse; 4 | import kong.unirest.JsonNode; 5 | import kong.unirest.Unirest; 6 | import net.hypixel.api.util.ResourceType; 7 | import org.junit.jupiter.api.Assertions; 8 | import org.junit.jupiter.params.ParameterizedTest; 9 | import org.junit.jupiter.params.provider.Arguments; 10 | import org.junit.jupiter.params.provider.MethodSource; 11 | 12 | import java.util.stream.Stream; 13 | 14 | public class TestResources { 15 | 16 | static Stream getResourceTypes() { 17 | return Stream.of(ResourceType.values()) 18 | .map(Arguments::of); 19 | } 20 | 21 | @ParameterizedTest 22 | @MethodSource("getResourceTypes") 23 | void testResource(ResourceType resourceType) { 24 | String url = String.format("%sresources/%s", HypixelAPI.BASE_URL, resourceType.getPath()); 25 | HttpResponse response = Unirest.get(url).asJson(); 26 | Assertions.assertEquals(200, response.getStatus(), String.format("Got an invalid status code for %s", resourceType)); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /hypixel-api-example/README.md: -------------------------------------------------------------------------------- 1 | # HypixelAPI Java Examples 2 | 3 | This codebase serves as examples for how to integrate the HypixelAPI into your project. -------------------------------------------------------------------------------- /hypixel-api-example/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | hypixel-api 7 | net.hypixel 8 | 4.4 9 | 10 | 4.0.0 11 | 12 | hypixel-api-example 13 | 14 | 15 | true 16 | 17 | 18 | 19 | 20 | 21 | org.apache.maven.plugins 22 | maven-compiler-plugin 23 | 24 | 1.8 25 | 1.8 26 | 27 | 28 | 29 | org.apache.maven.plugins 30 | maven-surefire-plugin 31 | 2.22.2 32 | 33 | 34 | 35 | 36 | 37 | 38 | net.hypixel 39 | hypixel-api-transport-apache 40 | 4.4 41 | 42 | 43 | org.junit.jupiter 44 | junit-jupiter 45 | 5.9.3 46 | test 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /hypixel-api-example/src/main/java/net/hypixel/api/example/ExampleUtil.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.example; 2 | 3 | import net.hypixel.api.HypixelAPI; 4 | import net.hypixel.api.apache.ApacheHttpClient; 5 | import net.hypixel.api.reply.AbstractReply; 6 | 7 | import java.util.UUID; 8 | import java.util.function.BiConsumer; 9 | 10 | public class ExampleUtil { 11 | 12 | private static String getApiKey() { 13 | String apiKey = System.getenv("HYPIXEL_API_KEY"); 14 | if (apiKey != null) { 15 | return apiKey; 16 | } 17 | 18 | return System.getProperty("apiKey", "64bd424e-ccb0-42ed-8b66-6e42a135afb4"); // arbitrary key, replace with your own to test or use the property 19 | } 20 | 21 | public static final HypixelAPI API; 22 | 23 | static { 24 | API = new HypixelAPI(new ApacheHttpClient(UUID.fromString(getApiKey()))); 25 | } 26 | 27 | public static final UUID HYPIXEL = UUID.fromString("f7c77d99-9f15-4a66-a87d-c4a51ef30d19"); 28 | public static final String GUILD_ID = "53bd67d7ed503e868873eceb"; 29 | 30 | /** 31 | * Keep the program alive till we explicitly exit. 32 | */ 33 | public static void await() { 34 | while (!Thread.interrupted()) { 35 | try { 36 | Thread.sleep(1000); 37 | } catch (InterruptedException e) { 38 | e.printStackTrace(); 39 | } 40 | } 41 | } 42 | 43 | public static BiConsumer getTestConsumer() { 44 | return (result, throwable) -> { 45 | if (throwable != null) { 46 | throwable.printStackTrace(); 47 | System.exit(0); 48 | return; 49 | } 50 | 51 | System.out.println(result); 52 | 53 | System.exit(0); 54 | }; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /hypixel-api-example/src/main/java/net/hypixel/api/example/GetBoostersExample.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.example; 2 | 3 | public class GetBoostersExample { 4 | public static void main(String[] args) { 5 | ExampleUtil.API.getBoosters().whenComplete(ExampleUtil.getTestConsumer()); 6 | ExampleUtil.await(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /hypixel-api-example/src/main/java/net/hypixel/api/example/GetCountsExample.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.example; 2 | 3 | public class GetCountsExample { 4 | public static void main(String[] args) { 5 | ExampleUtil.API.getCounts().whenComplete(ExampleUtil.getTestConsumer()); 6 | ExampleUtil.await(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /hypixel-api-example/src/main/java/net/hypixel/api/example/GetGuildExample.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.example; 2 | 3 | import java.time.LocalDate; 4 | import java.util.ArrayList; 5 | import java.util.Comparator; 6 | import java.util.List; 7 | import java.util.concurrent.ExecutionException; 8 | import net.hypixel.api.HypixelAPI; 9 | import net.hypixel.api.data.type.GameType; 10 | import net.hypixel.api.data.type.GuildAchievement; 11 | import net.hypixel.api.reply.GuildReply; 12 | import net.hypixel.api.reply.GuildReply.Guild; 13 | import net.hypixel.api.reply.GuildReply.Guild.Member; 14 | import net.hypixel.api.reply.GuildReply.Guild.Rank; 15 | import net.hypixel.api.util.IGuildLeveling; 16 | 17 | /** 18 | * A sample app for demonstrating how guilds can be fetched & used from the Hypixel API. 19 | */ 20 | public class GetGuildExample { 21 | 22 | public static void main(String[] args) { 23 | /* 24 | * Make sure you have a HypixelAPI object set up. You can see how this is done by going to 25 | * the ExampleUtil class. 26 | * 27 | * See the finally{} block below for how to shutdown this API once you're all done. 28 | */ 29 | HypixelAPI api = ExampleUtil.API; 30 | 31 | /* 32 | * Skip below the try/catch/finally block to see how this is used. 33 | */ 34 | GuildReply apiReply; 35 | 36 | try { 37 | /* 38 | * We'll be fetching the guild's stats using its ID for this example, but guilds can 39 | * also be looked up using their name, or one of their members' Minecraft UUIDs. 40 | * - HypixelAPI.getGuildByName(String) 41 | * - HypixelAPI.getGuildByPlayer(UUID) 42 | */ 43 | String guildId = ExampleUtil.GUILD_ID; 44 | 45 | /* 46 | * Here, we store the response from the API in our variable. 47 | * 48 | * We call `.get()` at the end so that we can use the reply in the same thread. 49 | * The downside is that the current thread freezes (or "blocks") until the API responds. 50 | * If this is a problem for you, instead use: 51 | * 52 | * .whenComplete((apiReply, error) -> { 53 | * // Do something with apiReply (in a different thread)... 54 | * }); 55 | * 56 | * But for a simple command-line app like this one, `.get()` will do the job. 57 | */ 58 | apiReply = api.getGuildById(guildId).get(); 59 | 60 | } catch (ExecutionException e) { 61 | System.err.println("Oh no, our API request failed!"); 62 | 63 | /* 64 | * If an ExecutionException is thrown, it's typically because of an API error. 65 | * Use `getCause()` to determine what the actual problem is. 66 | */ 67 | e.getCause().printStackTrace(); 68 | return; 69 | 70 | } catch (InterruptedException e) { 71 | // Shouldn't happen under normal circumstances. 72 | System.err.println("Oh no, the guild fetch thread was interrupted!"); 73 | e.printStackTrace(); 74 | Thread.currentThread().interrupt(); 75 | return; 76 | 77 | } finally { 78 | /* 79 | * Once you're finished with all your requests, you can shutdown your HypixelAPI object. 80 | * 81 | * If your app is meant to run continuously, you probably don't want to do this until 82 | * the app is stopped/closed. For this example though, we only need the one request. 83 | */ 84 | api.shutdown(); 85 | } 86 | 87 | /* 88 | * Now that we have the guild, we can start to read its information and stats! 89 | */ 90 | Guild guild = apiReply.getGuild(); 91 | 92 | // =================================================== 93 | // Check out the methods referenced below to see how 94 | // each type of stat is retrieved! (their code can be 95 | // found further down in this file) 96 | // =================================================== 97 | 98 | /* 99 | * First we'll display some basic information about the guild, like its name, tag, 100 | * description, and level. 101 | */ 102 | printGuildSummary(guild); 103 | System.out.println(); 104 | 105 | /* 106 | * After that, we'll print the guild's high-score for each tiered guild achievement. 107 | */ 108 | printAchievementScores(guild); 109 | System.out.println(); 110 | 111 | /* 112 | * Then we'll display how much experience the guild has earned from each game on Hypixel, 113 | * as well as an overall total. 114 | */ 115 | printGuildXpPerGame(guild); 116 | System.out.println(); 117 | 118 | /* 119 | * Next, we'll print some info about each of the guild's ranks, if it has any. 120 | */ 121 | List ranks = guild.getRanks(); 122 | if (!ranks.isEmpty()) { 123 | printGuildRanks(ranks); 124 | System.out.println(); 125 | } 126 | 127 | /* 128 | * Finally, we'll print some information about each member in the guild. 129 | * 130 | * This might print out A LOT, so you may want to comment the following line out if you're 131 | * focusing on some of the guild's other info. 132 | */ 133 | printGuildMembers(guild.getMembers()); 134 | } 135 | 136 | private static void printGuildSummary(Guild guild) { 137 | /* 138 | * First, we'll print the guild's name. If it also has a tag, we'll print that on the same 139 | * line. 140 | */ 141 | System.out.print(guild.getName()); 142 | if (guild.getTag() != null) { 143 | System.out.print(" [" + guild.getTag() + "]"); 144 | } 145 | System.out.println(); 146 | 147 | int guildLevel = (int) IGuildLeveling.getLevel(guild.getExperience()); 148 | 149 | System.out.println("\tID: " + guild.getId()); 150 | System.out.println("\tLevel: " + guildLevel); 151 | System.out.println("\tCreated At: " + guild.getCreationDate()); 152 | System.out.println("\tDescription: \"" + guild.getDescription() + '"'); 153 | System.out.println("\tGames: " + guild.getPreferredGames()); 154 | System.out.println("\tBanner: " + guild.getBanner()); 155 | } 156 | 157 | private static void printAchievementScores(Guild guild) { 158 | System.out.println("Guild Achievement High-Scores"); 159 | 160 | /* 161 | * Displays the guild's high-score for each tiered achievement. The meaning of "score" 162 | * varies between achievements, but an explanation for each can be found in the 163 | * `GuildAchievement` class. 164 | */ 165 | for (GuildAchievement achievement : GuildAchievement.values()) { 166 | int highScore = guild.getAchievementHighScore(achievement); 167 | System.out.println("\t" + achievement + ": " + highScore); 168 | } 169 | } 170 | 171 | private static void printGuildXpPerGame(Guild guild) { 172 | System.out.println("Guild XP Breakdown"); 173 | 174 | /* 175 | * This line prints the guild's total experience from all games. 176 | */ 177 | System.out.println("\tOVERALL: " + guild.getExperience()); 178 | 179 | /* 180 | * Then we loop through each game and see how much experience the guild's earned from it. 181 | */ 182 | for (GameType game : GameType.values()) { 183 | long experienceForGame = guild.getExperienceForGame(game); 184 | System.out.println("\t" + game.getName() + ": " + experienceForGame); 185 | } 186 | } 187 | 188 | private static void printGuildRanks(List ranks) { 189 | System.out.println("Ranks (" + ranks.size() + " total)"); 190 | 191 | /* 192 | * This just sorts the list in reverse order by priority, so that higher-level ranks (like 193 | * officer) are printed before lower-level ones (like member). 194 | * 195 | * The first line copies the list beforehand, because `Guild.getRanks()` returns an 196 | * immutable (unmodifiable) list that can't be altered (including sorting). 197 | */ 198 | ranks = new ArrayList<>(ranks); 199 | ranks.sort(Comparator.comparingInt(Rank::getPriority).reversed()); 200 | 201 | for (Rank rank : ranks) { 202 | /* 203 | * Here we print the rank's name. If it also has a chat tag, we print that on the 204 | * same line. 205 | */ 206 | System.out.print("\t" + rank.getName()); 207 | if (rank.getChatTag() != null) { 208 | System.out.print(" [" + rank.getChatTag() + "]"); 209 | } 210 | System.out.println(); 211 | 212 | System.out.println("\t\tPriority: " + rank.getPriority()); 213 | System.out.println("\t\tCreated at " + rank.getCreationDate()); 214 | } 215 | } 216 | 217 | private static void printGuildMembers(List members) { 218 | System.out.println("Members (" + members.size() + " total)"); 219 | 220 | for (Member member : members) { 221 | /* 222 | * First we'll print some basic information about each member. 223 | * 224 | * Notice how we only have the member's UUID. If you want to get their name, you'll have 225 | * to make a separate request to the API using this UUID. See the GetPlayerExample class 226 | * for an example of that. 227 | */ 228 | System.out.println("\tUUID: " + member.getUuid()); 229 | System.out.println("\tRank: " + member.getRank()); 230 | System.out.println("\tJoin date: " + member.getJoinDate()); 231 | 232 | /* 233 | * Then, we'll print how much guild experience they earned for the last 7 days. We'll 234 | * sum up these daily totals to print an overall (weekly) total at the end. 235 | * 236 | * The code for the `getWeekDates()` function can be found at the bottom of the class. 237 | */ 238 | int weeklyExp = 0; 239 | System.out.println("\tWeekly Exp: "); 240 | for (LocalDate date : getWeekDates()) { 241 | int dailyExp = member.getExperienceEarned(date); 242 | weeklyExp += dailyExp; 243 | 244 | // ": " 245 | System.out.println("\t\t" + date + ": " + dailyExp); 246 | } 247 | System.out.println("\t\tWeekly Total: " + weeklyExp); 248 | System.out.println(); 249 | } 250 | } 251 | 252 | /* 253 | * Returns an array containing the date for each of the past 7 days (including today). 254 | * We use this to get each member's weekly experience by day (see `main` method above). 255 | */ 256 | private static LocalDate[] getWeekDates() { 257 | LocalDate[] week = new LocalDate[7]; 258 | for (int i = 0; i < 7; i++) { 259 | week[i] = LocalDate.now().minusDays(i); 260 | } 261 | return week; 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /hypixel-api-example/src/main/java/net/hypixel/api/example/GetLeaderboardsExample.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.example; 2 | 3 | public class GetLeaderboardsExample { 4 | public static void main(String[] args) { 5 | ExampleUtil.API.getLeaderboards().whenComplete(ExampleUtil.getTestConsumer()); 6 | ExampleUtil.await(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /hypixel-api-example/src/main/java/net/hypixel/api/example/GetPetsExample.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.example; 2 | 3 | import net.hypixel.api.HypixelAPI; 4 | import net.hypixel.api.pets.IPetRarity; 5 | import net.hypixel.api.pets.IPetType; 6 | import net.hypixel.api.pets.Pet; 7 | import net.hypixel.api.pets.PetStats; 8 | import net.hypixel.api.reply.PlayerReply; 9 | 10 | import java.util.Map; 11 | 12 | public class GetPetsExample { 13 | public static void main(String[] args) { 14 | HypixelAPI api = ExampleUtil.API; 15 | 16 | api.getPetRepository() 17 | .exceptionally(throwable -> { 18 | throwable.printStackTrace(); 19 | System.exit(0); 20 | return null; 21 | }) 22 | .thenAccept(petRepository -> { 23 | System.out.println("Fetched pet rarities:"); 24 | for (IPetRarity rarity : petRepository.getRarities()) { 25 | System.out.println("\t" + rarity.getName()); 26 | System.out.println("\t\tColor: " + rarity.getColor()); 27 | } 28 | 29 | System.out.println(); 30 | System.out.println("Fetched pet types:"); 31 | 32 | for (IPetType type : petRepository.getTypes()) { 33 | System.out.println("\t" + type.getKey()); 34 | System.out.println("\t\tName: " + type.getName()); 35 | System.out.println("\t\tRarity: " + type.getRarity()); 36 | } 37 | 38 | System.out.println(); 39 | 40 | api.getPlayerByUuid(ExampleUtil.HYPIXEL) 41 | .exceptionally(throwable -> { 42 | throwable.printStackTrace(); 43 | System.exit(0); 44 | return null; 45 | }) 46 | .thenAccept(playerReply -> { 47 | PlayerReply.Player player = playerReply.getPlayer(); 48 | 49 | if (!player.exists()) { 50 | System.err.println("Player not found!"); 51 | System.exit(0); 52 | return; 53 | } 54 | 55 | PetStats petStats = player.getPetStats(petRepository); 56 | 57 | System.out.println("Pet stats of \"" + player.getName() + "\":"); 58 | 59 | if (petStats == null) { 60 | System.out.println("No pet stats found for player."); 61 | } else { 62 | for (Map.Entry entry : petStats.listAllPets().entrySet()) { 63 | System.out.println("\t" + entry.getKey().getKey() + ": " + entry.getValue().getLevel()); 64 | } 65 | } 66 | 67 | IPetType catBlack = petRepository.getTypeByKey("CAT_BLACK"); 68 | IPetType blaze = petRepository.getTypeByKey("BLAZE"); 69 | 70 | System.out.println(); 71 | System.out.println("Does " + player.getName() + " have the " + catBlack.getName() + 72 | " pet? " + (petRepository.hasPlayerUnlocked(catBlack, player) ? "Yes." : "No.")); 73 | System.out.println("Does " + player.getName() + " have the " + blaze.getName() + 74 | " pet? " + (petRepository.hasPlayerUnlocked(blaze, player) ? "Yes." : "No.")); 75 | 76 | System.exit(0); 77 | }); 78 | }); 79 | 80 | ExampleUtil.await(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /hypixel-api-example/src/main/java/net/hypixel/api/example/GetPlayerExample.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.example; 2 | 3 | import net.hypixel.api.HypixelAPI; 4 | import net.hypixel.api.reply.PlayerReply; 5 | import net.hypixel.api.reply.PlayerReply.Player; 6 | 7 | import java.util.UUID; 8 | import java.util.concurrent.ExecutionException; 9 | 10 | /** 11 | * A sample app for demonstrating how players can be fetched & used from the Hypixel API. 12 | */ 13 | public class GetPlayerExample { 14 | 15 | public static void main(String[] args) { 16 | /* 17 | * Make sure you have a HypixelAPI object set up. You can see how this is done by going to 18 | * the ExampleUtil class. 19 | * 20 | * See the finally{} block below for how to shutdown this API once you're all done. 21 | */ 22 | HypixelAPI api = ExampleUtil.API; 23 | 24 | /* 25 | * Skip to below the try/catch/finally block to see how this is used. 26 | */ 27 | PlayerReply apiReply; 28 | 29 | try { 30 | UUID playerUuid = ExampleUtil.HYPIXEL; 31 | /* 32 | * Here, we store the response from the API in our variable. 33 | * 34 | * We call `.get()` at the end so that we can use the reply in the same thread. 35 | * The downside is that the current thread freezes (or "blocks") until the API responds. 36 | * If this is a problem for you, instead use: 37 | * 38 | * .whenComplete((apiReply, error) -> { 39 | * // Do something with apiReply (in a different thread)... 40 | * }); 41 | * 42 | * But for a simple command-line app like this one, `.get()` will do the job. 43 | */ 44 | apiReply = api.getPlayerByUuid(playerUuid).get(); 45 | 46 | } catch (ExecutionException e) { 47 | System.err.println("Oh no, our API request failed!"); 48 | 49 | /* 50 | * If an ExecutionException is thrown, it's typically because of an API error. 51 | * Use `getCause()` to determine what the actual problem is. 52 | */ 53 | e.getCause().printStackTrace(); 54 | return; 55 | 56 | } catch (InterruptedException e) { 57 | // Shouldn't happen under normal circumstances. 58 | System.err.println("Oh no, the player fetch thread was interrupted!"); 59 | e.printStackTrace(); 60 | Thread.currentThread().interrupt(); 61 | return; 62 | 63 | } finally { 64 | /* 65 | * Once you're finished with all your requests, you can shutdown your HypixelAPI object. 66 | * 67 | * If your app is meant to run continuously, you probably don't want to do this until 68 | * the app is stopped/closed. For this example though, we only need the one request. 69 | */ 70 | api.shutdown(); 71 | } 72 | 73 | /* 74 | * Now that we have the player, we can start to read their stats! (if they actually exist) 75 | */ 76 | Player player = apiReply.getPlayer(); 77 | if (!player.exists()) { 78 | System.err.println("Player not found!"); 79 | 80 | api.shutdown(); 81 | return; 82 | } 83 | 84 | /* 85 | * The player class has some built-in getters, like for the player's name, rank, and UUID. 86 | */ 87 | System.out.println("Here are some of \"" + player.getName() + "\"'s stats!"); 88 | System.out.println(); 89 | System.out.println("UUID ----------> " + player.getUuid()); 90 | System.out.println("Rank ----------> " + player.getHighestRank()); 91 | System.out.println("On Build Team? > " + player.isOnBuildTeam()); 92 | System.out.println("Exact Level ---> " + player.getNetworkLevel()); 93 | System.out.println("Experience ----> " + player.getNetworkExp()); 94 | System.out.println("Karma ---------> " + player.getKarma()); 95 | System.out.println("Last Game Type > " + player.getMostRecentGameType()); 96 | 97 | /* 98 | * If you want to find a stat that doesn't have a built-in method, you can use the 99 | * `getProperty()` method. 100 | * 101 | * If you also know what type of property it is (like a string or number), you can use more 102 | * specific methods, like `getStringProperty(...)`, `getIntProperty(...)`, and so on. 103 | */ 104 | System.out.println("Language ------> " + player.getStringProperty("userLanguage", null)); 105 | 106 | /* 107 | * Some of the property methods also accept a default value, which gets returned if the 108 | * field does not exist for the player. In this case, we return `0` as the default number of 109 | * deaths. 110 | * 111 | * If a stat is a bit deeper in the player object, you can separate each layer of the path 112 | * using dots, like below. 113 | * - In our case, it's the equivalent of getting the player's "stats" object, then the 114 | * "SkyWars" object inside that, and finally the "deaths" stat inside that. 115 | */ 116 | System.out.println("SkyWars Deaths > " + player.getIntProperty("stats.SkyWars.deaths", 0)); 117 | 118 | /* 119 | * If you need the entire player JSON returned by the API, you can get it using the player's 120 | * `.getRaw()` method. 121 | */ 122 | System.out.println("Raw JSON ------> " + player.getRaw()); 123 | 124 | /* 125 | * RateLimit object is available to any reply from a request to an authenticated endpoint and returns context 126 | * to the rate limit of the used API key. 127 | */ 128 | System.out.println("Rate Limit ----> " + apiReply.getRateLimit()); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /hypixel-api-example/src/main/java/net/hypixel/api/example/GetPunishmentStatsExample.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.example; 2 | 3 | public class GetPunishmentStatsExample { 4 | public static void main(String[] args) { 5 | ExampleUtil.API.getPunishmentStats().whenComplete(ExampleUtil.getTestConsumer()); 6 | ExampleUtil.await(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /hypixel-api-example/src/main/java/net/hypixel/api/example/GetRecentGamesExample.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.example; 2 | 3 | public class GetRecentGamesExample { 4 | 5 | public static void main(String[] args) { 6 | ExampleUtil.API.getRecentGames(ExampleUtil.HYPIXEL).whenComplete(ExampleUtil.getTestConsumer()); 7 | ExampleUtil.await(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /hypixel-api-example/src/main/java/net/hypixel/api/example/GetResourceExample.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.example; 2 | 3 | import net.hypixel.api.util.ResourceType; 4 | 5 | public class GetResourceExample { 6 | public static void main(String[] args) { 7 | ExampleUtil.API.getResource(ResourceType.CHALLENGES).whenComplete(ExampleUtil.getTestConsumer()); 8 | ExampleUtil.await(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /hypixel-api-example/src/main/java/net/hypixel/api/example/GetStatusExample.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.example; 2 | 3 | public class GetStatusExample { 4 | public static void main(String[] args) { 5 | // online may vary from player to player, this is a setting that can be disabled by the player 6 | // see comment in StatusReply 7 | ExampleUtil.API.getStatus(ExampleUtil.HYPIXEL).whenComplete(ExampleUtil.getTestConsumer()); 8 | ExampleUtil.await(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /hypixel-api-example/src/main/java/net/hypixel/api/example/skyblock/GetAuctionsExample.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.example.skyblock; 2 | 3 | import net.hypixel.api.example.ExampleUtil; 4 | 5 | public class GetAuctionsExample { 6 | public static void main(String[] args) { 7 | ExampleUtil.API.getSkyBlockAuctions(0).whenComplete((page0, throwable) -> { 8 | if (throwable != null) { 9 | throwable.printStackTrace(); 10 | System.exit(0); 11 | return; 12 | } 13 | 14 | System.out.println(page0); 15 | if (page0.hasNextPage()) { 16 | ExampleUtil.API.getSkyBlockAuctions(page0.getPage() + 1).whenComplete(ExampleUtil.getTestConsumer()); 17 | } else { 18 | System.exit(0); 19 | } 20 | }); 21 | ExampleUtil.await(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /hypixel-api-example/src/main/java/net/hypixel/api/example/skyblock/GetBazaarExample.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.example.skyblock; 2 | 3 | import net.hypixel.api.example.ExampleUtil; 4 | 5 | public class GetBazaarExample { 6 | public static void main(String[] args) { 7 | ExampleUtil.API.getSkyBlockBazaar().whenComplete(ExampleUtil.getTestConsumer()); 8 | ExampleUtil.await(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /hypixel-api-example/src/main/java/net/hypixel/api/example/skyblock/GetBingoDataExample.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.example.skyblock; 2 | 3 | import net.hypixel.api.example.ExampleUtil; 4 | 5 | public class GetBingoDataExample { 6 | public static void main(String[] args) { 7 | ExampleUtil.API.getSkyblockBingoData(ExampleUtil.HYPIXEL).whenComplete(ExampleUtil.getTestConsumer()); 8 | ExampleUtil.await(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /hypixel-api-example/src/main/java/net/hypixel/api/example/skyblock/GetFireSalesExample.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.example.skyblock; 2 | 3 | import net.hypixel.api.example.ExampleUtil; 4 | 5 | public class GetFireSalesExample { 6 | public static void main(String[] args) { 7 | ExampleUtil.API.getSkyBlockFireSales().whenComplete(ExampleUtil.getTestConsumer()); 8 | ExampleUtil.await(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /hypixel-api-example/src/main/java/net/hypixel/api/example/skyblock/GetNewsExample.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.example.skyblock; 2 | 3 | import net.hypixel.api.example.ExampleUtil; 4 | 5 | public class GetNewsExample { 6 | public static void main(String[] args) { 7 | ExampleUtil.API.getSkyBlockNews().whenComplete(ExampleUtil.getTestConsumer()); 8 | ExampleUtil.await(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /hypixel-api-example/src/main/java/net/hypixel/api/example/skyblock/GetProfileExample.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.example.skyblock; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | import net.hypixel.api.example.ExampleUtil; 6 | import net.hypixel.api.reply.skyblock.SkyBlockProfileReply; 7 | 8 | import java.util.Map.Entry; 9 | import java.util.Set; 10 | import java.util.concurrent.CompletableFuture; 11 | 12 | public class GetProfileExample { 13 | 14 | public static void main(String[] args) { 15 | ExampleUtil.API.getPlayerByUuid(ExampleUtil.HYPIXEL).whenComplete((reply, error) -> { 16 | if (error != null) { 17 | error.printStackTrace(); 18 | System.exit(0); 19 | return; 20 | } 21 | 22 | // Get all of the player's profiles. 23 | JsonObject profiles = reply.getPlayer().getObjectProperty("stats.SkyBlock.profiles"); 24 | if (profiles == null || profiles.entrySet().isEmpty()) { 25 | System.out.println("Player has no SkyBlock profiles"); 26 | System.exit(0); 27 | return; 28 | } 29 | 30 | // Request each profile from the API & print the reply. 31 | Set> profileEntries = profiles.entrySet(); 32 | CompletableFuture[] requests = new CompletableFuture[profileEntries.size()]; 33 | int i = 0; 34 | for (Entry profile : profileEntries) { 35 | requests[i] = requestProfile(profile.getKey()); 36 | i++; 37 | } 38 | 39 | // Only exit once all requests are completed. 40 | CompletableFuture.allOf(requests).whenComplete((ignored, profileError) -> { 41 | if (profileError != null) { 42 | profileError.printStackTrace(); 43 | } 44 | System.exit(0); 45 | }); 46 | }); 47 | ExampleUtil.await(); 48 | } 49 | 50 | private static CompletableFuture requestProfile(String profileId) { 51 | return ExampleUtil.API.getSkyBlockProfile(profileId).whenComplete((profileReply, ex) -> { 52 | if (ex != null) { 53 | ex.printStackTrace(); 54 | return; 55 | } 56 | 57 | System.out.println(profileReply); 58 | }); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /hypixel-api-example/src/main/java/net/hypixel/api/example/skyblock/GetProfilesExample.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.example.skyblock; 2 | 3 | import net.hypixel.api.example.ExampleUtil; 4 | 5 | public class GetProfilesExample { 6 | public static void main(String[] args) { 7 | ExampleUtil.API.getSkyBlockProfiles(ExampleUtil.HYPIXEL).whenComplete(ExampleUtil.getTestConsumer()); 8 | ExampleUtil.await(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /hypixel-api-example/src/test/java/net/hypixel/api/example/TestAuthenticatedEndpoints.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.example; 2 | 3 | import net.hypixel.api.reply.*; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.concurrent.ExecutionException; 8 | import java.util.concurrent.TimeUnit; 9 | import java.util.concurrent.TimeoutException; 10 | 11 | public class TestAuthenticatedEndpoints { 12 | 13 | @Test 14 | void boosters() throws ExecutionException, InterruptedException, TimeoutException { 15 | BoostersReply response = ExampleUtil.API.getBoosters().get(5, TimeUnit.SECONDS); 16 | 17 | Assertions.assertTrue(response.isSuccess()); 18 | } 19 | 20 | @Test 21 | void leaderboards() throws ExecutionException, InterruptedException, TimeoutException { 22 | LeaderboardsReply response = ExampleUtil.API.getLeaderboards().get(5, TimeUnit.SECONDS); 23 | 24 | Assertions.assertTrue(response.isSuccess()); 25 | } 26 | 27 | @Test 28 | void punishmentStats() throws ExecutionException, InterruptedException, TimeoutException { 29 | PunishmentStatsReply response = ExampleUtil.API.getPunishmentStats().get(5, TimeUnit.SECONDS); 30 | 31 | Assertions.assertTrue(response.isSuccess()); 32 | } 33 | 34 | @Test 35 | void player() throws ExecutionException, InterruptedException, TimeoutException { 36 | PlayerReply response = ExampleUtil.API.getPlayerByUuid(ExampleUtil.HYPIXEL).get(5, TimeUnit.SECONDS); 37 | 38 | Assertions.assertTrue(response.isSuccess()); 39 | Assertions.assertNotNull(response.getPlayer()); 40 | Assertions.assertNotNull(response.getPlayer().getName()); 41 | Assertions.assertNotNull(response.getPlayer().getUuid()); 42 | } 43 | 44 | @Test 45 | void guild() throws ExecutionException, InterruptedException, TimeoutException { 46 | GuildReply response = ExampleUtil.API.getGuildByPlayer(ExampleUtil.HYPIXEL).get(5, TimeUnit.SECONDS); 47 | 48 | Assertions.assertTrue(response.isSuccess()); 49 | Assertions.assertNotNull(response.getGuild()); 50 | Assertions.assertNotNull(response.getGuild().getName()); 51 | Assertions.assertNotNull(response.getGuild().getId()); 52 | } 53 | 54 | @Test 55 | void counts() throws ExecutionException, InterruptedException, TimeoutException { 56 | CountsReply response = ExampleUtil.API.getCounts().get(5, TimeUnit.SECONDS); 57 | 58 | Assertions.assertTrue(response.isSuccess()); 59 | Assertions.assertTrue(response.getPlayerCount() >= 0); 60 | Assertions.assertFalse(response.getGames().isEmpty()); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /hypixel-api-example/src/test/resources/junit-platform.properties: -------------------------------------------------------------------------------- 1 | junit.jupiter.execution.parallel.enabled=true -------------------------------------------------------------------------------- /hypixel-api-transport-apache/README.md: -------------------------------------------------------------------------------- 1 | Hypixel Public API - Apache Transport 2 | ====== 3 | 4 | ### Usage 5 | 6 | ```xml 7 | 8 | 9 | net.hypixel 10 | hypixel-api-transport-apache 11 | 4.4 12 | 13 | ``` 14 | 15 | Can also be included with Gradle. 16 | 17 | ```gradle 18 | dependencies { 19 | implementation 'net.hypixel:hypixel-api-transport-apache:4.4' 20 | } 21 | ``` 22 | 23 | ### Example code 24 | 25 | ```java 26 | public class Main { 27 | public static void main(String[] args) { 28 | HypixelHttpClient client = new ApacheHttpClient(UUID.fromString("your-api-key-here")); 29 | HypixelAPI hypixelAPI = new HypixelAPI(client); 30 | hypixelAPI.getPlayerByName("Hypixel") 31 | .exceptionally(throwable -> { 32 | // Handle exceptions here 33 | throwable.printStackTrace(); 34 | return null; 35 | }) 36 | .thenAccept(System.out::println); 37 | } 38 | } 39 | ``` 40 | 41 | ### Dependencies 42 | 43 | This transport depends on the following: 44 | 45 | * [Google Gson library - 2.10.1](https://mvnrepository.com/artifact/com.google.code.gson/gson) (for hypixel-api-core) 46 | * [Apache HttpClient - 4.5.14](https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient) -------------------------------------------------------------------------------- /hypixel-api-transport-apache/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | hypixel-api 7 | net.hypixel 8 | 4.4 9 | 10 | 4.0.0 11 | 12 | hypixel-api-transport-apache 13 | 14 | 15 | 1.8 16 | 1.8 17 | 18 | 19 | 20 | 21 | 22 | org.apache.maven.plugins 23 | maven-compiler-plugin 24 | 25 | 1.8 26 | 1.8 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | net.hypixel 35 | hypixel-api-core 36 | 4.4 37 | 38 | 39 | org.apache.httpcomponents 40 | httpclient 41 | 4.5.14 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /hypixel-api-transport-apache/src/main/java/net/hypixel/api/apache/ApacheHttpClient.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.apache; 2 | 3 | import net.hypixel.api.http.HypixelHttpClient; 4 | import net.hypixel.api.http.HypixelHttpResponse; 5 | import net.hypixel.api.http.RateLimit; 6 | import org.apache.http.HttpResponse; 7 | import org.apache.http.client.HttpClient; 8 | import org.apache.http.client.methods.HttpGet; 9 | import org.apache.http.impl.client.HttpClientBuilder; 10 | import org.apache.http.util.EntityUtils; 11 | 12 | import java.io.IOException; 13 | import java.util.UUID; 14 | import java.util.concurrent.CompletableFuture; 15 | import java.util.concurrent.ExecutorService; 16 | import java.util.concurrent.Executors; 17 | 18 | public class ApacheHttpClient implements HypixelHttpClient { 19 | 20 | private final UUID apiKey; 21 | private final ExecutorService executorService; 22 | private final HttpClient httpClient; 23 | 24 | public ApacheHttpClient(UUID apiKey) { 25 | this.apiKey = apiKey; 26 | this.executorService = Executors.newCachedThreadPool(); 27 | this.httpClient = HttpClientBuilder.create().setUserAgent(DEFAULT_USER_AGENT).build(); 28 | } 29 | 30 | @Override 31 | public CompletableFuture makeRequest(String url) { 32 | return CompletableFuture.supplyAsync(() -> { 33 | try { 34 | HttpResponse response = this.httpClient.execute(new HttpGet(url)); 35 | return new HypixelHttpResponse(response.getStatusLine().getStatusCode(), EntityUtils.toString(response.getEntity(), "UTF-8"), null); 36 | } catch (IOException e) { 37 | throw new RuntimeException(e); 38 | } 39 | }, this.executorService); 40 | } 41 | 42 | @Override 43 | public CompletableFuture makeAuthenticatedRequest(String url) { 44 | return CompletableFuture.supplyAsync(() -> { 45 | HttpGet request = new HttpGet(url); 46 | request.addHeader("API-Key", this.apiKey.toString()); 47 | try { 48 | HttpResponse response = this.httpClient.execute(request); 49 | return new HypixelHttpResponse(response.getStatusLine().getStatusCode(), EntityUtils.toString(response.getEntity(), "UTF-8"), createRateLimitResponse(response)); 50 | } catch (IOException e) { 51 | throw new RuntimeException(e); 52 | } 53 | }, this.executorService); 54 | } 55 | 56 | private RateLimit createRateLimitResponse(HttpResponse response) { 57 | if (response.getStatusLine().getStatusCode() != 200) { 58 | return null; 59 | } 60 | 61 | int limit = Integer.parseInt(response.getFirstHeader("RateLimit-Limit").getValue()); 62 | int remaining = Integer.parseInt(response.getFirstHeader("RateLimit-Remaining").getValue()); 63 | int reset = Integer.parseInt(response.getFirstHeader("RateLimit-Reset").getValue()); 64 | return new RateLimit(limit, remaining, reset); 65 | } 66 | 67 | @Override 68 | public void shutdown() { 69 | this.executorService.shutdown(); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /hypixel-api-transport-reactor/README.md: -------------------------------------------------------------------------------- 1 | Hypixel Public API - Reactive Transport 2 | ====== 3 | 4 | ### Usage 5 | 6 | ```xml 7 | 8 | 9 | net.hypixel 10 | hypixel-api-transport-reactor 11 | 4.4 12 | 13 | ``` 14 | 15 | Can also be included with Gradle. 16 | 17 | ```gradle 18 | dependencies { 19 | implementation 'net.hypixel:hypixel-api-transport-reactor:4.4' 20 | } 21 | ``` 22 | 23 | ### Example code 24 | 25 | ```java 26 | public class Main { 27 | public static void main(String[] args) { 28 | HypixelHttpClient client = new ReactorHttpClient(UUID.fromString("your-api-key-here")); 29 | HypixelAPI hypixelAPI = new HypixelAPI(client); 30 | hypixelAPI.getPlayerByName("Hypixel") 31 | .exceptionally(throwable -> { 32 | // Handle exceptions here 33 | throwable.printStackTrace(); 34 | return null; 35 | }) 36 | .thenAccept(System.out::println); 37 | } 38 | } 39 | ``` 40 | 41 | ### Dependencies 42 | 43 | This transport depends on the following: 44 | 45 | * [Google Gson library - 2.10.1](https://mvnrepository.com/artifact/com.google.code.gson/gson) (for hypixel-api-core) 46 | * [Reactor Core 3.4.5](https://mvnrepository.com/artifact/io.projectreactor/reactor-core) (for reactor netty) 47 | * Reactor Netty [(project-reactor)](https://projectreactor.io/docs): 48 | * [Netty Core 1.0.6](https://mvnrepository.com/artifact/io.projectreactor.netty/reactor-netty-core) 49 | * [Netty Http 1.0.6](https://mvnrepository.com/artifact/io.projectreactor.netty/reactor-netty-http) -------------------------------------------------------------------------------- /hypixel-api-transport-reactor/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | hypixel-api 7 | net.hypixel 8 | 4.4 9 | 10 | 4.0.0 11 | 12 | hypixel-api-transport-reactor 13 | 14 | 15 | 1.8 16 | 1.8 17 | 18 | 19 | 20 | 21 | 22 | org.apache.maven.plugins 23 | maven-compiler-plugin 24 | 25 | 1.8 26 | 1.8 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | io.projectreactor 36 | reactor-bom 37 | 2020.0.6 38 | pom 39 | import 40 | 41 | 42 | 43 | 44 | 45 | 46 | net.hypixel 47 | hypixel-api-core 48 | 4.4 49 | 50 | 51 | io.projectreactor.netty 52 | reactor-netty-core 53 | 54 | 55 | io.projectreactor.netty 56 | reactor-netty-http 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /hypixel-api-transport-reactor/src/main/java/net/hypixel/api/reactor/ReactorHttpClient.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.reactor; 2 | 3 | import io.netty.handler.codec.http.HttpResponseStatus; 4 | import net.hypixel.api.http.HypixelHttpClient; 5 | import net.hypixel.api.http.HypixelHttpResponse; 6 | import net.hypixel.api.http.RateLimit; 7 | import reactor.core.Disposable; 8 | import reactor.core.publisher.Flux; 9 | import reactor.core.publisher.Mono; 10 | import reactor.core.publisher.MonoSink; 11 | import reactor.core.scheduler.Schedulers; 12 | import reactor.netty.http.client.HttpClient; 13 | import reactor.netty.http.client.HttpClientResponse; 14 | import reactor.util.function.Tuple3; 15 | 16 | import java.time.Duration; 17 | import java.util.UUID; 18 | import java.util.concurrent.*; 19 | import java.util.concurrent.atomic.AtomicBoolean; 20 | import java.util.concurrent.locks.Condition; 21 | import java.util.concurrent.locks.ReentrantLock; 22 | 23 | public class ReactorHttpClient implements HypixelHttpClient { 24 | 25 | private final HttpClient httpClient; 26 | private final UUID apiKey; 27 | 28 | // Marker to reset the request counter and release waiting threads 29 | private final AtomicBoolean firstRequestReturned = new AtomicBoolean(false); 30 | // Marker to only schedule a reset clock once on error 429 31 | private final AtomicBoolean overflowStartedNewClock = new AtomicBoolean(false); 32 | 33 | // Callbacks that will trigger their corresponding requests 34 | private final ArrayBlockingQueue blockingQueue; 35 | 36 | // For shutting down the flux that emits request callbacks 37 | private final Disposable requestCallbackFluxDisposable; 38 | private final ExecutorService requestCallbackFluxExecutorService = Executors.newSingleThreadExecutor(); 39 | 40 | private final ReentrantLock lock = new ReentrantLock(true); 41 | private final Condition limitResetCondition = lock.newCondition(); 42 | 43 | /* 44 | * How many requests we can send before reaching the limit 45 | * Starts as 1 so the first request returns and resets this value before allowing other requests to be sent. 46 | */ 47 | private int actionsLeftThisMinute = 1; 48 | 49 | /** 50 | * Constructs a new instance of this client using the specified API key. 51 | * 52 | * @param apiKey the key associated with this connection 53 | * @param minDelayBetweenRequests minimum time between sending requests (in ms) default is 8 54 | * @param bufferCapacity fixed size of blockingQueue 55 | */ 56 | public ReactorHttpClient(UUID apiKey, long minDelayBetweenRequests, int bufferCapacity) { 57 | this.apiKey = apiKey; 58 | this.httpClient = HttpClient.create().secure(); 59 | this.blockingQueue = new ArrayBlockingQueue<>(bufferCapacity); 60 | 61 | this.requestCallbackFluxDisposable = Flux.generate((synchronousSink) -> { 62 | try { 63 | RequestCallback callback = blockingQueue.take(); 64 | // prune skipped/completed requests to avoid counting them 65 | while (callback.isCanceled()) { 66 | callback = blockingQueue.take(); 67 | } 68 | 69 | this.decrementActionsOrWait(); 70 | 71 | synchronousSink.next(callback); 72 | } catch (InterruptedException e) { 73 | throw new AssertionError("This should not have been possible", e); 74 | } 75 | }).subscribeOn(Schedulers.fromExecutorService(this.requestCallbackFluxExecutorService)).delayElements(Duration.ofMillis(minDelayBetweenRequests), Schedulers.boundedElastic()).subscribe(RequestCallback::sendRequest); 76 | } 77 | 78 | public ReactorHttpClient(UUID apiKey, long minDelayBetweenRequests) { 79 | this(apiKey, minDelayBetweenRequests, 500); 80 | } 81 | 82 | public ReactorHttpClient(UUID apiKey, int bufferCapacity) { 83 | this(apiKey, 8, bufferCapacity); 84 | } 85 | 86 | public ReactorHttpClient(UUID apiKey) { 87 | this(apiKey, 8, 500); 88 | } 89 | 90 | /** 91 | * Canceling the returned future will result in canceling the sending of the request if still possible 92 | */ 93 | @Override 94 | public CompletableFuture makeRequest(String url) { 95 | return toHypixelResponseFuture(makeRequest(url, false)); 96 | } 97 | 98 | /** 99 | * Canceling the returned future will result in canceling the sending of the request if still possible 100 | */ 101 | @Override 102 | public CompletableFuture makeAuthenticatedRequest(String url) { 103 | return toHypixelResponseFuture(makeRequest(url, true)); 104 | } 105 | 106 | private static CompletableFuture toHypixelResponseFuture(Mono> result) { 107 | return result.map(tuple -> new HypixelHttpResponse(tuple.getT2(), tuple.getT1(), tuple.getT3())) 108 | .toFuture(); 109 | } 110 | 111 | @Override 112 | public void shutdown() { 113 | this.requestCallbackFluxDisposable.dispose(); 114 | this.requestCallbackFluxExecutorService.shutdown(); 115 | } 116 | 117 | /** 118 | * Makes a request to the Hypixel api and returns a {@link Mono} containing 119 | * the response body and status code, canceling this mono will prevent the request from being sent if possible 120 | * 121 | * @param path full url 122 | * @param isAuthenticated whether to enable authentication or not 123 | */ 124 | public Mono> makeRequest(String path, boolean isAuthenticated) { 125 | return Mono.create(sink -> { 126 | RequestCallback callback = new RequestCallback(path, sink, isAuthenticated, this); 127 | 128 | try { 129 | this.blockingQueue.put(callback); 130 | } catch (InterruptedException e) { 131 | sink.error(e); 132 | throw new AssertionError("Queue insertion interrupted. This should not have been possible", e); 133 | } 134 | }); 135 | } 136 | 137 | private void decrementActionsOrWait() throws InterruptedException { 138 | this.lock.lock(); 139 | try { 140 | while (this.actionsLeftThisMinute <= 0) { 141 | this.limitResetCondition.await(); 142 | } 143 | this.actionsLeftThisMinute--; 144 | } finally { 145 | this.lock.unlock(); 146 | } 147 | } 148 | 149 | 150 | private void incrementActionsLeftThisMinute() { 151 | this.lock.lock(); 152 | try { 153 | this.actionsLeftThisMinute++; 154 | this.limitResetCondition.signal(); 155 | } finally { 156 | this.lock.unlock(); 157 | } 158 | } 159 | 160 | private void setActionsLeftThisMinute(int value) { 161 | this.lock.lock(); 162 | try { 163 | this.actionsLeftThisMinute = Math.max(0, value); 164 | this.limitResetCondition.signal(); 165 | } finally { 166 | this.lock.unlock(); 167 | } 168 | } 169 | 170 | /** 171 | * Reads response status and retries error 429 (too many requests) 172 | * The first request after every limit reset will be used to schedule the next limit reset 173 | * 174 | * @param response the {@link HttpClientResponse} from our request 175 | * @param requestCallback the callback controlling our request 176 | * @return whether to return the request body or wait for a retry 177 | */ 178 | private ResponseHandlingResult handleResponse(HttpClientResponse response, RequestCallback requestCallback) throws InterruptedException { 179 | if (response.status() == HttpResponseStatus.TOO_MANY_REQUESTS) { 180 | System.out.println("Too many requests were sent, is something else using the same API Key?!!"); 181 | int timeRemaining = Math.max(1, response.responseHeaders().getInt("ratelimit-reset", 10)); 182 | 183 | if (this.overflowStartedNewClock.compareAndSet(false, true)) { 184 | this.setActionsLeftThisMinute(0); 185 | resetForFirstRequest(timeRemaining); 186 | } 187 | 188 | // execute this last to prevent a possible exception from messing up our clock synchronization 189 | this.blockingQueue.put(requestCallback); 190 | return new ResponseHandlingResult(false, response.status().code(), null); 191 | } 192 | 193 | int limit = Math.max(1, response.responseHeaders().getInt("ratelimit-limit", 10)); 194 | int timeRemaining = Math.max(1, response.responseHeaders().getInt("ratelimit-reset", 10)); 195 | int requestsRemaining = response.responseHeaders().getInt("ratelimit-remaining", 110); 196 | RateLimit rateLimit = new RateLimit(limit, requestsRemaining, timeRemaining); 197 | 198 | if (this.firstRequestReturned.compareAndSet(false, true)) { 199 | this.setActionsLeftThisMinute(requestsRemaining); 200 | resetForFirstRequest(timeRemaining); 201 | } 202 | 203 | return new ResponseHandlingResult(true, response.status().code(), rateLimit); 204 | } 205 | 206 | /** 207 | * Wakes up all waiting threads in the specified amount of seconds 208 | * (Adds two seconds to account for sync errors in the server). 209 | * 210 | * @param timeRemaining how much time is left until the next reset 211 | */ 212 | private void resetForFirstRequest(int timeRemaining) { 213 | Schedulers.parallel().schedule(() -> { 214 | this.firstRequestReturned.set(false); 215 | this.overflowStartedNewClock.set(false); 216 | this.setActionsLeftThisMinute(1); 217 | }, timeRemaining + 2, TimeUnit.SECONDS); 218 | } 219 | 220 | /** 221 | * Controls a request 222 | */ 223 | private static class RequestCallback { 224 | private final String url; 225 | private final MonoSink> requestResultSink; 226 | private final ReactorHttpClient requestRateLimiter; 227 | private final boolean isAuthenticated; 228 | private final ReentrantLock lock = new ReentrantLock(); 229 | private boolean isCanceled = false; 230 | 231 | private RequestCallback(String url, MonoSink> requestResultSink, boolean isAuthenticated, ReactorHttpClient requestRateLimiter) { 232 | this.url = url; 233 | this.requestResultSink = requestResultSink; 234 | this.requestRateLimiter = requestRateLimiter; 235 | this.isAuthenticated = isAuthenticated; 236 | 237 | this.requestResultSink.onCancel(this::setCanceled); 238 | } 239 | 240 | private void setCanceled() { 241 | this.lock.lock(); 242 | try { 243 | this.isCanceled = true; 244 | } finally { 245 | this.lock.unlock(); 246 | } 247 | } 248 | 249 | public boolean isCanceled() { 250 | this.lock.lock(); 251 | try { 252 | return this.isCanceled; 253 | } finally { 254 | this.lock.unlock(); 255 | } 256 | } 257 | 258 | private void sendRequest() { 259 | if (this.isCanceled()) { 260 | this.requestRateLimiter.incrementActionsLeftThisMinute(); 261 | return; 262 | } 263 | 264 | (this.isAuthenticated ? requestRateLimiter.httpClient.headers(headers -> headers.add("API-Key", requestRateLimiter.apiKey.toString())) : requestRateLimiter.httpClient).get() 265 | .uri(url) 266 | .responseSingle((response, body) -> { 267 | try { 268 | ResponseHandlingResult result = requestRateLimiter.handleResponse(response, this); 269 | if (result.allowToPass) { 270 | return Mono.zip(body.asString(), Mono.just(result.statusCode), Mono.just(result.rateLimit)); 271 | } 272 | return Mono.empty(); 273 | } catch (InterruptedException e) { 274 | this.requestResultSink.error(e); 275 | throw new AssertionError("ERROR: Queue insertion got interrupted, serious problem! (this should not happen!!)", e); 276 | } 277 | }).subscribe(this.requestResultSink::success); 278 | } 279 | } 280 | 281 | /** 282 | * Data object 283 | */ 284 | private static class ResponseHandlingResult { 285 | public final boolean allowToPass; 286 | public final int statusCode; 287 | public final RateLimit rateLimit; 288 | 289 | public ResponseHandlingResult(boolean allowToPass, int statusCode, RateLimit rateLimit) { 290 | this.allowToPass = allowToPass; 291 | this.statusCode = statusCode; 292 | this.rateLimit = rateLimit; 293 | } 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /hypixel-api-transport-unirest/README.md: -------------------------------------------------------------------------------- 1 | Hypixel Public API - Unirest Transport 2 | ====== 3 | 4 | ### Usage 5 | 6 | ```xml 7 | 8 | 9 | net.hypixel 10 | hypixel-api-transport-unirest 11 | 4.4 12 | 13 | ``` 14 | 15 | Can also be included with Gradle. 16 | 17 | ```gradle 18 | dependencies { 19 | implementation 'net.hypixel:hypixel-api-transport-unirest:4.4' 20 | } 21 | ``` 22 | 23 | ### Example code 24 | 25 | ```java 26 | public class Main { 27 | public static void main(String[] args) { 28 | HypixelHttpClient client = new UnirestHttpClient(UUID.fromString("your-api-key-here")); 29 | HypixelAPI hypixelAPI = new HypixelAPI(client); 30 | hypixelAPI.getPlayerByName("Hypixel") 31 | .exceptionally(throwable -> { 32 | // Handle exceptions here 33 | throwable.printStackTrace(); 34 | return null; 35 | }) 36 | .thenAccept(System.out::println); 37 | } 38 | } 39 | ``` 40 | 41 | ### Dependencies 42 | 43 | This transport depends on the following: 44 | 45 | * [Google Gson library - 2.10.1](https://mvnrepository.com/artifact/com.google.code.gson/gson) (for hypixel-api-core) 46 | * [Unirest Java - 3.14.4](https://mvnrepository.com/artifact/com.konghq/unirest-java) -------------------------------------------------------------------------------- /hypixel-api-transport-unirest/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | hypixel-api 7 | net.hypixel 8 | 4.4 9 | 10 | 4.0.0 11 | 12 | hypixel-api-transport-unirest 13 | 14 | 15 | 1.8 16 | 1.8 17 | 18 | 19 | 20 | 21 | 22 | org.apache.maven.plugins 23 | maven-compiler-plugin 24 | 25 | 1.8 26 | 1.8 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | net.hypixel 35 | hypixel-api-core 36 | 4.4 37 | 38 | 39 | com.konghq 40 | unirest-java 41 | 3.14.4 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /hypixel-api-transport-unirest/src/main/java/net/hypixel/api/unirest/UnirestHttpClient.java: -------------------------------------------------------------------------------- 1 | package net.hypixel.api.unirest; 2 | 3 | import kong.unirest.HttpResponse; 4 | import kong.unirest.Unirest; 5 | import net.hypixel.api.http.HypixelHttpClient; 6 | import net.hypixel.api.http.HypixelHttpResponse; 7 | import net.hypixel.api.http.RateLimit; 8 | 9 | import java.util.UUID; 10 | import java.util.concurrent.CompletableFuture; 11 | 12 | public class UnirestHttpClient implements HypixelHttpClient { 13 | 14 | private final UUID apiKey; 15 | 16 | public UnirestHttpClient(UUID apiKey) { 17 | this.apiKey = apiKey; 18 | } 19 | 20 | @Override 21 | public CompletableFuture makeRequest(String url) { 22 | return Unirest.get(url) 23 | .header("User-Agent", DEFAULT_USER_AGENT) 24 | .asStringAsync() 25 | .thenApply(res -> new HypixelHttpResponse(res.getStatus(), res.getBody(), null)); 26 | } 27 | 28 | @Override 29 | public CompletableFuture makeAuthenticatedRequest(String url) { 30 | return Unirest.get(url) 31 | .header("User-Agent", DEFAULT_USER_AGENT) 32 | .header("API-Key", this.apiKey.toString()) 33 | .asStringAsync() 34 | .thenApply(res -> new HypixelHttpResponse(res.getStatus(), res.getBody(), createRateLimitResponse(res))); 35 | } 36 | 37 | private RateLimit createRateLimitResponse(HttpResponse response) { 38 | if (response.getStatus() != 200) { 39 | return null; 40 | } 41 | 42 | int limit = Integer.parseInt(response.getHeaders().getFirst("RateLimit-Limit")); 43 | int remaining = Integer.parseInt(response.getHeaders().getFirst("RateLimit-Remaining")); 44 | int reset = Integer.parseInt(response.getHeaders().getFirst("RateLimit-Reset")); 45 | return new RateLimit(limit, remaining, reset); 46 | } 47 | 48 | @Override 49 | public void shutdown() { 50 | Unirest.shutDown(); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | net.hypixel 8 | hypixel-api 9 | pom 10 | 4.4 11 | 12 | 13 | hypixel-api-core 14 | hypixel-api-example 15 | hypixel-api-transport-apache 16 | hypixel-api-transport-unirest 17 | hypixel-api-transport-reactor 18 | 19 | 20 | 21 | 22 | Hypixel 23 | https://repo.hypixel.net/repository/Hypixel/ 24 | 25 | 26 | 27 | 28 | 29 | 30 | org.apache.maven.plugins 31 | maven-compiler-plugin 32 | 2.3.2 33 | 34 | UTF-8 35 | 1.8 36 | 1.8 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-source-plugin 42 | 3.0.1 43 | 44 | 45 | attach-sources 46 | 47 | jar 48 | 49 | 50 | 51 | 52 | 53 | org.apache.maven.plugins 54 | maven-javadoc-plugin 55 | 3.0.1 56 | 57 | none 58 | true 59 | 60 | 61 | 62 | org.apache.maven.plugins 63 | maven-surefire-plugin 64 | 2.22.2 65 | 66 | 67 | 68 | 69 | --------------------------------------------------------------------------------