callable) {
- super(chartId);
- this.callable = callable;
- }
-
- @Override
- protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
- int value = callable.call();
- if (value == 0) {
- // Null = skip the chart
- return null;
- }
- return new JsonObjectBuilder().appendField("value", value).build();
- }
- }
-
- /**
- * An extremely simple JSON builder.
- *
- * While this class is neither feature-rich nor the most performant one, it's sufficient enough
- * for its use-case.
- */
- public static class JsonObjectBuilder {
-
- private StringBuilder builder = new StringBuilder();
-
- private boolean hasAtLeastOneField = false;
-
- public JsonObjectBuilder() {
- builder.append("{");
- }
-
- /**
- * Appends a null field to the JSON.
- *
- * @param key The key of the field.
- * @return A reference to this object.
- */
- public JsonObjectBuilder appendNull(String key) {
- appendFieldUnescaped(key, "null");
- return this;
- }
-
- /**
- * Appends a string field to the JSON.
- *
- * @param key The key of the field.
- * @param value The value of the field.
- * @return A reference to this object.
- */
- public JsonObjectBuilder appendField(String key, String value) {
- if (value == null) {
- throw new IllegalArgumentException("JSON value must not be null");
- }
- appendFieldUnescaped(key, "\"" + escape(value) + "\"");
- return this;
- }
-
- /**
- * Appends an integer field to the JSON.
- *
- * @param key The key of the field.
- * @param value The value of the field.
- * @return A reference to this object.
- */
- public JsonObjectBuilder appendField(String key, int value) {
- appendFieldUnescaped(key, String.valueOf(value));
- return this;
- }
-
- /**
- * Appends an object to the JSON.
- *
- * @param key The key of the field.
- * @param object The object.
- * @return A reference to this object.
- */
- public JsonObjectBuilder appendField(String key, JsonObject object) {
- if (object == null) {
- throw new IllegalArgumentException("JSON object must not be null");
- }
- appendFieldUnescaped(key, object.toString());
- return this;
- }
-
- /**
- * Appends a string array to the JSON.
- *
- * @param key The key of the field.
- * @param values The string array.
- * @return A reference to this object.
- */
- public JsonObjectBuilder appendField(String key, String[] values) {
- if (values == null) {
- throw new IllegalArgumentException("JSON values must not be null");
- }
- String escapedValues =
- Arrays.stream(values)
- .map(value -> "\"" + escape(value) + "\"")
- .collect(Collectors.joining(","));
- appendFieldUnescaped(key, "[" + escapedValues + "]");
- return this;
- }
-
- /**
- * Appends an integer array to the JSON.
- *
- * @param key The key of the field.
- * @param values The integer array.
- * @return A reference to this object.
- */
- public JsonObjectBuilder appendField(String key, int[] values) {
- if (values == null) {
- throw new IllegalArgumentException("JSON values must not be null");
- }
- String escapedValues =
- Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(","));
- appendFieldUnescaped(key, "[" + escapedValues + "]");
- return this;
- }
-
- /**
- * Appends an object array to the JSON.
- *
- * @param key The key of the field.
- * @param values The integer array.
- * @return A reference to this object.
- */
- public JsonObjectBuilder appendField(String key, JsonObject[] values) {
- if (values == null) {
- throw new IllegalArgumentException("JSON values must not be null");
- }
- String escapedValues =
- Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(","));
- appendFieldUnescaped(key, "[" + escapedValues + "]");
- return this;
- }
-
- /**
- * Appends a field to the object.
- *
- * @param key The key of the field.
- * @param escapedValue The escaped value of the field.
- */
- private void appendFieldUnescaped(String key, String escapedValue) {
- if (builder == null) {
- throw new IllegalStateException("JSON has already been built");
- }
- if (key == null) {
- throw new IllegalArgumentException("JSON key must not be null");
- }
- if (hasAtLeastOneField) {
- builder.append(",");
- }
- builder.append("\"").append(escape(key)).append("\":").append(escapedValue);
- hasAtLeastOneField = true;
- }
-
- /**
- * Builds the JSON string and invalidates this builder.
- *
- * @return The built JSON string.
- */
- public JsonObject build() {
- if (builder == null) {
- throw new IllegalStateException("JSON has already been built");
- }
- JsonObject object = new JsonObject(builder.append("}").toString());
- builder = null;
- return object;
- }
-
- /**
- * Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt.
- *
- *
This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'.
- * Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n").
- *
- * @param value The value to escape.
- * @return The escaped value.
- */
- private static String escape(String value) {
- final StringBuilder builder = new StringBuilder();
- for (int i = 0; i < value.length(); i++) {
- char c = value.charAt(i);
- if (c == '"') {
- builder.append("\\\"");
- } else if (c == '\\') {
- builder.append("\\\\");
- } else if (c <= '\u000F') {
- builder.append("\\u000").append(Integer.toHexString(c));
- } else if (c <= '\u001F') {
- builder.append("\\u00").append(Integer.toHexString(c));
- } else {
- builder.append(c);
- }
- }
- return builder.toString();
- }
-
- /**
- * A super simple representation of a JSON object.
- *
- *
This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not
- * allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String,
- * JsonObject)}.
- */
- public static class JsonObject {
-
- private final String value;
-
- private JsonObject(String value) {
- this.value = value;
- }
-
- @Override
- public String toString() {
- return value;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/pl/minecon724/realweather/weather/GetStateTask.java b/src/main/java/pl/minecon724/realweather/weather/GetStateTask.java
new file mode 100644
index 0000000..4ba6bdb
--- /dev/null
+++ b/src/main/java/pl/minecon724/realweather/weather/GetStateTask.java
@@ -0,0 +1,73 @@
+package pl.minecon724.realweather.weather;
+
+import java.util.List;
+import java.util.logging.Logger;
+
+import org.bukkit.Bukkit;
+import org.bukkit.WeatherType;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitRunnable;
+
+import pl.minecon724.realweather.map.WorldMap;
+import pl.minecon724.realweather.map.WorldMap.Type;
+import pl.minecon724.realweather.map.exceptions.GeoIPException;
+import pl.minecon724.realweather.weather.WeatherState.ConditionSimple;
+import pl.minecon724.realweather.weather.WeatherState.State;
+import pl.minecon724.realweather.weather.provider.Provider;
+
+public class GetStateTask extends BukkitRunnable {
+
+ Logger logger = Logger.getLogger("weather scheduler");
+
+ Provider provider;
+ WorldMap worldMap;
+ List worlds;
+
+ public GetStateTask(
+ Provider provider,
+ WorldMap worldMap,
+ List worlds
+ ) {
+ this.provider = provider;
+ this.worldMap = worldMap;
+ this.worlds = worlds;
+ }
+
+ // That's a lot of variables
+
+ @Override
+ public void run() {
+
+ if (worldMap.getType() == Type.POINT) {
+ double[] position;
+ try {
+ position = worldMap.getPosition(null);
+ } catch (GeoIPException e) { return; }
+ State state = provider.request_state(position);
+
+ for (World world : worlds) {
+ if (world == null) return;
+ world.setThundering(state.getSimple() == ConditionSimple.THUNDER ? true : false);
+ world.setStorm(state.getSimple() == ConditionSimple.CLEAR ? false : true);
+ }
+
+ } else {
+ for (Player player : Bukkit.getOnlinePlayers()) {
+ double[] position;
+
+ try {
+ position = worldMap.getPosition(player);
+ } catch (GeoIPException e) {
+ logger.info("GeoIP error for " + player.getName());
+ e.printStackTrace();
+ continue;
+ }
+
+ State state = provider.request_state(position);
+ player.setPlayerWeather(state.getSimple() == ConditionSimple.CLEAR ? WeatherType.CLEAR : WeatherType.DOWNFALL);
+ }
+ }
+
+ }
+}
diff --git a/src/main/java/pl/minecon724/realweather/weather/Providers.java b/src/main/java/pl/minecon724/realweather/weather/Providers.java
new file mode 100644
index 0000000..9983e03
--- /dev/null
+++ b/src/main/java/pl/minecon724/realweather/weather/Providers.java
@@ -0,0 +1,35 @@
+package pl.minecon724.realweather.weather;
+
+import org.bukkit.configuration.ConfigurationSection;
+
+import pl.minecon724.realweather.weather.exceptions.ProviderException;
+import pl.minecon724.realweather.weather.provider.OpenWeatherMapProvider;
+import pl.minecon724.realweather.weather.provider.Provider;
+
+public class Providers {
+ /**
+ * get Provider by name
+ * @param name name of provider
+ * @param config configuration of provider
+ * @return subclass of Provider or null if invalid
+ * @throws ProviderException
+ * @see Provider
+ */
+ public static Provider getByName(String name, ConfigurationSection config)
+ throws ProviderException {
+
+ switch (name) {
+ case "openweathermap":
+ return openWeatherMap(config);
+ }
+
+ return null;
+ }
+
+ public static OpenWeatherMapProvider openWeatherMap(ConfigurationSection config)
+ throws ProviderException {
+
+ return new OpenWeatherMapProvider(
+ config.getString("apiKey"));
+ }
+}
diff --git a/src/main/java/pl/minecon724/realweather/weather/WeatherCommander.java b/src/main/java/pl/minecon724/realweather/weather/WeatherCommander.java
new file mode 100644
index 0000000..d17c9b8
--- /dev/null
+++ b/src/main/java/pl/minecon724/realweather/weather/WeatherCommander.java
@@ -0,0 +1,64 @@
+package pl.minecon724.realweather.weather;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.bukkit.World;
+import org.bukkit.configuration.ConfigurationSection;
+
+import pl.minecon724.realweather.RW;
+import pl.minecon724.realweather.map.WorldMap;
+import pl.minecon724.realweather.weather.exceptions.DisabledException;
+import pl.minecon724.realweather.weather.exceptions.ProviderException;
+import pl.minecon724.realweather.weather.provider.Provider;
+
+public class WeatherCommander {
+ WorldMap worldMap;
+ RW plugin;
+
+ boolean enabled;
+ List worlds;
+ String providerName;
+ Provider provider;
+ ConfigurationSection providerConfig;
+
+ GetStateTask getStateTask;
+
+ public WeatherCommander(WorldMap worldMap, RW plugin) {
+ this.worldMap = worldMap;
+ this.plugin = plugin;
+ }
+
+ /**
+ * Initialize weather commander
+ * @param config "weather" ConfigurationSection
+ * @throws DisabledException if disabled in config
+ * @throws ProviderException if invalid provider config
+ */
+ public void init(ConfigurationSection config)
+ throws DisabledException, ProviderException {
+ enabled = config.getBoolean("enabled");
+
+ if (!enabled)
+ throw new DisabledException();
+
+ worlds = config.getStringList("worlds");
+
+ providerName = config.getString("provider.choice");
+
+ // this can be null
+ providerConfig = config.getConfigurationSection("provider")
+ .getConfigurationSection(providerName);
+
+ provider = Providers.getByName(providerName, providerConfig);
+ provider.init();
+ }
+
+ public void start() {
+ getStateTask = new GetStateTask(provider,
+ worldMap,
+ new ArrayList());
+
+ getStateTask.runTaskTimerAsynchronously(plugin, 0, 1200);
+ }
+}
diff --git a/src/main/java/pl/minecon724/realweather/WeatherState.java b/src/main/java/pl/minecon724/realweather/weather/WeatherState.java
similarity index 74%
rename from src/main/java/pl/minecon724/realweather/WeatherState.java
rename to src/main/java/pl/minecon724/realweather/weather/WeatherState.java
index 3e58177..738c86c 100644
--- a/src/main/java/pl/minecon724/realweather/WeatherState.java
+++ b/src/main/java/pl/minecon724/realweather/weather/WeatherState.java
@@ -1,4 +1,4 @@
-package pl.minecon724.realweather;
+package pl.minecon724.realweather.weather;
public class WeatherState {
@@ -14,9 +14,9 @@ public class WeatherState {
// Variables
- Condition condition;
- ConditionLevel level;
- ConditionSimple simple;
+ private Condition condition;
+ private ConditionLevel level;
+ private ConditionSimple simple;
// Constructor
@@ -47,5 +47,17 @@ public class WeatherState {
this.simple = ConditionSimple.CLEAR;
}
}
+
+ public Condition getCondition() {
+ return condition;
+ }
+
+ public ConditionLevel getLevel() {
+ return level;
+ }
+
+ public ConditionSimple getSimple() {
+ return simple;
+ }
}
}
diff --git a/src/main/java/pl/minecon724/realweather/weather/exceptions/DisabledException.java b/src/main/java/pl/minecon724/realweather/weather/exceptions/DisabledException.java
new file mode 100644
index 0000000..dde43be
--- /dev/null
+++ b/src/main/java/pl/minecon724/realweather/weather/exceptions/DisabledException.java
@@ -0,0 +1,5 @@
+package pl.minecon724.realweather.weather.exceptions;
+
+public class DisabledException extends Exception {
+
+}
diff --git a/src/main/java/pl/minecon724/realweather/weather/exceptions/ProviderException.java b/src/main/java/pl/minecon724/realweather/weather/exceptions/ProviderException.java
new file mode 100644
index 0000000..27063af
--- /dev/null
+++ b/src/main/java/pl/minecon724/realweather/weather/exceptions/ProviderException.java
@@ -0,0 +1,5 @@
+package pl.minecon724.realweather.weather.exceptions;
+
+public class ProviderException extends Exception {
+
+}
diff --git a/src/main/java/pl/minecon724/realweather/provider/OpenWeatherMapProvider.java b/src/main/java/pl/minecon724/realweather/weather/provider/OpenWeatherMapProvider.java
similarity index 91%
rename from src/main/java/pl/minecon724/realweather/provider/OpenWeatherMapProvider.java
rename to src/main/java/pl/minecon724/realweather/weather/provider/OpenWeatherMapProvider.java
index d8cae5d..a077d40 100644
--- a/src/main/java/pl/minecon724/realweather/provider/OpenWeatherMapProvider.java
+++ b/src/main/java/pl/minecon724/realweather/weather/provider/OpenWeatherMapProvider.java
@@ -1,4 +1,4 @@
-package pl.minecon724.realweather.provider;
+package pl.minecon724.realweather.weather.provider;
import java.io.BufferedReader;
import java.io.InputStream;
@@ -9,8 +9,7 @@ import java.nio.charset.Charset;
import org.json.JSONObject;
-import pl.minecon724.realweather.Provider;
-import pl.minecon724.realweather.WeatherState.*;
+import pl.minecon724.realweather.weather.WeatherState.*;
public class OpenWeatherMapProvider implements Provider {
@@ -30,13 +29,13 @@ public class OpenWeatherMapProvider implements Provider {
}
}
- public State request_state(double lat, double lon) {
+ public State request_state(double[] coords) {
JSONObject json = new JSONObject();
try {
URL url = new URL(
endpoint + String.format("/data/2.5/weather?lat=%s&lon=%s&appid=%s",
- Double.toString(lat), Double.toString(lon), apiKey
+ Double.toString(coords[0]), Double.toString(coords[1]), apiKey
));
InputStream is = url.openStream();
@@ -153,4 +152,9 @@ public class OpenWeatherMapProvider implements Provider {
State state = new State(condition, level);
return state;
}
+
+ @Override
+ public String getName() {
+ return "OpenWeatherMap";
+ }
}
diff --git a/src/main/java/pl/minecon724/realweather/weather/provider/Provider.java b/src/main/java/pl/minecon724/realweather/weather/provider/Provider.java
new file mode 100644
index 0000000..7527300
--- /dev/null
+++ b/src/main/java/pl/minecon724/realweather/weather/provider/Provider.java
@@ -0,0 +1,9 @@
+package pl.minecon724.realweather.weather.provider;
+
+import pl.minecon724.realweather.weather.WeatherState;
+
+public interface Provider {
+ public void init();
+ public WeatherState.State request_state(double[] coords);
+ public String getName();
+}
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 3570e7c..905f94e 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -1,71 +1,73 @@
weather:
+ enabled: true
+
worlds:
- world
- second_world
- third_world
+
+ provider:
+ # Weather provider
+ choice: openweathermap
+ # Configure it here
+ openweathermap:
+ apiKey: 'd3d37fd3511ef1d4b44c7d574e9b56b8' # PLEASE get your own @ https://home.openweathermap.org/users/sign_up
+ # More providers soon!
+
+map:
# "point" - static location
# "player" - player's IP location (fake weather)
- # "map" - world resembles a real-world globe
- source: point
+ # "globe" - world resembles a real-world globe
+ type: point
+
point:
latitude: 41.84201
longitude: -89.485937
+
player:
# Get your own @ https://www.maxmind.com/en/geolite2/signup
geolite2_accountId: 710438
- geolite2_apiKey: 'qLeseHp4QNQcqRGn'
- map:
+ geolite2_api_key: 'qLeseHp4QNQcqRGn'
+
+ globe:
# Valid latitude range: -90 to 90
# Valid longitude range: -180 to 180
# 1 degree of latitude and longitude is about 111 km
# The defaults here assume 1 block = ~1 km
- # Latitude scale, 1 block = degrees
- scale_lat: 0.009
- # Longitude scale, 1 block = degrees
- scale_lon: 0.009
- # What to do if player exceeds the range specified above
- # 1 - do nothing (clamp to nearest allowed value)
- # 2 - wrap the number
- # for example; if a player's position on map converts to 94 degrees (out of bounds), it becomes -86 degrees
- on_exceed: 2
+ # 1 block = degrees
+ scale_latitude: 0.009
+ scale_longitude: 0.009
-provider:
- # Weather provider
- choice: openweathermap
- # Configure it here
- openweathermap:
- apiKey: 'd3d37fd3511ef1d4b44c7d574e9b56b8' # PLEASE get your own @ https://home.openweathermap.org/users/sign_up
- # More providers soon!
+ # What to do if player exceeds the range specified above
+ # false - do nothing (clamp to nearest allowed value)
+ # true - wrap the number
+ # for example; if a player's position on map converts to 94 degrees (out of bounds), it becomes -86 degrees
+ wrap: true
realtime:
# warning: this removes sleep
enabled: false
+
worlds:
- world
+
# "auto" to use server's timezone
# Alternatively choose one of these: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
+ # if "real" each player's timezone is varied based on where they are
+ # config from map.globe is used, also forces virtual
+ # WARNING: it is purely cosmetical
timezone: 'auto'
+
# x day cycles / 24 hrs
timeScale: 1.0
+
# How often should we recalculate the time (in ticks)
# Very minimal impact on performance
interval: 1
-settings:
- # Delay between rechecking weather
- # in ticks, 20 is one second
- # Shouldn't affect performance
- timeBetweenRecheck: 600
- # Whether to display an actionbar containing info
- actionbar: true
+ # makes time visible to players not in sync with server
+ virtual: false
- # Advanced options
- timeBeforeInitialRun: 0
- debug: false
- debugAllowDox: false
-
-messages:
- # settings.actionbar toggles this
- # %weather_full% - full state description, such as "extreme thunder"
- # %weather% - short state description, such as "rain"
- actionbarInfo: "&b%weather_full%"
+ # if above true, locks the server's time (in ticks)
+ # -1 to disable
+ lock: 0
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 4206008..1842148 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -4,9 +4,5 @@ api-version: 1.16
author: Minecon724
main: pl.minecon724.realweather.RW
libraries:
- - org.json:json:20220320
- - com.maxmind.geoip2:geoip2:4.1.0
-commands:
- realweather:
- alias: rw
- description: RealWeather main command
\ No newline at end of file
+ - org.json:json:20231013
+ - com.maxmind.geoip2:geoip2:4.2.0
\ No newline at end of file