0.5 progress

(re)added some features, some refactoring
errors have been eliminated but still needs work
This commit is contained in:
Minecon724 2024-01-14 15:01:02 +00:00
parent e6f4fd527c
commit c34d635dd1
18 changed files with 359 additions and 154 deletions

View file

@ -3,3 +3,5 @@
- gh actions
- readd metrics
- fix realtime
- cache cleaning
- prevent packets

View file

@ -1,23 +1,14 @@
package pl.minecon724.realweather;
import java.time.ZoneId;
import java.util.List;
import com.maxmind.geoip2.WebServiceClient;
import org.bukkit.Bukkit;
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.RTTask;
import pl.minecon724.realweather.weather.GetStateTask;
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.ProviderException;
import pl.minecon724.realweather.weather.provider.OpenWeatherMapProvider;
import pl.minecon724.realweather.weather.provider.Provider;
public class RW extends JavaPlugin {
FileConfiguration config;
@ -40,22 +31,22 @@ public class RW extends JavaPlugin {
weatherCommander.init(
config.getConfigurationSection("weather")
);
weatherCommander.start();
} catch (DisabledException e) {
getLogger().info("Weather module disabled");
} // we leave ProviderException
} catch (IllegalArgumentException e) {
e.printStackTrace();
getServer().getPluginManager().disablePlugin(this);
}
if (realtimeSec.getBoolean("enabled")) {
ZoneId zone;
try {
zone = ZoneId.of(realtimeSec.getString("timezone"));
} catch (Exception e) {
zone = ZoneId.systemDefault();
}
new RTTask(
realtimeSec.getDouble("timeScale"),
zone,
realtimeSec.getStringList("worlds")
).runTaskTimer(this, 0, realtimeSec.getLong("interval"));
RealTimeCommander realTimeCommander = new RealTimeCommander(this);
try {
realTimeCommander.init(
config.getConfigurationSection("time")
);
realTimeCommander.start();
} catch (DisabledException e) {
getLogger().info("Time module disabled");
}
long end = System.currentTimeMillis();

View file

@ -0,0 +1,48 @@
package pl.minecon724.realweather.map;
import com.maxmind.geoip2.record.Location;
public class Coordinates {
public double latitude, longitude;
public Coordinates(double latitude, double longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
/**
* converts Coordinates to double array
* @return array in order of latitude, longitude
*/
public double[] toDoubleArray() {
return new double[] {
this.latitude,
this.longitude
};
}
public Coordinates clamp() {
this.latitude = Math.max(-90.0, Math.min(90.0, this.latitude));
this.longitude = Math.max(-180.0, Math.min(180.0, this.longitude));
return this;
}
public Coordinates wrap() {
this.latitude = wrapDouble(-90.0, 90.0, this.latitude);
this.longitude = wrapDouble(-180.0, 180.0, this.longitude);
return this;
}
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()
);
}
}

View file

@ -2,26 +2,29 @@ package pl.minecon724.realweather.map;
import java.io.IOException;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
import com.maxmind.geoip2.WebServiceClient;
import com.maxmind.geoip2.exception.GeoIp2Exception;
import com.maxmind.geoip2.record.Location;
import pl.minecon724.realweather.map.exceptions.GeoIPException;
public class GeoLocator {
private static GeoLocator INSTANCE = null;
private WebServiceClient client;
private Map<InetAddress, Coordinates> cache;
public static void init(int accountId, String apiKey) {
INSTANCE = new GeoLocator(
new WebServiceClient.Builder(accountId, apiKey)
.host("geolite.info").build());
}
public GeoLocator(WebServiceClient client) {
this.client = client;
}
public static GeoLocator fromConfig(int accountId, String apiKey) {
return new GeoLocator(
new WebServiceClient.Builder(accountId, apiKey)
.host("geolite.info").build()
);
this.cache = new HashMap<>();
}
/**
@ -31,19 +34,35 @@ public class GeoLocator {
* @throws GeoIp2Exception
* @throws IOException
*/
public double[] getLocation(InetAddress address)
public static Coordinates getCoordinates(InetAddress address)
throws GeoIPException {
Location location;
GeoLocator instance = INSTANCE;
Coordinates coordinates = null;
coordinates = instance.lookup(address);
if (coordinates != null)
return coordinates;
try {
location = client.city(address).getLocation();
coordinates = Coordinates.fromGeoIpLocation(
instance.client.city(address).getLocation()
);
} catch (IOException | GeoIp2Exception e) {
throw new GeoIPException(e.getMessage());
}
return new double[] {
location.getLatitude(),
location.getLongitude()
};
instance.store(address, 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);
}
}

View file

@ -3,35 +3,24 @@ package pl.minecon724.realweather.map;
import org.bukkit.Location;
public class Globe {
double scaleLatitide, scaleLongitude;
boolean wrap;
private static double[] SCALE;
private static boolean WRAP;
public Globe(double scaleLatitude,
double scaleLongitude,
boolean wrap) {
this.scaleLatitide = scaleLatitude;
this.scaleLongitude = scaleLongitude;
this.wrap = wrap;
public static void init(double scaleLatitude, double scaleLongitude, boolean wrap) {
SCALE = new double[] { scaleLatitude, scaleLongitude };
WRAP = wrap;
}
double wrapDouble(double min, double max, double val) {
return min + (val - min) % (max - min);
}
public static Coordinates toCoordiates(Location loc) {
Coordinates coordinates = new Coordinates(
-loc.getZ() * SCALE[0],
loc.getX() * SCALE[1]);
public double[] playerPosAsCoords(Location loc) {
double[] out = new double[] {
loc.getX() * scaleLongitude,
-loc.getZ() * scaleLatitide
};
if (WRAP)
coordinates.wrap();
else
coordinates.clamp();
if (wrap) {
out[0] = Math.max(-180.0, Math.min(180.0, out[0]));
out[1] = Math.max(-90.0, Math.min(90.0, out[1]));
} else {
out[0] = wrapDouble(-180.0, 180.0, out[0]);
out[1] = wrapDouble(-90.0, 90.0, out[1]);
}
return out;
return coordinates;
}
}

View file

@ -8,18 +8,12 @@ import pl.minecon724.realweather.map.exceptions.GeoIPException;
public class WorldMap {
private final Type type;
private double[] point;
private GeoLocator geoLocator;
private Globe globe;
private Coordinates point;
public WorldMap(Type type,
double[] point,
GeoLocator geoLocator,
Globe globe) {
Coordinates point) {
this.type = type;
this.point = point;
this.geoLocator = geoLocator;
this.globe = globe;
}
public static WorldMap fromConfig(ConfigurationSection config)
@ -33,43 +27,41 @@ public class WorldMap {
throw new IllegalArgumentException("Invalid type");
}
double[] point = null;
GeoLocator geoLocator = null;
Globe globe = null;
Coordinates point = null;
if (type == Type.POINT) {
point = new double[] {
point = new Coordinates(
config.getDouble("point.latitude"),
config.getDouble("point.longitude")
};
);
} else if (type == Type.PLAYER) {
geoLocator = GeoLocator.fromConfig(
GeoLocator.init(
config.getInt("player.geolite2_accountId"),
config.getString("player.geolite2_api_key")
);
} else if (type == Type.GLOBE) {
globe = new Globe(
Globe.init(
config.getDouble("globe.scale_latitude"),
config.getDouble("globe.scale_longitude"),
config.getBoolean("globe.wrap")
);
}
WorldMap worldMap = new WorldMap(type, point, geoLocator, globe);
WorldMap worldMap = new WorldMap(type, point);
return worldMap;
}
/**
* get player position as coords
* get coordinates of player
* @param player the player
* @return latitude, longitude
* @return Coordinates
* @throws GeoIPException
*/
public double[] getPosition(Player player) throws GeoIPException {
public Coordinates getCoordinates(Player player) throws GeoIPException {
switch (this.type) {
case POINT:
@ -78,10 +70,11 @@ public class WorldMap {
if (player.getAddress().getAddress().isAnyLocalAddress())
throw new GeoIPException(player.getName() + "'s IP is local, check your proxy settings");
return geoLocator.getLocation(
player.getAddress().getAddress());
return GeoLocator.getCoordinates(
player.getAddress().getAddress()
);
case GLOBE:
return globe.playerPosAsCoords(player.getLocation());
return Globe.toCoordiates(player.getLocation());
}
// this wont happen because we cover each type

View file

@ -0,0 +1,68 @@
package pl.minecon724.realweather.realtime;
import java.time.ZoneId;
import java.util.List;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.EventHandler;
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;
public class RealTimeCommander implements Listener {
RW plugin;
List<String> worldNames;
double scale;
ZoneId timezone;
RealTimeTask task;
public RealTimeCommander(RW plugin) {
this.plugin = plugin;
}
public void init(ConfigurationSection config)
throws DisabledException {
if (!config.getBoolean("enabled"))
throw new DisabledException();
try {
timezone = ZoneId.of(config.getString("timezone"));
} catch (Exception e) {
timezone = ZoneId.systemDefault();
}
worldNames = config.getStringList("worlds");
scale = config.getDouble("scale");
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
public void start() {
task = new RealTimeTask(scale, timezone);
task.runTaskTimer(plugin, 0, 1);
}
@EventHandler
public void onWorldLoad(WorldLoadEvent event) {
World world = event.getWorld();
if (worldNames.contains(world.getName()))
task.worlds.add(world);
}
@EventHandler
public void onWorldUnload(WorldUnloadEvent event) {
World world = event.getWorld();
task.worlds.remove(world);
}
}

View file

@ -5,24 +5,17 @@ import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.scheduler.BukkitRunnable;
public class RTTask extends BukkitRunnable {
public class RealTimeTask extends BukkitRunnable {
double timeScale;
ZoneId timezone;
List<World> worlds;
List<World> worlds = new ArrayList<>();
public RTTask(double timeScale, ZoneId timezone, List<String> worlds) {
public RealTimeTask(double timeScale, ZoneId timezone) {
this.timeScale = timeScale;
this.timezone = timezone;
this.worlds = new ArrayList<World>();
for (String s : worlds) {
World world = Bukkit.getWorld(s);
if (world == null) continue;
this.worlds.add(world);
}
}
@Override

View file

@ -1,19 +1,20 @@
package pl.minecon724.realweather.weather;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
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.plugin.PluginManager;
import org.bukkit.scheduler.BukkitRunnable;
import pl.minecon724.realweather.map.Coordinates;
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.events.WeatherSyncEvent;
import pl.minecon724.realweather.weather.provider.Provider;
public class GetStateTask extends BukkitRunnable {
@ -22,16 +23,17 @@ public class GetStateTask extends BukkitRunnable {
Provider provider;
WorldMap worldMap;
List<World> worlds;
State storedState;
Map<Player, State> playerStoredState = new HashMap<>();
PluginManager pluginManager = Bukkit.getPluginManager();
public GetStateTask(
Provider provider,
WorldMap worldMap,
List<World> worlds
WorldMap worldMap
) {
this.provider = provider;
this.worldMap = worldMap;
this.worlds = worlds;
}
// That's a lot of variables
@ -40,32 +42,44 @@ public class GetStateTask extends BukkitRunnable {
public void run() {
if (worldMap.getType() == Type.POINT) {
double[] position;
Coordinates coordinates;
try {
position = worldMap.getPosition(null);
coordinates = worldMap.getCoordinates(null);
} catch (GeoIPException e) { return; }
State state = provider.request_state(position);
State state = provider.request_state(coordinates);
for (World world : worlds) {
if (world == null) return;
world.setThundering(state.getSimple() == ConditionSimple.THUNDER ? true : false);
world.setStorm(state.getSimple() == ConditionSimple.CLEAR ? false : true);
if (!state.equals(storedState)) {
pluginManager.callEvent(
new WeatherSyncEvent(null, storedState, state)
);
storedState = state;
}
} else {
for (Player player : Bukkit.getOnlinePlayers()) {
double[] position;
Coordinates coordinates;
try {
position = worldMap.getPosition(player);
coordinates = worldMap.getCoordinates(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);
State state = provider.request_state(coordinates);
if (!state.equals(playerStoredState.get(player))) {
pluginManager.callEvent(
new WeatherSyncEvent(
player,
playerStoredState.get(player),
state)
);
playerStoredState.put(player, state);
}
}
}

View file

@ -2,7 +2,6 @@ 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;
@ -15,8 +14,7 @@ public class Providers {
* @throws ProviderException
* @see Provider
*/
public static Provider getByName(String name, ConfigurationSection config)
throws ProviderException {
public static Provider getByName(String name, ConfigurationSection config) {
switch (name) {
case "openweathermap":
@ -26,8 +24,7 @@ public class Providers {
return null;
}
public static OpenWeatherMapProvider openWeatherMap(ConfigurationSection config)
throws ProviderException {
public static OpenWeatherMapProvider openWeatherMap(ConfigurationSection config) {
return new OpenWeatherMapProvider(
config.getString("apiKey"));

View file

@ -0,0 +1,55 @@
package pl.minecon724.realweather.weather;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.event.world.WorldUnloadEvent;
import pl.minecon724.realweather.weather.WeatherState.State;
import pl.minecon724.realweather.weather.events.WeatherSyncEvent;
public class WeatherChanger implements Listener {
private List<String> worldNames;
private List<World> worlds = new ArrayList<>();
public WeatherChanger(List<String> worldNames) {
this.worldNames = worldNames;
}
@EventHandler
public void onWorldLoad(WorldLoadEvent event) {
World world = event.getWorld();
if (worldNames.contains(world.getName()))
worlds.add(world);
}
@EventHandler
public void onWorldUnload(WorldUnloadEvent event) {
World world = event.getWorld();
worlds.remove(world);
}
@EventHandler
public void onWeatherSync(WeatherSyncEvent event) {
Player player = event.getPlayer();
State state = event.getState();
if (player != null) {
player.sendMessage("local: " + state.getCondition().name() + " "
+ state.getLevel().name() + " " + state.getSimple().name());
} else {
Bukkit.getServer().broadcastMessage("global: " + state.getCondition().name() + " "
+ state.getLevel().name() + " " + state.getSimple().name());
}
}
}

View file

@ -1,15 +1,12 @@
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 {
@ -17,7 +14,7 @@ public class WeatherCommander {
RW plugin;
boolean enabled;
List<String> worlds;
List<String> worldNames;
String providerName;
Provider provider;
ConfigurationSection providerConfig;
@ -36,13 +33,13 @@ public class WeatherCommander {
* @throws ProviderException if invalid provider config
*/
public void init(ConfigurationSection config)
throws DisabledException, ProviderException {
throws DisabledException, IllegalArgumentException {
enabled = config.getBoolean("enabled");
if (!enabled)
throw new DisabledException();
worlds = config.getStringList("worlds");
worldNames = config.getStringList("worlds");
providerName = config.getString("provider.choice");
@ -51,13 +48,16 @@ public class WeatherCommander {
.getConfigurationSection(providerName);
provider = Providers.getByName(providerName, providerConfig);
if (provider == null)
throw new IllegalArgumentException("Invalid provider: " + providerName);
provider.init();
plugin.getServer().getPluginManager().registerEvents(
new WeatherChanger(worldNames), plugin);
}
public void start() {
getStateTask = new GetStateTask(provider,
worldMap,
new ArrayList<World>());
getStateTask = new GetStateTask(provider, worldMap);
getStateTask.runTaskTimerAsynchronously(plugin, 0, 1200);
}

View file

@ -59,5 +59,11 @@ public class WeatherState {
public ConditionSimple getSimple() {
return simple;
}
public boolean equals(State state) {
return (state != null
&& this.getCondition() == state.getCondition()
&& this.getLevel() == state.getLevel());
}
}
}

View file

@ -0,0 +1,39 @@
package pl.minecon724.realweather.weather.events;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import pl.minecon724.realweather.weather.WeatherState.State;
public class WeatherSyncEvent extends PlayerEvent {
private static final HandlerList HANDLERS = new HandlerList();
private State oldState;
private State state;
public WeatherSyncEvent(Player player, State oldState, State state) {
super(player);
this.oldState = oldState;
this.state = state;
}
@Override
public HandlerList getHandlers() {
return HANDLERS;
}
public static HandlerList getHandlerList() {
return HANDLERS;
}
public State getOldState() {
return oldState;
}
public State getState() {
return state;
}
}

View file

@ -1,5 +0,0 @@
package pl.minecon724.realweather.weather.exceptions;
public class ProviderException extends Exception {
}

View file

@ -9,6 +9,7 @@ import java.nio.charset.Charset;
import org.json.JSONObject;
import pl.minecon724.realweather.map.Coordinates;
import pl.minecon724.realweather.weather.WeatherState.*;
public class OpenWeatherMapProvider implements Provider {
@ -29,13 +30,13 @@ public class OpenWeatherMapProvider implements Provider {
}
}
public State request_state(double[] coords) {
public State request_state(Coordinates coordinates) {
JSONObject json = new JSONObject();
try {
URL url = new URL(
endpoint + String.format("/data/2.5/weather?lat=%s&lon=%s&appid=%s",
Double.toString(coords[0]), Double.toString(coords[1]), apiKey
Double.toString(coordinates.latitude), Double.toString(coordinates.longitude), apiKey
));
InputStream is = url.openStream();

View file

@ -1,9 +1,10 @@
package pl.minecon724.realweather.weather.provider;
import pl.minecon724.realweather.map.Coordinates;
import pl.minecon724.realweather.weather.WeatherState;
public interface Provider {
public void init();
public WeatherState.State request_state(double[] coords);
public WeatherState.State request_state(Coordinates coordinates);
public String getName();
}

View file

@ -44,7 +44,7 @@ map:
# for example; if a player's position on map converts to 94 degrees (out of bounds), it becomes -86 degrees
wrap: true
realtime:
time:
# warning: this removes sleep
enabled: false
@ -59,15 +59,9 @@ realtime:
timezone: 'auto'
# x day cycles / 24 hrs
timeScale: 1.0
scale: 1.0
# How often should we recalculate the time (in ticks)
# Very minimal impact on performance
interval: 1
# makes time visible to players not in sync with server
virtual: false
# if above true, locks the server's time (in ticks)
# -1 to disable
lock: 0
# TODO
# time based on... time?
# uses settings from map
real: false