feat: spigot branch
This commit is contained in:
parent
ff892a7451
commit
4948610860
35 changed files with 144 additions and 1291 deletions
14
common/build.gradle.kts
Normal file
14
common/build.gradle.kts
Normal file
|
@ -0,0 +1,14 @@
|
|||
plugins {
|
||||
id("java")
|
||||
}
|
||||
|
||||
group = "xyz.ineanto.nicko"
|
||||
version = project.version
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("org.spigotmc:spigot-api:1.23.3-R0.1-SNAPSHOT")
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package xyz.ineanto.nicko.action;
|
||||
|
||||
public class ActionResult {
|
||||
private final String errorKey;
|
||||
private boolean error = false;
|
||||
|
||||
public static ActionResult ok() {
|
||||
return new ActionResult();
|
||||
}
|
||||
|
||||
public static ActionResult error() {
|
||||
return new ActionResult(null);
|
||||
}
|
||||
|
||||
public static ActionResult error(String errorMessage) {
|
||||
return new ActionResult(errorMessage);
|
||||
}
|
||||
|
||||
private ActionResult() {
|
||||
this.errorKey = null;
|
||||
}
|
||||
|
||||
private ActionResult(String errorMessage) {
|
||||
this.errorKey = errorMessage;
|
||||
this.error = true;
|
||||
}
|
||||
|
||||
public boolean isError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public String getErrorKey() {
|
||||
return errorKey;
|
||||
}
|
||||
}
|
155
common/src/main/java/xyz/ineanto/nicko/mojang/MojangAPI.java
Normal file
155
common/src/main/java/xyz/ineanto/nicko/mojang/MojangAPI.java
Normal file
|
@ -0,0 +1,155 @@
|
|||
package xyz.ineanto.nicko.mojang;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class MojangAPI {
|
||||
public static final String URL_NAME = "https://api.mojang.com/users/profiles/minecraft/{name}";
|
||||
public static final String URL_SKIN = "https://sessionserver.mojang.com/session/minecraft/profile/{uuid}?unsigned=false";
|
||||
|
||||
private final Logger logger = Logger.getLogger("MojangAPI");
|
||||
private final HashMap<String, String> uuidToName = new HashMap<>();
|
||||
private final ExecutorService worker = Executors.newFixedThreadPool(6);
|
||||
|
||||
private final CacheLoader<String, Optional<MojangSkin>> skinLoader = new CacheLoader<>() {
|
||||
@Nonnull
|
||||
public Optional<MojangSkin> load(@Nonnull String uuid) throws Exception {
|
||||
return getSkinFromMojang(uuid);
|
||||
}
|
||||
};
|
||||
|
||||
private final LoadingCache<String, Optional<MojangSkin>> skinCache = CacheBuilder
|
||||
.newBuilder()
|
||||
.recordStats()
|
||||
.expireAfterWrite(24, TimeUnit.HOURS)
|
||||
.build(skinLoader);
|
||||
|
||||
private final CacheLoader<String, Optional<String>> uuidLoader = new CacheLoader<>() {
|
||||
@Nonnull
|
||||
public Optional<String> load(@Nonnull String name) throws Exception {
|
||||
return getUUIDFromMojang(name);
|
||||
}
|
||||
};
|
||||
|
||||
private final LoadingCache<String, Optional<String>> uuidCache = CacheBuilder
|
||||
.newBuilder()
|
||||
.expireAfterWrite(2, TimeUnit.DAYS)
|
||||
.build(uuidLoader);
|
||||
|
||||
public Optional<MojangSkin> getSkin(String uuid) throws IOException, ExecutionException {
|
||||
return skinCache.get(uuid);
|
||||
}
|
||||
|
||||
public Optional<MojangSkin> getSkinWithoutCaching(String uuid) throws IOException, ExecutionException, InterruptedException {
|
||||
return getSkinFromMojang(uuid);
|
||||
}
|
||||
|
||||
public Optional<String> getUUID(String name) throws IOException, ExecutionException {
|
||||
return uuidCache.get(name);
|
||||
}
|
||||
|
||||
private Optional<String> getUUIDFromMojang(String name) throws ExecutionException, InterruptedException {
|
||||
final String parametrizedUrl = URL_NAME.replace("{name}", name);
|
||||
final JsonObject object = getRequestToUrl(parametrizedUrl);
|
||||
if (hasNoError(object)) {
|
||||
final JsonElement idObject = object.get("id");
|
||||
final String uuid = idObject.getAsString();
|
||||
final Optional<String> uuidOptional = Optional.of(uuid);
|
||||
uuidCache.put(name, uuidOptional);
|
||||
uuidToName.put(uuid, name);
|
||||
return uuidOptional;
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public void eraseFromCache(String uuid) {
|
||||
skinCache.invalidate(uuid);
|
||||
uuidToName.remove(uuid);
|
||||
uuidCache.invalidate(uuid);
|
||||
}
|
||||
|
||||
private Optional<MojangSkin> getSkinFromMojang(String uuid) throws ExecutionException, InterruptedException {
|
||||
final String parametrizedUrl = URL_SKIN.replace("{uuid}", uuid);
|
||||
final JsonObject object = getRequestToUrl(parametrizedUrl);
|
||||
if (hasNoError(object)) {
|
||||
final MojangSkin skin = MojangSkin.buildFromJson(object);
|
||||
return Optional.of(skin);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public String getUUIDName(String uuid) {
|
||||
return uuidToName.get(uuid);
|
||||
}
|
||||
|
||||
private JsonObject getRequestToUrl(String parametrizedUrl) throws ExecutionException, InterruptedException {
|
||||
return worker.submit(() -> {
|
||||
final URL url = URI.create(parametrizedUrl).toURL();
|
||||
final HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
|
||||
con.setDoInput(true);
|
||||
con.setRequestMethod("GET");
|
||||
|
||||
switch (con.getResponseCode()) {
|
||||
case 404:
|
||||
case 400:
|
||||
logger.warning("Failed to parse request: Invalid Name");
|
||||
return getErrorObject();
|
||||
case 429:
|
||||
logger.warning("Failed to parse request: The connection is throttled.");
|
||||
return getErrorObject();
|
||||
case 200:
|
||||
final BufferedReader input = new BufferedReader(new InputStreamReader(con.getInputStream()));
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
String line;
|
||||
while ((line = input.readLine()) != null) {
|
||||
builder.append(line);
|
||||
}
|
||||
|
||||
try {
|
||||
final JsonElement jsonElt = JsonParser.parseString(builder.toString());
|
||||
return jsonElt.getAsJsonObject();
|
||||
} catch (JsonParseException | IllegalStateException exception) {
|
||||
logger.warning("Failed to parse request (" + parametrizedUrl + ")!");
|
||||
return getErrorObject();
|
||||
}
|
||||
default:
|
||||
logger.warning("Unhandled response code from Mojang: " + con.getResponseCode());
|
||||
return getErrorObject();
|
||||
}
|
||||
}).get();
|
||||
}
|
||||
|
||||
private JsonObject getErrorObject() {
|
||||
final JsonObject errorObject = new JsonObject();
|
||||
errorObject.addProperty("error", "An error occurred.");
|
||||
return errorObject;
|
||||
}
|
||||
|
||||
private boolean hasNoError(JsonObject object) {
|
||||
return object.get("error") == null;
|
||||
}
|
||||
|
||||
public LoadingCache<String, Optional<MojangSkin>> getSkinCache() {
|
||||
return skinCache;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package xyz.ineanto.nicko.mojang;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public record MojangSkin(String value, String signature) {
|
||||
public static MojangSkin buildFromJson(JsonObject object) {
|
||||
final JsonObject properties = object.get("properties").getAsJsonArray().get(0).getAsJsonObject();
|
||||
final String value = properties.get("value").getAsString();
|
||||
final String signature = properties.get("signature").getAsString();
|
||||
return new MojangSkin(value, signature);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package xyz.ineanto.nicko.mojang;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MojangUtils {
|
||||
public static boolean isUsernameInvalid(String username) {
|
||||
return !Pattern.matches("^\\w{3,16}$", username);
|
||||
}
|
||||
|
||||
public static UUID fromTrimmed(String trimmedUUID) throws IllegalArgumentException {
|
||||
if (trimmedUUID == null) throw new IllegalArgumentException();
|
||||
StringBuilder builder = new StringBuilder(trimmedUUID.trim());
|
||||
/* Backwards adding to avoid index adjustments */
|
||||
try {
|
||||
builder.insert(20, "-");
|
||||
builder.insert(16, "-");
|
||||
builder.insert(12, "-");
|
||||
builder.insert(8, "-");
|
||||
} catch (StringIndexOutOfBoundsException e) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
return UUID.fromString(builder.toString());
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue