From 2d2040feeac36157b0a438375806fd7fc4af90e6 Mon Sep 17 00:00:00 2001 From: TechnicJelle <22576047+TechnicJelle@users.noreply.github.com> Date: Sat, 17 Feb 2024 01:17:17 +0100 Subject: [PATCH] Cache player names gotten from Mojang API --- .gitignore | 3 + .../BlueMapOfflinePlayerMarkers.java | 2 + .../common/Server.java | 57 +++++++++++++++++-- .../impl/paper/PaperServer.java | 8 +++ src/test/java/LoadOfflineMarkersTest.java | 2 + src/test/java/mockery/MockServer.java | 5 ++ 6 files changed, 73 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 8081d9e..05a520d 100644 --- a/.gitignore +++ b/.gitignore @@ -114,3 +114,6 @@ run/ # Minecraft server for testing testserver/ + +# Player Name Caches +cachedPlayerNames.json diff --git a/src/main/java/com/technicjelle/bluemapofflineplayermarkers/BlueMapOfflinePlayerMarkers.java b/src/main/java/com/technicjelle/bluemapofflineplayermarkers/BlueMapOfflinePlayerMarkers.java index f580b9f..94f4eb1 100644 --- a/src/main/java/com/technicjelle/bluemapofflineplayermarkers/BlueMapOfflinePlayerMarkers.java +++ b/src/main/java/com/technicjelle/bluemapofflineplayermarkers/BlueMapOfflinePlayerMarkers.java @@ -41,6 +41,7 @@ public void onEnable() { config = new PaperConfig(this); Singletons.init(new PaperServer(this), getLogger(), config, new BlueMapMarkerHandler(), new BMApiStatus()); + Singletons.getServer().startUp(); //all actual startup and shutdown logic moved to BlueMapAPI enable/disable methods, so `/bluemap reload` also reloads this plugin BlueMapAPI.onEnable(onEnableListener); @@ -74,6 +75,7 @@ public void onEnable() { public void onDisable() { BlueMapAPI.unregisterListener(onEnableListener); BlueMapAPI.unregisterListener(onDisableListener); + Singletons.getServer().shutDown(); Singletons.getLogger().info("BlueMap Offline Player Markers plugin disabled!"); Singletons.cleanup(); } diff --git a/src/main/java/com/technicjelle/bluemapofflineplayermarkers/common/Server.java b/src/main/java/com/technicjelle/bluemapofflineplayermarkers/common/Server.java index 6d6f7e8..8f080fe 100644 --- a/src/main/java/com/technicjelle/bluemapofflineplayermarkers/common/Server.java +++ b/src/main/java/com/technicjelle/bluemapofflineplayermarkers/common/Server.java @@ -1,22 +1,62 @@ package com.technicjelle.bluemapofflineplayermarkers.common; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import com.technicjelle.bluemapofflineplayermarkers.core.Singletons; -import java.io.IOException; -import java.io.InputStreamReader; +import java.io.*; import java.net.URL; import java.net.URLConnection; import java.nio.file.Path; import java.time.Instant; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; import java.util.UUID; public interface Server { - Gson _gson = new Gson(); + Gson _gson = new GsonBuilder() + .setLenient() +// .setPrettyPrinting() //Disabled to discourage people from editing the file by hand + .enableComplexMapKeySerialization() + .create(); + + Map _cachedPlayerNames = new HashMap<>(); + String _cacheFileName = "cachedPlayerNames.json"; + + default void startUp() { + //load cached player names + Path cacheFolder = Singletons.getServer().getConfigFolder(); + File cacheFile = new File(cacheFolder.toFile(), _cacheFileName); + if (cacheFile.exists()) { + try (InputStreamReader reader = new FileReader(cacheFile)) { + Map map = _gson.fromJson(reader, new TypeToken>() {}.getType()); + if (map != null) { + _cachedPlayerNames.putAll(map); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + default void shutDown() { + //save cached player names + Path cacheFolder = Singletons.getServer().getConfigFolder(); + File cacheFile = new File(cacheFolder.toFile(), _cacheFileName); + try (Writer writer = new FileWriter(cacheFile)) { + _gson.toJson(_cachedPlayerNames, writer); + } catch (IOException e) { + throw new RuntimeException(e); + } + } boolean isPlayerOnline(UUID playerUUID); + Path getConfigFolder(); + Path getPlayerDataFolder(); /** @@ -29,15 +69,24 @@ public interface Server { /** * Requests the player's name from the Mojang API. May be slow. + * * @throws IOException If there was an error with the connection. */ static String nameFromMojangAPI(UUID playerUUID) throws IOException { + String name = _cachedPlayerNames.get(playerUUID); + if (name != null) { + Singletons.getLogger().info("Requested player name from Mojang API, but returning from cache instead for " + playerUUID); + return name; + } + Singletons.getLogger().info("Requesting player name from Mojang API for " + playerUUID); URL url = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + playerUUID); URLConnection request = url.openConnection(); request.connect(); JsonObject response = _gson.fromJson(new InputStreamReader(request.getInputStream()), JsonObject.class); - return response.get("name").getAsString(); + name = response.get("name").getAsString(); + _cachedPlayerNames.put(playerUUID, name); + return name; } Optional guessWorldUUID(Object object); diff --git a/src/main/java/com/technicjelle/bluemapofflineplayermarkers/impl/paper/PaperServer.java b/src/main/java/com/technicjelle/bluemapofflineplayermarkers/impl/paper/PaperServer.java index b6c77c4..367f58e 100644 --- a/src/main/java/com/technicjelle/bluemapofflineplayermarkers/impl/paper/PaperServer.java +++ b/src/main/java/com/technicjelle/bluemapofflineplayermarkers/impl/paper/PaperServer.java @@ -2,6 +2,7 @@ import com.destroystokyo.paper.profile.PlayerProfile; import com.technicjelle.bluemapofflineplayermarkers.common.Server; +import com.technicjelle.bluemapofflineplayermarkers.core.Singletons; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.World; @@ -15,9 +16,11 @@ import java.util.UUID; public class PaperServer implements Server { + final JavaPlugin plugin; final org.bukkit.Server server; public PaperServer(JavaPlugin plugin) { + this.plugin = plugin; this.server = plugin.getServer(); } @@ -27,6 +30,11 @@ public boolean isPlayerOnline(UUID playerUUID) { return op.isOnline(); } + @Override + public Path getConfigFolder() { + return plugin.getDataFolder().toPath(); + } + @Override public Path getPlayerDataFolder() { //I really don't like "getWorlds().get(0)" as a way to get the main world, but as far as I can tell there is no other way diff --git a/src/test/java/LoadOfflineMarkersTest.java b/src/test/java/LoadOfflineMarkersTest.java index 824b42f..90cde6d 100644 --- a/src/test/java/LoadOfflineMarkersTest.java +++ b/src/test/java/LoadOfflineMarkersTest.java @@ -7,6 +7,7 @@ public class LoadOfflineMarkersTest { @After public void cleanup() { + Singletons.getServer().shutDown(); Singletons.cleanup(); } @@ -19,6 +20,7 @@ public void extract_info_from_playerdata_files() { new MockMarkerHandler(), new MockBMApiStatus() ); + Singletons.getServer().startUp(); FileMarkerLoader.loadOfflineMarkers(); } } diff --git a/src/test/java/mockery/MockServer.java b/src/test/java/mockery/MockServer.java index 9b5efe5..82acc3a 100644 --- a/src/test/java/mockery/MockServer.java +++ b/src/test/java/mockery/MockServer.java @@ -22,6 +22,11 @@ public boolean isPlayerOnline(UUID playerUUID) { return false; } + @Override + public Path getConfigFolder() { + return getPlayerDataFolder(); + } + @Override public Path getPlayerDataFolder() { Path path = Paths.get("").resolve("src/test/resources/" + playerDataFolderName);