govno kod
Some checks failed
Build / build (push) Failing after 3h3m20s

This commit is contained in:
2026-05-01 23:51:16 +03:00
commit 6179d4fbc9
906 changed files with 116097 additions and 0 deletions

13
Mojang_API/.gitignore vendored Normal file
View File

@@ -0,0 +1,13 @@
#IntelliJ
.idea/*
**.iml
#Maven
target/*
#Eclipse
**/.settings/*
**/.project
**/.classpath
/target/

110
Mojang_API/pom.xml Normal file
View File

@@ -0,0 +1,110 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>invsee-plus-plus</artifactId>
<groupId>com.janboerman.invsee</groupId>
<version>0.31.11-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mojang-api</artifactId>
<name>Mojang API</name>
<build>
<defaultGoal>clean package</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>${maven-shade-plugin.version}</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>org.json.simple</pattern>
<shadedPattern>com.janboerman.invsee.jsonsimple</shadedPattern>
</relocation>
</relocations>
<artifactSet>
<excludes>
<exclude>junit:junit</exclude>
<exclude>org.hamcrest:hamcrest-core</exclude>
</excludes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<executions>
<execution>
<id>compile-java-8</id>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<release>8</release>
</configuration>
</execution>
<execution>
<id>compile-java-11</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<release>11</release>
<compileSourceRoots>
<compileSourceRoot>${project.basedir}/src/main/java11</compileSourceRoot>
</compileSourceRoots>
<multiReleaseOutput>true</multiReleaseOutput>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${maven-jar-plugin.version}</version>
<configuration>
<archive>
<manifestEntries>
<Multi-Release>true</Multi-Release>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.janboerman.invsee</groupId>
<artifactId>utils</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,81 @@
package com.janboerman.invsee.mojangapi;
import static com.janboerman.invsee.mojangapi.ResponseUtils.readJSONObject;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import com.janboerman.invsee.utils.Rethrow;
import org.json.simple.JSONObject;
public class ElectroidAPI {
private final Executor asyncExecutor;
public ElectroidAPI(Executor asyncExecutor) {
this.asyncExecutor = asyncExecutor;
}
public CompletableFuture<Optional<UUID>> lookupUniqueId(String userName) {
return CompletableFuture.supplyAsync(() -> {
try {
URL url = new URL("https://api.ashcon.app/mojang/v2/user/" + userName);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5 * 1000); //5 seconds
connection.setRequestProperty("Accept", "application/json");
connection.setRequestProperty("User-Agent", "InvSee++/ElectroidAPI");
int statusCode = connection.getResponseCode();
switch (statusCode) {
case HttpURLConnection.HTTP_OK:
JSONObject json = readJSONObject(connection);
String uuid = (String) json.get("uuid");
return Optional.of(UUID.fromString(uuid));
case HttpURLConnection.HTTP_NO_CONTENT:
return Optional.empty();
default:
throw new RuntimeException("Unexpected response from Electroid mojang api, status code=" + statusCode + ".");
}
} catch (IOException e) {
return Rethrow.unchecked(e);
}
}, asyncExecutor);
}
public CompletableFuture<Optional<String>> lookupUserName(UUID uniqueId) {
return CompletableFuture.supplyAsync(() -> {
try {
URL url = new URL("https://api.ashcon.app/mojang/v2/user/" + uniqueId.toString());
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5 * 1000); //5 seconds
connection.setRequestProperty("Accept", "application/json");
connection.setRequestProperty("User-Agent", "InvSee++/ElectroidAPI");
int statusCode = connection.getResponseCode();
switch (statusCode) {
case HttpURLConnection.HTTP_OK:
JSONObject json = readJSONObject(connection);
String userName = (String) json.get("username");
return Optional.of(userName);
case HttpURLConnection.HTTP_NO_CONTENT:
return Optional.empty();
default:
throw new RuntimeException("Unexpected response from Electroid mojang api, status code=" + statusCode + ".");
}
} catch (IOException e) {
return Rethrow.unchecked(e);
}
}, asyncExecutor);
}
}

