diff --git a/.gitignore b/.gitignore index 0af6ecd0550..6b445d7f9ce 100644 --- a/.gitignore +++ b/.gitignore @@ -227,3 +227,4 @@ config.yml logs/ public-key.pem locales/ +cache/ \ No newline at end of file diff --git a/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitConfiguration.java b/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitConfiguration.java index df98b408dba..fbfcce80c29 100644 --- a/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitConfiguration.java +++ b/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitConfiguration.java @@ -151,6 +151,11 @@ public boolean isCacheChunks() { return true; // We override this as with Bukkit, we have direct access to the server implementation } + @Override + public int getCacheSkins() { + return config.getInt("cache-skins", 0); + } + @Override public boolean isAboveBedrockNetherBuilding() { return config.getBoolean("above-bedrock-nether-building", false); diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeConfiguration.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeConfiguration.java index d983aec1c0d..59dc077f0aa 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeConfiguration.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeConfiguration.java @@ -150,6 +150,11 @@ public boolean isCacheChunks() { return config.getBoolean("cache-chunks", false); } + @Override + public int getCacheSkins() { + return config.getInt("cache-skins", 0); + } + @Override public boolean isAboveBedrockNetherBuilding() { return config.getBoolean("above-bedrock-nether-building", false); diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java index fc14847081b..627b0ac48c3 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java @@ -144,6 +144,11 @@ public boolean isCacheChunks() { return node.getNode("cache-chunks").getBoolean(false); } + @Override + public int getCacheSkins() { + return node.getNode("cache-skins").getInt(0); + } + @Override public boolean isAboveBedrockNetherBuilding() { return node.getNode("above-bedrock-nether-building").getBoolean(false); diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneConfiguration.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneConfiguration.java index bd0292040e2..d9eb6c822dc 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneConfiguration.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneConfiguration.java @@ -83,6 +83,9 @@ public class GeyserStandaloneConfiguration implements GeyserConfiguration { @JsonProperty("cache-chunks") private boolean cacheChunks; + @JsonProperty(value = "cache-skins", defaultValue = "0") + private int cacheSkins; + @JsonProperty("above-bedrock-nether-building") private boolean isAboveBedrockNetherBuilding; diff --git a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityConfiguration.java b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityConfiguration.java index aef0edaa5c8..5f5f9503bf6 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityConfiguration.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityConfiguration.java @@ -85,9 +85,12 @@ public class GeyserVelocityConfiguration implements GeyserConfiguration { @JsonProperty("default-locale") private String defaultLocale; - @JsonProperty("cache-chunks") + @JsonProperty(value = "cache-chunks") private boolean cacheChunks; + @JsonProperty(value = "cache-skins", defaultValue = "0") + private int cacheSkins; + @JsonProperty("above-bedrock-nether-building") private boolean aboveBedrockNetherBuilding; diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConfiguration.java b/connector/src/main/java/org/geysermc/connector/GeyserConfiguration.java index 8a39323b606..e641dfbdb92 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConfiguration.java @@ -68,6 +68,8 @@ public interface GeyserConfiguration { boolean isCacheChunks(); + int getCacheSkins(); + IMetricsInfo getMetrics(); interface IBedrockConfiguration { diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java index 2b39b33b6da..7a4d4f5a58e 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java @@ -39,6 +39,10 @@ import java.net.URL; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.FileTime; import java.util.Arrays; import java.util.Base64; import java.util.Map; @@ -285,11 +289,33 @@ public static void storeEarGeometry(UUID playerID, boolean isSlim) { } private static Skin supplySkin(UUID uuid, String textureUrl) { - byte[] skin = EMPTY_SKIN.getSkinData(); + // First see if we have a cached file + Path skinFile = Paths.get("cache", "skins", Base64.getEncoder().encodeToString(textureUrl.getBytes())); try { - skin = requestImage(textureUrl, null); + Files.setLastModifiedTime(skinFile, FileTime.fromMillis(System.currentTimeMillis())); + GeyserConnector.getInstance().getLogger().debug("Reading cached skin from file " + skinFile.toString() + " for " + textureUrl); + return new Skin(uuid, textureUrl, Files.readAllBytes(skinFile), System.currentTimeMillis(), false, false); + } catch (IOException ignored) {} + + try { + byte[] skin = requestImage(textureUrl, null); + + // Write cache if we are allowed + if (GeyserConnector.getInstance().getConfig().getCacheSkins() > 0) { + //noinspection ResultOfMethodCallIgnored + skinFile.getParent().toFile().mkdirs(); + try { + Files.write(skinFile, skin); + GeyserConnector.getInstance().getLogger().debug("Writing cached skin to file " + skinFile.toString() + " for " + textureUrl); + } catch (IOException e) { + GeyserConnector.getInstance().getLogger().error("Failed to write cached skin to file " + skinFile.toString() + " for " + textureUrl); + } + } + + return new Skin(uuid, textureUrl, skin, System.currentTimeMillis(), false, false); } catch (Exception ignored) {} // just ignore I guess - return new Skin(uuid, textureUrl, skin, System.currentTimeMillis(), false, false); + + return new Skin(uuid, "empty", EMPTY_SKIN.getSkinData(), System.currentTimeMillis(), false, false); } private static Cape supplyCape(String capeUrl, CapeProvider provider) {