diff --git a/src/main/java/io/github/jack1424/realtimeweather/Configurator.java b/src/main/java/io/github/jack1424/realtimeweather/Configurator.java index c91110a..8138d7d 100644 --- a/src/main/java/io/github/jack1424/realtimeweather/Configurator.java +++ b/src/main/java/io/github/jack1424/realtimeweather/Configurator.java @@ -1,11 +1,15 @@ package io.github.jack1424.realtimeweather; +import io.github.jack1424.realtimeweather.requests.WeatherRequestObject; import org.bukkit.configuration.file.FileConfiguration; import org.json.simple.parser.ParseException; import javax.naming.ConfigurationException; import java.io.IOException; +import java.time.LocalTime; import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.time.zone.ZoneRulesException; import java.util.Objects; import java.util.TimeZone; @@ -16,7 +20,7 @@ public class Configurator { private TimeZone timeZone; private boolean debug, timeEnabled, weatherEnabled, blockTimeSetCommand, blockWeatherCommand, disableBedsAtNight, disableBedsDuringThunder; private long timeSyncInterval, weatherSyncInterval; - private String apiKey, lat, lon, disableBedsAtNightMessage, disableBedsDuringThunderMessage; + private String sunriseSunset, sunriseSunsetLatitude, sunriseSunsetLongitude, apiKey, weatherLatitude, weatherLongitude, disableBedsAtNightMessage, disableBedsDuringThunderMessage, sunriseCustomTime, sunsetCustomTime; public Configurator(RealTimeWeather rtw) { this.rtw = rtw; @@ -34,6 +38,14 @@ public class Configurator { setDisableBedsAtNightMessage(configFile.getString("DisableBedsAtNightMessage")); setTimeSyncInterval(configFile.getLong("TimeSyncInterval")); setTimeZone(configFile.getString("Timezone")); + setSunriseSunset(configFile.getString("SunriseSunset")); + if (getSunriseSunset().equals("real")) { + setSunriseSunsetLatitude(configFile.getString("SunriseSunsetLatitude")); + setSunriseSunsetLongitude(configFile.getString("SunriseSunsetLongitude")); + } else if (getSunriseSunset().equals("custom")) { + setSunriseCustomTime(configFile.getString("SunriseCustomTime")); + setSunsetCustomTime(configFile.getString("SunsetCustomTime")); + } } catch (ConfigurationException e) { rtw.getLogger().severe((e.getMessage())); rtw.getLogger().severe("Error loading time configuration. Check that the values in your configuration file are valid."); @@ -50,8 +62,8 @@ public class Configurator { setDisableBedsDuringThunderMessage(configFile.getString("DisableBedsDuringThunderMessage")); setWeatherSyncInterval(configFile.getLong("WeatherSyncInterval")); setAPIKey(configFile.getString("APIKey")); - setLat(configFile.getString("Latitude")); - setLon(configFile.getString("Longitude")); + setWeatherLatitude(configFile.getString("WeatherLatitude")); + setWeatherLongitude(configFile.getString("WeatherLongitude")); } catch (ConfigurationException e) { rtw.getLogger().severe(e.getMessage()); rtw.getLogger().severe("Error loading weather configuration. Check that the values in your configuration file are valid."); @@ -132,6 +144,66 @@ public class Configurator { rtw.debug("TimeZone set to " + value); } + public String getSunriseSunset() { + return sunriseSunset; + } + + public void setSunriseSunset(String value) throws ConfigurationException { + value = value.toLowerCase(); + if (value.equals("default") || value.equals("real") || value.equals("custom")) { + sunriseSunset = value; + rtw.debug("SunriseSunset set to " + value); + } else { + throw new ConfigurationException("SunriseSunset value invalid (must be default or real or custom)"); + } + } + + public String getSunriseSunsetLatitude() { + return sunriseSunsetLatitude; + } + + public void setSunriseSunsetLatitude(String value) { + sunriseSunsetLatitude = value; + rtw.debug("SunriseSunsetLatitude set to " + value); + } + + public String getSunriseSunsetLongitude() { + return sunriseSunsetLongitude; + } + + public void setSunriseSunsetLongitude(String value) { + sunriseSunsetLongitude = value; + rtw.debug("SunriseSunsetLongitude set to " + value); + } + + public String getSunriseCustomTime() { + return sunriseCustomTime; + } + + public void setSunriseCustomTime(String value) throws ConfigurationException { + try { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("h:mm:ss a"); + sunriseCustomTime = LocalTime.parse(value, formatter).format(formatter); + rtw.debug("SunriseCustomTime set to " + value); + } catch (DateTimeParseException e) { + throw new ConfigurationException("SunriseCustomTime value invalid (check format)"); + } + } + + public String getSunsetCustomTime() { + return sunsetCustomTime; + } + + public void setSunsetCustomTime(String value) throws ConfigurationException { + try { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("h:mm:ss a"); + sunsetCustomTime = LocalTime.parse(value, formatter).format(formatter); + rtw.debug("SunsetCustomTime set to " + value); + } catch (DateTimeParseException e) { + throw new ConfigurationException("SunsetCustomTime value invalid (check format)"); + } + } + public boolean isWeatherEnabled() { return weatherEnabled; } @@ -186,7 +258,7 @@ public class Configurator { public void setAPIKey(String value) throws ConfigurationException { try { - new RequestObject(Objects.requireNonNull(value), "0", "0"); + new WeatherRequestObject(Objects.requireNonNull(value), "0", "0"); } catch (NullPointerException e) { throw new ConfigurationException("The APIKey cannot be blank"); } @@ -199,11 +271,11 @@ public class Configurator { rtw.debug("APIKey set to " + value); } - public String getLat() { - return lat; + public String getWeatherLatitude() { + return weatherLatitude; } - public void setLat(String value) throws ConfigurationException { + public void setWeatherLatitude(String value) throws ConfigurationException { try { double doubleValue = Double.parseDouble(Objects.requireNonNull(value)); if (doubleValue < -90 || doubleValue > 90) @@ -214,15 +286,15 @@ public class Configurator { throw new ConfigurationException("The entered latitude might not be a number (or is too long)"); } - lat = value; + weatherLatitude = value; rtw.debug("Latitude set to " + value); } - public String getLon() { - return lon; + public String getWeatherLongitude() { + return weatherLongitude; } - public void setLon(String value) throws ConfigurationException { + public void setWeatherLongitude(String value) throws ConfigurationException { try { double doubleValue = Double.parseDouble(Objects.requireNonNull(value)); if (doubleValue < -180 || doubleValue > 180) @@ -233,7 +305,7 @@ public class Configurator { throw new ConfigurationException("The entered longitude might not be a number (or is too long)"); } - lon = value; + weatherLongitude = value; rtw.debug("Longitude set to " + value); } } diff --git a/src/main/java/io/github/jack1424/realtimeweather/RealTimeWeather.java b/src/main/java/io/github/jack1424/realtimeweather/RealTimeWeather.java index e1f7782..d8ae885 100644 --- a/src/main/java/io/github/jack1424/realtimeweather/RealTimeWeather.java +++ b/src/main/java/io/github/jack1424/realtimeweather/RealTimeWeather.java @@ -1,10 +1,13 @@ package io.github.jack1424.realtimeweather; +import io.github.jack1424.realtimeweather.requests.*; import org.bstats.bukkit.Metrics; import org.bstats.charts.SimplePie; import org.bukkit.World; import org.bukkit.plugin.java.JavaPlugin; +import javax.naming.ConfigurationException; +import java.time.LocalTime; import java.util.Calendar; import java.util.logging.Logger; @@ -60,6 +63,12 @@ public final class RealTimeWeather extends JavaPlugin { debug("Enabling time zone sync..."); debug("Syncing time with " + config.getTimeZone().getDisplayName()); + if (config.getSunriseSunset().equals("real")) + debug("Syncing sunrise/sunset with " + config.getSunriseSunsetLatitude() + " " + config.getSunriseSunsetLongitude()); + + 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"); @@ -69,14 +78,35 @@ public final class RealTimeWeather extends JavaPlugin { Calendar cal = Calendar.getInstance(config.getTimeZone()); for (World world : getServer().getWorlds()) if (world.getEnvironment().equals(World.Environment.NORMAL)) - world.setTime((1000 * cal.get(Calendar.HOUR_OF_DAY)) + (16 * (cal.get(Calendar.MINUTE) + 1)) - 6000); + 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 { + config.setSunriseSunset("default"); + } catch (ConfigurationException ex) { + throw new RuntimeException(ex); + } + + 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()); } private void setupWeather() { try { - new RequestObject(config.getAPIKey(), config.getLat(), config.getLon()); + new WeatherRequestObject(config.getAPIKey(), config.getWeatherLatitude(), config.getWeatherLongitude()); } catch (Exception e) { logger.severe(e.getMessage()); logger.severe("Disabling weather sync..."); @@ -95,7 +125,7 @@ public final class RealTimeWeather extends JavaPlugin { debug("Syncing weather..."); try { - RequestObject request = new RequestObject(config.getAPIKey(), config.getLat(), config.getLon()); + WeatherRequestObject request = new WeatherRequestObject(config.getAPIKey(), config.getWeatherLatitude(), config.getWeatherLongitude()); debug("Setting weather (Rain: " + request.isRaining() + ", Thunder: " + request.isThundering() + ")..."); for (World world : getServer().getWorlds()) @@ -112,6 +142,31 @@ public final class RealTimeWeather extends JavaPlugin { debug("Weather sync enabled"); } + private long calculateWorldTime(Calendar cal, String sunriseTime, String sunsetTime) { + String[] sunriseTimeSplit = sunriseTime.split(":"); + String[] sunsetTimeSplit = sunsetTime.split(":"); + + long sunriseMinutes = Long.parseLong(sunriseTimeSplit[0]) * 60 + Long.parseLong(sunriseTimeSplit[1]) + Long.parseLong(sunriseTimeSplit[2].substring(0, 2)) / 60; + long sunsetMinutes = Long.parseLong(sunsetTimeSplit[0]) * 60 + Long.parseLong(sunsetTimeSplit[1]) + Long.parseLong(sunsetTimeSplit[2].substring(0, 2)) / 60; + + if (sunriseTimeSplit[2].substring(3).equalsIgnoreCase("PM")) + sunriseMinutes += 720; + if (sunsetTimeSplit[2].substring(3).equalsIgnoreCase("PM")) + sunsetMinutes += 720; + + LocalTime currentTime = LocalTime.of(cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE)); + int currentMinutes = currentTime.getHour() * 60 + currentTime.getMinute(); + + if (currentMinutes >= sunriseMinutes && currentMinutes < sunsetMinutes) { + return ((currentMinutes - sunriseMinutes) / (sunsetMinutes - sunriseMinutes) * 13569) + 23041; + } else { + if (currentMinutes < sunriseMinutes) { + currentMinutes += 1440; + } + return ((currentMinutes - sunsetMinutes) / (1440 - sunsetMinutes + sunriseMinutes) * 13569) + 12610; + } + } + public Configurator getConfigurator() { return config; } diff --git a/src/main/java/io/github/jack1424/realtimeweather/requests/SunriseSunsetRequestObject.java b/src/main/java/io/github/jack1424/realtimeweather/requests/SunriseSunsetRequestObject.java new file mode 100644 index 0000000..d503fe0 --- /dev/null +++ b/src/main/java/io/github/jack1424/realtimeweather/requests/SunriseSunsetRequestObject.java @@ -0,0 +1,59 @@ +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; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; + +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()); + } + scanner.close(); + + JSONObject response = (JSONObject) ((JSONObject) new JSONParser().parse(data.toString())).get("results"); + sunriseTime = response.get("sunrise").toString(); + sunsetTime = response.get("sunset").toString(); + + if (sunriseTime.equalsIgnoreCase("null") || sunsetTime.equalsIgnoreCase("null")) + throw new ConfigurationException("Time(s) returned null. Check the sunrise/sunset longitude and latitude."); + + DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("h:mm:ss a"); + LocalDate currentDate = LocalDate.now(ZoneId.of("UTC")); + sunriseTime = ZonedDateTime.of(currentDate, LocalTime.parse(sunriseTime, timeFormatter), ZoneId.of("UTC")).withZoneSameInstant(timeZone.toZoneId()).format(timeFormatter); + sunsetTime = ZonedDateTime.of(currentDate, LocalTime.parse(sunsetTime, timeFormatter), ZoneId.of("UTC")).withZoneSameInstant(timeZone.toZoneId()).format(timeFormatter); + } + + public String getSunriseTime() { + return sunriseTime; + } + + public String getSunsetTime() { + return sunsetTime; + } +} diff --git a/src/main/java/io/github/jack1424/realtimeweather/RequestObject.java b/src/main/java/io/github/jack1424/realtimeweather/requests/WeatherRequestObject.java similarity index 89% rename from src/main/java/io/github/jack1424/realtimeweather/RequestObject.java rename to src/main/java/io/github/jack1424/realtimeweather/requests/WeatherRequestObject.java index 5768ad2..c1873d3 100644 --- a/src/main/java/io/github/jack1424/realtimeweather/RequestObject.java +++ b/src/main/java/io/github/jack1424/realtimeweather/requests/WeatherRequestObject.java @@ -1,4 +1,4 @@ -package io.github.jack1424.realtimeweather; +package io.github.jack1424.realtimeweather.requests; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -12,10 +12,10 @@ import java.net.ProtocolException; import java.net.URL; import java.util.Scanner; -public class RequestObject { +public class WeatherRequestObject { private boolean rain = false, thunder = false; - public RequestObject(String apiKey, String lat, String lon) throws IOException, ParseException, ConfigurationException { + 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)); HttpsURLConnection con = (HttpsURLConnection) url.openConnection(); diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index abaeee3..52135c9 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -22,6 +22,21 @@ TimeSyncInterval: 100 # You can find a full list of timezones here: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List # Timezone: 'Etc/UTC' # # +# Set to default to use "default" Minecraft sunrise/sunset times (always 5:02 AM and 6:36 PM respectfully) # +# Set to real to sync the sunrise/sunset times with a real world location (configure below) # +# Set to custom to use custom sunrise/sunset times (configure below) # +SunriseSunset: default +# Enter the latitude and longitude of the location that you want to sync the sunrise and sunset with # +# If you set this to a place with no sunrise/sunset (like the North Pole), the default times will be used # +# NOTE: You can find the latitude and longitude of a location at https://www.latlong.net/ # +# This feature is provided for free by SunriseSunset.io # +SunriseSunsetLatitude: '0' +SunriseSunsetLongitude: '0' +# Enter the times that you would like to use for sunrise and sunset # +# You MUST include the seconds (even if they're just 00) # +# Sunrise is defined as 23041 ticks and sunset is 12610 ticks in-game # +SunriseCustomTime: '5:02:27 AM' +SunsetCustomTime: '6:36:36 PM' ###################################################################################################################### ################################# WEATHER SYNC SETTINGS ############################################################## @@ -49,8 +64,8 @@ APIKey: 'API_KEY' # # # Enter the latitude and longitude of the location that you want to sync your world(s) weather with # # NOTE: You can find the latitude and longitude of a location at https://www.latlong.net/ # -Latitude: '0' -Longitude: '0' +WeatherLatitude: '0' +WeatherLongitude: '0' # # ######################################################################################################################