diff --git a/bukkit/src/main/java/me/william278/husksync/HuskSyncBukkit.java b/bukkit/src/main/java/me/william278/husksync/HuskSyncBukkit.java index eded38b6..a15d9e9c 100644 --- a/bukkit/src/main/java/me/william278/husksync/HuskSyncBukkit.java +++ b/bukkit/src/main/java/me/william278/husksync/HuskSyncBukkit.java @@ -1,6 +1,7 @@ package me.william278.husksync; -import me.william278.husksync.bukkit.PlayerSetter; +import me.william278.husksync.bukkit.util.BukkitUpdateChecker; +import me.william278.husksync.bukkit.util.PlayerSetter; import me.william278.husksync.bukkit.config.ConfigLoader; import me.william278.husksync.bukkit.data.BukkitDataCache; import me.william278.husksync.bukkit.listener.BukkitRedisListener; @@ -56,7 +57,8 @@ public final class HuskSyncBukkit extends JavaPlugin { new RedisMessage.MessageTarget(Settings.ServerType.BUNGEECORD, null), serverUUID.toString(), Boolean.toString(isMySqlPlayerDataBridgeInstalled), - Bukkit.getName()) + Bukkit.getName(), + getInstance().getDescription().getVersion()) .send(); attempts[0]++; if (attempts[0] == 10) { @@ -96,6 +98,11 @@ public final class HuskSyncBukkit extends JavaPlugin { reloadConfig(); ConfigLoader.loadSettings(getConfig()); + // Do update checker + if (Settings.automaticUpdateChecks) { + new BukkitUpdateChecker().logToConsole(); + } + // Check if MySqlPlayerDataBridge is installed Plugin mySqlPlayerDataBridge = Bukkit.getPluginManager().getPlugin("MySqlPlayerDataBridge"); if (mySqlPlayerDataBridge != null) { diff --git a/bukkit/src/main/java/me/william278/husksync/bukkit/config/ConfigLoader.java b/bukkit/src/main/java/me/william278/husksync/bukkit/config/ConfigLoader.java index a12590e5..46fb4f0b 100644 --- a/bukkit/src/main/java/me/william278/husksync/bukkit/config/ConfigLoader.java +++ b/bukkit/src/main/java/me/william278/husksync/bukkit/config/ConfigLoader.java @@ -7,6 +7,7 @@ public class ConfigLoader { public static void loadSettings(FileConfiguration config) throws IllegalArgumentException { Settings.serverType = Settings.ServerType.BUKKIT; + Settings.automaticUpdateChecks = config.getBoolean("check_for_updates", true); Settings.redisHost = config.getString("redis_settings.host", "localhost"); Settings.redisPort = config.getInt("redis_settings.port", 6379); Settings.redisPassword = config.getString("redis_settings.password", ""); diff --git a/bukkit/src/main/java/me/william278/husksync/bukkit/data/DataViewer.java b/bukkit/src/main/java/me/william278/husksync/bukkit/data/DataViewer.java index 3758d529..2ffb805c 100644 --- a/bukkit/src/main/java/me/william278/husksync/bukkit/data/DataViewer.java +++ b/bukkit/src/main/java/me/william278/husksync/bukkit/data/DataViewer.java @@ -3,7 +3,7 @@ package me.william278.husksync.bukkit.data; import me.william278.husksync.HuskSyncBukkit; import me.william278.husksync.PlayerData; import me.william278.husksync.Settings; -import me.william278.husksync.bukkit.PlayerSetter; +import me.william278.husksync.bukkit.util.PlayerSetter; import me.william278.husksync.redis.RedisMessage; import org.bukkit.Bukkit; import org.bukkit.entity.Player; diff --git a/bukkit/src/main/java/me/william278/husksync/bukkit/listener/BukkitRedisListener.java b/bukkit/src/main/java/me/william278/husksync/bukkit/listener/BukkitRedisListener.java index 78d01f23..285c22e0 100644 --- a/bukkit/src/main/java/me/william278/husksync/bukkit/listener/BukkitRedisListener.java +++ b/bukkit/src/main/java/me/william278/husksync/bukkit/listener/BukkitRedisListener.java @@ -2,12 +2,12 @@ package me.william278.husksync.bukkit.listener; import de.themoep.minedown.MineDown; import me.william278.husksync.HuskSyncBukkit; -import me.william278.husksync.MessageManager; +import me.william278.husksync.util.MessageManager; import me.william278.husksync.PlayerData; import me.william278.husksync.Settings; import me.william278.husksync.bukkit.config.ConfigLoader; import me.william278.husksync.bukkit.data.DataViewer; -import me.william278.husksync.bukkit.PlayerSetter; +import me.william278.husksync.bukkit.util.PlayerSetter; import me.william278.husksync.bukkit.migrator.MPDBDeserializer; import me.william278.husksync.migrator.MPDBPlayerData; import me.william278.husksync.redis.RedisListener; diff --git a/bukkit/src/main/java/me/william278/husksync/bukkit/listener/EventListener.java b/bukkit/src/main/java/me/william278/husksync/bukkit/listener/EventListener.java index c07b312e..899633e7 100644 --- a/bukkit/src/main/java/me/william278/husksync/bukkit/listener/EventListener.java +++ b/bukkit/src/main/java/me/william278/husksync/bukkit/listener/EventListener.java @@ -1,14 +1,9 @@ package me.william278.husksync.bukkit.listener; import me.william278.husksync.HuskSyncBukkit; -import me.william278.husksync.PlayerData; -import me.william278.husksync.Settings; -import me.william278.husksync.bukkit.data.DataSerializer; import me.william278.husksync.bukkit.data.DataViewer; -import me.william278.husksync.bukkit.PlayerSetter; -import me.william278.husksync.redis.RedisMessage; +import me.william278.husksync.bukkit.util.PlayerSetter; import org.bukkit.Bukkit; -import org.bukkit.attribute.Attribute; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -21,7 +16,6 @@ import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.event.player.*; import java.io.IOException; -import java.util.Objects; import java.util.logging.Level; public class EventListener implements Listener { diff --git a/bukkit/src/main/java/me/william278/husksync/bukkit/migrator/MPDBDeserializer.java b/bukkit/src/main/java/me/william278/husksync/bukkit/migrator/MPDBDeserializer.java index fbc671c6..424e066f 100644 --- a/bukkit/src/main/java/me/william278/husksync/bukkit/migrator/MPDBDeserializer.java +++ b/bukkit/src/main/java/me/william278/husksync/bukkit/migrator/MPDBDeserializer.java @@ -2,7 +2,7 @@ package me.william278.husksync.bukkit.migrator; import me.william278.husksync.HuskSyncBukkit; import me.william278.husksync.PlayerData; -import me.william278.husksync.bukkit.PlayerSetter; +import me.william278.husksync.bukkit.util.PlayerSetter; import me.william278.husksync.bukkit.data.DataSerializer; import me.william278.husksync.migrator.MPDBPlayerData; import net.craftersland.data.bridge.PD; diff --git a/bukkit/src/main/java/me/william278/husksync/bukkit/util/BukkitUpdateChecker.java b/bukkit/src/main/java/me/william278/husksync/bukkit/util/BukkitUpdateChecker.java new file mode 100644 index 00000000..6e927c93 --- /dev/null +++ b/bukkit/src/main/java/me/william278/husksync/bukkit/util/BukkitUpdateChecker.java @@ -0,0 +1,20 @@ +package me.william278.husksync.bukkit.util; + +import me.william278.husksync.HuskSyncBukkit; +import me.william278.husksync.util.UpdateChecker; + +import java.util.logging.Level; + +public class BukkitUpdateChecker extends UpdateChecker { + + private static final HuskSyncBukkit plugin = HuskSyncBukkit.getInstance(); + + public BukkitUpdateChecker() { + super(plugin.getDescription().getVersion()); + } + + @Override + public void log(Level level, String message) { + plugin.getLogger().log(level, message); + } +} diff --git a/bukkit/src/main/java/me/william278/husksync/bukkit/PlayerSetter.java b/bukkit/src/main/java/me/william278/husksync/bukkit/util/PlayerSetter.java similarity index 99% rename from bukkit/src/main/java/me/william278/husksync/bukkit/PlayerSetter.java rename to bukkit/src/main/java/me/william278/husksync/bukkit/util/PlayerSetter.java index c9832fd0..7dfd048a 100644 --- a/bukkit/src/main/java/me/william278/husksync/bukkit/PlayerSetter.java +++ b/bukkit/src/main/java/me/william278/husksync/bukkit/util/PlayerSetter.java @@ -1,4 +1,4 @@ -package me.william278.husksync.bukkit; +package me.william278.husksync.bukkit.util; import me.william278.husksync.HuskSyncBukkit; import me.william278.husksync.PlayerData; diff --git a/bukkit/src/main/resources/config.yml b/bukkit/src/main/resources/config.yml index b9c49c08..77b888eb 100644 --- a/bukkit/src/main/resources/config.yml +++ b/bukkit/src/main/resources/config.yml @@ -12,4 +12,5 @@ synchronisation_settings: statistics: true game_mode: true advancements: true - location: false \ No newline at end of file + location: false +check_for_updates: true \ No newline at end of file diff --git a/bungeecord/src/main/java/me/william278/husksync/HuskSyncBungeeCord.java b/bungeecord/src/main/java/me/william278/husksync/HuskSyncBungeeCord.java index 5daacf6a..e99998ea 100644 --- a/bungeecord/src/main/java/me/william278/husksync/HuskSyncBungeeCord.java +++ b/bungeecord/src/main/java/me/william278/husksync/HuskSyncBungeeCord.java @@ -10,6 +10,7 @@ import me.william278.husksync.bungeecord.data.sql.SQLite; import me.william278.husksync.bungeecord.listener.BungeeEventListener; import me.william278.husksync.bungeecord.listener.BungeeRedisListener; import me.william278.husksync.bungeecord.migrator.MPDBMigrator; +import me.william278.husksync.bungeecord.util.BungeeUpdateChecker; import me.william278.husksync.redis.RedisMessage; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.plugin.Plugin; @@ -69,6 +70,11 @@ public final class HuskSyncBungeeCord extends Plugin { // Load locales from messages ConfigLoader.loadMessageStrings(Objects.requireNonNull(ConfigManager.getMessages())); + // Do update checker + if (Settings.automaticUpdateChecks) { + new BungeeUpdateChecker(getDescription().getVersion()).logToConsole(); + } + // Initialize the database database = switch (Settings.dataStorageType) { case SQLITE -> new SQLite(this); @@ -141,5 +147,5 @@ public final class HuskSyncBungeeCord extends Plugin { /** * A record representing a server synchronised on the network and whether it has MySqlPlayerDataBridge installed */ - public record Server(UUID serverUUID, boolean hasMySqlPlayerDataBridge) { } + public record Server(UUID serverUUID, boolean hasMySqlPlayerDataBridge, String huskSyncVersion, String serverBrand) { } } diff --git a/bungeecord/src/main/java/me/william278/husksync/bungeecord/command/HuskSyncCommand.java b/bungeecord/src/main/java/me/william278/husksync/bungeecord/command/HuskSyncCommand.java index a391217d..ad3cbf69 100644 --- a/bungeecord/src/main/java/me/william278/husksync/bungeecord/command/HuskSyncCommand.java +++ b/bungeecord/src/main/java/me/william278/husksync/bungeecord/command/HuskSyncCommand.java @@ -2,7 +2,8 @@ package me.william278.husksync.bungeecord.command; import de.themoep.minedown.MineDown; import me.william278.husksync.HuskSyncBungeeCord; -import me.william278.husksync.MessageManager; +import me.william278.husksync.bungeecord.util.BungeeUpdateChecker; +import me.william278.husksync.util.MessageManager; import me.william278.husksync.PlayerData; import me.william278.husksync.Settings; import me.william278.husksync.bungeecord.config.ConfigLoader; @@ -30,6 +31,7 @@ public class HuskSyncCommand extends Command implements TabExecutor { private final static SubCommand[] SUB_COMMANDS = {new SubCommand("about", null), new SubCommand("status", "husksync.command.admin"), new SubCommand("reload", "husksync.command.admin"), + new SubCommand("update", "husksync.command.admin"), new SubCommand("invsee", "husksync.command.inventory"), new SubCommand("echest", "husksync.command.ender_chest")}; @@ -47,6 +49,42 @@ public class HuskSyncCommand extends Command implements TabExecutor { if (args.length >= 1) { switch (args[0].toLowerCase(Locale.ROOT)) { case "about", "info" -> sendAboutInformation(player); + case "update" -> { + if (!player.hasPermission("husksync.command.inventory")) { + sender.sendMessage(new MineDown(MessageManager.getMessage("error_no_permission")).toComponent()); + return; + } + sender.sendMessage(new MineDown("[Checking for HuskSync updates...](gray)").toComponent()); + ProxyServer.getInstance().getScheduler().runAsync(plugin, () -> { + // Check Bukkit servers needing updates + int updatesNeeded = 0; + String bukkitBrand = "Spigot"; + String bukkitVersion = "1.0"; + for (HuskSyncBungeeCord.Server server : HuskSyncBungeeCord.synchronisedServers) { + BungeeUpdateChecker updateChecker = new BungeeUpdateChecker(server.huskSyncVersion()); + if (!updateChecker.isUpToDate()) { + updatesNeeded++; + bukkitBrand = server.serverBrand(); + bukkitVersion = server.huskSyncVersion(); + } + } + + // Check Bungee servers needing updates and send message + BungeeUpdateChecker proxyUpdateChecker = new BungeeUpdateChecker(ProxyServer.getInstance().getVersion()); + if (proxyUpdateChecker.isUpToDate() && updatesNeeded == 0) { + sender.sendMessage(new MineDown("[HuskSync](#00fb9a bold) [| HuskSync is up-to-date, running Version " + proxyUpdateChecker.getLatestVersion() + "](#00fb9a)").toComponent()); + } else { + sender.sendMessage(new MineDown("[HuskSync](#00fb9a bold) [| Your servers are not up-to-date:](#00fb9a)").toComponent()); + if (!proxyUpdateChecker.isUpToDate()) { + sender.sendMessage(new MineDown("[•](white) [HuskSync on the " + ProxyServer.getInstance().getName() + " proxy is outdated (Latest: " + proxyUpdateChecker.getLatestVersion() + ", Running: " + proxyUpdateChecker.getCurrentVersion() + ")](#00fb9a)").toComponent()); + } + if (updatesNeeded > 0) { + sender.sendMessage(new MineDown("[•](white) [HuskSync on " + updatesNeeded + " connected " + bukkitBrand + " servers are outdated (Latest: " + proxyUpdateChecker.getLatestVersion() + ", Running: " + bukkitVersion + ")](#00fb9a)").toComponent()); + } + sender.sendMessage(new MineDown("[•](white) [Download links:](#00fb9a) [[⏩ Spigot]](gray open_url=https://www.spigotmc.org/resources/husktowns.92672/updates) [•](#262626) [[⏩ Polymart]](gray open_url=https://polymart.org/resource/husktowns.1056/updates)").toComponent()); + } + }); + } case "invsee", "openinv", "inventory" -> { if (!player.hasPermission("husksync.command.inventory")) { sender.sendMessage(new MineDown(MessageManager.getMessage("error_no_permission")).toComponent()); diff --git a/bungeecord/src/main/java/me/william278/husksync/bungeecord/config/ConfigLoader.java b/bungeecord/src/main/java/me/william278/husksync/bungeecord/config/ConfigLoader.java index d12712de..0b765126 100644 --- a/bungeecord/src/main/java/me/william278/husksync/bungeecord/config/ConfigLoader.java +++ b/bungeecord/src/main/java/me/william278/husksync/bungeecord/config/ConfigLoader.java @@ -1,6 +1,6 @@ package me.william278.husksync.bungeecord.config; -import me.william278.husksync.MessageManager; +import me.william278.husksync.util.MessageManager; import me.william278.husksync.Settings; import net.md_5.bungee.config.Configuration; @@ -12,6 +12,7 @@ public class ConfigLoader { Settings.language = config.getString("language", "en-gb"); Settings.serverType = Settings.ServerType.BUNGEECORD; + Settings.automaticUpdateChecks = config.getBoolean("check_for_updates", true); Settings.redisHost = config.getString("redis_settings.host", "localhost"); Settings.redisPort = config.getInt("redis_settings.port", 6379); Settings.redisPassword = config.getString("redis_settings.password", ""); diff --git a/bungeecord/src/main/java/me/william278/husksync/bungeecord/listener/BungeeRedisListener.java b/bungeecord/src/main/java/me/william278/husksync/bungeecord/listener/BungeeRedisListener.java index 06c82d91..9a243325 100644 --- a/bungeecord/src/main/java/me/william278/husksync/bungeecord/listener/BungeeRedisListener.java +++ b/bungeecord/src/main/java/me/william278/husksync/bungeecord/listener/BungeeRedisListener.java @@ -2,7 +2,7 @@ package me.william278.husksync.bungeecord.listener; import de.themoep.minedown.MineDown; import me.william278.husksync.HuskSyncBungeeCord; -import me.william278.husksync.MessageManager; +import me.william278.husksync.util.MessageManager; import me.william278.husksync.PlayerData; import me.william278.husksync.Settings; import me.william278.husksync.bungeecord.data.DataManager; @@ -122,12 +122,15 @@ public class BungeeRedisListener extends RedisListener { final UUID serverUUID = UUID.fromString(message.getMessageDataElements()[0]); final boolean hasMySqlPlayerDataBridge = Boolean.parseBoolean(message.getMessageDataElements()[1]); final String bukkitBrand = message.getMessageDataElements()[2]; + final String huskSyncVersion = message.getMessageDataElements()[3]; try { new RedisMessage(RedisMessage.MessageType.CONNECTION_HANDSHAKE, new RedisMessage.MessageTarget(Settings.ServerType.BUKKIT, null), serverUUID.toString(), plugin.getProxy().getName()) .send(); - HuskSyncBungeeCord.synchronisedServers.add(new HuskSyncBungeeCord.Server(serverUUID, hasMySqlPlayerDataBridge)); + HuskSyncBungeeCord.synchronisedServers.add( + new HuskSyncBungeeCord.Server(serverUUID, hasMySqlPlayerDataBridge, + huskSyncVersion, bukkitBrand)); log(Level.INFO, "Completed handshake with " + bukkitBrand + " server (" + serverUUID + ")"); } catch (IOException e) { log(Level.SEVERE, "Failed to serialize handshake message data"); diff --git a/bungeecord/src/main/java/me/william278/husksync/bungeecord/util/BungeeUpdateChecker.java b/bungeecord/src/main/java/me/william278/husksync/bungeecord/util/BungeeUpdateChecker.java new file mode 100644 index 00000000..610e5317 --- /dev/null +++ b/bungeecord/src/main/java/me/william278/husksync/bungeecord/util/BungeeUpdateChecker.java @@ -0,0 +1,20 @@ +package me.william278.husksync.bungeecord.util; + +import me.william278.husksync.HuskSyncBungeeCord; +import me.william278.husksync.util.UpdateChecker; + +import java.util.logging.Level; + +public class BungeeUpdateChecker extends UpdateChecker { + + private static final HuskSyncBungeeCord plugin = HuskSyncBungeeCord.getInstance(); + + public BungeeUpdateChecker(String versionToCheck) { + super(versionToCheck); + } + + @Override + public void log(Level level, String message) { + plugin.getLogger().log(level, message); + } +} diff --git a/bungeecord/src/main/resources/bungee-config.yml b/bungeecord/src/main/resources/bungee-config.yml index ea286ff7..63dd5594 100644 --- a/bungeecord/src/main/resources/bungee-config.yml +++ b/bungeecord/src/main/resources/bungee-config.yml @@ -18,4 +18,5 @@ data_storage_settings: maximum_lifetime: 1800000 keepalive_time: 0 connection_timeout: 5000 -config_file_version: 1.0 \ No newline at end of file +check_for_updates: true +config_file_version: 1.0.1 \ No newline at end of file diff --git a/common/src/main/java/me/william278/husksync/Settings.java b/common/src/main/java/me/william278/husksync/Settings.java index 3ef7bbc8..97f97a2d 100644 --- a/common/src/main/java/me/william278/husksync/Settings.java +++ b/common/src/main/java/me/william278/husksync/Settings.java @@ -9,8 +9,8 @@ public class Settings { * General settings */ - // Messages language - public static String language; + // Whether to do automatic update checks on startup + public static boolean automaticUpdateChecks; // The type of THIS server (Bungee or Bukkit) public static ServerType serverType; @@ -24,6 +24,9 @@ public class Settings { * Bungee / Proxy server-only settings */ + // Messages language + public static String language; + // SQL settings public static DataStorageType dataStorageType; diff --git a/common/src/main/java/me/william278/husksync/MessageManager.java b/common/src/main/java/me/william278/husksync/util/MessageManager.java similarity index 97% rename from common/src/main/java/me/william278/husksync/MessageManager.java rename to common/src/main/java/me/william278/husksync/util/MessageManager.java index 89488506..05993a27 100644 --- a/common/src/main/java/me/william278/husksync/MessageManager.java +++ b/common/src/main/java/me/william278/husksync/util/MessageManager.java @@ -1,4 +1,4 @@ -package me.william278.husksync; +package me.william278.husksync.util; import java.util.HashMap; diff --git a/common/src/main/java/me/william278/husksync/util/UpdateChecker.java b/common/src/main/java/me/william278/husksync/util/UpdateChecker.java new file mode 100644 index 00000000..06855a3b --- /dev/null +++ b/common/src/main/java/me/william278/husksync/util/UpdateChecker.java @@ -0,0 +1,56 @@ +package me.william278.husksync.util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; +import java.util.logging.Level; + +public abstract class UpdateChecker { + + private final static int SPIGOT_PROJECT_ID = 97144; + + private final String currentVersion; + private String latestVersion; + + public UpdateChecker(String currentVersion) { + this.currentVersion = currentVersion; + + try { + final URL url = new URL("https://api.spigotmc.org/legacy/update.php?resource=" + SPIGOT_PROJECT_ID); + URLConnection urlConnection = url.openConnection(); + this.latestVersion = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())).readLine(); + } catch (IOException e) { + log(Level.WARNING, "Failed to check for updates: An IOException occurred."); + this.latestVersion = "Unknown"; + } catch (Exception e) { + log(Level.WARNING, "Failed to check for updates: An exception occurred."); + this.latestVersion = "Unknown"; + } + } + + public boolean isUpToDate() { + if (latestVersion.equalsIgnoreCase("Unknown")) { + return true; + } + return latestVersion.equals(currentVersion); + } + + public String getLatestVersion() { + return latestVersion; + } + + public String getCurrentVersion() { + return currentVersion; + } + + public abstract void log(Level level, String message); + + public void logToConsole() { + if (!isUpToDate()) { + log(Level.WARNING, "A new version of HuskSync is available: Version " + + latestVersion + " (Currently running: " + currentVersion + ")"); + } + } +}