diff --git a/TODO.md b/TODO.md index e8af59c..74ccc5e 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,14 @@ +Milestone: yesterday +- fix bugs + +Milestone: 0.5.1 - local maxmind -- account for real sun movement, not just time - readd metrics -- fix realtime - cache cleaning -- prevent packets + +Milestone: 0.6.0 +- account for real sun movement, not just time + +Milestone: future +- weather simulator (weather is clientside rn and doesnt have things such as lightning or other effects) - tests \ No newline at end of file diff --git a/src/main/java/pl/minecon724/realweather/ConfigUtils.java b/src/main/java/pl/minecon724/realweather/ConfigUtils.java deleted file mode 100644 index 44aea5d..0000000 --- a/src/main/java/pl/minecon724/realweather/ConfigUtils.java +++ /dev/null @@ -1,16 +0,0 @@ -package pl.minecon724.realweather; - -import net.md_5.bungee.api.ChatColor; - -public class ConfigUtils { - public static String parsePlaceholders(String key, String value, String[] data) { - if (key.equals("messages.actionbarInfo")) { - value = value.replaceAll("%weather_full%", data[0] + " " + data[1]).replaceAll("%weather%", data[1]); - } - return value; - } - - public static String color(String str) { - return ChatColor.translateAlternateColorCodes('&', str); - } -} \ No newline at end of file diff --git a/src/main/java/pl/minecon724/realweather/RW.java b/src/main/java/pl/minecon724/realweather/RW.java index a020b31..42e5b3d 100644 --- a/src/main/java/pl/minecon724/realweather/RW.java +++ b/src/main/java/pl/minecon724/realweather/RW.java @@ -22,6 +22,8 @@ public class RW extends JavaPlugin { saveDefaultConfig(); config = getConfig(); + SubLogger.init(getLogger()); + WorldMap.init( config.getConfigurationSection("map") ); diff --git a/src/main/java/pl/minecon724/realweather/SubLogger.java b/src/main/java/pl/minecon724/realweather/SubLogger.java new file mode 100644 index 0000000..8076e3f --- /dev/null +++ b/src/main/java/pl/minecon724/realweather/SubLogger.java @@ -0,0 +1,29 @@ +package pl.minecon724.realweather; + +import java.util.logging.Level; +import java.util.logging.Logger; + +public class SubLogger { + private static Logger logger; + private String name; + + static void init(Logger loger) { + logger = loger; + } + + public SubLogger(String name) { + this.name = name; + } + + public void log(Level level, String format, Object... args) { + 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)); + } + + public void info(String format, Object... args) { + this.log(Level.INFO, format, args); + } +} diff --git a/src/main/java/pl/minecon724/realweather/map/WorldMap.java b/src/main/java/pl/minecon724/realweather/map/WorldMap.java index d22b90d..fd4411d 100644 --- a/src/main/java/pl/minecon724/realweather/map/WorldMap.java +++ b/src/main/java/pl/minecon724/realweather/map/WorldMap.java @@ -30,7 +30,7 @@ public class WorldMap { Type type; try { - type = Type.valueOf(config.getString("type")); + type = Type.valueOf(config.getString("type").toUpperCase()); } catch (NullPointerException e) { throw new IllegalArgumentException("Invalid type"); } diff --git a/src/main/java/pl/minecon724/realweather/map/exceptions/GeoIPException.java b/src/main/java/pl/minecon724/realweather/map/exceptions/GeoIPException.java index 33ebb8d..736d751 100644 --- a/src/main/java/pl/minecon724/realweather/map/exceptions/GeoIPException.java +++ b/src/main/java/pl/minecon724/realweather/map/exceptions/GeoIPException.java @@ -1,9 +1,7 @@ package pl.minecon724.realweather.map.exceptions; public class GeoIPException extends Exception { - - public GeoIPException(String string) { - super(string); + public GeoIPException(String message) { + super(message); } - } diff --git a/src/main/java/pl/minecon724/realweather/realtime/PlayerTimeSyncTask.java b/src/main/java/pl/minecon724/realweather/realtime/PlayerTimeSyncTask.java index 394b234..77491ac 100644 --- a/src/main/java/pl/minecon724/realweather/realtime/PlayerTimeSyncTask.java +++ b/src/main/java/pl/minecon724/realweather/realtime/PlayerTimeSyncTask.java @@ -1,24 +1,26 @@ package pl.minecon724.realweather.realtime; import java.util.List; -import java.util.logging.Logger; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; +import pl.minecon724.realweather.SubLogger; import pl.minecon724.realweather.map.Coordinates; import pl.minecon724.realweather.map.WorldMap; import pl.minecon724.realweather.map.exceptions.GeoIPException; public class PlayerTimeSyncTask extends BukkitRunnable { private WorldMap worldMap = WorldMap.getInstance(); - private Logger logger = Logger.getLogger("timezone sync"); + private SubLogger subLogger = new SubLogger("playertime"); + private double scale; private List worlds; - public PlayerTimeSyncTask(List worlds) { + public PlayerTimeSyncTask(double scale, List worlds) { + this.scale = scale; this.worlds = worlds; } @@ -26,6 +28,7 @@ public class PlayerTimeSyncTask extends BukkitRunnable { public void run() { for (Player player : Bukkit.getOnlinePlayers()) { if (!worlds.contains(player.getWorld())) { + subLogger.info("resetting %s's time as they're in an excluded world", player.getName()); player.resetPlayerTime(); continue; } @@ -35,21 +38,37 @@ public class PlayerTimeSyncTask extends BukkitRunnable { try { coordinates = worldMap.getCoordinates(player); } catch (GeoIPException e) { - logger.warning( - String.format("Unable to determine GeoIP for %s (%s)", - player.getAddress().getHostString())); + subLogger.info("Unable to determine GeoIP for %s (%s)", + player.getAddress().getHostString(), e.getMessage()); continue; } - // longitude - // / 15 as 15 degrees is 1 hour - // * 60 to minutes - // * 60 again to seconds - // * 20 to ticks - long offset = (long) (coordinates.longitude / 15 * 72000); + /* + * reasoning here: + * earth is divided into timezones each covering 15 deg longitude + * so first we find how many hours to offset + * a day is 24h (no way) + * in minecraft its 24000 ticks + * so, 1h in ticks: 24000t / 24h = 1000t + * seconds in day: 24h * 3600s = 86400s + * seconds to ticks: 86400s * 20t = 1728000t + * day irl is 1728000t, in minecraft its 24000t + * for each minecraft tick, ticks irl: 1728000t / 24000t = 72t + * we divide offset by that to sync time + * then multiply by scale, thats obvious + */ - player.setPlayerTime(offset, true); + double offset = coordinates.longitude / 15; + offset *= 1000; + offset *= scale; + + // why no modulo? because we also modify day + + long time = (long) offset; + + player.setPlayerTime(time, true); + subLogger.info("%s's time is now off by %d ticks", player.getName(), time); } } diff --git a/src/main/java/pl/minecon724/realweather/realtime/RealTimeCommander.java b/src/main/java/pl/minecon724/realweather/realtime/RealTimeCommander.java index 608fb05..3cb4670 100644 --- a/src/main/java/pl/minecon724/realweather/realtime/RealTimeCommander.java +++ b/src/main/java/pl/minecon724/realweather/realtime/RealTimeCommander.java @@ -1,8 +1,12 @@ package pl.minecon724.realweather.realtime; import java.time.ZoneId; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import org.bukkit.GameRule; import org.bukkit.World; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.event.EventHandler; @@ -14,17 +18,18 @@ import pl.minecon724.realweather.RW; import pl.minecon724.realweather.weather.exceptions.DisabledException; public class RealTimeCommander implements Listener { - RW plugin; + private RW plugin; - List worldNames; - double scale; - ZoneId timezone; - boolean perPlayer; + private List worldNames; + private double scale; + private ZoneId timezone; + private boolean perPlayer; - volatile List worlds; + private volatile List worlds = new ArrayList<>(); + private Map savedGamerule = new HashMap<>(); - RealTimeTask task; - PlayerTimeSyncTask playerTimeSyncTask; + private RealTimeTask task; + private PlayerTimeSyncTask playerTimeSyncTask; public RealTimeCommander(RW plugin) { this.plugin = plugin; @@ -50,12 +55,16 @@ public class RealTimeCommander implements Listener { } public void start() { - task = new RealTimeTask(scale, timezone, worlds); + // to save processing, run only when necessary + long period = (long) Math.ceil(72 / scale); + period = Math.max(period, 1); - task.runTaskTimer(plugin, 0, 1); + task = new RealTimeTask(scale, timezone, worlds); + task.runTaskTimer(plugin, 0, period); if (perPlayer) { - playerTimeSyncTask = new PlayerTimeSyncTask(worlds); + playerTimeSyncTask = new PlayerTimeSyncTask(scale, worlds); + playerTimeSyncTask.runTaskTimerAsynchronously(plugin, 0, 40); } } @@ -63,8 +72,12 @@ public class RealTimeCommander implements Listener { public void onWorldLoad(WorldLoadEvent event) { World world = event.getWorld(); - if (worldNames.contains(world.getName())) + if (worldNames.contains(world.getName())) { worlds.add(world); + + savedGamerule.put(world, world.getGameRuleValue(GameRule.DO_DAYLIGHT_CYCLE)); + world.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false); + } } @EventHandler @@ -72,5 +85,8 @@ public class RealTimeCommander implements Listener { World world = event.getWorld(); worlds.remove(world); + if (savedGamerule.containsKey(world)) { + world.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, savedGamerule.remove(world)); + } } } diff --git a/src/main/java/pl/minecon724/realweather/realtime/RealTimeTask.java b/src/main/java/pl/minecon724/realweather/realtime/RealTimeTask.java index 363b46e..36adf1d 100644 --- a/src/main/java/pl/minecon724/realweather/realtime/RealTimeTask.java +++ b/src/main/java/pl/minecon724/realweather/realtime/RealTimeTask.java @@ -7,33 +7,37 @@ import java.util.List; import org.bukkit.World; import org.bukkit.scheduler.BukkitRunnable; +import pl.minecon724.realweather.SubLogger; + public class RealTimeTask extends BukkitRunnable { - double timeScale; + private SubLogger subLogger = new SubLogger("timer"); + double scale; ZoneId timezone; List worlds; - public RealTimeTask(double timeScale, ZoneId timezone, List worlds) { - this.timeScale = timeScale; + public RealTimeTask(double scale, ZoneId timezone, List worlds) { + this.scale = scale; this.timezone = timezone; this.worlds = worlds; } @Override public void run() { - long now = ZonedDateTime.now(timezone).toInstant().getEpochSecond(); - now *= timeScale; - now %= 86400; + double now = ZonedDateTime.now(timezone).toInstant().getEpochSecond(); + now /= 3600; // to hour - // day irl is 86400 secs - // in game, its 1200 secs - // to align, 86400 / 1200 = 72 - // then we convert to ticks by multiplying 20 (1s = 20t) - // we subtract 24000 - 18000 = 6000 because 18000 is midnight - double time = (now / 72.0) * 20 - 6000; - time %= 24000; + // explaination in PlayerTimeSyncTask line 47 + now *= 1000; // reallife s to mc ticks + now -= 6000; // 0t is actually 6:00 + + now *= scale; // scale + now %= 24000; + + long time = (long) now; for (World w : worlds) { - w.setFullTime((long)time); + w.setFullTime(time); + subLogger.info("Updated time for %s (to %d)", w.getName(), time); } } -} +} \ 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 index eeb8568..1cf3e3a 100644 --- a/src/main/java/pl/minecon724/realweather/weather/GetStateTask.java +++ b/src/main/java/pl/minecon724/realweather/weather/GetStateTask.java @@ -1,14 +1,16 @@ package pl.minecon724.realweather.weather; +import java.io.IOException; import java.util.HashMap; import java.util.Map; -import java.util.logging.Logger; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.plugin.PluginManager; import org.bukkit.scheduler.BukkitRunnable; +import pl.minecon724.realweather.RW; +import pl.minecon724.realweather.SubLogger; import pl.minecon724.realweather.map.Coordinates; import pl.minecon724.realweather.map.WorldMap; import pl.minecon724.realweather.map.WorldMap.Type; @@ -19,68 +21,79 @@ import pl.minecon724.realweather.weather.provider.Provider; public class GetStateTask extends BukkitRunnable { - Logger logger = Logger.getLogger("weather scheduler"); + private SubLogger subLogger = new SubLogger("weather updater"); - Provider provider; - WorldMap worldMap; + private RW plugin; + private Provider provider; + private WorldMap worldMap; - State storedState; - Map playerStoredState = new HashMap<>(); - PluginManager pluginManager = Bukkit.getPluginManager(); + private State storedState; + private Map playerStoredState = new HashMap<>(); + private PluginManager pluginManager = Bukkit.getPluginManager(); public GetStateTask( + RW plugin, Provider provider, WorldMap worldMap ) { + this.plugin = plugin; this.provider = provider; this.worldMap = worldMap; } - // That's a lot of variables + private void callEvent(Player player, State storedState, State state) { + new BukkitRunnable() { + + @Override + public void run() { + pluginManager.callEvent( + new WeatherSyncEvent(player, storedState, state) + ); + } + + }.runTask(plugin); + } @Override public void run() { - - if (worldMap.getType() == Type.POINT) { - Coordinates coordinates; - try { - coordinates = worldMap.getCoordinates(null); - } catch (GeoIPException e) { return; } - State state = provider.request_state(coordinates); - - if (!state.equals(storedState)) { - pluginManager.callEvent( - new WeatherSyncEvent(null, storedState, state) - ); - - storedState = state; - } - - } else { - for (Player player : Bukkit.getOnlinePlayers()) { + try { + if (worldMap.getType() == Type.POINT) { Coordinates coordinates; - try { - coordinates = worldMap.getCoordinates(player); - } catch (GeoIPException e) { - logger.info("GeoIP error for " + player.getName()); - e.printStackTrace(); - continue; - } - + coordinates = worldMap.getCoordinates(null); + } catch (GeoIPException e) { return; } State state = provider.request_state(coordinates); + + if (!state.equals(storedState)) { + callEvent(null, storedState, state); + storedState = state; + } - if (!state.equals(playerStoredState.get(player))) { - pluginManager.callEvent( - new WeatherSyncEvent( - player, - playerStoredState.get(player), - state) - ); + } else { + for (Player player : Bukkit.getOnlinePlayers()) { + Coordinates coordinates; - playerStoredState.put(player, state); + try { + coordinates = worldMap.getCoordinates(player); + } catch (GeoIPException e) { + subLogger.info("GeoIP error for %s", player.getName()); + e.printStackTrace(); + continue; + } + + State state = provider.request_state(coordinates); + + if (!state.equals(playerStoredState.get(player))) { + callEvent(player, + playerStoredState.get(player), + state); + + playerStoredState.put(player, state); + } } } + } catch (IOException e) { + subLogger.info("Error updating: %s", e.getMessage()); } } diff --git a/src/main/java/pl/minecon724/realweather/weather/WeatherChanger.java b/src/main/java/pl/minecon724/realweather/weather/WeatherChanger.java index 744239f..d0f1a37 100644 --- a/src/main/java/pl/minecon724/realweather/weather/WeatherChanger.java +++ b/src/main/java/pl/minecon724/realweather/weather/WeatherChanger.java @@ -3,7 +3,6 @@ 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; @@ -11,12 +10,14 @@ import org.bukkit.event.Listener; import org.bukkit.event.world.WorldLoadEvent; import org.bukkit.event.world.WorldUnloadEvent; +import pl.minecon724.realweather.SubLogger; import pl.minecon724.realweather.weather.WeatherState.State; import pl.minecon724.realweather.weather.events.WeatherSyncEvent; public class WeatherChanger implements Listener { private List worldNames; private List worlds = new ArrayList<>(); + private SubLogger subLogger = new SubLogger("weatherchanger"); public WeatherChanger(List worldNames) { this.worldNames = worldNames; @@ -25,9 +26,13 @@ public class WeatherChanger implements Listener { @EventHandler public void onWorldLoad(WorldLoadEvent event) { World world = event.getWorld(); + subLogger.info("World %s is loading", world.getName()); - if (worldNames.contains(world.getName())) + if (worldNames.contains(world.getName())) { worlds.add(world); + subLogger.info("World %s has been registered", world.getName()); + + } } @EventHandler @@ -35,6 +40,7 @@ public class WeatherChanger implements Listener { World world = event.getWorld(); worlds.remove(world); + subLogger.info("World %s unloaded", world.getName()); } @EventHandler @@ -42,12 +48,11 @@ public class WeatherChanger implements Listener { Player player = event.getPlayer(); State state = event.getState(); + if (player != null) { - player.sendMessage("local: " + state.getCondition().name() + " " - + state.getLevel().name() + " " + state.getSimple().name()); + subLogger.info("new weather for %s: %s %s", player.getName(), state.getCondition().name(), state.getLevel().name()); } else { - Bukkit.getServer().broadcastMessage("global: " + state.getCondition().name() + " " - + state.getLevel().name() + " " + state.getSimple().name()); + subLogger.info("new weather: %s %s", state.getCondition().name(), state.getLevel().name()); } } diff --git a/src/main/java/pl/minecon724/realweather/weather/WeatherCommander.java b/src/main/java/pl/minecon724/realweather/weather/WeatherCommander.java index 6d58e90..f3a7e08 100644 --- a/src/main/java/pl/minecon724/realweather/weather/WeatherCommander.java +++ b/src/main/java/pl/minecon724/realweather/weather/WeatherCommander.java @@ -1,25 +1,30 @@ 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.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.provider.Provider; public class WeatherCommander { private WorldMap worldMap = WorldMap.getInstance(); - RW plugin; + private RW plugin; - boolean enabled; - List worldNames; - String providerName; - Provider provider; - ConfigurationSection providerConfig; + private boolean enabled; + private List worldNames; + private String providerName; + private Provider provider; + private ConfigurationSection providerConfig; - GetStateTask getStateTask; + private GetStateTask getStateTask; + + private SubLogger subLogger = new SubLogger("weather"); public WeatherCommander(RW plugin) { this.plugin = plugin; @@ -52,13 +57,23 @@ public class WeatherCommander { throw new IllegalArgumentException("Invalid provider: " + providerName); provider.init(); + try { + provider.request_state(new Coordinates(0, 0)); + } catch (IOException e) { + subLogger.info("Provider test failed, errors may occur", new Object[0]); + e.printStackTrace(); + } + plugin.getServer().getPluginManager().registerEvents( new WeatherChanger(worldNames), plugin); + + subLogger.info("done", new Object[0]); } public void start() { - getStateTask = new GetStateTask(provider, worldMap); + getStateTask = new GetStateTask(plugin, provider, worldMap); getStateTask.runTaskTimerAsynchronously(plugin, 0, 1200); + subLogger.info("started", new Object[0]); } } 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 82b9d90..bcb3c1b 100644 --- a/src/main/java/pl/minecon724/realweather/weather/provider/OpenWeatherMapProvider.java +++ b/src/main/java/pl/minecon724/realweather/weather/provider/OpenWeatherMapProvider.java @@ -1,12 +1,14 @@ 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 pl.minecon724.realweather.map.Coordinates; @@ -30,7 +32,7 @@ public class OpenWeatherMapProvider implements Provider { } } - public State request_state(Coordinates coordinates) { + public State request_state(Coordinates coordinates) throws IOException { JSONObject json = new JSONObject(); try { @@ -48,10 +50,17 @@ public class OpenWeatherMapProvider implements Provider { } is.close(); json = new JSONObject(sb.toString()); - } catch (Exception e) { e.printStackTrace(); } + } catch (Exception e) { + throw new IOException("Couldn't contact openweathermap"); + } - int stateId = json.getJSONArray("weather") - .getJSONObject(0).getInt("id"); + int stateId; + try { + stateId = json.getJSONArray("weather") + .getJSONObject(0).getInt("id"); + } catch (JSONException e) { + throw new IOException("Invalid data from openweathermap"); + } // Here comes the mess Condition condition = Condition.CLEAR; @@ -150,6 +159,7 @@ public class OpenWeatherMapProvider implements Provider { level = ConditionLevel.EXTREME; } } + State state = new State(condition, level); return state; } diff --git a/src/main/java/pl/minecon724/realweather/weather/provider/Provider.java b/src/main/java/pl/minecon724/realweather/weather/provider/Provider.java index 91206e9..003cc7d 100644 --- a/src/main/java/pl/minecon724/realweather/weather/provider/Provider.java +++ b/src/main/java/pl/minecon724/realweather/weather/provider/Provider.java @@ -1,10 +1,12 @@ package pl.minecon724.realweather.weather.provider; +import java.io.IOException; + import pl.minecon724.realweather.map.Coordinates; import pl.minecon724.realweather.weather.WeatherState; public interface Provider { public void init(); - public WeatherState.State request_state(Coordinates coordinates); + public WeatherState.State request_state(Coordinates coordinates) throws IOException; public String getName(); } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 091921f..243ab27 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -53,12 +53,11 @@ time: # "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 + # basically how many Minecraft days during a real day scale: 1.0 # time based on... time? diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 1842148..d4d1727 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,6 +1,7 @@ name: RealWeather version: ${project.version} api-version: 1.16 +load: STARTUP author: Minecon724 main: pl.minecon724.realweather.RW libraries: