From ecde2bb2a72bea593481d4f58913ebd03039e1a3 Mon Sep 17 00:00:00 2001
From: Liam
Date: Wed, 17 Apr 2024 21:06:43 +0100
Subject: [PATCH] pls work first try
---
.gitea/workflows/publish.yml | 21 ++
.gitignore | 31 +++
.idea/.gitignore | 3 +
.idea/encodings.xml | 7 +
.idea/misc.xml | 14 ++
.idea/vcs.xml | 6 +
pom.xml | 67 +++++++
src/main/java/xyz/mcutils/McUtilsAPI.java | 112 +++++++++++
.../java/xyz/mcutils/common/DateUtils.java | 22 +++
.../java/xyz/mcutils/common/WebRequest.java | 58 ++++++
.../xyz/mcutils/exception/ErrorResponse.java | 34 ++++
.../xyz/mcutils/models/CachedResponse.java | 58 ++++++
.../xyz/mcutils/models/dns/DNSRecord.java | 29 +++
.../xyz/mcutils/models/dns/impl/ARecord.java | 15 ++
.../mcutils/models/dns/impl/SRVRecord.java | 31 +++
.../mojang/CachedMojangEndpointStatus.java | 35 ++++
.../mcutils/models/player/CachedPlayer.java | 62 ++++++
.../models/player/CachedPlayerSkinPart.java | 12 ++
.../models/player/CachedUsernameToUuid.java | 19 ++
.../java/xyz/mcutils/models/player/Cape.java | 8 +
.../java/xyz/mcutils/models/player/Skin.java | 51 +++++
.../server/CachedBedrockMinecraftServer.java | 75 ++++++++
.../server/CachedJavaMinecraftServer.java | 181 ++++++++++++++++++
.../server/CachedServerBlockedStatus.java | 12 ++
.../models/server/CachedServerIcon.java | 12 ++
.../models/server/MinecraftServer.java | 97 ++++++++++
.../mcutils/models/server/ServerPlatform.java | 20 ++
src/test/java/xyz/mcutils/MojangTests.java | 15 ++
src/test/java/xyz/mcutils/PlayerTests.java | 77 ++++++++
src/test/java/xyz/mcutils/ServerTests.java | 74 +++++++
30 files changed, 1258 insertions(+)
create mode 100644 .gitea/workflows/publish.yml
create mode 100644 .gitignore
create mode 100644 .idea/.gitignore
create mode 100644 .idea/encodings.xml
create mode 100644 .idea/misc.xml
create mode 100644 .idea/vcs.xml
create mode 100644 pom.xml
create mode 100644 src/main/java/xyz/mcutils/McUtilsAPI.java
create mode 100644 src/main/java/xyz/mcutils/common/DateUtils.java
create mode 100644 src/main/java/xyz/mcutils/common/WebRequest.java
create mode 100644 src/main/java/xyz/mcutils/exception/ErrorResponse.java
create mode 100644 src/main/java/xyz/mcutils/models/CachedResponse.java
create mode 100644 src/main/java/xyz/mcutils/models/dns/DNSRecord.java
create mode 100644 src/main/java/xyz/mcutils/models/dns/impl/ARecord.java
create mode 100644 src/main/java/xyz/mcutils/models/dns/impl/SRVRecord.java
create mode 100644 src/main/java/xyz/mcutils/models/mojang/CachedMojangEndpointStatus.java
create mode 100644 src/main/java/xyz/mcutils/models/player/CachedPlayer.java
create mode 100644 src/main/java/xyz/mcutils/models/player/CachedPlayerSkinPart.java
create mode 100644 src/main/java/xyz/mcutils/models/player/CachedUsernameToUuid.java
create mode 100644 src/main/java/xyz/mcutils/models/player/Cape.java
create mode 100644 src/main/java/xyz/mcutils/models/player/Skin.java
create mode 100644 src/main/java/xyz/mcutils/models/server/CachedBedrockMinecraftServer.java
create mode 100644 src/main/java/xyz/mcutils/models/server/CachedJavaMinecraftServer.java
create mode 100644 src/main/java/xyz/mcutils/models/server/CachedServerBlockedStatus.java
create mode 100644 src/main/java/xyz/mcutils/models/server/CachedServerIcon.java
create mode 100644 src/main/java/xyz/mcutils/models/server/MinecraftServer.java
create mode 100644 src/main/java/xyz/mcutils/models/server/ServerPlatform.java
create mode 100644 src/test/java/xyz/mcutils/MojangTests.java
create mode 100644 src/test/java/xyz/mcutils/PlayerTests.java
create mode 100644 src/test/java/xyz/mcutils/ServerTests.java
diff --git a/.gitea/workflows/publish.yml b/.gitea/workflows/publish.yml
new file mode 100644
index 0000000..e700acf
--- /dev/null
+++ b/.gitea/workflows/publish.yml
@@ -0,0 +1,21 @@
+name: Publish package to the Maven Central Repository
+on:
+ push:
+ branches:
+ - master
+ -
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up Maven Central Repository
+ uses: actions/setup-java@v4
+ with:
+ java-version: '17'
+ distribution: 'adopt'
+ - name: Publish package
+ run: mvn --batch-mode deploy
+ env:
+ MAVEN_USERNAME: ${{ secrets.REPO_USERNAME }}
+ MAVEN_PASSWORD: ${{ secrets.REPO_PASSWORD }}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..77440ae
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,31 @@
+### ME template
+*.class
+*.log
+*.ctxt
+.mtj.tmp/
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+hs_err_pid*
+replay_pid*
+.idea
+cmake-build-*/
+.idea/**/mongoSettings.xml
+*.iws
+out/
+build/
+work/
+.idea_modules/
+atlassian-ide-plugin.xml
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+git.properties
+pom.xml.versionsBackup
+application.yml
+target/
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..aa00ffa
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..010b430
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..b820fdd
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,67 @@
+
+
+ 4.0.0
+
+ xyz.mcutils
+ mcutils-java-library
+ 1.0-SNAPSHOT
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+ mcutils-java-library
+ https://repo.fascinated.cc/public/mcutils-java-library/
+
+
+
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.32
+ provided
+
+
+ com.google.code.gson
+ gson
+ 2.10.1
+ compile
+
+
+
+
+ org.springframework
+ spring-web
+ 6.1.6
+ compile
+
+
+ org.apache.httpcomponents.client5
+ httpclient5
+ 5.3
+ compile
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.10.2
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.10.2
+ test
+
+
+
\ No newline at end of file
diff --git a/src/main/java/xyz/mcutils/McUtilsAPI.java b/src/main/java/xyz/mcutils/McUtilsAPI.java
new file mode 100644
index 0000000..000a3e9
--- /dev/null
+++ b/src/main/java/xyz/mcutils/McUtilsAPI.java
@@ -0,0 +1,112 @@
+package xyz.mcutils;
+
+import xyz.mcutils.common.WebRequest;
+import xyz.mcutils.exception.ErrorResponse;
+import xyz.mcutils.models.mojang.CachedMojangEndpointStatus;
+import xyz.mcutils.models.player.CachedPlayer;
+import xyz.mcutils.models.player.CachedPlayerSkinPart;
+import xyz.mcutils.models.player.CachedUsernameToUuid;
+import xyz.mcutils.models.player.Skin;
+import xyz.mcutils.models.server.*;
+
+public class McUtilsAPI {
+ private static final String API_ENDPOINT = "https://api.mcutils.xyz";
+ private static final String MOJANG_API_STATUS_ENDPOINT = API_ENDPOINT + "/mojang/status";
+ private static final String PLAYER_ENDPOINT = API_ENDPOINT + "/player/%s";
+ private static final String USERNAME_TO_UUID_ENDPOINT = API_ENDPOINT + "/player/uuid/%s";
+ private static final String PLAYER_SKIN_PART_ENDPOINT = API_ENDPOINT + "/player/%s/%s";
+ private static final String SERVER_ENDPOINT = API_ENDPOINT + "/server/%s/%s";
+ private static final String SERVER_ICON_ENDPOINT = API_ENDPOINT + "/server/icon/%s";
+ private static final String SERVER_BLOCKED_STATUS_ENDPOINT = API_ENDPOINT + "/server/blocked/%s";
+
+ /**
+ * Gets the status of the Mojang APIs.
+ *
+ * @return The status of the Mojang API.
+ * @throws ErrorResponse If an error occurs.
+ */
+ public static CachedMojangEndpointStatus getMojangApiStatus() throws ErrorResponse {
+ return WebRequest.get(MOJANG_API_STATUS_ENDPOINT, CachedMojangEndpointStatus.class);
+ }
+
+
+ /**
+ * Gets a player from the API.
+ *
+ * @param id The id of the player to get.
+ * @return The player.
+ * @throws ErrorResponse If an error occurs.
+ */
+ public static CachedPlayer getPlayer(String id) throws ErrorResponse {
+ return WebRequest.get(PLAYER_ENDPOINT.formatted(id), CachedPlayer.class);
+ }
+
+ /**
+ * Gets a player from the API.
+ *
+ * @param username The username of the player to get.
+ * @return The player.
+ * @throws ErrorResponse If an error occurs.
+ */
+ public static CachedUsernameToUuid getUsernameToUuid(String username) throws ErrorResponse {
+ return WebRequest.get(USERNAME_TO_UUID_ENDPOINT.formatted(username), CachedUsernameToUuid.class);
+ }
+
+ /**
+ * Gets a player skin part from the API.
+ *
+ * @param part The part of the skin to get.
+ * @param id The id of the player to get.
+ * @return The player skin part.
+ * @throws ErrorResponse If an error occurs.
+ */
+ public static CachedPlayerSkinPart getPlayerSkinPart(Skin.SkinPart part, String id) throws ErrorResponse {
+ byte[] partBytes = WebRequest.get(PLAYER_SKIN_PART_ENDPOINT.formatted(part, id), byte[].class);
+ return new CachedPlayerSkinPart(partBytes);
+ }
+
+ /**
+ * Gets a Bedrock server from the API.
+ *
+ * @param id The id of the server to get.
+ * @return The server.
+ * @throws ErrorResponse If an error occurs.
+ */
+ public static CachedBedrockMinecraftServer getBedrockServer(String id) throws ErrorResponse {
+ return WebRequest.get(SERVER_ENDPOINT.formatted(ServerPlatform.BEDROCK.name(), id), CachedBedrockMinecraftServer.class);
+ }
+
+ /**
+ * Gets a Java server from the API.
+ *
+ * @param id The id of the server to get.
+ * @return The server.
+ * @throws ErrorResponse If an error occurs.
+ */
+ public static CachedJavaMinecraftServer getJavaServer(String id) throws ErrorResponse {
+ return WebRequest.get(SERVER_ENDPOINT.formatted(ServerPlatform.JAVA.name(), id), CachedJavaMinecraftServer.class);
+ }
+
+ /**
+ * Gets the icon of a Java server from the API.
+ *
+ * @param id The id of the server to get the icon of.
+ * @return The server icon.
+ * @throws ErrorResponse If an error occurs.
+ */
+ public static CachedServerIcon getServerIcon(String id) throws ErrorResponse {
+ byte[] bytes = WebRequest.get(SERVER_ICON_ENDPOINT.formatted(id), byte[].class);
+ return new CachedServerIcon(bytes);
+ }
+
+ /**
+ * Gets the blocked status of a Java server from the API.
+ *
+ * @param id The id of the server to get the blocked status of.
+ * @return The server blocked status.
+ * @throws ErrorResponse If an error occurs.
+ */
+ public static CachedServerBlockedStatus getServerBlockedStatus(String id) throws ErrorResponse {
+ return WebRequest.get(SERVER_BLOCKED_STATUS_ENDPOINT.formatted(id), CachedServerBlockedStatus.class);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/xyz/mcutils/common/DateUtils.java b/src/main/java/xyz/mcutils/common/DateUtils.java
new file mode 100644
index 0000000..d6d376b
--- /dev/null
+++ b/src/main/java/xyz/mcutils/common/DateUtils.java
@@ -0,0 +1,22 @@
+package xyz.mcutils.common;
+
+import lombok.SneakyThrows;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+public class DateUtils {
+ private static final SimpleDateFormat FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ENGLISH);
+
+ /**
+ * Converts a string to a date.
+ *
+ * @param date The date to convert
+ * @return The date
+ */
+ @SneakyThrows
+ public static Date stringToDate(String date) {
+ return FORMATTER.parse(date);
+ }
+}
diff --git a/src/main/java/xyz/mcutils/common/WebRequest.java b/src/main/java/xyz/mcutils/common/WebRequest.java
new file mode 100644
index 0000000..7896462
--- /dev/null
+++ b/src/main/java/xyz/mcutils/common/WebRequest.java
@@ -0,0 +1,58 @@
+package xyz.mcutils.common;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonObject;
+import lombok.experimental.UtilityClass;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.RestClient;
+import xyz.mcutils.exception.ErrorResponse;
+
+@UtilityClass
+public class WebRequest {
+ private static final Gson GSON = new GsonBuilder()
+ .setPrettyPrinting()
+ .create();
+
+ /**
+ * The web client.
+ */
+ private static final RestClient CLIENT;
+
+ static {
+ HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
+ requestFactory.setConnectTimeout(2500); // 2.5 seconds
+ CLIENT = RestClient.builder()
+ .requestFactory(requestFactory)
+ .build();
+ }
+
+ /**
+ * Gets a response from the given URL.
+ *
+ * @param url the url
+ * @return the response
+ * @param the type of the response
+ */
+ public static T get(String url, Class clazz) throws ErrorResponse {
+ try {
+ ResponseEntity responseEntity = CLIENT.get()
+ .uri(url)
+ .retrieve()
+ .toEntity(clazz);
+
+ return responseEntity.getBody();
+ } catch (HttpClientErrorException ex) {
+ JsonObject json = GSON.fromJson(ex.getResponseBodyAsString(), JsonObject.class);
+ throw new ErrorResponse(
+ HttpStatus.valueOf(ex.getStatusCode().value()),
+ json.get("code").getAsInt(),
+ json.get("message").getAsString(),
+ DateUtils.stringToDate(json.get("timestamp").getAsString())
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/xyz/mcutils/exception/ErrorResponse.java b/src/main/java/xyz/mcutils/exception/ErrorResponse.java
new file mode 100644
index 0000000..76d7220
--- /dev/null
+++ b/src/main/java/xyz/mcutils/exception/ErrorResponse.java
@@ -0,0 +1,34 @@
+package xyz.mcutils.exception;
+
+import io.micrometer.common.lang.NonNull;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.ToString;
+import org.springframework.http.HttpStatus;
+
+import java.util.Date;
+
+@Getter
+@ToString @AllArgsConstructor
+public class ErrorResponse extends RuntimeException {
+ /**
+ * The status code of this error.
+ */
+ @NonNull
+ private final HttpStatus status;
+
+ /**
+ * The HTTP code of this error.
+ */
+ private final int code;
+
+ /**
+ * The message of this error.
+ */
+ @NonNull private final String message;
+
+ /**
+ * The timestamp this error occurred.
+ */
+ @NonNull private final Date timestamp;
+}
\ No newline at end of file
diff --git a/src/main/java/xyz/mcutils/models/CachedResponse.java b/src/main/java/xyz/mcutils/models/CachedResponse.java
new file mode 100644
index 0000000..7cc1acb
--- /dev/null
+++ b/src/main/java/xyz/mcutils/models/CachedResponse.java
@@ -0,0 +1,58 @@
+package xyz.mcutils.models;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+public class CachedResponse {
+
+ /**
+ * The cache information for this response.
+ */
+ private Cache cache;
+
+ @AllArgsConstructor
+ @Getter
+ @Setter
+ public static class Cache {
+ /**
+ * Whether this request is cached.
+ */
+ private boolean cached;
+
+ /**
+ * The unix timestamp of when this was cached.
+ */
+ private long cachedTime;
+
+ /**
+ * Create a new cache information object with the default values.
+ *
+ * The default values are:
+ *
+ *
+ *
cached: true
+ *
cachedAt: {@link System#currentTimeMillis()}
+ *
+ *
+ *
+ *
+ * @return the default cache information object
+ */
+ public static Cache defaultCache() {
+ return new Cache(true, System.currentTimeMillis());
+ }
+
+ /**
+ * Sets if this request is cached.
+ *
+ * @param cached the new value of if this request is cached
+ */
+ public void setCached(boolean cached) {
+ this.cached = cached;
+ if (!cached) {
+ cachedTime = -1;
+ }
+ }
+ }
+}
diff --git a/src/main/java/xyz/mcutils/models/dns/DNSRecord.java b/src/main/java/xyz/mcutils/models/dns/DNSRecord.java
new file mode 100644
index 0000000..026c379
--- /dev/null
+++ b/src/main/java/xyz/mcutils/models/dns/DNSRecord.java
@@ -0,0 +1,29 @@
+package xyz.mcutils.models.dns;
+
+import io.micrometer.common.lang.NonNull;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Setter @Getter
+@NoArgsConstructor @AllArgsConstructor
+public class DNSRecord {
+ /**
+ * The type of this record.
+ */
+ @NonNull
+ private Type type;
+
+ /**
+ * The TTL (Time To Live) of this record.
+ */
+ private long ttl;
+
+ /**
+ * Types of a record.
+ */
+ public enum Type {
+ A, SRV
+ }
+}
diff --git a/src/main/java/xyz/mcutils/models/dns/impl/ARecord.java b/src/main/java/xyz/mcutils/models/dns/impl/ARecord.java
new file mode 100644
index 0000000..60dbcf9
--- /dev/null
+++ b/src/main/java/xyz/mcutils/models/dns/impl/ARecord.java
@@ -0,0 +1,15 @@
+package xyz.mcutils.models.dns.impl;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import xyz.mcutils.models.dns.DNSRecord;
+
+@Setter @Getter
+@NoArgsConstructor
+public final class ARecord extends DNSRecord {
+ /**
+ * The address of this record, null if unresolved.
+ */
+ private String address;
+}
\ No newline at end of file
diff --git a/src/main/java/xyz/mcutils/models/dns/impl/SRVRecord.java b/src/main/java/xyz/mcutils/models/dns/impl/SRVRecord.java
new file mode 100644
index 0000000..1363d7e
--- /dev/null
+++ b/src/main/java/xyz/mcutils/models/dns/impl/SRVRecord.java
@@ -0,0 +1,31 @@
+package xyz.mcutils.models.dns.impl;
+
+import io.micrometer.common.lang.NonNull;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import xyz.mcutils.models.dns.DNSRecord;
+
+@Setter @Getter
+@NoArgsConstructor
+public final class SRVRecord extends DNSRecord {
+ /**
+ * The priority of this record.
+ */
+ private int priority;
+
+ /**
+ * The weight of this record.
+ */
+ private int weight;
+
+ /**
+ * The port of this record.
+ */
+ private int port;
+
+ /**
+ * The target of this record.
+ */
+ @NonNull private String target;
+}
\ No newline at end of file
diff --git a/src/main/java/xyz/mcutils/models/mojang/CachedMojangEndpointStatus.java b/src/main/java/xyz/mcutils/models/mojang/CachedMojangEndpointStatus.java
new file mode 100644
index 0000000..c7c5ef1
--- /dev/null
+++ b/src/main/java/xyz/mcutils/models/mojang/CachedMojangEndpointStatus.java
@@ -0,0 +1,35 @@
+package xyz.mcutils.models.mojang;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import xyz.mcutils.models.CachedResponse;
+
+import java.util.Map;
+
+@AllArgsConstructor
+@Getter
+public class CachedMojangEndpointStatus extends CachedResponse {
+
+ /**
+ * The list of endpoints and their status.
+ */
+ private Map endpoints;
+
+ public enum Status {
+ /**
+ * The service is online and operational.
+ */
+ ONLINE,
+
+ /**
+ * The service is online, but may be experiencing issues.
+ * This could be due to high load or other issues.
+ */
+ DEGRADED,
+
+ /**
+ * The service is offline and not operational.
+ */
+ OFFLINE
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/xyz/mcutils/models/player/CachedPlayer.java b/src/main/java/xyz/mcutils/models/player/CachedPlayer.java
new file mode 100644
index 0000000..55329bf
--- /dev/null
+++ b/src/main/java/xyz/mcutils/models/player/CachedPlayer.java
@@ -0,0 +1,62 @@
+package xyz.mcutils.models.player;
+
+import lombok.Getter;
+import xyz.mcutils.models.CachedResponse;
+
+import java.util.UUID;
+
+@Getter
+public class CachedPlayer extends CachedResponse {
+
+ /**
+ * The UUID of the player
+ */
+ private UUID uniqueId;
+
+ /**
+ * The trimmed UUID of the player
+ */
+ private String trimmedUniqueId;
+
+ /**
+ * The username of the player
+ */
+ private String username;
+
+ /**
+ * The skin of the player, null if the
+ * player does not have a skin
+ */
+ private Skin skin;
+
+ /**
+ * The cape of the player, null if the
+ * player does not have a cape
+ */
+ private Cape cape;
+
+ /**
+ * The raw properties of the player
+ */
+ private ProfileProperty[] rawProperties;
+
+ /**
+ * A profile property for the player.
+ */
+ private static class ProfileProperty {
+ /**
+ * The name of the property.
+ */
+ private String name;
+
+ /**
+ * The base64 value of the property.
+ */
+ private String value;
+
+ /**
+ * Whether the property is signed.
+ */
+ private boolean signed;
+ }
+}
diff --git a/src/main/java/xyz/mcutils/models/player/CachedPlayerSkinPart.java b/src/main/java/xyz/mcutils/models/player/CachedPlayerSkinPart.java
new file mode 100644
index 0000000..3a0ce2d
--- /dev/null
+++ b/src/main/java/xyz/mcutils/models/player/CachedPlayerSkinPart.java
@@ -0,0 +1,12 @@
+package xyz.mcutils.models.player;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor @Getter
+public class CachedPlayerSkinPart {
+ /**
+ * The bytes for the skin part
+ */
+ private byte[] partBytes;
+}
diff --git a/src/main/java/xyz/mcutils/models/player/CachedUsernameToUuid.java b/src/main/java/xyz/mcutils/models/player/CachedUsernameToUuid.java
new file mode 100644
index 0000000..ca08814
--- /dev/null
+++ b/src/main/java/xyz/mcutils/models/player/CachedUsernameToUuid.java
@@ -0,0 +1,19 @@
+package xyz.mcutils.models.player;
+
+import lombok.Getter;
+import xyz.mcutils.models.CachedResponse;
+
+import java.util.UUID;
+
+@Getter
+public class CachedUsernameToUuid extends CachedResponse {
+ /**
+ * The username of the player.
+ */
+ private String username;
+
+ /**
+ * The unique id of the player.
+ */
+ private UUID uniqueId;
+}
diff --git a/src/main/java/xyz/mcutils/models/player/Cape.java b/src/main/java/xyz/mcutils/models/player/Cape.java
new file mode 100644
index 0000000..bfafc7a
--- /dev/null
+++ b/src/main/java/xyz/mcutils/models/player/Cape.java
@@ -0,0 +1,8 @@
+package xyz.mcutils.models.player;
+
+public class Cape {
+ /**
+ * The URL of the cape
+ */
+ private String url;
+}
\ No newline at end of file
diff --git a/src/main/java/xyz/mcutils/models/player/Skin.java b/src/main/java/xyz/mcutils/models/player/Skin.java
new file mode 100644
index 0000000..e709c71
--- /dev/null
+++ b/src/main/java/xyz/mcutils/models/player/Skin.java
@@ -0,0 +1,51 @@
+package xyz.mcutils.models.player;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Skin {
+ /**
+ * The URL for the skin
+ */
+ private String url;
+
+ /**
+ * The model for the skin
+ */
+ private Model model;
+
+ /**
+ * The legacy status of the skin
+ */
+ private boolean legacy;
+
+ /**
+ * The part URLs of the skin
+ */
+ private Map parts = new HashMap<>();
+
+ /**
+ * The model of the skin.
+ */
+ public enum Model {
+ DEFAULT,
+ SLIM
+ }
+
+ @AllArgsConstructor
+ @Getter
+ public enum SkinPart {
+ HEAD("head"),
+ FACE("face"),
+ BODY("body");
+
+ /**
+ * The name of the skin part
+ */
+ private final String name;
+ }
+
+}
diff --git a/src/main/java/xyz/mcutils/models/server/CachedBedrockMinecraftServer.java b/src/main/java/xyz/mcutils/models/server/CachedBedrockMinecraftServer.java
new file mode 100644
index 0000000..83c3c39
--- /dev/null
+++ b/src/main/java/xyz/mcutils/models/server/CachedBedrockMinecraftServer.java
@@ -0,0 +1,75 @@
+package xyz.mcutils.models.server;
+
+import lombok.Getter;
+import lombok.ToString;
+
+@Getter
+public class CachedBedrockMinecraftServer extends MinecraftServer {
+ /**
+ * The unique ID of this server.
+ */
+ private String id;
+
+ /**
+ * The edition of this server.
+ */
+ private Edition edition;
+
+ /**
+ * The version information of this server.
+ */
+ private Version version;
+
+ /**
+ * The gamemode of this server.
+ */
+ private GameMode gamemode;
+
+ /**
+ * The edition of a Bedrock server.
+ */
+ @Getter
+ public enum Edition {
+ /**
+ * Minecraft: Pocket Edition.
+ */
+ MCPE,
+
+ /**
+ * Minecraft: Education Edition.
+ */
+ MCEE
+ }
+
+ /**
+ * Version information for a server.
+ */
+ @Getter @ToString
+ public static class Version {
+ /**
+ * The protocol version of the server.
+ */
+ private int protocol;
+
+ /**
+ * The version name of the server.
+ */
+ private String name;
+ }
+
+ /**
+ * The gamemode of a server.
+ */
+ @Getter @ToString
+ public static class GameMode {
+ /**
+ * The name of this gamemode.
+ */
+ private String name;
+
+ /**
+ * The numeric of this gamemode.
+ */
+ private int numericId;
+ }
+}
diff --git a/src/main/java/xyz/mcutils/models/server/CachedJavaMinecraftServer.java b/src/main/java/xyz/mcutils/models/server/CachedJavaMinecraftServer.java
new file mode 100644
index 0000000..4f3cda3
--- /dev/null
+++ b/src/main/java/xyz/mcutils/models/server/CachedJavaMinecraftServer.java
@@ -0,0 +1,181 @@
+package xyz.mcutils.models.server;
+
+import lombok.Getter;
+import lombok.ToString;
+
+@Getter
+public class CachedJavaMinecraftServer extends MinecraftServer {
+
+ /**
+ * The version of the server.
+ */
+ private Version version;
+
+ /**
+ * The favicon of the server.
+ */
+ private Favicon favicon;
+
+ /**
+ * The mods running on this server.
+ */
+ private ForgeModInfo modInfo;
+
+ /**
+ * The mods running on this server.
+ *
+ * This is only used for servers
+ * running 1.13 and above.
+ *
+ */
+ private ForgeData forgeData;
+
+ /**
+ * Whether the server prevents chat reports.
+ */
+ private boolean preventsChatReports;
+
+ /**
+ * Whether the server enforces secure chat.
+ */
+ private boolean enforcesSecureChat;
+
+ /**
+ * Whether the server has previews chat enabled.
+ *
+ * Chat Preview sends chat messages to the server as they are typed, even before they're sent.
+ * More information
+ *
+ */
+ private boolean previewsChat;
+
+ /**
+ * The mojang blocked status for the server.
+ */
+ private boolean mojangBlocked;
+
+ @Getter
+ public static class Version {
+ /**
+ * The version name of the server.
+ */
+
+ private String name;
+
+ /**
+ * The server platform.
+ */
+ private String platform;
+
+ /**
+ * The protocol version.
+ */
+ private int protocol;
+
+ /**
+ * The name of the protocol, null if not found.
+ */
+ private String protocolName;
+ }
+
+ @Getter
+ public static class Favicon {
+
+ /**
+ * The raw base64 of the favicon.
+ */
+ private String base64;
+
+ /**
+ * The url to the favicon.
+ */
+ private String url;
+ }
+
+ /**
+ * Forge mod information for a server.
+ */
+ @Getter @ToString
+ public static class ForgeModInfo {
+ /**
+ * The type of modded server this is.
+ */
+ private String type;
+
+ /**
+ * The list of mods on this server, null or empty if none.
+ */
+ private ForgeMod[] modList;
+
+ /**
+ * A forge mod for a server.
+ */
+ @Getter @ToString
+ private static class ForgeMod {
+ /**
+ * The id of this mod.
+ */
+ private String name;
+
+ /**
+ * The version of this mod.
+ */
+ private String version;
+ }
+ }
+
+ @Getter
+ public static class ForgeData {
+
+ /**
+ * The list of mod channels on this server, null or empty if none.
+ */
+ private Channel[] channels;
+
+ /**
+ * The list of mods on this server, null or empty if none.
+ */
+ private Mod[] mods;
+
+ /**
+ * Whether the mod list is truncated.
+ */
+ private boolean truncated;
+
+ /**
+ * The version of the FML network.
+ */
+ private int fmlNetworkVersion;
+
+ @Getter
+ public static class Channel {
+ /**
+ * The id of this mod channel.
+ */
+ private String name;
+
+ /**
+ * The version of this mod channel.
+ */
+ private String version;
+
+ /**
+ * Whether this mod channel is required to join.
+ */
+ private boolean required;
+ }
+
+ @Getter
+ public static class Mod {
+ /**
+ * The id of this mod.
+ */
+ private String name;
+
+ /**
+ * The version of this mod.
+ */
+ private String version;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/xyz/mcutils/models/server/CachedServerBlockedStatus.java b/src/main/java/xyz/mcutils/models/server/CachedServerBlockedStatus.java
new file mode 100644
index 0000000..cb9be57
--- /dev/null
+++ b/src/main/java/xyz/mcutils/models/server/CachedServerBlockedStatus.java
@@ -0,0 +1,12 @@
+package xyz.mcutils.models.server;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor @Getter
+public class CachedServerBlockedStatus {
+ /**
+ * Whether the server is Mojang blocked.
+ */
+ private boolean blocked;
+}
diff --git a/src/main/java/xyz/mcutils/models/server/CachedServerIcon.java b/src/main/java/xyz/mcutils/models/server/CachedServerIcon.java
new file mode 100644
index 0000000..73d6e35
--- /dev/null
+++ b/src/main/java/xyz/mcutils/models/server/CachedServerIcon.java
@@ -0,0 +1,12 @@
+package xyz.mcutils.models.server;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor @Getter
+public class CachedServerIcon {
+ /**
+ * The bytes for the server icon.
+ */
+ private byte[] bytes;
+}
diff --git a/src/main/java/xyz/mcutils/models/server/MinecraftServer.java b/src/main/java/xyz/mcutils/models/server/MinecraftServer.java
new file mode 100644
index 0000000..b6d8ac6
--- /dev/null
+++ b/src/main/java/xyz/mcutils/models/server/MinecraftServer.java
@@ -0,0 +1,97 @@
+package xyz.mcutils.models.server;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.ToString;
+import xyz.mcutils.models.dns.DNSRecord;
+
+import java.util.UUID;
+
+@Getter
+public class MinecraftServer {
+ /**
+ * The hostname of the server.
+ */
+ private String hostname;
+
+ /**
+ * The IP address of the server.
+ */
+ private String ip;
+
+ /**
+ * The port of the server.
+ */
+ private int port;
+
+ /**
+ * The DNS records for the server.
+ */
+ private DNSRecord[] records;
+
+ /**
+ * The motd for the server.
+ */
+ private MOTD motd;
+
+ /**
+ * The players on the server.
+ */
+ private Players players;
+
+ @AllArgsConstructor @Getter
+ private static class MOTD {
+
+ /**
+ * The raw motd lines
+ */
+ private String[] raw;
+
+ /**
+ * The clean motd lines
+ */
+ private String[] clean;
+
+ /**
+ * The html motd lines
+ */
+ private String[] html;
+ }
+
+ /**
+ * Player count data for a server.
+ */
+ @Getter
+ private static class Players {
+ /**
+ * The online players on this server.
+ */
+ private int online;
+
+ /**
+ * The maximum allowed players on this server.
+ */
+ private int max;
+
+ /**
+ * A sample of players on this server, null or empty if no sample.
+ */
+ private Sample[] sample;
+
+ /**
+ * A sample player.
+ */
+ @Getter @ToString
+ private static class Sample {
+ /**
+ * The unique id of this player.
+ */
+ private UUID id;
+
+ /**
+ * The name of this player.
+ */
+ private String name;
+ }
+ }
+}
diff --git a/src/main/java/xyz/mcutils/models/server/ServerPlatform.java b/src/main/java/xyz/mcutils/models/server/ServerPlatform.java
new file mode 100644
index 0000000..3d2d1d0
--- /dev/null
+++ b/src/main/java/xyz/mcutils/models/server/ServerPlatform.java
@@ -0,0 +1,20 @@
+package xyz.mcutils.models.server;
+
+import lombok.Getter;
+
+@Getter
+public enum ServerPlatform {
+ /**
+ * The platform is Java Edition.
+ */
+ JAVA,
+ /**
+ * The platform is Bedrock Edition.
+ */
+ BEDROCK;
+
+ /**
+ * The name of the platform.
+ */
+ private final String name = name().toLowerCase();
+}
diff --git a/src/test/java/xyz/mcutils/MojangTests.java b/src/test/java/xyz/mcutils/MojangTests.java
new file mode 100644
index 0000000..d7d500e
--- /dev/null
+++ b/src/test/java/xyz/mcutils/MojangTests.java
@@ -0,0 +1,15 @@
+package xyz.mcutils;
+
+import lombok.SneakyThrows;
+import org.junit.jupiter.api.Test;
+import xyz.mcutils.models.mojang.CachedMojangEndpointStatus;
+
+public class MojangTests {
+
+ @Test
+ @SneakyThrows
+ public void ensureMojangApiStatusSuccess() {
+ CachedMojangEndpointStatus status = McUtilsAPI.getMojangApiStatus();
+ assert !status.getEndpoints().isEmpty();
+ }
+}
diff --git a/src/test/java/xyz/mcutils/PlayerTests.java b/src/test/java/xyz/mcutils/PlayerTests.java
new file mode 100644
index 0000000..39ade31
--- /dev/null
+++ b/src/test/java/xyz/mcutils/PlayerTests.java
@@ -0,0 +1,77 @@
+package xyz.mcutils;
+
+import lombok.SneakyThrows;
+import org.junit.jupiter.api.Test;
+import xyz.mcutils.exception.ErrorResponse;
+import xyz.mcutils.models.player.CachedPlayer;
+import xyz.mcutils.models.player.CachedPlayerSkinPart;
+import xyz.mcutils.models.player.CachedUsernameToUuid;
+import xyz.mcutils.models.player.Skin;
+
+public class PlayerTests {
+
+ private final String testPlayerUuid = "eeab5f8a-18dd-4d58-af78-2b3c4543da48";
+ private final String testPlayer = "ImFascinated";
+ private final String testInvalidPlayer = "invalidplayeromgyeswow";
+
+ @Test
+ @SneakyThrows
+ public void ensurePlayerLookupUuidSuccess() {
+ CachedPlayer player = McUtilsAPI.getPlayer(testPlayerUuid);
+ assert player.getUsername().equals(testPlayer);
+ }
+
+ @Test
+ @SneakyThrows
+ public void ensurePlayerLookupUuidFailure() {
+ try {
+ McUtilsAPI.getPlayer(testInvalidPlayer);
+ } catch (ErrorResponse ex) {
+ assert ex.getCode() == 404;
+ }
+ }
+
+ @Test
+ @SneakyThrows
+ public void ensurePlayerLookupUsernameSuccess() {
+ CachedPlayer player = McUtilsAPI.getPlayer(testPlayer);
+ assert player.getUsername().equals(testPlayer);
+ }
+
+ @Test
+ @SneakyThrows
+ public void ensurePlayerLookupUsernameFailure() {
+ try {
+ McUtilsAPI.getPlayer(testInvalidPlayer);
+ } catch (ErrorResponse ex) {
+ assert ex.getCode() == 404;
+ }
+ }
+
+ @Test
+ @SneakyThrows
+ public void ensurePlayerUsernameToUuidLookupSuccess() {
+ CachedUsernameToUuid player = McUtilsAPI.getUsernameToUuid(testPlayer);
+ assert player.getUsername().equals(testPlayer);
+ assert player.getUniqueId().toString().equals(testPlayerUuid);
+ }
+
+ @Test
+ @SneakyThrows
+ public void ensurePlayerUsernameToUuidLookupFailure() {
+ try {
+ McUtilsAPI.getUsernameToUuid(testInvalidPlayer);
+ } catch (ErrorResponse ex) {
+ assert ex.getCode() == 404;
+ }
+ }
+
+ @Test
+ @SneakyThrows
+ public void ensurePlayerSkinPartsLookupSuccess() {
+ for (Skin.SkinPart part : Skin.SkinPart.values()) {
+ CachedPlayerSkinPart partBytes = McUtilsAPI.getPlayerSkinPart(part, testPlayer);
+ assert partBytes.getPartBytes() != null;
+ }
+ }
+}
diff --git a/src/test/java/xyz/mcutils/ServerTests.java b/src/test/java/xyz/mcutils/ServerTests.java
new file mode 100644
index 0000000..f53c1c2
--- /dev/null
+++ b/src/test/java/xyz/mcutils/ServerTests.java
@@ -0,0 +1,74 @@
+package xyz.mcutils;
+
+import lombok.SneakyThrows;
+import org.junit.jupiter.api.Test;
+import xyz.mcutils.exception.ErrorResponse;
+import xyz.mcutils.models.server.CachedBedrockMinecraftServer;
+import xyz.mcutils.models.server.CachedJavaMinecraftServer;
+import xyz.mcutils.models.server.CachedServerBlockedStatus;
+import xyz.mcutils.models.server.CachedServerIcon;
+
+public class ServerTests {
+
+ private final String testJavaServer = "play.hypixel.net";
+ private final String testBedrockServer = "geo.hivebedrock.network";
+ private final String testInvalidServer = "invalidhostnamehahahahahayesslmaooo";
+
+ @Test
+ @SneakyThrows
+ public void ensureJavaServerLookupSuccess() {
+ CachedJavaMinecraftServer server = McUtilsAPI.getJavaServer(testJavaServer);
+ assert server.getHostname().equals(testJavaServer);
+ }
+
+ @Test
+ @SneakyThrows
+ public void ensureJavaServerLookupFailure() {
+ try {
+ McUtilsAPI.getJavaServer(testInvalidServer);
+ } catch (ErrorResponse ex) {
+ assert ex.getCode() == 400;
+ }
+ }
+
+ @Test
+ @SneakyThrows
+ public void ensureJavaServerIconLookupSuccess() {
+ CachedServerIcon icon = McUtilsAPI.getServerIcon(testJavaServer);
+ assert icon.getBytes() != null;
+ }
+
+ @Test
+ @SneakyThrows
+ public void ensureJavaServerIconLookupFailure() {
+ try {
+ McUtilsAPI.getServerIcon(testInvalidServer);
+ } catch (ErrorResponse ex) {
+ assert ex.getCode() == 400;
+ }
+ }
+
+ @Test
+ @SneakyThrows
+ public void ensureJavaServerBlockedLookupSuccess() {
+ CachedServerBlockedStatus status = McUtilsAPI.getServerBlockedStatus(testJavaServer);
+ assert !status.isBlocked();
+ }
+
+ @Test
+ @SneakyThrows
+ public void ensureBedrockServerLookupSuccess() {
+ CachedBedrockMinecraftServer server = McUtilsAPI.getBedrockServer(testBedrockServer);
+ assert server.getHostname().equals(testBedrockServer);
+ }
+
+ @Test
+ @SneakyThrows
+ public void ensureBedrockServerLookupFailure() {
+ try {
+ McUtilsAPI.getBedrockServer(testInvalidServer);
+ } catch (ErrorResponse ex) {
+ assert ex.getCode() == 400;
+ }
+ }
+}