View File

@@ -0,0 +1,127 @@
package com.janboerman.invsee.mojangapi;
import static com.janboerman.invsee.mojangapi.ResponseUtils.readJSONObject;
import static com.janboerman.invsee.mojangapi.ResponseUtils.charsetFromHeader;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import com.janboerman.invsee.utils.Compat;
import com.janboerman.invsee.utils.Rethrow;
import com.janboerman.invsee.utils.UUIDHelper;
import org.json.simple.JSONObject;
public class MojangAPI {
private final Executor asyncExecutor;
public MojangAPI(Executor asyncExecutor) {
this.asyncExecutor = asyncExecutor;
}
public CompletableFuture<Optional<UUID>> lookupUniqueId(String userName) {
return CompletableFuture.supplyAsync(() -> {
try {
URL url = new URL("https://api.mojang.com/users/profiles/minecraft/" + userName);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5 * 1000); //5 seconds
connection.setRequestProperty("Accept", "application/json");
connection.setRequestProperty("User-Agent", "InvSee++/MojangAPI");
int statusCode = connection.getResponseCode();
if (statusCode == HttpURLConnection.HTTP_OK) {
JSONObject json = readJSONObject(connection);
String id = (String) json.get("id");
UUID uuid = UUIDHelper.dashed(id);
return Optional.of(uuid);
}
else {
return handleNotOk(statusCode, connection);
}
} catch (IOException e) {
return Rethrow.unchecked(e);
}
}, asyncExecutor);
}
public CompletableFuture<Optional<String>> lookupUserName(UUID uniqueId) {
return CompletableFuture.supplyAsync(() -> {
try {
URL url = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + UUIDHelper.unDashed(uniqueId));
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5 * 1000); //5 seconds
connection.setRequestProperty("Accept", "application/json");
connection.setRequestProperty("User-Agent", "InvSee++/MojangAPI");
int statusCode = connection.getResponseCode();
if (statusCode == HttpURLConnection.HTTP_OK) {
JSONObject profileJson = readJSONObject(connection);
String userName = (String) profileJson.get("name");
return Optional.of(userName);
}
else {
return handleNotOk(statusCode, connection);
}
} catch (IOException e) {
return Rethrow.unchecked(e);
}
}, asyncExecutor);
}
private static <T> Optional<T> handleNotOk(int statusCode, HttpURLConnection connection) throws IOException {
assert statusCode == connection.getResponseCode();
switch (statusCode) {
case ResponseUtils.HTTP_NO_CONTENT: return handleNoContent(connection);
case ResponseUtils.HTTP_BAD_REQUEST: return handleBadRequest(connection);
case ResponseUtils.HTTP_TOO_MANY_REQUESTS: return handleTooManyRequests(connection);
default: return handleUnknownStatusCode(statusCode);
}
}
private static <T> Optional<T> handleNoContent(HttpURLConnection connection) {
return Optional.empty();
}
private static <T> Optional<T> handleBadRequest(HttpURLConnection connection) {
JSONObject jsonObject = readJSONObject(connection);
String error = (String) jsonObject.get("error");
String errorMessage = (String) jsonObject.get("errorMessage");
throw new RuntimeException("We sent a bad request to Mojang. We got a(n) " + error + " with the following message: " + errorMessage);
}
private static <T> Optional<T> handleTooManyRequests(HttpURLConnection connection) {
try (InputStream inputStream = connection.getInputStream()) {
byte[] bytes = Compat.readAllBytes(inputStream);
Charset charset = charsetFromHeader(connection.getHeaderField("Content-Type"));
String errorMessage = new String(bytes, charset);
throw new RuntimeException("We were rate limited by Mojang. Error message: " + errorMessage);
} catch (IOException e) {
throw new RuntimeException("Exception occurred when processing 429 (rate limited) response.", e);
}
}
private static <T> Optional<T> handleUnknownStatusCode(int statusCode) {
throw new RuntimeException("Unexpected status code from Mojang API: " + statusCode);
}
}

View File

@@ -0,0 +1,56 @@
package com.janboerman.invsee.mojangapi;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
class ResponseUtils {
static final int HTTP_NO_CONTENT = HttpURLConnection.HTTP_NO_CONTENT;
static final int HTTP_BAD_REQUEST = HttpURLConnection.HTTP_BAD_REQUEST;
static final int HTTP_TOO_MANY_REQUESTS = 429;
private ResponseUtils() {}
static JSONObject readJSONObject(HttpURLConnection connection) {
Charset charset = charsetFromHeader(connection.getHeaderField("Content-Type"));
try (InputStream inputStream = connection.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, charset)) {
Object json = new JSONParser().parse(inputStreamReader);
if (json instanceof JSONObject) {
return (JSONObject) json;
} else {
throw new RuntimeException("Expected response to be represented as a JSON Object, instead we got: " + json);
}
} catch (IOException ioe) {
throw new RuntimeException("Could not read http response body", ioe);
} catch (ParseException pe) {
throw new RuntimeException("Invalid JSON from api", pe);
}
}
static Charset charsetFromHeader(String contentType) {
if (contentType != null) {
int indexOfSemi = contentType.indexOf(';');
if (indexOfSemi != -1) {
String charsetPart = contentType.substring(indexOfSemi + 1).trim();
String[] charSetKeyAndValue = charsetPart.split("=", 2);
if (charSetKeyAndValue.length == 2 && "charset".equalsIgnoreCase(charSetKeyAndValue[0])) {
String charsetName = charSetKeyAndValue[1];
return Charset.forName(charsetName);
}
}
}
return StandardCharsets.UTF_8;
}
}

View File

@@ -0,0 +1,89 @@
package com.janboerman.invsee.mojangapi;
import org.json.simple.JSONObject;
import java.io.InputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import static com.janboerman.invsee.mojangapi.ResponseUtils.*;
/**
* API to resolve players' usernames and unique IDs via <a href="https://github.com/Electroid/mojang-api">Electroid/mojang-api</a>.
*/
public class ElectroidAPI {
//https://github.com/Electroid/mojang-api
private final HttpClient httpClient;
public ElectroidAPI(Executor asyncExecutor) {
this.httpClient = HttpClient.newBuilder()
.executor(asyncExecutor)
.build();
}
@Deprecated
public ElectroidAPI(HttpClient httpClient) {
this.httpClient = httpClient;
}
@Deprecated
public ElectroidAPI() {
this(HttpClient.newHttpClient());
}
public CompletableFuture<Optional<UUID>> lookupUniqueId(String userName) {
CompletableFuture<HttpResponse<InputStream>> future = httpClient.sendAsync(HttpRequest
.newBuilder(URI.create("https://api.ashcon.app/mojang/v2/user/" + userName))
.header("Accept", "application/json")
.header("User-Agent", "InvSee++/ElectroidAPI")
.timeout(Duration.ofSeconds(5))
.build(), HttpResponse.BodyHandlers.ofInputStream());
return future.thenApply(response -> {
if (response.statusCode() == 200) {
//ok!
JSONObject json = readJSONObject(response);
String uuid = (String) json.get("uuid"); // already contains dashes.
return Optional.of(UUID.fromString(uuid));
} else if (response.statusCode() == 204) {
//no content
return Optional.empty();
} else {
//unexpected
throw new RuntimeException("Unexpected response from Electroid mojang api, status code=" + response.statusCode() + ".");
}
});
}
public CompletableFuture<Optional<String>> lookupUserName(UUID uniqueId) {
CompletableFuture<HttpResponse<InputStream>> future = httpClient.sendAsync(HttpRequest
.newBuilder(URI.create("https://api.ashcon.app/mojang/v2/user/" + uniqueId.toString())) // uuid with dashes allowed
.header("Accept", "application/json")
.header("User-Agent", "InvSee++/ElectroidAPI")
.timeout(Duration.ofSeconds(5))
.build(), HttpResponse.BodyHandlers.ofInputStream());
return future.thenApply(response -> {
if (response.statusCode() == 200) {
//ok!
JSONObject json = readJSONObject(response);
String userName = (String) json.get("username");
return Optional.of(userName);
} else if (response.statusCode() == 204) {
//no content
return Optional.empty();
} else {
//unexpected
throw new RuntimeException("Unexpected response from Electroid mojang api, status code=" + response.statusCode() + ".");
}
});
}
}

