local geoip, remove maxmind dependency
This commit is contained in:
parent
3abb437c5b
commit
31afa3b10f
20 changed files with 437 additions and 164 deletions
6
TODO.md
6
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)
|
||||
|
|
14
pom.xml
14
pom.xml
|
@ -2,7 +2,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>pl.minecon724</groupId>
|
||||
<artifactId>realweather</artifactId>
|
||||
<version>0.5.0.1</version>
|
||||
<version>0.5.1-DEV</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
|
@ -24,15 +24,9 @@
|
|||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20231013</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.maxmind.geoip2</groupId>
|
||||
<artifactId>geoip2</artifactId>
|
||||
<version>4.2.0</version>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.10.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
|
|
@ -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 ) ) );
|
||||
}
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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<Integer, Coordinates> 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);
|
||||
}
|
||||
}
|
32
src/main/java/pl/minecon724/realweather/geoip/IPUtils.java
Normal file
32
src/main/java/pl/minecon724/realweather/geoip/IPUtils.java
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<InetAddress, Coordinates> cache;
|
||||
private SubLogger subLogger = new SubLogger("geolocator");
|
||||
private GeoIPDatabase database;
|
||||
private HashMap<InetAddress, Coordinates> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<String> 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"));
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<String> 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() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
package pl.minecon724.realweather.weather.exceptions;
|
||||
|
||||
public class DisabledException extends Exception {
|
||||
public class ModuleDisabledException extends Exception {
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package pl.minecon724.realweather.weather.exceptions;
|
||||
|
||||
public class WeatherProviderException extends Exception {
|
||||
|
||||
public WeatherProviderException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
|
@ -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<length; i++) {
|
||||
states[i] = request_state(coordinates[i]);
|
||||
}
|
||||
|
||||
return states;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package pl.minecon724.realweather.weather.provider;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import pl.minecon724.realweather.map.Coordinates;
|
||||
import pl.minecon724.realweather.weather.WeatherState;
|
||||
import pl.minecon724.realweather.weather.WeatherState.State;
|
||||
import pl.minecon724.realweather.weather.exceptions.WeatherProviderException;
|
||||
|
||||
public interface Provider {
|
||||
public void init();
|
||||
public WeatherState.State request_state(Coordinates coordinates) throws IOException;
|
||||
public String getName();
|
||||
|
||||
public State request_state(Coordinates coordinates) throws WeatherProviderException;
|
||||
public State[] request_state(Coordinates[] coordinates) throws WeatherProviderException;
|
||||
}
|
||||
|
|
|
@ -25,9 +25,7 @@ map:
|
|||
longitude: -89.485937
|
||||
|
||||
player:
|
||||
# Get your own @ https://www.maxmind.com/en/geolite2/signup
|
||||
geolite2_accountId: 710438
|
||||
geolite2_api_key: 'qLeseHp4QNQcqRGn'
|
||||
empty: for now
|
||||
|
||||
globe:
|
||||
# Valid latitude range: -90 to 90
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
name: RealWeather
|
||||
author: Minecon724
|
||||
|
||||
version: ${project.version}
|
||||
api-version: 1.16
|
||||
|
||||
load: STARTUP
|
||||
author: Minecon724
|
||||
main: pl.minecon724.realweather.RW
|
||||
main: pl.minecon724.realweather.RealWeatherPlugin
|
||||
|
||||
libraries:
|
||||
- org.json:json:20231013
|
||||
- com.maxmind.geoip2:geoip2:4.2.0
|
||||
- com.google.code.gson:gson:2.10.1
|
Loading…
Reference in a new issue