diff --git a/build.gradle b/build.gradle index 2012492..d6dbd5d 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group = 'io.github.Jack1424' -version = '1.3.0' +version = '1.4.0' repositories { mavenCentral() diff --git a/src/main/java/io/github/jack1424/realtimeweather/ConfigManager.java b/src/main/java/io/github/jack1424/realtimeweather/ConfigManager.java index 5a296b7..9dda9fe 100644 --- a/src/main/java/io/github/jack1424/realtimeweather/ConfigManager.java +++ b/src/main/java/io/github/jack1424/realtimeweather/ConfigManager.java @@ -1,6 +1,7 @@ package io.github.jack1424.realtimeweather; import io.github.jack1424.realtimeweather.requests.WeatherRequestObject; +import org.bukkit.World; import org.bukkit.configuration.file.FileConfiguration; import org.json.simple.parser.ParseException; @@ -11,6 +12,7 @@ import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.time.zone.ZoneRulesException; +import java.util.HashSet; import java.util.Objects; import java.util.TimeZone; @@ -18,9 +20,10 @@ public class ConfigManager { private final RealTimeWeather rtw; private final FileConfiguration configFile; private TimeZone timeZone; - private boolean debug, timeEnabled, weatherEnabled, blockTimeSetCommand, blockWeatherCommand, disableBedsAtNight, disableBedsDuringThunder; - private long timeSyncInterval, weatherSyncInterval; + private boolean debug, timeEnabled, weatherEnabled, timeSyncAllWorlds, weatherSyncAllWorlds, blockTimeSetCommand, blockWeatherCommand, disableBedsAtNight, disableBedsDuringThunder; + private long updateCheckInterval, timeSyncInterval, weatherSyncInterval; private String sunriseSunset, sunriseSunsetLatitude, sunriseSunsetLongitude, apiKey, weatherLatitude, weatherLongitude, disableBedsAtNightMessage, disableBedsDuringThunderMessage, sunriseCustomTime, sunsetCustomTime; + private HashSet timeSyncWorlds, weatherSyncWorlds; public ConfigManager(RealTimeWeather rtw) { this.rtw = rtw; @@ -33,6 +36,16 @@ public class ConfigManager { setTimeEnabled(configFile.getBoolean("SyncTime")); if (isTimeEnabled()) try { + timeSyncWorlds = new HashSet<>(); + setTimeSyncAllWorlds(configFile.getBoolean("TimeSyncAllWorlds")); + if (getTimeSyncAllWorlds()) { + for (World world : rtw.getServer().getWorlds()) + if (world.getEnvironment() == World.Environment.NORMAL) + addTimeSyncWorld(world.getName()); + } else { + for (String worldName : configFile.getStringList("TimeSyncWorlds")) + addTimeSyncWorld(worldName); + } setBlockTimeSetCommand(configFile.getBoolean("BlockTimeSetCommand")); setDisableBedsAtNight(configFile.getBoolean("DisableBedsAtNight")); setDisableBedsAtNightMessage(configFile.getString("DisableBedsAtNightMessage")); @@ -57,6 +70,16 @@ public class ConfigManager { setWeatherEnabled(configFile.getBoolean("SyncWeather")); if (isWeatherEnabled()) try { + weatherSyncWorlds = new HashSet<>(); + setWeatherSyncAllWorlds(configFile.getBoolean("WeatherSyncAllWorlds")); + if (getWeatherSyncAllWorlds()) { + for (World world : rtw.getServer().getWorlds()) + if (world.getEnvironment() == World.Environment.NORMAL) + addWeatherSyncWorld(world.getName()); + } else { + for (String worldName : configFile.getStringList("WeatherSyncWorlds")) + addWeatherSyncWorld(worldName); + } setBlockWeatherCommand(configFile.getBoolean("BlockWeatherCommand")); setDisableBedsDuringThunder(configFile.getBoolean("DisableBedsDuringThunder")); setDisableBedsDuringThunderMessage(configFile.getString("DisableBedsDuringThunderMessage")); @@ -71,6 +94,17 @@ public class ConfigManager { setWeatherEnabled(false); } + + setUpdateCheckInterval(configFile.getLong("UpdateCheckInterval")); + } + + public long getUpdateCheckInterval() { + return updateCheckInterval; + } + + public void setUpdateCheckInterval(long value) { + updateCheckInterval = value; + rtw.debug("updateCheckInterval set to " + value); } public boolean debugEnabled() { @@ -91,6 +125,29 @@ public class ConfigManager { rtw.debug("SyncTime set to " + value); } + public boolean getTimeSyncAllWorlds() { + return timeSyncAllWorlds; + } + + public void setTimeSyncAllWorlds(boolean value) { + timeSyncAllWorlds = value; + rtw.debug("TimeSyncAllWorlds set to " + value); + } + + public HashSet getTimeSyncWorlds() { + return timeSyncWorlds; + } + + public void addTimeSyncWorld(String worldName) throws ConfigurationException { + World world = rtw.getServer().getWorld(worldName); + + if (world == null) + throw new ConfigurationException("World \"" + worldName + "\" cannot be found"); + + timeSyncWorlds.add(world); + rtw.debug("World \"" + worldName + "\" added to TimeSyncWorlds"); + } + public boolean getBlockTimeSetCommand() { return blockTimeSetCommand; } @@ -213,6 +270,29 @@ public class ConfigManager { rtw.debug("SyncWeather set to " + value); } + public boolean getWeatherSyncAllWorlds() { + return weatherSyncAllWorlds; + } + + public void setWeatherSyncAllWorlds(boolean value) { + weatherSyncAllWorlds = value; + rtw.debug("WeatherSyncAllWorlds set to " + value); + } + + public HashSet getWeatherSyncWorlds() { + return weatherSyncWorlds; + } + + public void addWeatherSyncWorld(String worldName) throws ConfigurationException { + World world = rtw.getServer().getWorld(worldName); + + if (world == null) + throw new ConfigurationException("World \"" + worldName + "\" cannot be found"); + + weatherSyncWorlds.add(world); + rtw.debug("World \"" + worldName + "\" added to WeatherSyncWorlds"); + } + public boolean getBlockWeatherCommand() { return blockWeatherCommand; } diff --git a/src/main/java/io/github/jack1424/realtimeweather/EventHandlers.java b/src/main/java/io/github/jack1424/realtimeweather/EventHandlers.java index dceb20d..910a4c6 100644 --- a/src/main/java/io/github/jack1424/realtimeweather/EventHandlers.java +++ b/src/main/java/io/github/jack1424/realtimeweather/EventHandlers.java @@ -13,7 +13,7 @@ public class EventHandlers implements Listener { private final ConfigManager config; public EventHandlers(RealTimeWeather rtw) { - config = rtw.getConfigurator(); + config = rtw.getConfigManager(); } @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) diff --git a/src/main/java/io/github/jack1424/realtimeweather/RealTimeWeather.java b/src/main/java/io/github/jack1424/realtimeweather/RealTimeWeather.java index 7d4328e..ce4215f 100644 --- a/src/main/java/io/github/jack1424/realtimeweather/RealTimeWeather.java +++ b/src/main/java/io/github/jack1424/realtimeweather/RealTimeWeather.java @@ -43,6 +43,13 @@ public final class RealTimeWeather extends JavaPlugin { metrics.addCustomChart(new SimplePie("time_sync_enabled", () -> String.valueOf(config.isTimeEnabled()))); logger.info("Started!"); + + logger.info("Checking for updates..."); + logger.info(getUpdateCheck()); + + long updateCheckInterval = config.getUpdateCheckInterval(); + if (config.getUpdateCheckInterval() > 0) + getServer().getScheduler().scheduleSyncRepeatingTask(this, () -> logger.info(getUpdateCheck()), updateCheckInterval, updateCheckInterval); } @Override @@ -70,42 +77,44 @@ public final class RealTimeWeather extends JavaPlugin { if (config.getSunriseSunset().equals("custom")) debug("Using custom sunrise/sunset times. Sunrise: " + config.getSunriseCustomTime() + ", Sunset: " + config.getSunsetCustomTime()); - for (World world : getServer().getWorlds()) - if (world.getEnvironment().equals(World.Environment.NORMAL)) - world.setGameRuleValue("doDaylightCycle", "false"); + for (World world : config.getTimeSyncWorlds()) + world.setGameRuleValue("doDaylightCycle", "false"); getServer().getScheduler().scheduleSyncRepeatingTask(this, () -> { if (config.isTimeEnabled()) { Calendar cal = Calendar.getInstance(config.getTimeZone()); - for (World world : getServer().getWorlds()) - if (world.getEnvironment().equals(World.Environment.NORMAL)) - if (config.getSunriseSunset().equals("real")) { - SunriseSunsetRequestObject sunriseSunset; + for (World world : config.getTimeSyncWorlds()) + if (config.getSunriseSunset().equals("real")) { + SunriseSunsetRequestObject sunriseSunset; + try { + sunriseSunset = new SunriseSunsetRequestObject(config.getTimeZone(), config.getWeatherLatitude(), config.getWeatherLongitude()); + world.setTime(calculateWorldTime(cal, sunriseSunset.getSunriseTime(), sunriseSunset.getSunsetTime())); + } catch (Exception e) { + logger.severe(e.getMessage()); + logger.severe("Error getting sunrise/sunset times, using default sunrise/sunset times"); + try { - sunriseSunset = new SunriseSunsetRequestObject(config.getTimeZone(), config.getWeatherLatitude(), config.getWeatherLongitude()); - world.setTime(calculateWorldTime(cal, sunriseSunset.getSunriseTime(), sunriseSunset.getSunsetTime())); - } catch (Exception e) { - logger.severe(e.getMessage()); - logger.severe("Error getting sunrise/sunset times, using default sunrise/sunset times"); - - try { - config.setSunriseSunset("default"); - } catch (ConfigurationException ex) { - throw new RuntimeException(ex); - } - - world.setTime(calculateWorldTime(cal, "5:02:27 AM", "6:36:36 PM")); - return; + config.setSunriseSunset("default"); + } catch (ConfigurationException ex) { + throw new RuntimeException(ex); } - } else if (config.getSunriseSunset().equals("custom")) - world.setTime(calculateWorldTime(cal, config.getSunriseCustomTime(), config.getSunsetCustomTime())); - else + world.setTime(calculateWorldTime(cal, "5:02:27 AM", "6:36:36 PM")); + return; + } + } else if (config.getSunriseSunset().equals("custom")) { + world.setTime(calculateWorldTime(cal, config.getSunriseCustomTime(), config.getSunsetCustomTime())); + } else + world.setTime(calculateWorldTime(cal, "5:02:27 AM", "6:36:36 PM")); } }, 0L, config.getTimeSyncInterval()); + + debug("Weather sync enabled"); } private void setupWeather() { + debug("Enabling weather sync..."); + try { new WeatherRequestObject(config.getAPIKey(), config.getWeatherLatitude(), config.getWeatherLongitude()); } catch (Exception e) { @@ -116,11 +125,8 @@ public final class RealTimeWeather extends JavaPlugin { return; } - for (World world : getServer().getWorlds()) - if (world.getEnvironment().equals(World.Environment.NORMAL)) - world.setGameRuleValue("doWeatherCycle", "false"); - - debug("Enabling weather sync..."); + for (World world : config.getWeatherSyncWorlds()) + world.setGameRuleValue("doWeatherCycle", "false"); getServer().getScheduler().scheduleSyncRepeatingTask(this, () -> { debug("Syncing weather..."); @@ -129,11 +135,10 @@ public final class RealTimeWeather extends JavaPlugin { WeatherRequestObject request = new WeatherRequestObject(config.getAPIKey(), config.getWeatherLatitude(), config.getWeatherLongitude()); debug("Setting weather (Rain: " + request.isRaining() + ", Thunder: " + request.isThundering() + ")..."); - for (World world : getServer().getWorlds()) - if (world.getEnvironment().equals(World.Environment.NORMAL)) { - world.setStorm(request.isRaining()); - world.setThundering(request.isThundering()); - } + for (World world : config.getWeatherSyncWorlds()) { + world.setStorm(request.isRaining()); + world.setThundering(request.isThundering()); + } } catch (Exception e) { logger.severe("There was an error when attempting to get weather information"); debug(e.getMessage()); @@ -161,14 +166,31 @@ public final class RealTimeWeather extends JavaPlugin { if (currentMinutes >= sunriseMinutes && currentMinutes < sunsetMinutes) { return ((currentMinutes - sunriseMinutes) / (sunsetMinutes - sunriseMinutes) * 13569) + 23041; } else { - if (currentMinutes < sunriseMinutes) { + if (currentMinutes < sunriseMinutes) currentMinutes += 1440; - } + return ((currentMinutes - sunsetMinutes) / (1440 - sunsetMinutes + sunriseMinutes) * 13569) + 12610; } } - public ConfigManager getConfigurator() { + public String getUpdateCheck() { + String currentVersion = this.getDescription().getVersion(); + String latestVersion; + try { + debug("Getting latest version..."); + latestVersion = RequestFunctions.getLatestVersion(); + } catch (Exception exception) { + debug(exception.getMessage()); + return "There was an error getting the latest version"; + } + + if (currentVersion.equals(latestVersion)) { + return String.format("RealTimeWeather (v%s) is up to date!", currentVersion); + } else + return String.format("RealTimeWeather (v%s) is outdated! v%s is the latest version.", currentVersion, latestVersion); + } + + public ConfigManager getConfigManager() { return config; } diff --git a/src/main/java/io/github/jack1424/realtimeweather/requests/RequestFunctions.java b/src/main/java/io/github/jack1424/realtimeweather/requests/RequestFunctions.java new file mode 100644 index 0000000..119e55c --- /dev/null +++ b/src/main/java/io/github/jack1424/realtimeweather/requests/RequestFunctions.java @@ -0,0 +1,45 @@ +package io.github.jack1424.realtimeweather.requests; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Scanner; + +public class RequestFunctions { + public static Object makeRequest(String URLString) throws IOException, HTTPResponseException, ParseException { + int responseCode = getResponseCode(URLString); + if (responseCode > 399) + throw new HTTPResponseException(responseCode); + + Scanner scanner = new Scanner(new URL(URLString).openStream()); + StringBuilder response = new StringBuilder(); + while (scanner.hasNextLine()) + response.append(scanner.nextLine()); + scanner.close(); + + return new JSONParser().parse(response.toString()); + } + + public static int getResponseCode(String URLString) throws IOException { + URL url = new URL(URLString); + HttpURLConnection con = (HttpURLConnection) url.openConnection(); + con.setRequestMethod("GET"); + con.connect(); + return con.getResponseCode(); + } + + public static String getLatestVersion() throws Exception { + return ((JSONObject) ((JSONArray) makeRequest("https://api.modrinth.com/v2/project/WRA6ODcm/version")).get(0)).get("version_number").toString(); + } + + public static class HTTPResponseException extends Exception { + public HTTPResponseException(int responseCode) { + super(String.valueOf(responseCode)); + } + } +} diff --git a/src/main/java/io/github/jack1424/realtimeweather/requests/SunriseSunsetRequestObject.java b/src/main/java/io/github/jack1424/realtimeweather/requests/SunriseSunsetRequestObject.java index d503fe0..70bd9d9 100644 --- a/src/main/java/io/github/jack1424/realtimeweather/requests/SunriseSunsetRequestObject.java +++ b/src/main/java/io/github/jack1424/realtimeweather/requests/SunriseSunsetRequestObject.java @@ -1,14 +1,10 @@ package io.github.jack1424.realtimeweather.requests; import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import javax.naming.ConfigurationException; -import javax.net.ssl.HttpsURLConnection; import java.io.IOException; -import java.net.ProtocolException; -import java.net.URL; import java.time.LocalDate; import java.time.LocalTime; import java.time.ZoneId; @@ -20,23 +16,13 @@ public class SunriseSunsetRequestObject { private String sunriseTime, sunsetTime; public SunriseSunsetRequestObject(TimeZone timeZone, String lat, String lon) throws IOException, ParseException, ConfigurationException { - URL url = new URL(String.format("https://api.sunrisesunset.io/json?lat=%s&lng=%s&timezone=UTC", lat, lon)); - - HttpsURLConnection con = (HttpsURLConnection) url.openConnection(); - con.setRequestMethod("GET"); - con.connect(); - int responseCode = con.getResponseCode(); - if (responseCode > 399) - throw new ProtocolException("Server/client error (HTTP error " + responseCode + ")"); - - Scanner scanner = new Scanner(url.openStream()); - StringBuilder data = new StringBuilder(); - while (scanner.hasNext()) { - data.append(scanner.nextLine()); + JSONObject response; + try { + response = (JSONObject) ((JSONObject) RequestFunctions.makeRequest(String.format("https://api.sunrisesunset.io/json?lat=%s&lng=%s&timezone=UTC", lat, lon))).get("results"); + } catch (RequestFunctions.HTTPResponseException e) { + throw new IOException("Server/client error (HTTP error " + e.getMessage() + ")"); } - scanner.close(); - JSONObject response = (JSONObject) ((JSONObject) new JSONParser().parse(data.toString())).get("results"); sunriseTime = response.get("sunrise").toString(); sunsetTime = response.get("sunset").toString(); diff --git a/src/main/java/io/github/jack1424/realtimeweather/requests/WeatherRequestObject.java b/src/main/java/io/github/jack1424/realtimeweather/requests/WeatherRequestObject.java index c1873d3..0a215d3 100644 --- a/src/main/java/io/github/jack1424/realtimeweather/requests/WeatherRequestObject.java +++ b/src/main/java/io/github/jack1424/realtimeweather/requests/WeatherRequestObject.java @@ -2,57 +2,42 @@ package io.github.jack1424.realtimeweather.requests; import org.json.simple.JSONArray; import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import javax.naming.ConfigurationException; -import javax.net.ssl.HttpsURLConnection; import java.io.IOException; import java.net.ProtocolException; -import java.net.URL; -import java.util.Scanner; public class WeatherRequestObject { private boolean rain = false, thunder = false; public WeatherRequestObject(String apiKey, String lat, String lon) throws IOException, ParseException, ConfigurationException { - URL url = new URL(String.format("https://api.openweathermap.org/data/2.5/weather?lat=%s&lon=%s&appid=%s", lat, lon, apiKey)); + JSONArray conditions; + try { + conditions = (JSONArray) ((JSONObject) RequestFunctions.makeRequest(String.format("https://api.openweathermap.org/data/2.5/weather?lat=%s&lon=%s&appid=%s", lat, lon, apiKey))).get("weather"); + } catch (RequestFunctions.HTTPResponseException e) { + int responseCode = Integer.parseInt(e.getMessage()); + if (responseCode > 499) { + throw new ProtocolException("Server/client error (HTTP error " + responseCode + ")"); + } else if (responseCode > 399) { + String message = "Error when getting weather information: "; - HttpsURLConnection con = (HttpsURLConnection) url.openConnection(); - con.setRequestMethod("GET"); - con.connect(); - int responseCode = con.getResponseCode(); - if (responseCode > 499) { - throw new ProtocolException("Server/client error (HTTP error " + responseCode + ")"); + if (responseCode == 401) + throw new ConfigurationException(message + "API key invalid. Check the Wiki for troubleshooting steps."); + else + throw new ProtocolException(message + "Unknown error"); + } else { + throw new IOException("Server/client error (HTTP error " + e.getMessage() + ")"); + } } - else if (responseCode > 399) { - String message = "Error when getting weather information: "; - - if (responseCode == 401) - throw new ConfigurationException(message + "API key invalid. Check the Wiki for troubleshooting steps."); - else - throw new ProtocolException(message + "Unknown error"); - } - - Scanner scanner = new Scanner(url.openStream()); - StringBuilder data = new StringBuilder(); - while (scanner.hasNext()) { - data.append(scanner.nextLine()); - } - scanner.close(); - - JSONArray conditions = (JSONArray) ((JSONObject) new JSONParser().parse(data.toString())).get("weather"); for (Object rawCondition : conditions) { int id = Integer.parseInt(String.valueOf(((JSONObject) rawCondition).get("id"))); - while (id >= 10) id /= 10; - if (!rain) - rain = id == 2 || id == 3 || id == 5 || id == 6; - if (!thunder) - thunder = id == 2; + rain = id == 2 || id == 3 || id == 5 || id == 6; + thunder = id == 2; } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 452c66e..4cb1acf 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,11 +1,37 @@ -# RealTimeWeather Configuration File (v1.3.0) +# RealTimeWeather Configuration File (v1.4.0) # You can find detailed instructions at: https://github.com/Jack1424/RealTimeWeather/wiki#editing-the-configuration-file +######################################## Real Time Weather Settings ################################################## +# By default, RealTimeWeather will check if an update is available every 24 hours (1734000 ticks) # +# You can change the interval here (or set to 0 to disable update checks) # +# If this is disabled, RealTimeWeather will still check for updates on startup # +# You can find a handy tick calculator here: https://mapmaking.fr/tick/ # +UpdateCheckInterval: 1734000 +# +# Set to true for various console messages when time and weather sync are executed # +# This is useful if the plugin is not working, and you want to find out what's wrong # +# This will also provide error messages even when an error is caught and managed by RTM # +# Note: There will be no messages during a time sync because they happen very frequently # +# Note: Unhandled plugin errors will still be logged regardless of the debug value # +Debug: false +###################################################################################################################### + ################################# TIME SYNC SETTINGS ################################################################# # Set to true to enable time syncing, or false to disable # # All time-related settings will be ignored if this is set to false # SyncTime: false # # +# By default, RealTimeWeather will apply the time sync settings below to all worlds # +# End and nether worlds will not be synced # +# If you ony want to enable time syncing in some worlds on your server, set this to false # +TimeSyncAllWorlds: true +# # +# List the worlds that you want RealTimeWeather to sync the time in # +# End and nether worlds will not be synced # +# This only works if TimeSyncAllWorlds is set to false # +TimeSyncWorlds: + - world +# # # Set to false to enable the /time set command (not recommended) # BlockTimeSetCommand: true # # @@ -45,6 +71,17 @@ SunsetCustomTime: '6:36:36 PM' # All weather-related settings will be ignored if this is set to false # SyncWeather: false # # +# By default, RealTimeWeather will apply the weather sync settings below to all worlds # +# End and nether worlds will not be synced # +# If you ony want to enable weather syncing in some worlds on your server, set this to false # +WeatherSyncAllWorlds: true +# # +# List the worlds that you want RealTimeWeather to sync the weather in # +# End and nether worlds will not be synced # +# This only works if WeatherSyncAllWorlds is set to false # +WeatherSyncWorlds: + - world +# # # Set to false to enable the /weather command (not recommended) # BlockWeatherCommand: true # # @@ -70,11 +107,4 @@ APIKey: 'API_KEY' WeatherLatitude: '0' WeatherLongitude: '0' # # -###################################################################################################################### - -# Set to true for various console messages when time and weather sync are executed -# This is useful if the plugin is not working, and you want to find out what's wrong -# This will also provide error messages even when an error is caught and managed by RTM -# Note: There will be no messages during a time sync because they happen very frequently -# Note: Unhandled plugin errors will still be logged regardless of the debug value -Debug: false +###################################################################################################################### \ No newline at end of file