View File

@@ -0,0 +1,156 @@
package com.janboerman.invsee.mojangapi;
import com.janboerman.invsee.utils.UUIDHelper;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.Charset;
import java.time.Duration;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import org.json.simple.JSONObject;
import static com.janboerman.invsee.mojangapi.ResponseUtils.*;
public class MojangAPI {
//https://wiki.vg/Mojang_API
private final HttpClient httpClient;
public MojangAPI(Executor asyncExecutor) {
this.httpClient = HttpClient.newBuilder()
.executor(asyncExecutor)
.build();
}
/**
* Creates the Mojang API instance using the given HTTP client for all requests.
* @param httpClient the client to use for HTTP requests
*/
@Deprecated
public MojangAPI(HttpClient httpClient) {
this.httpClient = Objects.requireNonNull(httpClient);
}
/**
* Creates the instance of the Mojang API using {@link HttpClient#newHttpClient()}.
*/
@Deprecated
public MojangAPI() {
this(HttpClient.newHttpClient());
}
public CompletableFuture<Optional<UUID>> lookupUniqueId(String userName) {
CompletableFuture<HttpResponse<InputStream>> future = httpClient.sendAsync(HttpRequest
.newBuilder(URI.create("https://api.mojang.com/users/profiles/minecraft/" + userName))
.header("Accept", "application/json")
.header("User-Agent", "InvSee++/MojangAPI")
.timeout(Duration.ofSeconds(5))
.build(), HttpResponse.BodyHandlers.ofInputStream());
return future.thenApply((HttpResponse<InputStream> response) -> {
int statusCode = response.statusCode();
if (statusCode == 200) {
//ok!
JSONObject json = readJSONObject(response);
String id = (String) json.get("id");
UUID uuid = UUIDHelper.dashed(id);
return Optional.of(uuid);
}
else {
//not ok
return handleNotOk(response);
}
});
}
public CompletableFuture<Optional<String>> lookupUserName(UUID uniqueId) {
//no longer use name history api: https://wiki.vg/Mojang_API#UUID_to_Name_History_.28Removed.29,
//use profile api instead:
CompletableFuture<HttpResponse<InputStream>> future = httpClient.sendAsync(HttpRequest
.newBuilder(URI.create("https://sessionserver.mojang.com/session/minecraft/profile/" + UUIDHelper.unDashed(uniqueId)))
.header("Accept", "application/json")
.header("User-Agent", "InvSee++/MojangAPI")
.timeout(Duration.ofSeconds(5))
.build(), HttpResponse.BodyHandlers.ofInputStream());
return future.thenApply((HttpResponse<InputStream> response) -> {
int statusCode = response.statusCode();
if (statusCode == 200) {
//ok!
JSONObject profileJson = readJSONObject(response);
String userName = (String) profileJson.get("name");
return Optional.of(userName);
}
else {
//not ok
return handleNotOk(response);
}
});
}
private static <T> Optional<T> handleNotOk(HttpResponse<InputStream> response) {
int statusCode = response.statusCode();
if (statusCode == HTTP_NO_CONTENT) {
//no content - a player with that username does not exist.
return handleNoContent(response);
}
else if (statusCode == HTTP_BAD_REQUEST) {
//bad request
return handleBadRequest(response);
}
else if (statusCode == HTTP_TOO_MANY_REQUESTS) {
//rate limited
return handleTooManyRequests(response);
}
else {
//unknown response code - undocumented behaviour from mojang's api
return handleUnknownStatusCode(response);
}
}
private static <T> Optional<T> handleNoContent(HttpResponse<InputStream> response) {
return Optional.empty();
}
private static <T> Optional<T> handleBadRequest(HttpResponse<InputStream> response) {
JSONObject jsonObject = readJSONObject(response);
String error = (String) jsonObject.get("error");
String errorMessage = (String) jsonObject.get("errorMessage");
throw new RuntimeException("We sent a bad request to Mojang. We got a(n) " + error + " with the following message: " + errorMessage);
}
private static <T> Optional<T> handleTooManyRequests(HttpResponse<InputStream> response) {
try (InputStream inputStream = response.body()) {
byte[] bytes = inputStream.readAllBytes();
Charset charset = charsetFromHeaders(response.headers());
String errorMessage = new String(bytes, charset);
throw new RuntimeException("We were rate limited by Mojang. Error message: " + errorMessage);
} catch (IOException e) {
throw new RuntimeException("Exception occurred when processing 429 (rate limited) response.", e);
}
}
private static <T> Optional<T> handleUnknownStatusCode(HttpResponse<InputStream> response) {
throw new RuntimeException("Unexpected status code from Mojang API: " + response.statusCode());
}
}

