Skip to content

Commit

Permalink
Use api.ashcon.app only for uuid resolve, for skins use a mojang api
Browse files Browse the repository at this point in the history
  • Loading branch information
Leymooo committed Jan 9, 2019
1 parent 9898f94 commit 2043e92
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 28 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>ru.leymooo</groupId>
<artifactId>simpleskins</artifactId>
<version>1.0.1</version>
<version>1.1</version>
<packaging>jar</packaging>

<name>SimpleSkins</name>
Expand Down
39 changes: 35 additions & 4 deletions src/main/java/ru/leymooo/simpleskins/SimpleSkins.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,21 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import net.kyori.text.Component;

import net.kyori.text.serializer.ComponentSerializers;
import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration;
import ru.leymooo.simpleskins.command.SkinCommand;

@Plugin(id = "simpleskins", name = "SimpleSkins", version = "1.0.1",
@Plugin(id = "simpleskins", name = "SimpleSkins", version = "1.1",
description = "Simple skins restorer plugin for velocity",
authors = "Leymooo")
public class SimpleSkins {
Expand All @@ -46,6 +47,7 @@ public class SimpleSkins {
private final Path dataDirectory;
private final SkinUtils skinUtils;
private final ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
private final UuidFetchCache uuidCache = new UuidFetchCache();
private RoundIterator<SkinUtils.FetchResult> defaultSkins;
private Configuration config;
private DataBaseUtils dataBaseUtils;
Expand Down Expand Up @@ -133,6 +135,7 @@ private void initDefaultSkins() {
try {
future.get();
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
}
}
this.defaultSkins = new RoundIterator<>(skins);
Expand All @@ -141,15 +144,31 @@ private void initDefaultSkins() {

public Optional<SkinUtils.FetchResult> fetchSkin(Optional<Player> player, String name, boolean logError, boolean fromDatabase) {
try {
return fromDatabase ? dataBaseUtils.getProperty(name) : Optional.of(skinUtils.fetchSkin(name));
if (fromDatabase) {
return dataBaseUtils.getProperty(name);
}
UUID uuid = (uuid = checkUUID(name)) == null ? skinUtils.fetchUUID(name) : uuid;
if (uuidCache.isWorking(uuid)) {
player.ifPresent(pl -> pl.sendMessage(deserialize("messages.working")));
return Optional.empty();
}
uuidCache.addWorking(uuid);
try {
Optional<SkinUtils.FetchResult> result = uuidCache.getIfCached(uuid);
result = result.isPresent() ? result : Optional.of(skinUtils.fetchSkin(uuid));
uuidCache.cache(result.get());
return result;
} finally {
uuidCache.removeWorking(uuid);
}
} catch (SkinUtils.UserNotFoundExeption ex) {
if (logError) {
logger.error("Can not fetch skin for '{}': {}", name, ex.getMessage());
}
} catch (IOException | JsonSyntaxException ex) {
logger.error("Can not fetch skin", ex);
}
player.ifPresent(pl -> pl.sendMessage(ComponentSerializers.LEGACY.deserialize(config.getString("messages.skin-not-found"), '&')));
player.ifPresent(pl -> pl.sendMessage(deserialize("messages.skin-not-found")));
return Optional.empty();
}

Expand Down Expand Up @@ -181,4 +200,16 @@ public SkinUtils getSkinUtils() {
return skinUtils;
}

public Component deserialize(String configKey) {
return ComponentSerializers.LEGACY.deserialize(config.getString(configKey), '&');
}

private UUID checkUUID(String toCheck) {
try {
return UUID.fromString(toCheck);
} catch (IllegalArgumentException ex) {
return null;
}
}

}
41 changes: 41 additions & 0 deletions src/main/java/ru/leymooo/simpleskins/UuidFetchCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package ru.leymooo.simpleskins;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import ru.leymooo.simpleskins.utils.SkinUtils.FetchResult;

public class UuidFetchCache {

private final Set<UUID> working = new HashSet<>();
private final Cache<UUID, FetchResult> cache = CacheBuilder.newBuilder()
.concurrencyLevel(Runtime.getRuntime().availableProcessors())
.expireAfterWrite(1, TimeUnit.MINUTES)
.build();

public boolean isWorking(UUID id) {
return working.contains(id);
}

public void addWorking(UUID id) {
working.add(id);
}

public void removeWorking(UUID id) {
working.remove(id);
}

public Optional<FetchResult> getIfCached(UUID id) {
return Optional.ofNullable(cache.getIfPresent(id));
}

public void cache(FetchResult result) {
if (result.getId() != null) {
cache.put(result.getId(), result);
}
}
}
16 changes: 8 additions & 8 deletions src/main/java/ru/leymooo/simpleskins/command/SkinCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ public SkinCommand(SimpleSkins plugin) {
@Override
public void execute(CommandSource cs, String[] args) {
if (plugin.getConfig().getBoolean("use-permission") && !cs.hasPermission("simpleskins.skin")) {
cs.sendMessage(ComponentSerializers.LEGACY.deserialize(plugin.getConfig().getString("messages.no-permission"), '&'));
cs.sendMessage(plugin.deserialize("messages.no-permission"));
return;
}
if (cs instanceof Player) {
if (args.length == 0) {
cs.sendMessage(ComponentSerializers.LEGACY.deserialize(plugin.getConfig().getString("messages.help"), '&'));
cs.sendMessage(plugin.deserialize("messages.help"));
return;
}
if (working.contains(cs)) {
cs.sendMessage(ComponentSerializers.LEGACY.deserialize(plugin.getConfig().getString("messages.working"), '&'));
cs.sendMessage(plugin.deserialize("messages.working"));
return;
}
Player player = (Player) cs;
Expand All @@ -45,25 +45,25 @@ public void execute(CommandSource cs, String[] args) {
plugin.getExecutorService().execute(() -> {
Optional<UUID> uuid = plugin.getDataBaseUtils().getUuid(player.getUsername());
if (uuid.isPresent()) {
cs.sendMessage(ComponentSerializers.LEGACY.deserialize(plugin.getConfig().getString("messages.fetching"), '&'));
cs.sendMessage(plugin.deserialize("messages.fetching"));
Optional<SkinUtils.FetchResult> newSkin = plugin.fetchSkin(Optional.of(player), uuid.get().toString(), false, false);
newSkin.ifPresent(skin -> {
cs.sendMessage(ComponentSerializers.LEGACY.deserialize(plugin.getConfig().getString("messages.skin-changed"), '&'));
cs.sendMessage(plugin.deserialize("messages.skin-changed"));
plugin.getSkinUtils().applySkin(player, skin.getProperty());
});
} else {
cs.sendMessage(ComponentSerializers.LEGACY.deserialize(plugin.getConfig().getString("messages.skin-update-error"), '&'));
cs.sendMessage(plugin.deserialize("messages.skin-update-error"));
}
working.remove(cs);
});
return;
}
plugin.getExecutorService().execute(() -> {
cs.sendMessage(ComponentSerializers.LEGACY.deserialize(plugin.getConfig().getString("messages.fetching"), '&'));
cs.sendMessage(plugin.deserialize("messages.fetching"));
Optional<SkinUtils.FetchResult> newSkin = plugin.fetchSkin(Optional.of(player),
args[0].equalsIgnoreCase("reset") ? player.getUsername() : args[0], false, false);
newSkin.ifPresent(skin -> {
cs.sendMessage(ComponentSerializers.LEGACY.deserialize(plugin.getConfig().getString("messages.skin-changed"), '&'));
cs.sendMessage(plugin.deserialize("messages.skin-changed"));
plugin.getDataBaseUtils().saveUser(player.getUsername(), skin);
plugin.getSkinUtils().applySkin(player, skin.getProperty());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public DataBaseUtils(SimpleSkins plugin) {
private void connect() {
try {
Class.forName("org.h2.Driver");
this.connection = DriverManager.getConnection("jdbc:h2:." + File.separator + plugin.getDataDirectory().toString() + File.separator + "users;mode=MySQL", null, null);
this.connection = DriverManager.getConnection("jdbc:h2:." + File.separator + plugin.getDataDirectory().toString() + File.separator + "users;mode=MySQL;MULTI_THREADED=1;", null, null);
try (Statement st = this.connection.createStatement()) {
String sql = "CREATE TABLE IF NOT EXISTS `Users` ("
+ "`Name` VARCHAR(16) NOT NULL PRIMARY KEY,"
Expand Down
51 changes: 37 additions & 14 deletions src/main/java/ru/leymooo/simpleskins/utils/SkinUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@

import com.google.common.io.CharStreams;
import com.google.common.net.HttpHeaders;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.api.util.UuidUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
Expand All @@ -51,7 +54,8 @@
*/
public class SkinUtils {

private static final String PROFILE_URL = "https://api.ashcon.app/mojang/v2/user/";
private static final String UUID_URL = "https://api.ashcon.app/mojang/v2/uuid/";
private static final String SKIN_URL = "https://sessionserver.mojang.com/session/minecraft/profile/";
private static final JsonParser JSON_PARSER = new JsonParser();
private final SimpleSkins plugin;

Expand Down Expand Up @@ -84,27 +88,46 @@ private List<GameProfile.Property> createProperties(List<GameProfile.Property> l
return properties;
}

public FetchResult fetchSkin(String username) throws IOException, UserNotFoundExeption, JsonSyntaxException {
HttpURLConnection connection = getConnection(PROFILE_URL + username);
public UUID fetchUUID(String username) throws IOException, UserNotFoundExeption, JsonSyntaxException {
if (username.length() > 16) {
throw new UserNotFoundExeption(username);
}
HttpURLConnection connection = getConnection(UUID_URL + username);
int responseCode = connection.getResponseCode();

if (responseCode == HttpURLConnection.HTTP_NO_CONTENT || responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
throw new UserNotFoundExeption(username);
} else if (responseCode == HttpURLConnection.HTTP_OK) {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
JsonObject paresed = JSON_PARSER.parse(reader).getAsJsonObject();
JsonObject skin = paresed.get("textures").getAsJsonObject().get("raw").getAsJsonObject();
return new FetchResult(UUID.fromString(paresed.get("uuid").getAsString()),
new GameProfile.Property("textures", skin.get("value").getAsString(), skin.get("signature").getAsString()));
}
} else {
if (validate(responseCode)) {
return UUID.fromString(getJson(connection).getAsJsonPrimitive().getAsString());
} else if (responseCode != HttpURLConnection.HTTP_NO_CONTENT && responseCode != HttpURLConnection.HTTP_NOT_FOUND) {
printErrorStream(connection, responseCode);
}
throw new UserNotFoundExeption(username);
}

public FetchResult fetchSkin(UUID uuid) throws IOException, UserNotFoundExeption, JsonSyntaxException {
HttpURLConnection connection = getConnection(SKIN_URL + UuidUtils.toUndashed(uuid) + "?unsigned=false");
int responseCode = connection.getResponseCode();

if (validate(responseCode)) {
JsonObject paresed = getJson(connection).getAsJsonObject();
JsonObject skin = paresed.getAsJsonArray("properties").get(0).getAsJsonObject();
return new FetchResult(UuidUtils.fromUndashed(paresed.get("id").getAsString()),
new GameProfile.Property("textures", skin.get("value").getAsString(), skin.get("signature").getAsString()));
} else if (responseCode != HttpURLConnection.HTTP_NO_CONTENT && responseCode != HttpURLConnection.HTTP_NOT_FOUND) {
printErrorStream(connection, responseCode);
}
throw new UserNotFoundExeption(uuid.toString());
}

private JsonElement getJson(HttpURLConnection connection) throws IOException {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
return JSON_PARSER.parse(reader);
}
}

private boolean validate(int responseCode) {
return responseCode == HttpURLConnection.HTTP_OK;
}
private static final int TIMEOUT = 5000;

private HttpURLConnection getConnection(String url) throws IOException {
Expand Down

0 comments on commit 2043e92

Please sign in to comment.