From 31afa3b10f7dbf606ec9132d08d52c68de9df358 Mon Sep 17 00:00:00 2001
From: Minecon724 <39800107+Minecon724@users.noreply.github.com>
Date: Sun, 21 Jan 2024 19:08:54 +0000
Subject: [PATCH] local geoip, remove maxmind dependency
---
TODO.md | 6 +
pom.xml | 14 +--
.../{RW.java => RealWeatherPlugin.java} | 44 ++++---
.../pl/minecon724/realweather/SubLogger.java | 56 ++++++++-
.../realweather/geoip/DatabaseDownloader.java | 51 ++++++++
.../realweather/geoip/GeoIPDatabase.java | 96 +++++++++++++++
.../minecon724/realweather/geoip/IPUtils.java | 32 +++++
.../realweather/map/Coordinates.java | 9 --
.../realweather/map/GeoLocator.java | 84 ++++++++-----
.../minecon724/realweather/map/WorldMap.java | 11 +-
.../realtime/RealTimeCommander.java | 12 +-
.../realweather/weather/GetStateTask.java | 13 +-
.../realweather/weather/WeatherChanger.java | 2 +-
.../realweather/weather/WeatherCommander.java | 22 ++--
...tion.java => ModuleDisabledException.java} | 2 +-
.../exceptions/WeatherProviderException.java | 9 ++
.../provider/OpenWeatherMapProvider.java | 115 +++++++++---------
.../weather/provider/Provider.java | 9 +-
src/main/resources/config.yml | 4 +-
src/main/resources/plugin.yml | 10 +-
20 files changed, 437 insertions(+), 164 deletions(-)
rename src/main/java/pl/minecon724/realweather/{RW.java => RealWeatherPlugin.java} (51%)
create mode 100644 src/main/java/pl/minecon724/realweather/geoip/DatabaseDownloader.java
create mode 100644 src/main/java/pl/minecon724/realweather/geoip/GeoIPDatabase.java
create mode 100644 src/main/java/pl/minecon724/realweather/geoip/IPUtils.java
rename src/main/java/pl/minecon724/realweather/weather/exceptions/{DisabledException.java => ModuleDisabledException.java} (52%)
create mode 100644 src/main/java/pl/minecon724/realweather/weather/exceptions/WeatherProviderException.java
diff --git a/TODO.md b/TODO.md
index 74ccc5e..2682303 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,3 +1,8 @@
+Current:
+- weather forecast https://openweathermap.org/forecast5
+- multiple conditions
+- on join event
+
Milestone: yesterday
- fix bugs
@@ -8,6 +13,7 @@ Milestone: 0.5.1
Milestone: 0.6.0
- account for real sun movement, not just time
+- release / debug separate versions
Milestone: future
- weather simulator (weather is clientside rn and doesnt have things such as lightning or other effects)
diff --git a/pom.xml b/pom.xml
index 0a1bb26..3c2ca4e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,7 +2,7 @@
4.0.0
pl.minecon724
realweather
- 0.5.0.1
+ 0.5.1-DEV
17
@@ -24,15 +24,9 @@
provided
- org.json
- json
- 20231013
- provided
-
-
- com.maxmind.geoip2
- geoip2
- 4.2.0
+ com.google.code.gson
+ gson
+ 2.10.1
provided
diff --git a/src/main/java/pl/minecon724/realweather/RW.java b/src/main/java/pl/minecon724/realweather/RealWeatherPlugin.java
similarity index 51%
rename from src/main/java/pl/minecon724/realweather/RW.java
rename to src/main/java/pl/minecon724/realweather/RealWeatherPlugin.java
index 0c19e40..d9efff9 100644
--- a/src/main/java/pl/minecon724/realweather/RW.java
+++ b/src/main/java/pl/minecon724/realweather/RealWeatherPlugin.java
@@ -1,35 +1,46 @@
package pl.minecon724.realweather;
-import com.maxmind.geoip2.WebServiceClient;
+import java.io.IOException;
+import java.util.logging.Logger;
+import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import pl.minecon724.realweather.map.WorldMap;
import pl.minecon724.realweather.realtime.RealTimeCommander;
import pl.minecon724.realweather.weather.WeatherCommander;
-import pl.minecon724.realweather.weather.exceptions.DisabledException;
+import pl.minecon724.realweather.weather.exceptions.ModuleDisabledException;
-public class RW extends JavaPlugin {
- FileConfiguration config;
+public class RealWeatherPlugin extends JavaPlugin {
- WebServiceClient client = null;
+ private final Logger logger = getLogger();
+
+ private FileConfiguration config;
@Override
public void onEnable() {
- long start = System.currentTimeMillis();
-
+
saveDefaultConfig();
config = getConfig();
SubLogger.init(
- getLogger(),
+ logger,
config.getBoolean("logging", false)
);
- WorldMap.init(
- config.getConfigurationSection("map")
- );
+ ConfigurationSection mapConfigurationSection = config.getConfigurationSection("map");
+
+ try {
+ WorldMap.init(
+ mapConfigurationSection,
+ getDataFolder()
+ );
+ } catch (IOException e) {
+ logger.severe("Unable to initialize WorldMap:");
+ e.printStackTrace();
+ getServer().getPluginManager().disablePlugin(this);
+ }
WeatherCommander weatherCommander = new WeatherCommander(this);
try {
@@ -37,9 +48,10 @@ public class RW extends JavaPlugin {
config.getConfigurationSection("weather")
);
weatherCommander.start();
- } catch (DisabledException e) {
- getLogger().info("Weather module disabled");
+ } catch (ModuleDisabledException e) {
+ logger.info("Weather is disabled by user");
} catch (IllegalArgumentException e) {
+ logger.severe("Couldn't initialize weather provider:");
e.printStackTrace();
getServer().getPluginManager().disablePlugin(this);
}
@@ -50,11 +62,9 @@ public class RW extends JavaPlugin {
config.getConfigurationSection("time")
);
realTimeCommander.start();
- } catch (DisabledException e) {
- getLogger().info("Time module disabled");
+ } catch (ModuleDisabledException e) {
+ logger.info("Time is disabled by user");
}
- long end = System.currentTimeMillis();
- this.getLogger().info( String.format( this.getName() + " enabled! (%s ms)", Long.toString( end-start ) ) );
}
}
diff --git a/src/main/java/pl/minecon724/realweather/SubLogger.java b/src/main/java/pl/minecon724/realweather/SubLogger.java
index 7c7887a..eca3833 100644
--- a/src/main/java/pl/minecon724/realweather/SubLogger.java
+++ b/src/main/java/pl/minecon724/realweather/SubLogger.java
@@ -4,30 +4,84 @@ import java.util.logging.Level;
import java.util.logging.Logger;
public class SubLogger {
+ // TODO TODO too many static
private static Logger LOGGER;
private static boolean ENABLED;
private String name;
+ /**
+ * Initialize the SubLogger
+ * @param logger parent logger, usually JavaPlugin#getLogger()
+ * @param enabled is logging enabled
+ */
static void init(Logger logger, boolean enabled) {
LOGGER = logger;
ENABLED = enabled;
}
+ /**
+ * Instantiate a SubLogger instance
+ * @param name name, it will be prefixing messages
+ */
public SubLogger(String name) {
this.name = name;
}
+ /**
+ * Log a message
+ * @param level
+ * @param format message, formatted like {@link String#format(String, Object...)}
+ * @param args args for formatting
+ */
public void log(Level level, String format, Object... args) {
if (!ENABLED) return;
Object[] combinedArgs = new Object[args.length + 1];
combinedArgs[0] = name;
System.arraycopy(args, 0, combinedArgs, 1, args.length);
-
+
LOGGER.log(level, String.format("[%s] " + format, combinedArgs));
}
+ /**
+ * Log an info message
+ * see {@link SubLogger#log(Level, String, Object...)}
+ * @param format message
+ * @param args args
+ */
public void info(String format, Object... args) {
this.log(Level.INFO, format, args);
}
+
+ public void info(String message) {
+ this.log(Level.INFO, message, new Object[0]);
+ }
+
+ /**
+ * Log a severe message
+ * see {@link SubLogger#log(Level, String, Object...)}
+ * @param format message
+ * @param args args
+ */
+ public void severe(String format, Object... args) {
+ this.log(Level.SEVERE, format, args);
+ }
+
+ public void severe(String message) {
+ this.log(Level.SEVERE, message, new Object[0]);
+ }
+
+ /**
+ * Log a warning message
+ * see {@link SubLogger#log(Level, String, Object...)}
+ * @param format message
+ * @param args args
+ */
+ public void warning(String format, Object... args) {
+ this.log(Level.WARNING, format, args);
+ }
+
+ public void warning(String message) {
+ this.log(Level.WARNING, message, new Object[0]);
+ }
}
diff --git a/src/main/java/pl/minecon724/realweather/geoip/DatabaseDownloader.java b/src/main/java/pl/minecon724/realweather/geoip/DatabaseDownloader.java
new file mode 100644
index 0000000..187b3a0
--- /dev/null
+++ b/src/main/java/pl/minecon724/realweather/geoip/DatabaseDownloader.java
@@ -0,0 +1,51 @@
+package pl.minecon724.realweather.geoip;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+import pl.minecon724.realweather.SubLogger;
+
+public class DatabaseDownloader {
+ private SubLogger subLogger = new SubLogger("download");
+ private URL downloadUrl;
+
+ public DatabaseDownloader(URL downloadUrl) {
+ this.downloadUrl = downloadUrl;
+ }
+
+ // TODO verify
+ public void download(File file, boolean ipv6) throws IOException {
+ URL url = new URL(downloadUrl, ipv6 ? "ipv6.geo.gz" : "ipv4.geo.gz");
+
+ URLConnection connection = url.openConnection();
+ connection.connect();
+ InputStream inputStream = connection.getInputStream();
+ ReadableByteChannel readableByteChannel = Channels.newChannel(inputStream);
+
+ file.getParentFile().mkdirs();
+ file.createNewFile();
+
+ FileOutputStream fileOutputStream = new FileOutputStream(file);
+ FileChannel fileChannel = fileOutputStream.getChannel();
+
+ long size = connection.getHeaderFieldLong("Content-Length", 0);
+ long position = 0;
+
+ while (position < size) {
+ position += fileChannel.transferFrom(readableByteChannel, position, 1048576);
+ subLogger.info("%d%%", (int)(1.0 * position / size * 100));
+ }
+
+
+ fileChannel.close();
+ fileOutputStream.close();
+ readableByteChannel.close();
+ inputStream.close(); // ok
+ }
+}
diff --git a/src/main/java/pl/minecon724/realweather/geoip/GeoIPDatabase.java b/src/main/java/pl/minecon724/realweather/geoip/GeoIPDatabase.java
new file mode 100644
index 0000000..801018a
--- /dev/null
+++ b/src/main/java/pl/minecon724/realweather/geoip/GeoIPDatabase.java
@@ -0,0 +1,96 @@
+package pl.minecon724.realweather.geoip;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.zip.GZIPInputStream;
+
+import com.google.common.primitives.Ints;
+import com.google.common.primitives.Shorts;
+
+import pl.minecon724.realweather.map.Coordinates;
+
+public class GeoIPDatabase {
+ private byte formatVersion;
+ private long timestamp;
+ public HashMap entries = new HashMap<>();
+
+ public void read(File file) throws IOException, FileNotFoundException {
+ if (!file.exists()) {
+ throw new FileNotFoundException();
+ }
+
+ if (timestamp != 0) {
+ throw new IOException("GeoIPDatabase can only be read once");
+ }
+
+ FileInputStream fileInputStream = new FileInputStream(file);
+ GZIPInputStream gzipInputStream = new GZIPInputStream(fileInputStream);
+
+ formatVersion = (byte) gzipInputStream.read();
+ byte[] bytes = gzipInputStream.readNBytes(2);
+ timestamp = recoverTime(bytes);
+
+ byte[] address;
+ Coordinates coordinates;
+
+ while (true) { // TODO true?
+ System.out.println(gzipInputStream.available());
+ address = gzipInputStream.readNBytes(4);
+ if (address.length == 0)
+ break;
+
+ coordinates = recoverCoordinates(gzipInputStream.readNBytes(6));
+
+ entries.put(IPUtils.toInt(address), coordinates);
+ }
+
+ gzipInputStream.close();
+ fileInputStream.close();
+ }
+
+ public String getTimestamp() {
+ return new SimpleDateFormat("dd.MM.yyyy HH:mm:ss")
+ .format(new Date(timestamp * 1000));
+ }
+
+ public byte getFormatVersion() {
+ return formatVersion;
+ }
+
+ /**
+ * 2 bytes to 4 bytes wow magic
+ * @param bytes 2 bytes
+ * @return
+ */
+ @SuppressWarnings("null") // TODO better way of this?
+ private long recoverTime(byte[] bytes) {
+ long timestamp = 1704067200; // first second of 2024
+ timestamp += Shorts.fromByteArray(bytes) * 60 * 10;
+ return timestamp;
+ }
+
+ /**
+ * Encoded to Coordinates
+ * @param bytes 3 bytes
+ * @return decoded Coordinates
+ */
+ private Coordinates recoverCoordinates(byte[] bytes) {
+ int skewedLatitude = Ints.fromBytes(
+ (byte)0, bytes[0], bytes[1], bytes[2]
+ );
+
+ int skewedLongitude = Ints.fromBytes(
+ (byte)0, bytes[3], bytes[4], bytes[5]
+ );
+
+ double latitude = (skewedLatitude - 900000) / 10000.0;
+ double longitude = (skewedLongitude - 1800000) / 10000.0;
+
+ return new Coordinates(latitude, longitude);
+ }
+}
diff --git a/src/main/java/pl/minecon724/realweather/geoip/IPUtils.java b/src/main/java/pl/minecon724/realweather/geoip/IPUtils.java
new file mode 100644
index 0000000..49d7ade
--- /dev/null
+++ b/src/main/java/pl/minecon724/realweather/geoip/IPUtils.java
@@ -0,0 +1,32 @@
+package pl.minecon724.realweather.geoip;
+
+import com.google.common.primitives.Ints;
+
+public class IPUtils {
+ public static byte[] getSubnetStart(byte[] addressBytes, byte subnet) {
+ int address = toInt(addressBytes);
+ int mask = 0xFFFFFFFF << (32 - subnet);
+
+ return fromInt(address & mask);
+ }
+
+ @SuppressWarnings("null")
+ public static int toInt(byte[] address) {
+ return Ints.fromByteArray(address);
+ }
+
+ public static byte[] fromInt(int value) {
+ return Ints.toByteArray(value);
+ }
+
+ public static String toString(byte[] address) {
+ String s = "";
+
+ for (int i=0; i<4; i++) {
+ s += Integer.toString(address[i] & 0xFF);
+ if (i < 3) s += ".";
+ }
+
+ return s;
+ }
+}
diff --git a/src/main/java/pl/minecon724/realweather/map/Coordinates.java b/src/main/java/pl/minecon724/realweather/map/Coordinates.java
index 35cf22a..e73270a 100644
--- a/src/main/java/pl/minecon724/realweather/map/Coordinates.java
+++ b/src/main/java/pl/minecon724/realweather/map/Coordinates.java
@@ -1,7 +1,5 @@
package pl.minecon724.realweather.map;
-import com.maxmind.geoip2.record.Location;
-
public class Coordinates {
public double latitude, longitude;
@@ -38,11 +36,4 @@ public class Coordinates {
private static double wrapDouble(double min, double max, double val) {
return min + (val - min) % (max - min);
}
-
- public static Coordinates fromGeoIpLocation(Location location) {
- return new Coordinates(
- location.getLatitude(),
- location.getLongitude()
- );
- }
}
diff --git a/src/main/java/pl/minecon724/realweather/map/GeoLocator.java b/src/main/java/pl/minecon724/realweather/map/GeoLocator.java
index 3b2736a..b6bd6e8 100644
--- a/src/main/java/pl/minecon724/realweather/map/GeoLocator.java
+++ b/src/main/java/pl/minecon724/realweather/map/GeoLocator.java
@@ -1,30 +1,48 @@
package pl.minecon724.realweather.map;
+import java.io.File;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.InetAddress;
+import java.net.URL;
import java.util.HashMap;
-import java.util.Map;
-
-import com.maxmind.geoip2.WebServiceClient;
-import com.maxmind.geoip2.exception.GeoIp2Exception;
+import pl.minecon724.realweather.SubLogger;
+import pl.minecon724.realweather.geoip.DatabaseDownloader;
+import pl.minecon724.realweather.geoip.GeoIPDatabase;
+import pl.minecon724.realweather.geoip.IPUtils;
import pl.minecon724.realweather.map.exceptions.GeoIPException;
public class GeoLocator {
private static GeoLocator INSTANCE = null;
- private WebServiceClient client;
- private Map cache;
+ private SubLogger subLogger = new SubLogger("geolocator");
+ private GeoIPDatabase database;
+ private HashMap cache = new HashMap<>();
- public static void init(int accountId, String apiKey) {
+ public static void init(File databaseFile, String downloadUrl) throws IOException {
INSTANCE = new GeoLocator(
- new WebServiceClient.Builder(accountId, apiKey)
- .host("geolite.info").build());
+ new GeoIPDatabase());
+
+ INSTANCE.load(databaseFile, downloadUrl);
}
- public GeoLocator(WebServiceClient client) {
- this.client = client;
- this.cache = new HashMap<>();
+ public GeoLocator(GeoIPDatabase database) {
+ this.database = database;
+ }
+
+ public void load(File databaseFile, String downloadUrl) throws IOException {
+ subLogger.info("This product includes GeoLite2 data created by MaxMind, available from https://www.maxmind.com");
+
+ try {
+ database.read(databaseFile);
+ } catch (FileNotFoundException e) {
+ new DatabaseDownloader(new URL(downloadUrl))
+ .download(databaseFile, false);
+ database.read(databaseFile);
+ }
+
+ subLogger.info("Database: %s", INSTANCE.database.getTimestamp());
}
/**
@@ -34,35 +52,37 @@ public class GeoLocator {
* @throws GeoIp2Exception
* @throws IOException
*/
- public static Coordinates getCoordinates(InetAddress address)
+ public static Coordinates getCoordinates(InetAddress inetAddress)
throws GeoIPException {
- GeoLocator instance = INSTANCE;
-
- Coordinates coordinates = null;
-
- coordinates = instance.lookup(address);
+ Coordinates coordinates = INSTANCE.cache.get(inetAddress);
if (coordinates != null)
return coordinates;
- try {
- coordinates = Coordinates.fromGeoIpLocation(
- instance.client.city(address).getLocation()
+ byte[] address = inetAddress.getAddress();
+ byte subnet = 32;
+
+ while (coordinates == null) {
+ if (subnet == 0) {
+ INSTANCE.subLogger.info("Not found :(");
+ coordinates = new Coordinates(0, 0);
+ break;
+ }
+
+ int query = IPUtils.toInt(address);
+
+ coordinates = INSTANCE.database.entries.get(
+ query
);
- } catch (IOException | GeoIp2Exception e) {
- throw new GeoIPException(e.getMessage());
+
+ INSTANCE.subLogger.info("trying %s/%d = %d", IPUtils.toString(address), subnet, query);
+
+ address = IPUtils.getSubnetStart(address, --subnet);
}
- instance.store(address, coordinates);
+ INSTANCE.subLogger.info("Done, caching");
+ INSTANCE.cache.put(inetAddress, coordinates);
return coordinates;
}
-
- private Coordinates lookup(InetAddress address) {
- return this.cache.get(address);
- }
-
- private void store(InetAddress address, Coordinates coordinates) {
- this.cache.put(address, coordinates);
- }
}
diff --git a/src/main/java/pl/minecon724/realweather/map/WorldMap.java b/src/main/java/pl/minecon724/realweather/map/WorldMap.java
index fd4411d..fb11c5f 100644
--- a/src/main/java/pl/minecon724/realweather/map/WorldMap.java
+++ b/src/main/java/pl/minecon724/realweather/map/WorldMap.java
@@ -1,5 +1,8 @@
package pl.minecon724.realweather.map;
+import java.io.File;
+import java.io.IOException;
+
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
@@ -24,8 +27,8 @@ public class WorldMap {
this.point = point;
}
- public static void init(ConfigurationSection config)
- throws IllegalArgumentException {
+ public static void init(ConfigurationSection config, File dataFolder)
+ throws IOException {
Type type;
@@ -45,8 +48,8 @@ public class WorldMap {
} else if (type == Type.PLAYER) {
GeoLocator.init(
- config.getInt("player.geolite2_accountId"),
- config.getString("player.geolite2_api_key")
+ dataFolder.toPath().resolve("geoip/ipv4.geo.gz").toFile(),
+ config.getString("player.download_url", "https://inferior.network/geoip/")
);
} else if (type == Type.GLOBE) {
diff --git a/src/main/java/pl/minecon724/realweather/realtime/RealTimeCommander.java b/src/main/java/pl/minecon724/realweather/realtime/RealTimeCommander.java
index 3cb4670..47d2786 100644
--- a/src/main/java/pl/minecon724/realweather/realtime/RealTimeCommander.java
+++ b/src/main/java/pl/minecon724/realweather/realtime/RealTimeCommander.java
@@ -14,11 +14,11 @@ import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.event.world.WorldUnloadEvent;
-import pl.minecon724.realweather.RW;
-import pl.minecon724.realweather.weather.exceptions.DisabledException;
+import pl.minecon724.realweather.RealWeatherPlugin;
+import pl.minecon724.realweather.weather.exceptions.ModuleDisabledException;
public class RealTimeCommander implements Listener {
- private RW plugin;
+ private RealWeatherPlugin plugin;
private List worldNames;
private double scale;
@@ -31,15 +31,15 @@ public class RealTimeCommander implements Listener {
private RealTimeTask task;
private PlayerTimeSyncTask playerTimeSyncTask;
- public RealTimeCommander(RW plugin) {
+ public RealTimeCommander(RealWeatherPlugin plugin) {
this.plugin = plugin;
}
public void init(ConfigurationSection config)
- throws DisabledException {
+ throws ModuleDisabledException {
if (!config.getBoolean("enabled"))
- throw new DisabledException();
+ throw new ModuleDisabledException();
try {
timezone = ZoneId.of(config.getString("timezone"));
diff --git a/src/main/java/pl/minecon724/realweather/weather/GetStateTask.java b/src/main/java/pl/minecon724/realweather/weather/GetStateTask.java
index 1cf3e3a..292cb4a 100644
--- a/src/main/java/pl/minecon724/realweather/weather/GetStateTask.java
+++ b/src/main/java/pl/minecon724/realweather/weather/GetStateTask.java
@@ -1,6 +1,5 @@
package pl.minecon724.realweather.weather;
-import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@@ -9,7 +8,7 @@ import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginManager;
import org.bukkit.scheduler.BukkitRunnable;
-import pl.minecon724.realweather.RW;
+import pl.minecon724.realweather.RealWeatherPlugin;
import pl.minecon724.realweather.SubLogger;
import pl.minecon724.realweather.map.Coordinates;
import pl.minecon724.realweather.map.WorldMap;
@@ -17,13 +16,14 @@ import pl.minecon724.realweather.map.WorldMap.Type;
import pl.minecon724.realweather.map.exceptions.GeoIPException;
import pl.minecon724.realweather.weather.WeatherState.State;
import pl.minecon724.realweather.weather.events.WeatherSyncEvent;
+import pl.minecon724.realweather.weather.exceptions.WeatherProviderException;
import pl.minecon724.realweather.weather.provider.Provider;
public class GetStateTask extends BukkitRunnable {
private SubLogger subLogger = new SubLogger("weather updater");
- private RW plugin;
+ private RealWeatherPlugin plugin;
private Provider provider;
private WorldMap worldMap;
@@ -32,7 +32,7 @@ public class GetStateTask extends BukkitRunnable {
private PluginManager pluginManager = Bukkit.getPluginManager();
public GetStateTask(
- RW plugin,
+ RealWeatherPlugin plugin,
Provider provider,
WorldMap worldMap
) {
@@ -92,8 +92,9 @@ public class GetStateTask extends BukkitRunnable {
}
}
}
- } catch (IOException e) {
- subLogger.info("Error updating: %s", e.getMessage());
+ } catch (WeatherProviderException e) {
+ subLogger.info("Weather provider error");
+ e.printStackTrace();
}
}
diff --git a/src/main/java/pl/minecon724/realweather/weather/WeatherChanger.java b/src/main/java/pl/minecon724/realweather/weather/WeatherChanger.java
index d0f1a37..ec85818 100644
--- a/src/main/java/pl/minecon724/realweather/weather/WeatherChanger.java
+++ b/src/main/java/pl/minecon724/realweather/weather/WeatherChanger.java
@@ -26,7 +26,7 @@ public class WeatherChanger implements Listener {
@EventHandler
public void onWorldLoad(WorldLoadEvent event) {
World world = event.getWorld();
- subLogger.info("World %s is loading", world.getName());
+ subLogger.info("World %s has been loaded", world.getName());
if (worldNames.contains(world.getName())) {
worlds.add(world);
diff --git a/src/main/java/pl/minecon724/realweather/weather/WeatherCommander.java b/src/main/java/pl/minecon724/realweather/weather/WeatherCommander.java
index f3a7e08..0004ff9 100644
--- a/src/main/java/pl/minecon724/realweather/weather/WeatherCommander.java
+++ b/src/main/java/pl/minecon724/realweather/weather/WeatherCommander.java
@@ -1,20 +1,20 @@
package pl.minecon724.realweather.weather;
-import java.io.IOException;
import java.util.List;
import org.bukkit.configuration.ConfigurationSection;
-import pl.minecon724.realweather.RW;
+import pl.minecon724.realweather.RealWeatherPlugin;
import pl.minecon724.realweather.SubLogger;
import pl.minecon724.realweather.map.Coordinates;
import pl.minecon724.realweather.map.WorldMap;
-import pl.minecon724.realweather.weather.exceptions.DisabledException;
+import pl.minecon724.realweather.weather.exceptions.ModuleDisabledException;
+import pl.minecon724.realweather.weather.exceptions.WeatherProviderException;
import pl.minecon724.realweather.weather.provider.Provider;
public class WeatherCommander {
private WorldMap worldMap = WorldMap.getInstance();
- private RW plugin;
+ private RealWeatherPlugin plugin;
private boolean enabled;
private List worldNames;
@@ -26,23 +26,23 @@ public class WeatherCommander {
private SubLogger subLogger = new SubLogger("weather");
- public WeatherCommander(RW plugin) {
+ public WeatherCommander(RealWeatherPlugin plugin) {
this.plugin = plugin;
}
/**
* Initialize weather commander
* @param config "weather" ConfigurationSection
- * @throws DisabledException if disabled in config
+ * @throws ModuleDisabledException if disabled in config
* @throws ProviderException if invalid provider config
*/
public void init(ConfigurationSection config)
- throws DisabledException, IllegalArgumentException {
+ throws ModuleDisabledException, IllegalArgumentException {
enabled = config.getBoolean("enabled");
if (!enabled)
- throw new DisabledException();
+ throw new ModuleDisabledException();
worldNames = config.getStringList("worlds");
@@ -59,15 +59,15 @@ public class WeatherCommander {
try {
provider.request_state(new Coordinates(0, 0));
- } catch (IOException e) {
- subLogger.info("Provider test failed, errors may occur", new Object[0]);
+ } catch (WeatherProviderException e) {
+ subLogger.severe("Provider test failed, errors may occur");
e.printStackTrace();
}
plugin.getServer().getPluginManager().registerEvents(
new WeatherChanger(worldNames), plugin);
- subLogger.info("done", new Object[0]);
+ subLogger.info("done");
}
public void start() {
diff --git a/src/main/java/pl/minecon724/realweather/weather/exceptions/DisabledException.java b/src/main/java/pl/minecon724/realweather/weather/exceptions/ModuleDisabledException.java
similarity index 52%
rename from src/main/java/pl/minecon724/realweather/weather/exceptions/DisabledException.java
rename to src/main/java/pl/minecon724/realweather/weather/exceptions/ModuleDisabledException.java
index dde43be..ae7e475 100644
--- a/src/main/java/pl/minecon724/realweather/weather/exceptions/DisabledException.java
+++ b/src/main/java/pl/minecon724/realweather/weather/exceptions/ModuleDisabledException.java
@@ -1,5 +1,5 @@
package pl.minecon724.realweather.weather.exceptions;
-public class DisabledException extends Exception {
+public class ModuleDisabledException extends Exception {
}
diff --git a/src/main/java/pl/minecon724/realweather/weather/exceptions/WeatherProviderException.java b/src/main/java/pl/minecon724/realweather/weather/exceptions/WeatherProviderException.java
new file mode 100644
index 0000000..9549eb6
--- /dev/null
+++ b/src/main/java/pl/minecon724/realweather/weather/exceptions/WeatherProviderException.java
@@ -0,0 +1,9 @@
+package pl.minecon724.realweather.weather.exceptions;
+
+public class WeatherProviderException extends Exception {
+
+ public WeatherProviderException(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/main/java/pl/minecon724/realweather/weather/provider/OpenWeatherMapProvider.java b/src/main/java/pl/minecon724/realweather/weather/provider/OpenWeatherMapProvider.java
index bcb3c1b..e476a77 100644
--- a/src/main/java/pl/minecon724/realweather/weather/provider/OpenWeatherMapProvider.java
+++ b/src/main/java/pl/minecon724/realweather/weather/provider/OpenWeatherMapProvider.java
@@ -1,18 +1,19 @@
package pl.minecon724.realweather.weather.provider;
import java.io.BufferedReader;
-import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
-import java.nio.charset.Charset;
-import org.json.JSONException;
-import org.json.JSONObject;
+import com.google.common.base.Charsets;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.gson.stream.JsonReader;
import pl.minecon724.realweather.map.Coordinates;
import pl.minecon724.realweather.weather.WeatherState.*;
+import pl.minecon724.realweather.weather.exceptions.WeatherProviderException;
public class OpenWeatherMapProvider implements Provider {
@@ -23,6 +24,11 @@ public class OpenWeatherMapProvider implements Provider {
public OpenWeatherMapProvider(String apiKey) {
this.apiKey = apiKey;
}
+
+ @Override
+ public String getName() {
+ return "OpenWeatherMap";
+ }
public void init() {
try {
@@ -32,34 +38,40 @@ public class OpenWeatherMapProvider implements Provider {
}
}
- public State request_state(Coordinates coordinates) throws IOException {
- JSONObject json = new JSONObject();
+ public State request_state(Coordinates coordinates) throws WeatherProviderException {
+ JsonObject jsonObject;
try {
URL url = new URL(
- endpoint + String.format("/data/2.5/weather?lat=%s&lon=%s&appid=%s",
- Double.toString(coordinates.latitude), Double.toString(coordinates.longitude), apiKey
+ String.format("%s/data/2.5/weather?lat=%f&lon=%f&appid=%s",
+ endpoint, coordinates.latitude, coordinates.longitude, apiKey
));
InputStream is = url.openStream();
- BufferedReader rd = new BufferedReader( new InputStreamReader(is, Charset.forName("UTF-8")) );
- StringBuilder sb = new StringBuilder();
- int c;
- while ((c = rd.read()) != -1) {
- sb.append((char) c);
- }
- is.close();
- json = new JSONObject(sb.toString());
+ BufferedReader rd = new BufferedReader(
+ new InputStreamReader(is, Charsets.UTF_8));
+
+ JsonReader jsonReader = new JsonReader(rd);
+ jsonObject = new Gson().fromJson(jsonReader, JsonObject.class);
} catch (Exception e) {
- throw new IOException("Couldn't contact openweathermap");
+ e.printStackTrace();
+ throw new WeatherProviderException("Couldn't contact openweathermap");
}
int stateId;
+
try {
- stateId = json.getJSONArray("weather")
- .getJSONObject(0).getInt("id");
- } catch (JSONException e) {
- throw new IOException("Invalid data from openweathermap");
+ stateId = jsonObject.getAsJsonArray("weather")
+ .get(0).getAsJsonObject()
+ .get("id").getAsInt();
+ /*
+ * org.json comparison:
+ * stateId = json.getJSONArray("weather").getJSONObject(0).getInt("id");
+ * so is it truly worth it? yes see loading jsonobject from inputstream
+ */
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new WeatherProviderException("Invalid data from openweathermap");
}
// Here comes the mess
@@ -67,60 +79,44 @@ public class OpenWeatherMapProvider implements Provider {
ConditionLevel level = ConditionLevel.LIGHT;
if (stateId < 300) {
condition = Condition.THUNDER;
- switch (stateId) {
- case 200:
- case 210:
- case 230:
+ switch (stateId % 10) {
+ case 0: // 200, 210, 230
level = ConditionLevel.LIGHT;
break;
- case 201:
- case 211:
- case 221:
- case 231:
+ case 1: // 201, 211, 221, 231
level = ConditionLevel.MODERATE;
break;
- case 202:
- case 212:
- case 232:
+ case 2: // 202, 212, 232
level = ConditionLevel.HEAVY;
}
} else if (stateId < 400) {
condition = Condition.DRIZZLE;
- switch (stateId) {
- case 300:
- case 310:
+ switch (stateId % 10) {
+ case 0: // 300, 310
level = ConditionLevel.LIGHT;
break;
- case 301:
- case 311:
- case 313:
- case 321:
+ case 1: // 301, 311, 321
+ case 3: // 313
level = ConditionLevel.MODERATE;
break;
- case 302:
- case 312:
- case 314:
+ case 2: // 302, 312
+ case 4: // 314
level = ConditionLevel.HEAVY;
}
} else if (stateId < 600) {
condition = Condition.RAIN;
- switch (stateId) {
- case 500:
- case 520:
+ switch (stateId % 10) {
+ case 0: // 500, 520
level = ConditionLevel.LIGHT;
break;
- case 501:
- case 511:
- case 521:
- case 531:
+ case 1: // 501, 511, 521, 531
level = ConditionLevel.MODERATE;
break;
- case 502:
- case 522:
+ case 2: // 502, 522
level = ConditionLevel.HEAVY;
break;
- case 503:
- case 504:
+ case 3: // 503
+ case 4: // 504
level = ConditionLevel.EXTREME;
}
} else if (stateId < 700) {
@@ -165,7 +161,16 @@ public class OpenWeatherMapProvider implements Provider {
}
@Override
- public String getName() {
- return "OpenWeatherMap";
+ public State[] request_state(Coordinates[] coordinates) throws WeatherProviderException {
+ // OpenWeatherMap doesnt support bulk requests
+
+ int length = coordinates.length;
+ State[] states = new State[length];
+
+ for (int i=0; i