View File

@@ -0,0 +1,81 @@
package com.janboerman.invsee.mojangapi;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.http.HttpHeaders;
import java.net.http.HttpResponse;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
final class ResponseUtils {
static final int HTTP_NO_CONTENT = 204;
static final int HTTP_BAD_REQUEST = 400;
static final int HTTP_TOO_MANY_REQUESTS = 429;
private ResponseUtils() {}
static JSONObject readJSONObject(HttpResponse<InputStream> response) {
Charset charset = charsetFromHeaders(response.headers());
try (InputStream inputStream = response.body();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, charset)) {
Object json = new JSONParser().parse(inputStreamReader);
if (json instanceof JSONObject) {
return (JSONObject) json;
} else {
throw new RuntimeException("Expected response to be represented as a JSON Object, instead we got: " + json);
}
} catch (IOException ioe) {
throw new RuntimeException("Could not read http response body", ioe);
} catch (ParseException pe) {
throw new RuntimeException("Invalid JSON from api", pe);
}
}
/*
static JSONArray readJSONArray(HttpResponse<InputStream> response) {
Charset charset = charsetFromHeaders(response.headers());
try (InputStream inputStream = response.body();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, charset)) {
Object json = new JSONParser().parse(inputStreamReader);
if (json instanceof JSONArray) {
return (JSONArray) json;
} else {
throw new RuntimeException("Expected response to be represented as a JSON Array, instead we got: " + json);
}
} catch (IOException ioe) {
throw new RuntimeException("Could not read http response body", ioe);
} catch (ParseException pe) {
throw new RuntimeException("Invalid JSON from Mojang api", pe);
}
}
*/
static Charset charsetFromHeaders(HttpHeaders headers) {
Optional<String> optionalContentType = headers.firstValue("Content-Type");
if (optionalContentType.isPresent()) {
String contentType = optionalContentType.get();
int indexOfSemi = contentType.indexOf(';');
if (indexOfSemi != -1) {
String charsetPart = contentType.substring(indexOfSemi + 1).trim();
String[] charSetKeyAndValue = charsetPart.split("=", 2);
if (charSetKeyAndValue.length == 2 && "charset".equalsIgnoreCase(charSetKeyAndValue[0])) {
String charsetName = charSetKeyAndValue[1];
return Charset.forName(charsetName);
}
}
}
//fallback
return StandardCharsets.UTF_8;
}
}