diff --git a/.gitignore b/.gitignore index 744289d..c63e02c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -# Project exclude paths -/target/ \ No newline at end of file +/target/ +.idea/** +out/ \ No newline at end of file diff --git a/pom.xml b/pom.xml index 57aa9a4..5ce75b6 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ net.aboodyy localtime-expansion - 1.2 + 1.3 diff --git a/src/main/java/net/aboodyy/localtime/DateManager.java b/src/main/java/net/aboodyy/localtime/DateManager.java index 807f359..693f61a 100644 --- a/src/main/java/net/aboodyy/localtime/DateManager.java +++ b/src/main/java/net/aboodyy/localtime/DateManager.java @@ -20,13 +20,11 @@ package net.aboodyy.localtime; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; import me.clip.placeholderapi.PlaceholderAPIPlugin; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.scheduler.BukkitRunnable; import java.io.BufferedReader; import java.io.InputStreamReader; @@ -34,14 +32,27 @@ import java.net.URL; import java.net.URLConnection; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.Date; +import java.util.Map; +import java.util.TimeZone; +import java.util.UUID; +import java.util.concurrent.*; +import java.util.logging.Level; public class DateManager implements Listener { private final Map timezones; - - DateManager() { - timezones = new HashMap<>(); + private final Cache cache; + private final ScheduledExecutorService executorService; + private int retryDelay; + + public DateManager() { + this.timezones = new ConcurrentHashMap<>(); + this.cache = CacheBuilder.newBuilder() + .expireAfterWrite(1, TimeUnit.DAYS) + .build(); + this.retryDelay = 5; // default to 5 seconds + this.executorService = Executors.newSingleThreadScheduledExecutor(); } public String getDate(String format, String timezone) { @@ -53,55 +64,85 @@ public String getDate(String format, String timezone) { return dateFormat.format(date); } - public String getTimeZone(Player player) { + public CompletableFuture getTimeZone(Player player) { final String FAILED = "[LocalTime] Couldn't get " + player.getName() + "'s timezone. Will use default timezone."; - String timezone = TimeZone.getDefault().getID(); - if (timezones.containsKey(player.getUniqueId())) - return timezones.get(player.getUniqueId()); + String cachedTimezone = cache.getIfPresent(player.getUniqueId().toString()); + if (cachedTimezone != null) { + return CompletableFuture.completedFuture(cachedTimezone); + } - InetSocketAddress address = player.getAddress(); - timezones.put(player.getUniqueId(), timezone); + String timezone = timezones.get(player.getUniqueId()); + if (timezone != null) { + return CompletableFuture.completedFuture(timezone); + } + InetSocketAddress address = player.getAddress(); if (address == null) { - Bukkit.getLogger().info(FAILED); - return timezone; + PlaceholderAPIPlugin.getInstance().getLogger().log(Level.WARNING, FAILED); + timezone = TimeZone.getDefault().getID(); + cache.put(player.getUniqueId().toString(), timezone); + return CompletableFuture.completedFuture(timezone); } - new BukkitRunnable() { - @Override - public void run() { - String timezone; + final String defaultTimezone = TimeZone.getDefault().getID(); + + CompletableFuture futureTimezone = CompletableFuture.supplyAsync(() -> { + String result = "undefined"; + int retries = 3; + while (retries-- > 0) { try { URL api = new URL("https://ipapi.co/" + address.getAddress().getHostAddress() + "/timezone/"); URLConnection connection = api.openConnection(); - - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream())); - timezone = bufferedReader.readLine(); + connection.setConnectTimeout(5000); + connection.setReadTimeout(5000); + connection.setRequestProperty("User-Agent", "Mozilla/5.0"); + + try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + result = bufferedReader.readLine(); + + if (result == null) { + result = "undefined"; + } else { + cache.put(player.getUniqueId().toString(), result); + } + break; + } } catch (Exception e) { - timezone = "undefined"; - } - - if (timezone.equalsIgnoreCase("undefined")) { - Bukkit.getLogger().info(FAILED); - timezone = TimeZone.getDefault().getID(); + result = "undefined"; + PlaceholderAPIPlugin.getInstance().getLogger().log(Level.WARNING, "[LocalTime] Exception while getting timezone for player " + player.getName() + ": " + e.getMessage(), e); + try { + Thread.sleep(retryDelay * 1000); + } catch (InterruptedException ignored) {} } + } - timezones.put(player.getUniqueId(), timezone); + if (result.equalsIgnoreCase("undefined")) { + PlaceholderAPIPlugin.getInstance().getLogger().log(Level.WARNING, FAILED); + result = defaultTimezone; } - }.runTaskAsynchronously(PlaceholderAPIPlugin.getInstance()); - return timezones.get(player.getUniqueId()); + timezones.put(player.getUniqueId(), result); + return result; + }, executorService); + + futureTimezone.exceptionally(ex -> { + PlaceholderAPIPlugin.getInstance().getLogger().log(Level.WARNING, "[LocalTime] Exception while getting timezone for player " + player.getName() + ": " + ex.getMessage(), ex); + cache.put(player.getUniqueId().toString(), defaultTimezone); + timezones.put(player.getUniqueId(), defaultTimezone); + return defaultTimezone; + }); + + return futureTimezone; } public void clear() { timezones.clear(); + cache.invalidateAll(); } - @SuppressWarnings("unused") - @EventHandler - public void onLeave(PlayerQuitEvent e) { - timezones.remove(e.getPlayer().getUniqueId()); + public void shutdown() { + this.executorService.shutdown(); } -} +} \ No newline at end of file diff --git a/src/main/java/net/aboodyy/localtime/LocalTimeExpansion.java b/src/main/java/net/aboodyy/localtime/LocalTimeExpansion.java index 8e39d46..ad62388 100644 --- a/src/main/java/net/aboodyy/localtime/LocalTimeExpansion.java +++ b/src/main/java/net/aboodyy/localtime/LocalTimeExpansion.java @@ -30,6 +30,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.concurrent.CompletableFuture; @SuppressWarnings("unused") public class LocalTimeExpansion extends PlaceholderExpansion implements Cacheable, Configurable { @@ -43,12 +44,12 @@ public String getIdentifier() { @Override public String getAuthor() { - return "aBooDyy"; + return "aBooDyy, Opal"; } @Override public String getVersion() { - return "1.2"; + return "1.3"; } @Override @@ -69,6 +70,7 @@ public Map getDefaults() { public void clear() { dateManager.clear(); HandlerList.unregisterAll(dateManager); + dateManager.shutdown(); } @Override @@ -82,7 +84,10 @@ public String onPlaceholderRequest(Player p, String identifier) { args = identifier.split("time_"); if (args.length < 2) return null; - return dateManager.getDate(args[1], dateManager.getTimeZone(p)); + CompletableFuture timezoneFuture = dateManager.getTimeZone(p); + String timezone = timezoneFuture.join(); + + return dateManager.getDate(args[1], timezone); } if (identifier.startsWith("timezone_")) { @@ -99,8 +104,12 @@ public String onPlaceholderRequest(Player p, String identifier) { return dateManager.getDate(format, args[1]); } - if (identifier.equalsIgnoreCase("time")) - return dateManager.getDate(format, dateManager.getTimeZone(p)); + if (identifier.equalsIgnoreCase("time")) { + CompletableFuture timezoneFuture = dateManager.getTimeZone(p); + String timezone = timezoneFuture.join(); + + return dateManager.getDate(format, timezone); + } return null; }