diff --git a/.idea/copyright/gpl3.xml b/.idea/copyright/gpl3.xml
new file mode 100644
index 0000000..429950e
--- /dev/null
+++ b/.idea/copyright/gpl3.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..19771df
--- /dev/null
+++ b/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DOMAINS-FIREWALL.md b/DOMAINS-FIREWALL.md
index 4cd8908..b99b2aa 100644
--- a/DOMAINS-FIREWALL.md
+++ b/DOMAINS-FIREWALL.md
@@ -2,7 +2,7 @@ If you're using a firewall, you must allow the following hosts:
- updater:
* git.m724.eu
- weather:
- * api.openweathermap.org
+ * api.open-meteo.com
- thunder:
* ws1.blitzortung.org
* ws7.blitzortung.org
diff --git a/notes.txt b/notes.txt
deleted file mode 100644
index 5f0f512..0000000
--- a/notes.txt
+++ /dev/null
@@ -1,73 +0,0 @@
-goal: realtime to in game time conversion
-There is no need to keep days
-
-minecraft day is 0 - 24000 ticks
-where 6000 ticks is noon (peak sun) and 18000 is midnight (peak moon)
-
-irl day is 0 - 86400 seconds
-
-0. s = epoch % 86400 to get seconds since midnight
- ^
- (* scale) here
-1. t = s / 72.0 to fit into minecraft day
-2. t = t * 20 to convert that to ticks
-3. t = t - 6000 to align noon and midnight
- this leaves us with negative time, so
-4. t = floorMod(t, 24000) to wrap if negative
-
-example:
-epoch = 1713593340
-
-0. getting seconds since midnight
- s = epoch % 86400
- s = 1713593340 % 86400
- s = 22140
-
-1. conversion to minecraft day length
- gs = s / 72.0
- gs = 22140 / 72.0
- gs = 307.5
-
-2. to ticks
- t = gs * 20
- t = 307.5 * 20
- t = 6150
-
-3. step 3
- t = t - 6000
- t = 6150 - 6000
- t = 150
-
-4. wrapping
- t = floorMod(150, 24000)
- t = 150
-
-
-goal: frequency of time update
-
-t = 72 / scale
-t is the period, in ticks of course
-
-to see how many irl seconds a tick represents:
-s = 3.6 * scale
-(from 1 / (1/72 * 20 * scale))
-
-however, some scales result in fractions
-here's how many in game aligned seconds have passed at the end of a real day:
- 84000 / 72 * scale * floor(72/scale)
-for scale 0.99: 83160
-so we'll be 14 minutes behind
-
-solution? for now let's warn and update time every tick
-to check:
-scale * floor(72/scale) == 72
-
-
-goal: offsetting by player position
-
-t = (longitude / 15) * 1000 * scale
-
-accounting for sunrise and sunset
-TODO, idk yet without
-update: this is now possible with 0.8.0 api
-
diff --git a/pom.xml b/pom.xml
index 71b767c..c0308a4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,7 +46,7 @@
eu.m724
wtapi
- 0.9.2
+ 0.9.3
eu.m724
diff --git a/src/main/java/eu/m724/realweather/Configs.java b/src/main/java/eu/m724/realweather/Configs.java
index 18ebb52..51e9bd1 100644
--- a/src/main/java/eu/m724/realweather/Configs.java
+++ b/src/main/java/eu/m724/realweather/Configs.java
@@ -1,22 +1,19 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather;
-import eu.m724.realweather.mapper.MapperConfig;
+import eu.m724.realweather.map.MapConfig;
import eu.m724.realweather.thunder.ThunderConfig;
import eu.m724.realweather.time.TimeConfig;
-import eu.m724.realweather.updater.UpdaterConfig;
-import eu.m724.realweather.weather.WeatherConfig;
+import eu.m724.realweather.weather.WeatherChanger;
-// TODO replaces GlobalConstants for configs
-public class Configs {
- static WeatherConfig weatherConfig;
- static TimeConfig timeConfig;
- static ThunderConfig thunderConfig;
- static MapperConfig mapperConfig;
- static UpdaterConfig updaterConfig;
-
- public static WeatherConfig weatherConfig() { return weatherConfig; }
- public static TimeConfig timeConfig() { return timeConfig; }
- public static ThunderConfig thunderConfig() { return thunderConfig; }
- public static MapperConfig mapperConfig() { return mapperConfig; }
- public static UpdaterConfig updaterConfig() { return updaterConfig; }
+public record Configs(
+ WeatherChanger weatherConfig,
+ TimeConfig timeConfig,
+ ThunderConfig thunderConfig,
+ MapConfig mapConfig
+) {
}
diff --git a/src/main/java/eu/m724/realweather/DebugLogger.java b/src/main/java/eu/m724/realweather/DebugLogger.java
index 6850958..030bbc0 100644
--- a/src/main/java/eu/m724/realweather/DebugLogger.java
+++ b/src/main/java/eu/m724/realweather/DebugLogger.java
@@ -1,18 +1,78 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather;
+import java.util.logging.Level;
import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
public class DebugLogger {
- static Logger baseLogger;
- static int debugLevel;
-
- public static int getDebugLevel() {
- return debugLevel;
+ private DebugLogger() {}
+ static Logger logger;
+
+ public static void info(String message, Object... format) {
+ log(Level.INFO, message, format);
}
-
-
- public static void info(String message, int minDebugLevel, Object... format) {
- if (debugLevel >= minDebugLevel)
- baseLogger.info(message.formatted(format));
+
+ public static void warning(String message, Object... format) {
+ log(Level.WARNING, message, format);
}
-}
+
+ public static void severe(String message, Object... format) {
+ log(Level.SEVERE, message, format);
+ }
+
+ public static void fine(String message, Object... format) {
+ log(Level.FINE, message, format);
+ }
+
+ public static void finer(String message, Object... format) {
+ log(Level.FINER, message, format);
+ }
+
+ private static void log(Level level, String message, Object... format) {
+
+ if (!logger.isLoggable(level)) {
+ return;
+ }
+
+ message = message.formatted(format);
+
+ if (logger.getLevel().intValue() <= Level.FINE.intValue()) {
+ message = "[" + getCaller() + "] " + message;
+ }
+
+ if (level.intValue() < Level.INFO.intValue()) { // levels below info are never logged even if set for some reason
+ // colors text gray (cyan is close to gray)
+ if (level == Level.FINE) {
+ message = "\033[38;5;250m" + message + "\033[39m";
+ } else {
+ message = "\033[38;5;245m" + message + "\033[39m";
+ }
+ level = Level.INFO;
+ }
+
+ logger.log(level, message);
+
+ }
+
+ private static String getCaller() {
+ String caller = Thread.currentThread().getStackTrace()[4].getClassName();
+
+ if (caller.startsWith("eu.m724.realweather.")) {
+ caller = caller.substring("eu.m724.realweather.".length());
+
+ String[] packages = caller.split("\\.");
+
+ caller = IntStream.range(0, packages.length - 1)
+ .mapToObj(i -> packages[i].substring(0, 2))
+ .collect(Collectors.joining(".")) + "." + packages[packages.length - 1];
+ }
+
+ return caller;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/eu/m724/realweather/GlobalConstants.java b/src/main/java/eu/m724/realweather/GlobalConstants.java
deleted file mode 100644
index e6c1d0b..0000000
--- a/src/main/java/eu/m724/realweather/GlobalConstants.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package eu.m724.realweather;
-
-import org.bukkit.plugin.Plugin;
-
-import eu.m724.realweather.mapper.Mapper;
-import eu.m724.realweather.weather.PlayerWeatherCache;
-
-// perhaps replace with a singleton
-// TODO actually, remove it altogether
-public class GlobalConstants {
- static Mapper mapper;
- static Plugin plugin;
- static PlayerWeatherCache playerWeatherCache;
-
- public static Mapper getMapper() {
- return mapper;
- }
- public static Plugin getPlugin() {
- return plugin;
- }
- public static PlayerWeatherCache getPlayerWeatherCache() {
- return playerWeatherCache;
- }
-
-}
diff --git a/src/main/java/eu/m724/realweather/RealWeatherPlugin.java b/src/main/java/eu/m724/realweather/RealWeatherPlugin.java
index 692a5c5..fd63d9c 100644
--- a/src/main/java/eu/m724/realweather/RealWeatherPlugin.java
+++ b/src/main/java/eu/m724/realweather/RealWeatherPlugin.java
@@ -1,3 +1,8 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather;
import java.io.File;
@@ -5,139 +10,213 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
-import java.util.logging.Logger;
+import java.util.logging.Level;
import eu.m724.mstats.MStatsPlugin;
-import eu.m724.wtapi.provider.exception.NoSuchProviderException;
+import eu.m724.realweather.map.WorldList;
+import eu.m724.realweather.updater.UpdateNotifier;
import org.bukkit.configuration.file.YamlConfiguration;
import eu.m724.realweather.commands.AdminCommand;
-import eu.m724.realweather.commands.GeoCommand;
-import eu.m724.realweather.commands.LocalTimeCommand;
-import eu.m724.realweather.commands.LocalWeatherCommand;
-import eu.m724.realweather.mapper.Mapper;
-import eu.m724.realweather.mapper.MapperConfig;
+import eu.m724.realweather.map.command.GeoCommand;
+import eu.m724.realweather.time.command.LocalTimeCommand;
+import eu.m724.realweather.weather.command.LocalWeatherCommand;
+import eu.m724.realweather.map.CoordinatesLocationConverter;
+import eu.m724.realweather.map.MapConfig;
import eu.m724.realweather.thunder.ThunderConfig;
import eu.m724.realweather.thunder.ThunderMaster;
import eu.m724.realweather.time.TimeConfig;
import eu.m724.realweather.time.TimeMaster;
import eu.m724.realweather.updater.PluginUpdater;
-import eu.m724.realweather.updater.UpdaterConfig;
-import eu.m724.realweather.weather.PlayerWeatherCache;
import eu.m724.realweather.weather.WeatherConfig;
import eu.m724.realweather.weather.WeatherMaster;
-import eu.m724.wtapi.provider.exception.ProviderException;
// TODO unmess this too
public class RealWeatherPlugin extends MStatsPlugin {
+ private Configs configs;
+ private WorldList worldList;
+
private WeatherMaster weatherMaster;
- private ThunderMaster thunderMaster;
private TimeMaster timeMaster;
- private PluginUpdater updater;
-
- private Logger logger;
+ private ThunderMaster thunderMaster;
+
+ private static RealWeatherPlugin INSTANCE;
+
+ private CoordinatesLocationConverter coordinatesLocationConverter;
@Override
public void onEnable() {
- logger = getLogger();
+ long start = System.nanoTime();
+ INSTANCE = this;
File dataFolder = getDataFolder();
File modulesFolder = new File("modules");
modulesFolder.mkdir();
-
- YamlConfiguration configuration,
- mapConfiguration, weatherConfiguration,
- thunderConfiguration, timeConfiguration;
-
- DebugLogger.info("loading configurations", 1);
+
boolean firstRun = !new File(dataFolder, "config.yml").exists();
+ YamlConfiguration configuration;
+
try {
configuration = getConfig("config.yml");
- mapConfiguration = getConfig("map.yml");
- weatherConfiguration = getConfig("modules/weather.yml");
- thunderConfiguration = getConfig("modules/thunder.yml");
- timeConfiguration = getConfig("modules/time.yml");
} catch (IOException e) {
- logger.severe("Failed to load config!");
- throw new RuntimeException(e);
+ DebugLogger.severe("Failed to load configuration:");
+ DebugLogger.severe(" " + e);
+
+ getServer().getPluginManager().disablePlugin(this);
+ return;
}
+ DebugLogger.logger = getLogger();
+
+ if (configuration.getBoolean("debug")) {
+ getLogger().setLevel(Level.FINEST);
+ DebugLogger.warning("Debug harms performance");
+ }
+
+
if (firstRun) {
- logger.warning("This is your first time running this plugin.");
- logger.warning("Please *shut down* the server, review the config files (enable modules, enter your API keys, etc.)");
- logger.warning("Don't forget to enable the plugin in config.yml");
+ DebugLogger.warning("This is your first time running this plugin.");
+ DebugLogger.warning("Please *shut down* the server, review the config files (enable modules, enter API keys, etc.)");
+ DebugLogger.warning("Don't forget to enable the plugin in config.yml");
getServer().getPluginManager().disablePlugin(this);
return;
}
-
- DebugLogger.baseLogger = logger;
- DebugLogger.debugLevel = configuration.getInt("debug");
-
- if (!configuration.getBoolean("enabled")) {
- logger.warning("Plugin disabled by administrator. Enable it in config.yml");
+
+ if (configuration.getBoolean("disabled")) {
+ DebugLogger.warning("Plugin disabled per config. Enable it in config.yml");
+
getServer().getPluginManager().disablePlugin(this);
return;
}
-
- GlobalConstants.plugin = this;
- GlobalConstants.playerWeatherCache = new PlayerWeatherCache();
-
- DebugLogger.info("loading mapper", 1);
- Configs.mapperConfig = MapperConfig.fromConfiguration(mapConfiguration);
- GlobalConstants.mapper = new Mapper();
- GlobalConstants.mapper.registerEvents(this);
try {
- DebugLogger.info("loading weather", 1);
- Configs.weatherConfig = WeatherConfig.fromConfiguration(weatherConfiguration);
- weatherMaster = new WeatherMaster();
- weatherMaster.init(this);
-
- DebugLogger.info("loading thunder", 1);
- Configs.thunderConfig = ThunderConfig.fromConfiguration(thunderConfiguration);
- thunderMaster = new ThunderMaster();
- thunderMaster.init(this);
-
- DebugLogger.info("loading time", 1);
- Configs.timeConfig = TimeConfig.fromConfiguration(timeConfiguration);
- timeMaster = new TimeMaster();
- timeMaster.init();
+ loadMapModules();
+ } catch (Exception e) {
+ DebugLogger.severe("Failed to load the Map module:");
+ DebugLogger.severe(" " + e);
- Configs.updaterConfig = UpdaterConfig.fromConfiguration(configuration.getConfigurationSection("updater"));
- updater = PluginUpdater.build(this, this.getFile());
- //updater.init();
- } catch (NoSuchProviderException e) {
- logger.severe("There's an error in your config:");
- logger.severe(" " + e.getMessage());
-
- getServer().getPluginManager().disablePlugin(this);
- return;
- } catch (ProviderException e) {
- logger.severe("Couldn't initialize provider!");
- logger.severe("Possible causes:");
- logger.severe("1. Your API key is invalid, or was added too recently");
- logger.severe("2. The provider or your internet is down");
- e.printStackTrace();
-
getServer().getPluginManager().disablePlugin(this);
return;
}
- getCommand("rwadmin").setExecutor(new AdminCommand(updater, thunderMaster));
- getCommand("geo").setExecutor(new GeoCommand());
+ try {
+ loadWeatherModule();
+ } catch (Exception e) {
+ DebugLogger.severe("Failed to load the Weather module:");
+ DebugLogger.severe(" " + e);
- if (Configs.timeConfig.enabled())
- getCommand("localtime").setExecutor(new LocalTimeCommand(timeMaster.getTimeConverter()));
-
- if (Configs.weatherConfig.enabled()) {
- getCommand("localweather").setExecutor(new LocalWeatherCommand());
+ getServer().getPluginManager().disablePlugin(this);
+ return;
}
+ try {
+ loadThunderModule();
+ } catch (Exception e) {
+ DebugLogger.severe("Failed to load the Thunder module:");
+ DebugLogger.severe(" " + e);
+
+ getServer().getPluginManager().disablePlugin(this);
+ return;
+ }
+
+ try {
+ loadTimeModule();
+ } catch (Exception e) {
+ DebugLogger.severe("Failed to load the Time module:");
+ DebugLogger.severe(" " + e);
+
+ getServer().getPluginManager().disablePlugin(this);
+ return;
+ }
+
+ DebugLogger.fine("Loading Updater");
+
+ PluginUpdater updater = PluginUpdater.build(this.getFile(), configuration.getString("updater.channel"));
+ if (configuration.getBoolean("updater.notify")) {
+ UpdateNotifier updateNotifier = new UpdateNotifier(updater);
+ updateNotifier.register();
+ }
+
+ DebugLogger.fine("Done loading Updater");
+
+ getCommand("rwadmin").setExecutor(new AdminCommand(this, updater));
+ getCommand("geo").setExecutor(new GeoCommand(coordinatesLocationConverter));
+
mStats(2);
- DebugLogger.info("ended loading", 1);
+ DebugLogger.fine("Plugin enabled. Took %.3f milliseconds", (System.nanoTime() - start) / 1000000.0);
+ }
+
+ // TODO repeating here!
+
+ private void loadMapModules() throws IOException {
+ DebugLogger.fine("Loading the Map modules");
+
+ YamlConfiguration yamlConfiguration = getConfig("map.yml");
+ MapConfig mapConfig = MapConfig.fromConfiguration(yamlConfiguration);
+ this.coordinatesLocationConverter = new CoordinatesLocationConverter(mapConfig);
+
+ this.worldList = new WorldList(mapConfig.worldNames(), mapConfig.worldNamesIsBlacklist());
+ getServer().getPluginManager().registerEvents(worldList, this);
+
+ DebugLogger.finer("Done loading Map modules");
+ }
+
+ private void loadWeatherModule() throws IOException {
+ DebugLogger.fine("Loading the Weather module");
+
+ YamlConfiguration yamlConfiguration = getConfig("modules/weather.yml");
+ WeatherConfig weatherConfig = WeatherConfig.fromConfiguration(yamlConfiguration);
+
+ if (!weatherConfig.enabled()) {
+ DebugLogger.finer("Weather module disabled per config");
+ return;
+ }
+
+ this.weatherMaster = new WeatherMaster(this, weatherConfig);
+ weatherMaster.init();
+
+ getCommand("localweather").setExecutor(new LocalWeatherCommand(weatherMaster.getPlayerWeatherStore()));
+
+ DebugLogger.finer("Enabled Weather module");
+ }
+
+ private void loadThunderModule() throws IOException {
+ DebugLogger.fine("Loading the Thunder module");
+
+ YamlConfiguration yamlConfiguration = getConfig("modules/thunder.yml");
+ ThunderConfig thunderConfig = ThunderConfig.fromConfiguration(yamlConfiguration);
+
+ if (!thunderConfig.enabled()) {
+ DebugLogger.finer("Thunder module disabled per config");
+ return;
+ }
+
+ this.thunderMaster = new ThunderMaster(this, thunderConfig);
+ thunderMaster.init();
+
+ DebugLogger.finer("Enabled Thunder module");
+ }
+
+ private void loadTimeModule() throws IOException {
+ DebugLogger.fine("Loading the Time module");
+
+ YamlConfiguration yamlConfiguration = getConfig("modules/time.yml");
+ TimeConfig timeConfig = TimeConfig.fromConfiguration(yamlConfiguration);
+
+ if (!timeConfig.enabled()) {
+ DebugLogger.finer("Time module disabled per config");
+ return;
+ }
+
+ this.timeMaster = new TimeMaster(this, timeConfig);
+ timeMaster.init();
+
+ getCommand("localtime").setExecutor(new LocalTimeCommand(timeMaster, coordinatesLocationConverter));
+
+ DebugLogger.finer("Enabled Time module");
}
public YamlConfiguration getConfig(String configFilePath) throws IOException {
@@ -145,15 +224,50 @@ public class RealWeatherPlugin extends MStatsPlugin {
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
if (!configFile.exists()) {
- final InputStream defConfigStream = getResource(configFilePath);
-
- if (defConfigStream == null)
- return null;
-
- config = YamlConfiguration.loadConfiguration(new InputStreamReader(defConfigStream, StandardCharsets.UTF_8));
+ try (InputStream defConfigStream = getResource(configFilePath)) {
+ if (defConfigStream == null)
+ return null;
+
+ config = YamlConfiguration.loadConfiguration(new InputStreamReader(defConfigStream, StandardCharsets.UTF_8));
+ }
+
config.save(configFile);
}
return config;
}
+
+ /**
+ * Gets the instance of RealWeather plugin.
+ *
+ * @return The instance of RealWeather plugin.
+ */
+ public static RealWeatherPlugin getInstance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Gets the coordinates to location converter.
+ *
+ * @return The coordinates to location converter.
+ */
+ public CoordinatesLocationConverter getCoordinatesLocationConverter() {
+ return coordinatesLocationConverter;
+ }
+
+ public WeatherMaster getWeatherMaster() {
+ return weatherMaster;
+ }
+
+ public TimeMaster getTimeMaster() {
+ return timeMaster;
+ }
+
+ public ThunderMaster getThunderMaster() {
+ return thunderMaster;
+ }
+
+ public WorldList getWorldList() {
+ return worldList;
+ }
}
diff --git a/src/main/java/eu/m724/realweather/api/weather/AsyncGlobalWeatherUpdateEvent.java b/src/main/java/eu/m724/realweather/api/weather/AsyncGlobalWeatherUpdateEvent.java
index acb07b9..04d2b56 100644
--- a/src/main/java/eu/m724/realweather/api/weather/AsyncGlobalWeatherUpdateEvent.java
+++ b/src/main/java/eu/m724/realweather/api/weather/AsyncGlobalWeatherUpdateEvent.java
@@ -1,3 +1,8 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather.api.weather;
import eu.m724.wtapi.object.Weather;
diff --git a/src/main/java/eu/m724/realweather/api/weather/AsyncPlayerWeatherUpdateEvent.java b/src/main/java/eu/m724/realweather/api/weather/AsyncPlayerWeatherUpdateEvent.java
index bb4a27a..4a1c445 100644
--- a/src/main/java/eu/m724/realweather/api/weather/AsyncPlayerWeatherUpdateEvent.java
+++ b/src/main/java/eu/m724/realweather/api/weather/AsyncPlayerWeatherUpdateEvent.java
@@ -1,3 +1,8 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather.api.weather;
import eu.m724.wtapi.object.Weather;
diff --git a/src/main/java/eu/m724/realweather/api/weather/AsyncWeatherUpdateEvent.java b/src/main/java/eu/m724/realweather/api/weather/AsyncWeatherUpdateEvent.java
index f93a967..31c038b 100644
--- a/src/main/java/eu/m724/realweather/api/weather/AsyncWeatherUpdateEvent.java
+++ b/src/main/java/eu/m724/realweather/api/weather/AsyncWeatherUpdateEvent.java
@@ -1,3 +1,8 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather.api.weather;
import eu.m724.wtapi.object.Weather;
diff --git a/src/main/java/eu/m724/realweather/commands/AdminCommand.java b/src/main/java/eu/m724/realweather/commands/AdminCommand.java
index 712dda9..362aa02 100644
--- a/src/main/java/eu/m724/realweather/commands/AdminCommand.java
+++ b/src/main/java/eu/m724/realweather/commands/AdminCommand.java
@@ -1,67 +1,80 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather.commands;
-import eu.m724.realweather.Configs;
+import eu.m724.realweather.RealWeatherPlugin;
+import eu.m724.realweather.map.CoordinatesLocationConverter;
+import eu.m724.realweather.time.TimeMaster;
+import eu.m724.realweather.updater.command.UpdateCommand;
+import eu.m724.realweather.weather.WeatherMaster;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
-import org.bukkit.plugin.Plugin;
-import eu.m724.realweather.GlobalConstants;
-import eu.m724.realweather.mapper.MapperConfig;
-import eu.m724.realweather.thunder.ThunderConfig;
import eu.m724.realweather.thunder.ThunderMaster;
-import eu.m724.realweather.time.TimeConfig;
import eu.m724.realweather.updater.PluginUpdater;
-import eu.m724.realweather.weather.WeatherConfig;
import net.md_5.bungee.api.ChatColor;
-// TODO unmess this all
public class AdminCommand implements CommandExecutor {
+ private final RealWeatherPlugin plugin;
private final UpdateCommand updateCommand;
- private final Plugin plugin = GlobalConstants.getPlugin();
-
- private final WeatherConfig weatherConfig = Configs.weatherConfig();
- private final TimeConfig timeConfig = Configs.timeConfig();
- private final ThunderConfig thunderConfig = Configs.thunderConfig();
- private final MapperConfig mapperConfig = Configs.mapperConfig();
-
+
+ private final WeatherMaster weatherMaster;
+ private final TimeMaster timeMaster;
private final ThunderMaster thunderMaster;
+ private final CoordinatesLocationConverter coordinatesLocationConverter;
- public AdminCommand(PluginUpdater updater, ThunderMaster thunderMaster) {
+ public AdminCommand(RealWeatherPlugin plugin, PluginUpdater updater) {
+ this.plugin = plugin;
this.updateCommand = new UpdateCommand(updater);
- this.thunderMaster = thunderMaster;
+ this.weatherMaster = plugin.getWeatherMaster();
+ this.timeMaster = plugin.getTimeMaster();
+ this.thunderMaster = plugin.getThunderMaster();
+ this.coordinatesLocationConverter = plugin.getCoordinatesLocationConverter();
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length > 0 && args[0].equals("update")) {
- return updateCommand.onCommand(sender, command, label, args);
+ updateCommand.updateCommand(sender, args);
+ return true;
}
colorize(sender, "\n&eRealWeather %s\n\n", plugin.getDescription().getVersion().replace("-SNAPSHOT", "&c-SNAPSHOT"));
- colorize(sender, "&6Coordinate scale: &b%d, %d &7blocks / deg", mapperConfig.scaleLatitude, mapperConfig.scaleLongitude);
+ colorize(sender, "&6Coordinate scale: &b%f, %f &7blocks / deg", coordinatesLocationConverter.getScaleLatitude(), coordinatesLocationConverter.getScaleLongitude());
+ colorize(sender, "&6Static point: &b%f, %f &7lat, lon", coordinatesLocationConverter.getStaticPoint().latitude(), coordinatesLocationConverter.getStaticPoint().longitude());
+ sender.sendMessage("");
- colorize(sender, "\n&6Weather: %s", weatherConfig.enabled() ? (weatherConfig.dynamic() ? "&aYes, dynamic" : "&aYes, static") : "&cDisabled");
-
- if (weatherConfig.enabled()) {
- colorize(sender, " &6Provider: &b%s", weatherConfig.provider());
+ if (weatherMaster != null) {
+ colorize(sender, "&6Weather: %s", weatherMaster.isDynamic() ? "&aYes, dynamic" : "&aYes, static");
+ colorize(sender, " &6Provider: &b%s", weatherMaster.getProviderName());
colorize(sender, " &6/localweather to see current weather");
+ } else {
+ colorize(sender, "&6Weather: %s", "&cDisabled");
}
- colorize(sender, "\n&6Time: %s", timeConfig.enabled() ? (timeConfig.dynamic() ? "&aYes, dynamic" : "&aYes, static") : "&cDisabled");
-
- if (timeConfig.enabled()) {
- colorize(sender, " &6Scale: &b%s&7x", timeConfig.scale());
+ sender.sendMessage("");
+
+ if (timeMaster != null) {
+ colorize(sender, "&6Time: %s", timeMaster.isDynamic() ? "&aYes, dynamic" : "&aYes, static");
+ colorize(sender, " &6Scale: &b%s&7x", timeMaster.getTimeConverter().getScale());
colorize(sender, " &6/localtime to see current time");
+ } else {
+ colorize(sender, "&6Time: &cDisabled");
}
- colorize(sender, "\n&6Thunder: %s", thunderConfig.enabled() ? "&aYes, dynamic" : "&cDisabled");
-
- if (thunderConfig.enabled()) {
- colorize(sender, " &6Provider: &b%s", thunderConfig.provider());
- colorize(sender, " &6Refresh period: &b%d &7ticks", thunderConfig.refreshPeriod());
+ sender.sendMessage("");
+
+ if (thunderMaster != null) {
+ colorize(sender, "&6Thunder: &aYes, dynamic");
+ colorize(sender, " &6Provider: &b%s", thunderMaster.getProviderName());
colorize(sender, " &6API latency: &b%d&7ms", thunderMaster.getLatency());
+ } else {
+ colorize(sender, "&6Thunder: &cDisabled");
}
return true;
diff --git a/src/main/java/eu/m724/realweather/commands/UpdateCommand.java b/src/main/java/eu/m724/realweather/commands/UpdateCommand.java
deleted file mode 100644
index e8d6ed8..0000000
--- a/src/main/java/eu/m724/realweather/commands/UpdateCommand.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package eu.m724.realweather.commands;
-
-import java.nio.file.NoSuchFileException;
-import java.time.Instant;
-import java.time.format.DateTimeFormatter;
-import java.util.concurrent.CompletableFuture;
-
-import org.bukkit.command.Command;
-import org.bukkit.command.CommandSender;
-
-import eu.m724.jarupdater.updater.Updater;
-import eu.m724.jarupdater.object.Version;
-
-/**
- * not actually a command but deserves a separate file
- */
-public class UpdateCommand {
- private final Updater updater;
-
- public UpdateCommand(Updater updater) {
- this.updater = updater;
- }
-
- public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
- if (!sender.hasPermission("realweather.admin.update")) return false;
-
- sender.sendMessage("Please wait");
- CompletableFuture latestFuture = updater.getLatestVersion();
-
- if (args.length == 0) {
- latestFuture.thenAccept(metadata -> {
- if (metadata != null) {
- sender.sendMessage("An update is available!");
- sender.sendMessage("RealWeather %s released %s".formatted(metadata.getLabel(), formatDate(metadata.getTimestamp())));
- sender.sendMessage("To download: /rwadmin update download");
- } else {
- sender.sendMessage("No new updates"); // TODO color
- }
- });
- } else {
- String action = args[1]; // remember this function is proxied
-
- if (action.equals("download")) {
- sender.sendMessage("Started download");
-
- updater.downloadLatestVersion().handle((file, ex) -> {
- sender.sendMessage("Download failed. See console for details.");
- ex.printStackTrace();
- return null;
- }).thenAccept(file -> {
- if (file != null)
- sender.sendMessage("Download finished, install with /rwadmin update install");
- });
-
- } else if (action.equals("install")) {
- try {
- updater.installLatestVersion().handle((v, ex) -> {
- sender.sendMessage("Install failed. See console for details.");
- ex.printStackTrace();
- return null;
- }).thenAccept(v -> {
- sender.sendMessage("Installation completed, restart server to apply");
- });
- } catch (NoSuchFileException e) {
- sender.sendMessage("Download the update first");
- }
- } else return false;
- }
-
- return true;
- }
-
- private String formatDate(long timestamp) { // TODO move this
- return DateTimeFormatter.ofPattern("dd.MM.yyyy").format(Instant.ofEpochSecond(timestamp));
- }
-
-}
diff --git a/src/main/java/eu/m724/realweather/map/CoordinatesLocationConverter.java b/src/main/java/eu/m724/realweather/map/CoordinatesLocationConverter.java
new file mode 100644
index 0000000..595b1fd
--- /dev/null
+++ b/src/main/java/eu/m724/realweather/map/CoordinatesLocationConverter.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
+package eu.m724.realweather.map;
+
+import org.bukkit.Location;
+import org.bukkit.World;
+
+import eu.m724.wtapi.object.Coordinates;
+
+public class CoordinatesLocationConverter {
+ private final double scaleLatitude;
+ private final double scaleLongitude;
+
+ private final Coordinates staticPoint;
+
+ public CoordinatesLocationConverter(MapConfig config) {
+ this.scaleLatitude = config.scaleLatitude();
+ this.scaleLongitude = config.scaleLongitude();
+ this.staticPoint = config.point();
+ }
+
+ /**
+ * Converts a {@link Location} to {@link Coordinates}
+ * The result is scaled and wrapped
+ *
+ * @param location the location to convert
+ * @return the coordinates
+ */
+ public Coordinates locationToCoordinates(Location location) {
+ // it's <-90, 90> (inclusive), but there's no point to make it that way here, because it's easier, and nice precision is enough
+ double latitude = (-location.getZ() / scaleLatitude) % 90;
+ // here it's <-180, 180) so it's correct, and we don't need excuses
+ double longitude = (location.getX() / scaleLongitude) % 180;
+
+ return new Coordinates(latitude, longitude);
+ }
+
+ /**
+ * Converts {@link Coordinates} to a {@link Location}
+ * The result is scaled, but not wrapped to world border or anything
+ *
+ * @param world the world of the location
+ * @param coordinates the coordinates to convert
+ * @return the location in {@code world}
+ */
+ public Location coordinatesToLocation(World world, Coordinates coordinates) {
+ double x = coordinates.longitude() * scaleLongitude;
+ double z = -coordinates.latitude() * scaleLatitude;
+
+ return new Location(world, x, 0, z);
+ }
+
+ public Coordinates getStaticPoint() {
+ return staticPoint;
+ }
+
+ public double getScaleLatitude() {
+ return scaleLatitude;
+ }
+
+ public double getScaleLongitude() {
+ return scaleLongitude;
+ }
+}
diff --git a/src/main/java/eu/m724/realweather/map/MapConfig.java b/src/main/java/eu/m724/realweather/map/MapConfig.java
new file mode 100644
index 0000000..b203e97
--- /dev/null
+++ b/src/main/java/eu/m724/realweather/map/MapConfig.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
+package eu.m724.realweather.map;
+
+import java.util.List;
+
+import org.bukkit.configuration.ConfigurationSection;
+
+import eu.m724.wtapi.object.Coordinates;
+
+public record MapConfig(
+ List worldNames,
+ boolean worldNamesIsBlacklist,
+
+ double scaleLatitude,
+ double scaleLongitude,
+
+ Coordinates point
+) {
+ public static MapConfig fromConfiguration(ConfigurationSection configuration) {
+ List worldNames = configuration.getStringList("worlds");
+ boolean worldBlacklist = configuration.getBoolean("isBlacklist");
+
+ double scaleLatitude = configuration.getDouble("dimensions.latitude");
+ double scaleLongitude = configuration.getDouble("dimensions.longitude");
+
+ Coordinates point = new Coordinates(
+ configuration.getDouble("point.latitude"),
+ configuration.getDouble("point.longitude")
+ );
+
+
+ return new MapConfig(worldNames, worldBlacklist, scaleLatitude, scaleLongitude, point);
+ }
+}
diff --git a/src/main/java/eu/m724/realweather/map/WorldList.java b/src/main/java/eu/m724/realweather/map/WorldList.java
new file mode 100644
index 0000000..bc1983b
--- /dev/null
+++ b/src/main/java/eu/m724/realweather/map/WorldList.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
+package eu.m724.realweather.map;
+
+import org.bukkit.World;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.world.WorldLoadEvent;
+import org.bukkit.event.world.WorldUnloadEvent;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class WorldList implements Listener {
+ private final List worldNames;
+ private final boolean isBlacklist;
+
+ private final List worlds = new ArrayList<>();
+
+ public WorldList(List worldNames, boolean isBlacklist) {
+ this.worldNames = worldNames;
+ this.isBlacklist = isBlacklist;
+ }
+
+ @EventHandler
+ void onWorldLoad(WorldLoadEvent e) {
+ if (worldNames.contains(e.getWorld().getName()) ^ isBlacklist) {
+ worlds.add(e.getWorld());
+ }
+ }
+
+ @EventHandler
+ void onWorldUnload(WorldUnloadEvent e) {
+ worlds.remove(e.getWorld());
+ }
+
+ public List getIncludedWorlds() {
+ return Collections.unmodifiableList(this.worlds);
+ }
+
+ public boolean isWorldIncluded(World world) {
+ return worlds.contains(world);
+ }
+}
diff --git a/src/main/java/eu/m724/realweather/commands/GeoCommand.java b/src/main/java/eu/m724/realweather/map/command/GeoCommand.java
similarity index 69%
rename from src/main/java/eu/m724/realweather/commands/GeoCommand.java
rename to src/main/java/eu/m724/realweather/map/command/GeoCommand.java
index 82fe36c..a45bf75 100644
--- a/src/main/java/eu/m724/realweather/commands/GeoCommand.java
+++ b/src/main/java/eu/m724/realweather/map/command/GeoCommand.java
@@ -1,4 +1,9 @@
-package eu.m724.realweather.commands;
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
+package eu.m724.realweather.map.command;
import org.bukkit.Location;
import org.bukkit.command.Command;
@@ -6,31 +11,29 @@ import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
-import eu.m724.realweather.GlobalConstants;
-import eu.m724.realweather.mapper.Mapper;
+import eu.m724.realweather.map.CoordinatesLocationConverter;
import eu.m724.wtapi.object.Coordinates;
import net.md_5.bungee.api.ChatColor;
public class GeoCommand implements CommandExecutor {
- //private final PlayerWeatherCache playerWeatherCache = GlobalConstants.getPlayerWeatherCache();
- private final Mapper mapper = GlobalConstants.getMapper();
+ private final CoordinatesLocationConverter coordinatesLocationConverter;
- @Override
+ public GeoCommand(CoordinatesLocationConverter coordinatesLocationConverter) {
+ this.coordinatesLocationConverter = coordinatesLocationConverter;
+ }
+
+ @Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
Player player = sender instanceof Player ? (Player) sender : null;
if (args.length == 0) {
if (player != null) {
Location location = player.getLocation();
- Coordinates coordinates = mapper.locationToCoordinates(location);
-
- //Weather weather = playerWeatherCache.getWeather(player);
- //String address = formatAddress(weather);
+ Coordinates coordinates = coordinatesLocationConverter.locationToCoordinates(location);
colorize(player, "");
colorize(player, "&6Geolocation: &b%f&7, &b%f &7(lat, lon)", coordinates.latitude(), coordinates.longitude());
colorize(player, "&7In-game Position: &3%f&8, &3%f &8(x, z)", location.getX(), location.getZ());
- //colorize(player, "&7City: &3%s", address);
colorize(player, "");
} else {
sender.sendMessage("You can't run this command without arguments as console");
@@ -49,12 +52,12 @@ public class GeoCommand implements CommandExecutor {
}
Location location = new Location(null, x, 0, z);
- Coordinates coordinates = mapper.locationToCoordinates(location);
+ Coordinates coordinates = coordinatesLocationConverter.locationToCoordinates(location);
colorize(sender, "");
- colorize(sender, "&6In-game Position: &b%f&7, &b%f &7(x, z)", location.getX(), location.getZ());
- colorize(sender, "&7Geolocation: &3%f&8, &3%f &8(lat, lon)", coordinates.latitude(), coordinates.longitude());
- colorize(sender, "&7Input interpreted as position, because you separated it with a space");
+ colorize(sender, "&6Geolocation: &b%f&8, &b%f &7(lat, lon)", coordinates.latitude(), coordinates.longitude());
+ colorize(sender, "&7In-game Position: &3%f&7, &3%f &8(x, z)", location.getX(), location.getZ());
+ colorize(sender, "&7Input interpreted as position, space used as separator");
colorize(sender, "");
} else {
double latitude, longitude;
@@ -69,12 +72,12 @@ public class GeoCommand implements CommandExecutor {
}
Coordinates coordinates = new Coordinates(latitude, longitude);
- Location location = mapper.coordinatesToLocation(null, coordinates);
+ Location location = coordinatesLocationConverter.coordinatesToLocation(null, coordinates);
colorize(sender, "");
colorize(sender, "&6In-game Position: &b%f&7, &b%f &7(x, z)", location.getX(), location.getZ());
colorize(sender, "&7Geolocation: &3%f&8, &3%f &8(lat, lon)", coordinates.latitude(), coordinates.longitude());
- colorize(sender, "&7Input interpreted as geolocation, because you separated with a comma");
+ colorize(sender, "&7Input interpreted as geolocation, comma used as separator");
colorize(sender, "");
}
diff --git a/src/main/java/eu/m724/realweather/mapper/Mapper.java b/src/main/java/eu/m724/realweather/mapper/Mapper.java
deleted file mode 100644
index 01c8fbe..0000000
--- a/src/main/java/eu/m724/realweather/mapper/Mapper.java
+++ /dev/null
@@ -1,103 +0,0 @@
-package eu.m724.realweather.mapper;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Consumer;
-
-import eu.m724.realweather.Configs;
-import org.bukkit.Location;
-import org.bukkit.World;
-import org.bukkit.plugin.Plugin;
-
-import eu.m724.wtapi.object.Coordinates;
-
-public class Mapper {
- private final MapperConfig config = Configs.mapperConfig();
- private final List worlds = new ArrayList<>();
-
- private final List> worldLoadConsumers = new ArrayList<>();
- private final List> worldUnloadConsumers = new ArrayList<>();
- // TODO game rules, I think I meant handling by this class
-
- /**
- * Registers a consumer which will be called on world load
- * @param consumer the consumer which will be called on world load
- */
- public void registerWorldLoadConsumer(Consumer consumer) {
- this.worldLoadConsumers.add(consumer);
- }
-
- /**
- * Registers a consumer which will be called on world unload
- * @param consumer the consumer which will be called on world unload
- */
- public void registerWorldUnloadConsumer(Consumer consumer) {
- this.worldUnloadConsumers.add(consumer);
- }
-
- /**
- * Registers events handled by mapper.
- * This should be called once on plugin load.
- * @param plugin the plugin to register events under
- */
- public void registerEvents(Plugin plugin) {
- plugin.getServer().getPluginManager().registerEvents(new MapperEventHandler(this), plugin);
- }
-
- /**
- * Converts a {@link Location} to {@link Coordinates}
- * The result is scaled and wrapped
- *
- * @param location the location to convert
- * @return the coordinates
- */
- public Coordinates locationToCoordinates(Location location) {
- // it's <-90, 90> (inclusive), but there's no point to make it that way here, because it's easier, and nice precision is enough
- double latitude = (-location.getZ() / config.scaleLatitude) % 90;
- // here it's <-180, 180) so it's correct, and we don't need excuses
- double longitude = (location.getX() / config.scaleLongitude) % 180;
-
- return new Coordinates(latitude, longitude);
- }
-
- /**
- * Converts {@link Coordinates} to a {@link Location}
- * The result is scaled, but not wrapped to world border or anything
- *
- * @param world the world of the location
- * @param coordinates the coordinates to convert
- * @return the location in {@code world}
- */
- public Location coordinatesToLocation(World world, Coordinates coordinates) {
- double x = coordinates.longitude() * config.scaleLongitude;
- double z = -coordinates.latitude() * config.scaleLatitude;
-
- return new Location(world, x, 0, z);
- }
-
- public Coordinates getPoint() {
- return config.point;
- }
-
- public List getWorlds() {
- return this.worlds;
- }
-
- boolean loadWorld(World world) {
- boolean loaded = config.worlds.contains(world.getName()) ^ config.worldBlacklist;
-
- if (loaded) {
- worlds.add(world);
- worldLoadConsumers.forEach(consumer -> consumer.accept(world));
- }
-
- return loaded;
- }
-
- void unloadWorld(World world) {
- if (worlds.remove(world)) {
- worldUnloadConsumers.forEach(consumer -> consumer.accept(world));
- }
- }
-
-}
diff --git a/src/main/java/eu/m724/realweather/mapper/MapperConfig.java b/src/main/java/eu/m724/realweather/mapper/MapperConfig.java
deleted file mode 100644
index ae0ea6f..0000000
--- a/src/main/java/eu/m724/realweather/mapper/MapperConfig.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package eu.m724.realweather.mapper;
-
-import java.util.List;
-
-import org.bukkit.configuration.ConfigurationSection;
-
-import eu.m724.wtapi.object.Coordinates;
-
-public class MapperConfig {
- public boolean worldBlacklist;
- public List worlds;
-
- public double scaleLatitude;
- public double scaleLongitude;
-
- public Coordinates point;
-
- public static MapperConfig fromConfiguration(ConfigurationSection configuration) {
- MapperConfig mapperConfig = new MapperConfig();
-
- mapperConfig.worldBlacklist = configuration.getBoolean("worldBlacklist");
- mapperConfig.worlds = configuration.getStringList("worlds");
-
- mapperConfig.scaleLatitude = configuration.getDouble("dimensions.latitude");
- mapperConfig.scaleLongitude = configuration.getDouble("dimensions.longitude");
-
- mapperConfig.point = new Coordinates(
- configuration.getDouble("point.latitude"),
- configuration.getDouble("point.longitude")
- );
-
- return mapperConfig;
- }
-}
diff --git a/src/main/java/eu/m724/realweather/mapper/MapperEventHandler.java b/src/main/java/eu/m724/realweather/mapper/MapperEventHandler.java
deleted file mode 100644
index e2c1892..0000000
--- a/src/main/java/eu/m724/realweather/mapper/MapperEventHandler.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package eu.m724.realweather.mapper;
-
-import org.bukkit.event.EventHandler;
-import org.bukkit.event.Listener;
-import org.bukkit.event.world.WorldLoadEvent;
-import org.bukkit.event.world.WorldUnloadEvent;
-
-public class MapperEventHandler implements Listener {
- private final Mapper mapper;
-
- public MapperEventHandler(Mapper mapper) {
- this.mapper = mapper;
- }
-
- @EventHandler
- public void onWorldLoad(WorldLoadEvent e) {
- mapper.loadWorld(e.getWorld());
- }
-
- @EventHandler
- public void onWorldUnload(WorldUnloadEvent e) {
- mapper.unloadWorld(e.getWorld());
- }
-}
diff --git a/src/main/java/eu/m724/realweather/thunder/AsyncLightningStrikeEvent.java b/src/main/java/eu/m724/realweather/thunder/AsyncLightningStrikeEvent.java
new file mode 100644
index 0000000..4d06151
--- /dev/null
+++ b/src/main/java/eu/m724/realweather/thunder/AsyncLightningStrikeEvent.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
+package eu.m724.realweather.thunder;
+
+import eu.m724.wtapi.provider.thunder.TimedStrike;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+
+/**
+ * Called on a lightning strike.
+ */
+public class AsyncLightningStrikeEvent extends Event implements Cancellable {
+ private static final HandlerList HANDLERS = new HandlerList();
+
+ private final TimedStrike timedStrike;
+
+ private boolean cancelled;
+
+ public AsyncLightningStrikeEvent(TimedStrike timedStrike) {
+ super(true);
+ this.timedStrike = timedStrike;
+ }
+
+ public TimedStrike getTimedStrike() {
+ return timedStrike;
+ }
+
+ public static HandlerList getHandlerList() {
+ return HANDLERS;
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return HANDLERS;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ @Override
+ public void setCancelled(boolean cancelled) {
+ this.cancelled = cancelled;
+ }
+}
diff --git a/src/main/java/eu/m724/realweather/thunder/LightningListener.java b/src/main/java/eu/m724/realweather/thunder/LightningListener.java
new file mode 100644
index 0000000..1773146
--- /dev/null
+++ b/src/main/java/eu/m724/realweather/thunder/LightningListener.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
+package eu.m724.realweather.thunder;
+
+import eu.m724.realweather.DebugLogger;
+import eu.m724.realweather.map.CoordinatesLocationConverter;
+import eu.m724.realweather.map.WorldList;
+import eu.m724.wtapi.provider.thunder.TimedStrike;
+import org.bukkit.Location;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+
+public class LightningListener implements Listener {
+ private final CoordinatesLocationConverter coordinatesLocationConverter;
+ private final WorldList worldList;
+
+ public LightningListener(CoordinatesLocationConverter coordinatesLocationConverter, WorldList worldList) {
+ this.coordinatesLocationConverter = coordinatesLocationConverter;
+ this.worldList = worldList;
+ }
+
+ @EventHandler(priority = EventPriority.LOWEST)
+ public void onLightningStrike(AsyncLightningStrikeEvent event) {
+ if (event.isCancelled()) return;
+
+ TimedStrike strike = event.getTimedStrike();
+
+ DebugLogger.finer("Strike: %f %f", strike.coordinates().latitude(), strike.coordinates().longitude());
+
+ worldList.getIncludedWorlds().forEach(w -> {
+ Location location = coordinatesLocationConverter.coordinatesToLocation(w, strike.coordinates());
+ DebugLogger.finer("In %s that converts to: %d %d", w.getName(), location.getBlockX(), location.getBlockZ());
+
+ // World#isLoaded, Chunk#isLoaded and probably others using Chunk, load the chunk and always return true
+ if (w.isChunkLoaded(location.getBlockX() / 16, location.getBlockZ() / 16)) {
+ location.setY(w.getHighestBlockYAt(location) + 1);
+ w.strikeLightning(location);
+ DebugLogger.finer("Spawned lightning in %s on y level %d", w.getName(), location.getBlockY());
+ }
+ });
+ }
+}
diff --git a/src/main/java/eu/m724/realweather/thunder/StrikeTask.java b/src/main/java/eu/m724/realweather/thunder/StrikeTask.java
deleted file mode 100644
index 6e14e2c..0000000
--- a/src/main/java/eu/m724/realweather/thunder/StrikeTask.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package eu.m724.realweather.thunder;
-
-import eu.m724.realweather.DebugLogger;
-import eu.m724.realweather.GlobalConstants;
-import eu.m724.realweather.mapper.Mapper;
-import eu.m724.wtapi.provider.thunder.TimedStrike;
-import org.bukkit.Location;
-import org.bukkit.scheduler.BukkitRunnable;
-
-public class StrikeTask extends BukkitRunnable {
- private final Mapper mapper = GlobalConstants.getMapper();
- private final TimedStrike strike;
-
- public StrikeTask(TimedStrike strike) {
- this.strike = strike;
- }
-
- @Override
- public void run() {
- DebugLogger.info("strike: %f %f", 2, strike.coordinates().latitude(), strike.coordinates().longitude());
-
- mapper.getWorlds().forEach(w -> {
- Location location = mapper.coordinatesToLocation(w, strike.coordinates());
- DebugLogger.info("in %s that converts to: %d %d", 2, w.getName(), location.getBlockX(), location.getBlockZ());
-
- // World#isLoaded, Chunk#isLoaded and probably others using Chunk, load the chunk and always return true
- if (w.isChunkLoaded(location.getBlockX() / 16, location.getBlockZ() / 16)) {
- location.setY(w.getHighestBlockYAt(location) + 1);
- w.strikeLightning(location);
- DebugLogger.info("spawned lightning in %s on y level %d", 2, w.getName(), location.getBlockY());
- }
- });
- }
-}
diff --git a/src/main/java/eu/m724/realweather/thunder/ThunderConfig.java b/src/main/java/eu/m724/realweather/thunder/ThunderConfig.java
index 9fbd146..0393ab1 100644
--- a/src/main/java/eu/m724/realweather/thunder/ThunderConfig.java
+++ b/src/main/java/eu/m724/realweather/thunder/ThunderConfig.java
@@ -1,23 +1,29 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather.thunder;
import org.bukkit.configuration.ConfigurationSection;
/**
+ * Configuration of the thunder module
*
- * @param enabled is thunder module enabled
- * @param provider The provider name, may or may not exist, if it doesn't, an error is thrown later
- * @param refreshPeriod how often probe for strikes, in ticks
+ * @param enabled Whether the thunder module is enabled
+ * @param provider The provider name
+ * @param apiKey API key for the provider, null if not necessary
*/
public record ThunderConfig(
boolean enabled,
String provider,
- int refreshPeriod
+ String apiKey
) {
public static ThunderConfig fromConfiguration(ConfigurationSection configuration) {
return new ThunderConfig(
configuration.getBoolean("enabled"),
configuration.getString("provider"),
- configuration.getInt("refreshPeriod")
+ configuration.getString("apiKey")
);
}
}
diff --git a/src/main/java/eu/m724/realweather/thunder/ThunderMaster.java b/src/main/java/eu/m724/realweather/thunder/ThunderMaster.java
index ef6ad6a..8a484db 100644
--- a/src/main/java/eu/m724/realweather/thunder/ThunderMaster.java
+++ b/src/main/java/eu/m724/realweather/thunder/ThunderMaster.java
@@ -1,43 +1,67 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather.thunder;
-import eu.m724.realweather.Configs;
-import eu.m724.realweather.GlobalConstants;
-import eu.m724.realweather.mapper.Mapper;
-import eu.m724.wtapi.provider.exception.NoSuchProviderException;
-import org.bukkit.plugin.Plugin;
-
import eu.m724.realweather.DebugLogger;
+import eu.m724.realweather.RealWeatherPlugin;
+import eu.m724.wtapi.provider.exception.NoSuchProviderException;
+
import eu.m724.wtapi.provider.Providers;
import eu.m724.wtapi.provider.exception.ProviderException;
import eu.m724.wtapi.provider.thunder.ThunderProvider;
public class ThunderMaster {
- private final Plugin plugin = GlobalConstants.getPlugin();
- private final ThunderConfig config = Configs.thunderConfig();
- private final Mapper mapper = GlobalConstants.getMapper();
+ private final RealWeatherPlugin plugin;
+ private final String providerName;
+ private final String apiKey;
private ThunderProvider provider;
-
- /**
+
+ public ThunderMaster(RealWeatherPlugin plugin, ThunderConfig config) {
+ this.plugin = plugin;
+ this.providerName = config.provider();
+ this.apiKey = config.apiKey();
+ }
+
+ /**
* initializes, tests and starts
+ *
* @throws ProviderException if provider initialization failed
* @throws NoSuchProviderException config issue
*/
- public void init(Plugin plugin) throws ProviderException, NoSuchProviderException {
- if (!config.enabled())
- return;
-
- provider = Providers.getThunderProvider(config.provider(), null);
+ public void init() throws ProviderException, NoSuchProviderException {
+ provider = Providers.getThunderProvider(providerName, apiKey);
- provider.registerStrikeConsumer(strike -> new StrikeTask(strike).runTaskLaterAsynchronously(plugin, 0));
+ // TODO is this good? Probably not
+ provider.registerStrikeConsumer(strike -> {
+ plugin.getServer().getPluginManager().callEvent(
+ new AsyncLightningStrikeEvent(strike)
+ );
+ });
+
+ provider.registerEventConsumer(event -> {
+ DebugLogger.fine("Thunder provider says: %s", event.message());
+
+ if (event.exception() != null) {
+ DebugLogger.severe("Thunder provider exception: %s", event.message());
+ DebugLogger.severe(" " + event.exception());
+ }
+ });
provider.start();
- DebugLogger.info("thunderprovider started", 3);
-
- DebugLogger.info("thunder loaded", 1);
+
+ DebugLogger.finer("Done initializing");
}
public long getLatency() {
return provider.getLatency();
}
+
+ // TODO should this be exposed?
+ public String getProviderName() {
+ return providerName;
+ }
}
diff --git a/src/main/java/eu/m724/realweather/time/AsyncPlayerTimeTask.java b/src/main/java/eu/m724/realweather/time/AsyncPlayerTimeTask.java
index e35729b..ce0595e 100644
--- a/src/main/java/eu/m724/realweather/time/AsyncPlayerTimeTask.java
+++ b/src/main/java/eu/m724/realweather/time/AsyncPlayerTimeTask.java
@@ -1,37 +1,45 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather.time;
+import eu.m724.realweather.map.WorldList;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import eu.m724.realweather.DebugLogger;
-import eu.m724.realweather.GlobalConstants;
-import eu.m724.realweather.mapper.Mapper;
+import eu.m724.realweather.map.CoordinatesLocationConverter;
import eu.m724.wtapi.object.Coordinates;
public class AsyncPlayerTimeTask extends BukkitRunnable {
- private final Server server = GlobalConstants.getPlugin().getServer();
- private final Mapper mapper = GlobalConstants.getMapper();
-
private final TimeConverter timeConverter;
+ private final Server server;
+ private final CoordinatesLocationConverter coordinatesLocationConverter;
+ private final WorldList worldList;
- AsyncPlayerTimeTask(TimeConverter timeConverter) {
+ AsyncPlayerTimeTask(TimeConverter timeConverter, Server server, CoordinatesLocationConverter coordinatesLocationConverter, WorldList worldList) {
this.timeConverter = timeConverter;
+ this.server = server;
+ this.coordinatesLocationConverter = coordinatesLocationConverter;
+ this.worldList = worldList;
}
@Override
public void run() {
for (Player player : server.getOnlinePlayers()) {
if (!player.hasPermission("realweather.dynamic")) continue;
- if (!mapper.getWorlds().contains(player.getWorld())) continue;
+ if (!worldList.isWorldIncluded(player.getWorld())) continue;
- Coordinates coordinates = mapper.locationToCoordinates(player.getLocation());
+ Coordinates coordinates = coordinatesLocationConverter.locationToCoordinates(player.getLocation());
long time = timeConverter.calculateZoneOffset(coordinates.longitude());
long ticks = timeConverter.millisToTicks(time);
player.setPlayerTime(ticks, true);
- DebugLogger.info("Time for %s: %d", 2, player.getName(), time);
+ DebugLogger.finer("Time for %s: %d", player.getName(), time);
}
}
}
diff --git a/src/main/java/eu/m724/realweather/time/SyncTimeUpdateTask.java b/src/main/java/eu/m724/realweather/time/SyncTimeUpdateTask.java
index fd6d1a1..b4208d3 100644
--- a/src/main/java/eu/m724/realweather/time/SyncTimeUpdateTask.java
+++ b/src/main/java/eu/m724/realweather/time/SyncTimeUpdateTask.java
@@ -1,23 +1,30 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather.time;
+import eu.m724.realweather.map.WorldList;
import org.bukkit.scheduler.BukkitRunnable;
import eu.m724.realweather.DebugLogger;
-import eu.m724.realweather.GlobalConstants;
-import eu.m724.realweather.mapper.Mapper;
+import eu.m724.realweather.map.CoordinatesLocationConverter;
/**
* This does world time, player time is basically offset of this, like timezone +0
*/
public class SyncTimeUpdateTask extends BukkitRunnable {
- private final Mapper mapper = GlobalConstants.getMapper();
-
private final TimeConverter timeConverter;
+ private final WorldList worldList;
+
private final long zoneOffset;
- SyncTimeUpdateTask(TimeConverter timeConverter, boolean dynamic) {
+ SyncTimeUpdateTask(TimeConverter timeConverter, WorldList worldList, CoordinatesLocationConverter coordinatesLocationConverter, boolean dynamic) {
this.timeConverter = timeConverter;
- this.zoneOffset = !dynamic ? timeConverter.calculateZoneOffset(mapper.getPoint().longitude()) : 0;
+ this.worldList = worldList;
+
+ this.zoneOffset = !dynamic ? timeConverter.calculateZoneOffset(coordinatesLocationConverter.getStaticPoint().longitude()) : 0;
}
@Override
@@ -27,10 +34,9 @@ public class SyncTimeUpdateTask extends BukkitRunnable {
long ticks = timeConverter.millisToTicks(time + zoneOffset);
- DebugLogger.info("Updating time: %d", 2, ticks);
+ DebugLogger.finer("Updating world time: %d", ticks);
- mapper.getWorlds().forEach(world -> world.setFullTime(ticks));
- // TODO add world handlers to mapper and don't calculate time each run
+ worldList.getIncludedWorlds().forEach(world -> world.setFullTime(ticks));
+ // TODO don't calculate time each run
}
-
}
diff --git a/src/main/java/eu/m724/realweather/time/TimeConfig.java b/src/main/java/eu/m724/realweather/time/TimeConfig.java
index c88fe5f..ab9eef3 100644
--- a/src/main/java/eu/m724/realweather/time/TimeConfig.java
+++ b/src/main/java/eu/m724/realweather/time/TimeConfig.java
@@ -1,3 +1,8 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather.time;
import org.bukkit.configuration.ConfigurationSection;
diff --git a/src/main/java/eu/m724/realweather/time/TimeConverter.java b/src/main/java/eu/m724/realweather/time/TimeConverter.java
index 3acb6b6..619f25c 100644
--- a/src/main/java/eu/m724/realweather/time/TimeConverter.java
+++ b/src/main/java/eu/m724/realweather/time/TimeConverter.java
@@ -1,15 +1,24 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather.time;
public class TimeConverter {
- public final double scale;
+ private final double scale;
public TimeConverter(double scale) {
+ if (scale <= 0.0) {
+ throw new IllegalArgumentException("Scale must be greater than 0.0");
+ }
+
this.scale = scale;
}
/**
* Divides time by predefined scale
- * ...slowing it down
+ * ...slowing it down (or speeding up)
*
* @param time unix milliseconds
* @return scaled unix milliseconds
@@ -48,4 +57,8 @@ public class TimeConverter {
public long calculateZoneOffset(double longitude) {
return (long) (longitude * 240000);
}
+
+ public double getScale() {
+ return scale;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/eu/m724/realweather/time/TimeMaster.java b/src/main/java/eu/m724/realweather/time/TimeMaster.java
index 8edbf97..b1c3a3b 100644
--- a/src/main/java/eu/m724/realweather/time/TimeMaster.java
+++ b/src/main/java/eu/m724/realweather/time/TimeMaster.java
@@ -1,45 +1,62 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather.time;
-import eu.m724.realweather.Configs;
-import org.bukkit.GameRule;
-import org.bukkit.plugin.Plugin;
+import eu.m724.realweather.RealWeatherPlugin;
import eu.m724.realweather.DebugLogger;
-import eu.m724.realweather.GlobalConstants;
-import eu.m724.realweather.mapper.Mapper;
+import eu.m724.realweather.map.CoordinatesLocationConverter;
+import eu.m724.realweather.map.WorldList;
public class TimeMaster {
- private final Mapper mapper = GlobalConstants.getMapper();
- private final Plugin plugin = GlobalConstants.getPlugin();
- private final TimeConfig timeConfig = Configs.timeConfig();
+ private final RealWeatherPlugin plugin;
+ private final TimeConverter timeConverter;
- // TODO I don't want to initialize this here
- private final TimeConverter timeConverter = new TimeConverter(timeConfig.scale());
+ private final boolean dynamic;
+
+ public TimeMaster(RealWeatherPlugin plugin, TimeConfig timeConfig) {
+ this.plugin = plugin;
+
+ this.timeConverter = new TimeConverter(timeConfig.scale());
+ this.dynamic = timeConfig.dynamic();
+ }
+
+ public void init() {
+ long period = timeConverter.calculateUpdatePeriod();
+ DebugLogger.fine("Updates every %d ticks", period);
+
+ if (timeConverter.getScale() * period != 72.0) {
+ // TODO does it matter in practice?
+ DebugLogger.warning("Time scale is not optimal. Time might be inaccurate or choppy.");
+ }
+
+ WorldList worldList = plugin.getWorldList();
+ CoordinatesLocationConverter coordinatesLocationConverter = plugin.getCoordinatesLocationConverter();
+
+ new SyncTimeUpdateTask(timeConverter, worldList, coordinatesLocationConverter, dynamic)
+ .runTaskTimer(plugin, 0, period);
+
+ if (dynamic) {
+ // Not period because this is offset
+ new AsyncPlayerTimeTask(timeConverter, plugin.getServer(), coordinatesLocationConverter, worldList)
+ .runTaskTimerAsynchronously(plugin, 0, 5 * 20); // 5 seconds
+ }
+
+ // TODO replace that
+ // coordinatesLocationConverter.registerWorldLoadConsumer(world -> world.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false));
+ // coordinatesLocationConverter.registerWorldUnloadConsumer(world -> world.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, true));
+
+ DebugLogger.finer("Done initializing");
+ }
- // TODO this is only used once
public TimeConverter getTimeConverter() {
return timeConverter;
}
-
- public void init() {
- if (!timeConfig.enabled())
- return;
-
- long period = timeConverter.calculateUpdatePeriod();
-
- if (timeConfig.scale() * period != 72.0) {
- // TODO log this properly
- DebugLogger.info("Warning: RealTime scale is not optimal. Time will be out of sync.", 0);
- }
-
- new SyncTimeUpdateTask(timeConverter, timeConfig.dynamic()).runTaskTimer(plugin, 0, period);
-
- if (timeConfig.dynamic())
- new AsyncPlayerTimeTask(timeConverter).runTaskTimerAsynchronously(plugin, 0, 60); // 5 seconds
-
- mapper.registerWorldLoadConsumer(world -> world.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false));
- mapper.registerWorldUnloadConsumer(world -> world.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, true));
-
- DebugLogger.info("time loaded, update every %d ticks", 1, period);
+
+ public boolean isDynamic() {
+ return dynamic;
}
}
diff --git a/src/main/java/eu/m724/realweather/commands/LocalTimeCommand.java b/src/main/java/eu/m724/realweather/time/command/LocalTimeCommand.java
similarity index 73%
rename from src/main/java/eu/m724/realweather/commands/LocalTimeCommand.java
rename to src/main/java/eu/m724/realweather/time/command/LocalTimeCommand.java
index 7c894f5..522d1f5 100644
--- a/src/main/java/eu/m724/realweather/commands/LocalTimeCommand.java
+++ b/src/main/java/eu/m724/realweather/time/command/LocalTimeCommand.java
@@ -1,29 +1,34 @@
-package eu.m724.realweather.commands;
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
+package eu.m724.realweather.time.command;
import java.time.Duration;
-import eu.m724.realweather.Configs;
+import eu.m724.realweather.map.CoordinatesLocationConverter;
import eu.m724.realweather.time.TimeConverter;
+import eu.m724.realweather.time.TimeMaster;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
-import eu.m724.realweather.GlobalConstants;
-import eu.m724.realweather.mapper.Mapper;
-import eu.m724.realweather.time.TimeConfig;
import eu.m724.wtapi.object.Coordinates;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ComponentBuilder;
public class LocalTimeCommand implements CommandExecutor {
- private final Mapper mapper = GlobalConstants.getMapper();
- private final TimeConfig timeConfig = Configs.timeConfig();
+ private final TimeMaster timeMaster;
private final TimeConverter timeConverter;
+ private final CoordinatesLocationConverter coordinatesLocationConverter;
- public LocalTimeCommand(TimeConverter timeConverter) {
- this.timeConverter = timeConverter;
+ public LocalTimeCommand(TimeMaster timeMaster, CoordinatesLocationConverter coordinatesLocationConverter) {
+ this.coordinatesLocationConverter = coordinatesLocationConverter;
+ this.timeMaster = timeMaster;
+ this.timeConverter = timeMaster.getTimeConverter();
}
@Override
@@ -46,8 +51,8 @@ public class LocalTimeCommand implements CommandExecutor {
sender.spigot().sendMessage(component);
- if (timeConfig.dynamic() && player != null && player.hasPermission("realweather.dynamic")) {
- Coordinates coordinates = mapper.locationToCoordinates(player.getLocation());
+ if (timeMaster.isDynamic() && player != null && player.hasPermission("realweather.dynamic")) {
+ Coordinates coordinates = coordinatesLocationConverter.locationToCoordinates(player.getLocation());
long offsetTime = timeConverter.calculateZoneOffset(coordinates.longitude());
long offsetTimeTicks = timeConverter.millisToTicks(offsetTime);
diff --git a/src/main/java/eu/m724/realweather/updater/PluginUpdater.java b/src/main/java/eu/m724/realweather/updater/PluginUpdater.java
index 7108a9b..3ae76ff 100644
--- a/src/main/java/eu/m724/realweather/updater/PluginUpdater.java
+++ b/src/main/java/eu/m724/realweather/updater/PluginUpdater.java
@@ -1,11 +1,14 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather.updater;
import eu.m724.jarupdater.environment.ConstantEnvironment;
import eu.m724.jarupdater.updater.Updater;
import eu.m724.jarupdater.verify.SignatureVerifier;
import eu.m724.jarupdater.verify.Verifier;
-import eu.m724.realweather.Configs;
-import org.bukkit.plugin.Plugin;
import eu.m724.jarupdater.download.Downloader;
import eu.m724.jarupdater.download.SimpleDownloader;
@@ -13,25 +16,23 @@ import eu.m724.jarupdater.environment.Environment;
import eu.m724.jarupdater.live.GiteaMetadataDAO;
import eu.m724.jarupdater.live.MetadataDAO;
import eu.m724.jarupdater.live.MetadataFacade;
+import eu.m724.realweather.RealWeatherPlugin;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
public class PluginUpdater extends Updater {
- private final UpdaterConfig updaterConfig = Configs.updaterConfig();
-
- final Plugin plugin;
-
- PluginUpdater(Plugin plugin, Environment environment, MetadataFacade metadataProvider, Downloader downloader, Verifier verifier) {
+ private PluginUpdater(Environment environment, MetadataFacade metadataProvider, Downloader downloader, Verifier verifier) {
super(environment, metadataProvider, downloader, verifier);
- this.plugin = plugin;
}
-
- public static PluginUpdater build(Plugin plugin, File file) {
+
+ public static PluginUpdater build(File file, String channel) {
+ RealWeatherPlugin plugin = RealWeatherPlugin.getInstance();
+
Environment environment = new ConstantEnvironment(
plugin.getDescription().getVersion(),
- Configs.updaterConfig().channel(),
+ channel,
file.toPath()
);
@@ -46,13 +47,6 @@ public class PluginUpdater extends Updater {
throw new RuntimeException(e);
}
- return new PluginUpdater(plugin, environment, metadataFacade, downloader, verifier);
- }
-
- public void init() {
- if (!updaterConfig.alert()) return;
-
- UpdateNotifier updateNotifier = new UpdateNotifier(this, (version) -> {});
- updateNotifier.register();
+ return new PluginUpdater(environment, metadataFacade, downloader, verifier);
}
}
diff --git a/src/main/java/eu/m724/realweather/updater/UpdateNotifier.java b/src/main/java/eu/m724/realweather/updater/UpdateNotifier.java
index ed9bc23..69f44a2 100644
--- a/src/main/java/eu/m724/realweather/updater/UpdateNotifier.java
+++ b/src/main/java/eu/m724/realweather/updater/UpdateNotifier.java
@@ -1,8 +1,14 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather.updater;
import java.util.concurrent.CompletionException;
-import java.util.function.Consumer;
+import eu.m724.realweather.RealWeatherPlugin;
+import net.md_5.bungee.api.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -14,55 +20,51 @@ import eu.m724.jarupdater.object.Version;
import eu.m724.realweather.DebugLogger;
public class UpdateNotifier extends BukkitRunnable implements Listener { // TODO move this to jarupdater
+ private final Plugin plugin = RealWeatherPlugin.getInstance();
+
private final PluginUpdater updater;
- private final Consumer updateConsumer;
- private final Plugin plugin;
private Version latestVersion;
- public UpdateNotifier(PluginUpdater updater, Consumer updateConsumer) {
+ public UpdateNotifier(PluginUpdater updater) {
this.updater = updater;
- this.updateConsumer = updateConsumer;
- this.plugin = updater.plugin;
}
public void register() {
- this.runTaskTimerAsynchronously(updater.plugin, 0, 432000); // 6h
+ this.runTaskTimerAsynchronously(plugin, 0, 6 * 3600 * 20); // 6h
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
@Override
public void run() {
- DebugLogger.info("update task running", 2);
+ DebugLogger.finer("Updater running");
try {
latestVersion = updater.getLatestVersion().join();
} catch (CompletionException e) {
- Throwable ex = e.getCause();
- DebugLogger.info("Error trying to contact update server: %s", 0, ex.getMessage());
-
- if (DebugLogger.getDebugLevel() >= 1)
- e.printStackTrace();
+ DebugLogger.warning("Error trying to contact update server:");
+ DebugLogger.warning(" %s", e.getCause().toString());
}
- if (latestVersion == null) return;
- DebugLogger.info("RealWeather is outdated. /rwadmin update", 0);
+ if (latestVersion == null) {
+ DebugLogger.fine("Plugin is up to date");
+ return;
+ }
+
+ DebugLogger.warning("RealWeather is outdated. /rwadmin update");
- for (Player player : updater.plugin.getServer().getOnlinePlayers()) {
+ for (Player player : plugin.getServer().getOnlinePlayers()) {
if (player.hasPermission("realweather.update.notify")) {
- player.sendMessage("RealWeather is outdated. /rwadmin update");
+ player.sendMessage(ChatColor.GOLD + "RealWeather is outdated. /rwadmin update");
}
}
-
- updateConsumer.accept(latestVersion);
-
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent e) {
Player player = e.getPlayer();
if (latestVersion != null && player.hasPermission("realweather.update.notify")) {
- player.sendMessage("RealWeather is outdated. /rwadmin update");
+ player.sendMessage(ChatColor.GOLD + "RealWeather is outdated. /rwadmin update");
}
}
}
diff --git a/src/main/java/eu/m724/realweather/updater/UpdaterConfig.java b/src/main/java/eu/m724/realweather/updater/UpdaterConfig.java
deleted file mode 100644
index 19a4d3d..0000000
--- a/src/main/java/eu/m724/realweather/updater/UpdaterConfig.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package eu.m724.realweather.updater;
-
-import org.bukkit.configuration.ConfigurationSection;
-
-/**
- *
- * @param alert alert admins about updates
- * @param channel update channel
- */
-public record UpdaterConfig(
- boolean alert, // this is different because I can't use notify in records sadly
- String channel
-) {
- public static UpdaterConfig fromConfiguration(ConfigurationSection configuration) {
- return new UpdaterConfig(
- configuration.getBoolean("notify"),
- configuration.getString("channel")
- );
- }
-}
diff --git a/src/main/java/eu/m724/realweather/updater/command/UpdateCommand.java b/src/main/java/eu/m724/realweather/updater/command/UpdateCommand.java
new file mode 100644
index 0000000..8607730
--- /dev/null
+++ b/src/main/java/eu/m724/realweather/updater/command/UpdateCommand.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
+package eu.m724.realweather.updater.command;
+
+import java.nio.file.NoSuchFileException;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+import eu.m724.realweather.DebugLogger;
+import eu.m724.realweather.updater.PluginUpdater;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.chat.ClickEvent;
+import net.md_5.bungee.api.chat.HoverEvent;
+import net.md_5.bungee.api.chat.TextComponent;
+import org.bukkit.command.CommandSender;
+
+import org.bukkit.entity.Player;
+
+public class UpdateCommand {
+ private final PluginUpdater updater;
+
+ private boolean updatePending = false;
+
+ public UpdateCommand(PluginUpdater updater) {
+ this.updater = updater;
+ }
+
+ private void sendChangelogMessage(CommandSender sender, String changelogUrl) {
+ if (changelogUrl != null) {
+ if (sender instanceof Player) {
+ TextComponent textComponent = new TextComponent("Click here to open changelog");
+ textComponent.setUnderlined(true);
+ textComponent.setColor(ChatColor.AQUA);
+ textComponent.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText(changelogUrl)));
+ textComponent.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, changelogUrl));
+ sender.spigot().sendMessage(textComponent);
+ } else {
+ sender.sendMessage("Changelog: " + changelogUrl);
+ }
+ }
+ }
+
+ public void updateCommand(CommandSender sender, String[] args) {
+ sender.sendMessage(ChatColor.GRAY + "Please wait...");
+ sender.sendMessage(ChatColor.GRAY + "Channel: " + updater.getEnvironment().getChannel());
+
+ if (updatePending) {
+ sender.sendMessage(ChatColor.YELLOW + "" + ChatColor.BOLD + "(!) Server restart required");
+ }
+
+ String action = args.length > 1 ? args[1] : null; // remember this function is proxied
+
+ if (action == null) {
+ updater.getLatestVersion().thenAccept(metadata -> {
+ updater.getCurrentVersion().thenAccept(metadata2 -> {
+ sender.sendMessage(ChatColor.GOLD + "You're on RealWeather" + metadata2.getLabel() + " released " + formatDate(metadata2.getTimestamp()));
+ sendChangelogMessage(sender, metadata2.getChangelogUrl());
+ }).exceptionally(e -> {
+ sender.sendMessage(ChatColor.RED + "An error has occurred, see console for details.");
+ DebugLogger.severe("Error retrieving information about current version:");
+ DebugLogger.severe(" " + e);
+ return null;
+ });
+
+ if (metadata != null) {
+ sender.sendMessage(ChatColor.YELLOW + "" + ChatColor.BOLD + "An update is available!");
+ sender.sendMessage(ChatColor.AQUA + "RealWeather " + metadata.getLabel() + ChatColor.GOLD + " released " + formatDate(metadata.getTimestamp()));
+ sendChangelogMessage(sender, metadata.getChangelogUrl());
+ sender.sendMessage(ChatColor.GOLD + "To download: /rwadmin update download");
+ } else {
+ sender.sendMessage(ChatColor.GRAY + "No new updates");
+ }
+ }).exceptionally(e -> {
+ sender.sendMessage(ChatColor.RED + "An error has occurred, see console for details.");
+ DebugLogger.severe("Error checking for update:");
+ DebugLogger.severe(" " + e);
+ return null;
+ });
+ } else {
+ if (action.equals("download")) {
+ sender.sendMessage(ChatColor.GRAY + "Started download");
+
+ updater.downloadLatestVersion().thenAccept(file -> {
+ sender.sendMessage(ChatColor.GREEN + "Download finished, install with /rwadmin update install"); // TODO make this clickable
+ }).exceptionally(e -> {
+ sender.sendMessage(ChatColor.RED + "An error has occurred, see console for details.");
+ DebugLogger.severe("Error downloading update:");
+ DebugLogger.severe(" " + e);
+ return null;
+ });
+ } else if (action.equals("install")) {
+ try {
+ updater.installLatestVersion().thenAccept(v -> {
+ sender.sendMessage(ChatColor.GREEN + "Installation completed, restart server to apply.");
+ updatePending = true;
+ }).exceptionally(e -> {
+ sender.sendMessage(ChatColor.RED + "An error has occurred, see console for details.");
+ DebugLogger.severe("Error installing update:");
+ DebugLogger.severe(" " + e);
+ return null;
+ });
+ } catch (NoSuchFileException e) {
+ sender.sendMessage(ChatColor.YELLOW + "Download the update first: /rwadmin update download");
+ }
+ }
+ }
+ }
+
+ private String formatDate(long timestamp) {
+ return LocalDate.ofEpochDay(timestamp / 86400).format(DateTimeFormatter.ofPattern("dd.MM.yyyy"));
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/eu/m724/realweather/weather/DynamicWeatherRetriever.java b/src/main/java/eu/m724/realweather/weather/DynamicWeatherRetriever.java
index e84284f..c0ca7cc 100644
--- a/src/main/java/eu/m724/realweather/weather/DynamicWeatherRetriever.java
+++ b/src/main/java/eu/m724/realweather/weather/DynamicWeatherRetriever.java
@@ -1,123 +1,87 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather.weather;
+import java.time.Duration;
+import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.concurrent.CompletableFuture;
+import eu.m724.realweather.RealWeatherPlugin;
import eu.m724.realweather.api.weather.AsyncPlayerWeatherUpdateEvent;
import eu.m724.wtapi.provider.weather.WeatherQueryResult;
-import org.bukkit.Server;
+import org.bukkit.World;
import org.bukkit.entity.Player;
-import org.bukkit.event.EventHandler;
-import org.bukkit.event.Listener;
-import org.bukkit.event.player.PlayerJoinEvent;
-import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitRunnable;
import eu.m724.realweather.DebugLogger;
-import eu.m724.realweather.GlobalConstants;
-import eu.m724.realweather.mapper.Mapper;
+import eu.m724.realweather.map.CoordinatesLocationConverter;
import eu.m724.wtapi.object.Coordinates;
import eu.m724.wtapi.object.Weather;
import eu.m724.wtapi.provider.weather.WeatherProvider;
-public class DynamicWeatherRetriever extends BukkitRunnable implements Listener {
+public class DynamicWeatherRetriever extends BukkitRunnable {
+ private final RealWeatherPlugin plugin;
private final WeatherProvider weatherProvider;
+ private final PlayerWeatherStore playerWeatherStore;
- private final Mapper mapper = GlobalConstants.getMapper();
- private final Plugin plugin = GlobalConstants.getPlugin();
- private final Server server = plugin.getServer();
- private final PlayerWeatherCache playerWeatherCache = GlobalConstants.getPlayerWeatherCache();
-
- // when to next update all players
- private long nextUpdate = 0;
- // players that need update asap
- private final Set neededUpdate = new HashSet<>();
+ private final CoordinatesLocationConverter coordinatesLocationConverter;
+ private final List worlds;
- public DynamicWeatherRetriever(WeatherProvider weatherProvider) {
+ public DynamicWeatherRetriever(RealWeatherPlugin plugin, WeatherProvider weatherProvider, PlayerWeatherStore playerWeatherStore) {
+ this.plugin = plugin;
this.weatherProvider = weatherProvider;
- }
+ this.playerWeatherStore = playerWeatherStore;
- private record CoordinatesResult(
- Map coordinatesPlayersMap,
- int coordinatesCount,
- int playerCount
- ) {}
-
- private CoordinatesResult makeCoordinates(Collection extends Player> players) {
- Map map = new HashMap<>();
- int coordinatesCount = 0;
- int playerCount = 0;
-
- long now = System.currentTimeMillis();
-
- for (Player player : players) {
- if (!player.hasPermission("realweather.dynamic")) continue;
- if (!mapper.getWorlds().contains(player.getWorld())) continue;
-
- Long lastUpdate = playerWeatherCache.getLastUpdate(player);
- if (lastUpdate != null && now - lastUpdate < 10000) continue;
-
- Coordinates coordinates = mapper.locationToCoordinates(player.getLocation());
- Coordinates closestCoordinates = null;
-
- for (Coordinates potential : map.keySet()) {
- //double distance = Math.sqrt(Math.pow(potential.latitude - coordinates.latitude, 2) + Math.pow(potential.longitude - potential.latitude, 2));
- // TODO setup for "bundling" that is one request for close players
- }
-
- if (closestCoordinates != null) {
- Player[] oldPlayerArray = map.get(coordinates);
- Player[] newPlayerArray = Arrays.copyOf(oldPlayerArray, oldPlayerArray.length + 1);
- newPlayerArray[oldPlayerArray.length] = player;
- map.put(coordinates, newPlayerArray);
- } else {
- map.put(coordinates, new Player[] { player });
- coordinatesCount++;
- }
-
- playerCount++;
- }
-
- return new CoordinatesResult(map, coordinatesCount, playerCount);
+ this.coordinatesLocationConverter = plugin.getCoordinatesLocationConverter();
+ this.worlds = plugin.getWorldList().getIncludedWorlds();
}
@Override
public void run() {
- DebugLogger.info("Weather retrieval", 3);
- long now = System.currentTimeMillis();
+ DebugLogger.finer("Updating weather");
+ LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
- CoordinatesResult coordinates;
- if (now > nextUpdate) {
- coordinates = makeCoordinates(server.getOnlinePlayers());
- // calculate acceptable request rate based on weather provider quota and active players
- float hourly = (float) weatherProvider.getQuota().getHourlyQuota() / coordinates.coordinatesCount();
- nextUpdate = now + Math.max(60000, (long) (3600000 / hourly));
- DebugLogger.info("Next update in %d", 3, nextUpdate);
- } else { // immediate update for those that need it right now
- if (neededUpdate.isEmpty()) return;
- DebugLogger.info("Players in need of update: %d", 2, neededUpdate.size());
- coordinates = makeCoordinates(neededUpdate);
- neededUpdate.clear();
- }
- Coordinates[] coordinatesArray = coordinates.coordinatesPlayersMap().keySet().toArray(Coordinates[]::new);
+ float maxHourlyUpdates = (float) weatherProvider.getQuota().getHourlyQuota() / plugin.getServer().getOnlinePlayers().size();
+ long updateDelay = Math.max(60, (long) (3600 / maxHourlyUpdates));
- if (coordinatesArray.length == 0) {
- DebugLogger.info("nothing to update, dynamic retriever done", 3);
+ DebugLogger.finer("Update delay: %d seconds", updateDelay);
+
+ Player[] playersToUpdate = plugin.getServer().getOnlinePlayers().stream().filter(player -> {
+ LocalDateTime lastUpdate = playerWeatherStore.getLastUpdate(player);
+
+ if (!player.hasPermission("realweather.dynamic")) return false;
+ if (!worlds.contains(player.getWorld())) return false;
+
+ if (lastUpdate == null) return true;
+ DebugLogger.finer("Player %s's last update: %s", player.getName(), lastUpdate.toString());
+ if (Duration.between(lastUpdate, now).getSeconds() > updateDelay) return true;
+ // TODO also by distance
+
+ return false;
+ }).toArray(Player[]::new);
+
+ if (playersToUpdate.length == 0) {
+ DebugLogger.finer("Nobody needs updating");
return;
}
+ Coordinates[] coordinatesArray = Arrays.stream(playersToUpdate).map(player ->
+ coordinatesLocationConverter.locationToCoordinates(player.getLocation())
+ ).toArray(Coordinates[]::new);
+
CompletableFuture weathersFuture =
weatherProvider.getWeather(coordinatesArray);
WeatherQueryResult result = weathersFuture.join();
if (result.exception() != null) {
- DebugLogger.info("An error occurred trying to retrieve weather data", 0);
-
- if (DebugLogger.getDebugLevel() > 0) {
- result.exception().printStackTrace();
- }
+ DebugLogger.severe("An error has occurred retrieving weather data");
+ DebugLogger.warning(" " + result.exception());
return;
}
@@ -126,22 +90,16 @@ public class DynamicWeatherRetriever extends BukkitRunnable implements Listener
for (int i=0; i weathers = new HashMap<>();
- HashMap lastUpdates = new HashMap<>();
-
- void put(Player player, Weather weather, Long lastUpdate) {
- weathers.put(player, weather);
- lastUpdates.put(player, lastUpdate);
- }
-
- public Weather getWeather(Player player) {
- return weathers.get(player);
- }
-
- public Long getLastUpdate(Player player) {
- return lastUpdates.get(player);
- }
-}
diff --git a/src/main/java/eu/m724/realweather/weather/PlayerWeatherStore.java b/src/main/java/eu/m724/realweather/weather/PlayerWeatherStore.java
new file mode 100644
index 0000000..4445cc5
--- /dev/null
+++ b/src/main/java/eu/m724/realweather/weather/PlayerWeatherStore.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
+package eu.m724.realweather.weather;
+
+import java.time.LocalDateTime;
+import java.util.HashMap;
+
+import org.bukkit.entity.Player;
+
+import eu.m724.wtapi.object.Weather;
+
+public class PlayerWeatherStore {
+ private final HashMap weathers = new HashMap<>();
+
+ void put(Player player, Weather weather) {
+ weathers.put(player, weather);
+ }
+
+ public Weather getWeather(Player player) {
+ return weathers.get(player);
+ }
+
+ public LocalDateTime getLastUpdate(Player player) {
+ Weather weather = weathers.get(player);
+ return weather != null ? weather.timestamp() : null;
+ }
+}
diff --git a/src/main/java/eu/m724/realweather/weather/StaticWeatherRetriever.java b/src/main/java/eu/m724/realweather/weather/StaticWeatherRetriever.java
index f4f624c..5f82a12 100644
--- a/src/main/java/eu/m724/realweather/weather/StaticWeatherRetriever.java
+++ b/src/main/java/eu/m724/realweather/weather/StaticWeatherRetriever.java
@@ -1,37 +1,42 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather.weather;
import eu.m724.realweather.DebugLogger;
-import eu.m724.realweather.GlobalConstants;
+import eu.m724.realweather.RealWeatherPlugin;
import eu.m724.realweather.api.weather.AsyncGlobalWeatherUpdateEvent;
-import eu.m724.realweather.mapper.Mapper;
+import eu.m724.realweather.map.CoordinatesLocationConverter;
import eu.m724.wtapi.object.Coordinates;
import eu.m724.wtapi.object.Weather;
import eu.m724.wtapi.provider.weather.WeatherProvider;
import eu.m724.wtapi.provider.weather.WeatherQueryResult;
-import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitRunnable;
public class StaticWeatherRetriever extends BukkitRunnable {
- private final Plugin plugin = GlobalConstants.getPlugin();
- private final Mapper mapper = GlobalConstants.getMapper();
-
+ private final RealWeatherPlugin plugin;
private final WeatherProvider weatherProvider;
+ private final CoordinatesLocationConverter coordinatesLocationConverter;
- public StaticWeatherRetriever(WeatherProvider weatherProvider) {
+ public StaticWeatherRetriever(RealWeatherPlugin plugin, WeatherProvider weatherProvider) {
+ this.plugin = plugin;
this.weatherProvider = weatherProvider;
+ this.coordinatesLocationConverter = plugin.getCoordinatesLocationConverter();
}
@Override
public void run() {
- Coordinates point = mapper.getPoint();
+ DebugLogger.finer("Updating weather");
+
+ Coordinates point = coordinatesLocationConverter.getStaticPoint();
WeatherQueryResult result = weatherProvider.getWeather(point).join();
if (result.exception() != null) {
- DebugLogger.info("An error occurred trying to retrieve weather data", 0);
-
- if (DebugLogger.getDebugLevel() > 0)
- result.exception().printStackTrace();
+ DebugLogger.severe("An error has occurred retrieving weather data");
+ DebugLogger.warning(" " + result.exception());
return;
}
@@ -43,6 +48,6 @@ public class StaticWeatherRetriever extends BukkitRunnable {
plugin.getServer().getPluginManager().callEvent(event);
- DebugLogger.info("static weather retriever is done", 3);
+ DebugLogger.fine("Done updating weather");
}
}
diff --git a/src/main/java/eu/m724/realweather/weather/WeatherChanger.java b/src/main/java/eu/m724/realweather/weather/WeatherChanger.java
index 0f2b644..97e603a 100644
--- a/src/main/java/eu/m724/realweather/weather/WeatherChanger.java
+++ b/src/main/java/eu/m724/realweather/weather/WeatherChanger.java
@@ -1,10 +1,14 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather.weather;
import eu.m724.realweather.DebugLogger;
-import eu.m724.realweather.GlobalConstants;
import eu.m724.realweather.api.weather.AsyncGlobalWeatherUpdateEvent;
import eu.m724.realweather.api.weather.AsyncPlayerWeatherUpdateEvent;
-import eu.m724.realweather.mapper.Mapper;
+import eu.m724.realweather.map.WorldList;
import eu.m724.wtapi.object.Weather;
import org.bukkit.WeatherType;
import org.bukkit.entity.Player;
@@ -12,26 +16,35 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
-// TODO make weather more comprehensive
+// TODO make weather more intricate
public class WeatherChanger implements Listener {
- private final Mapper mapper = GlobalConstants.getMapper();
+ private final WorldList worldList;
+
+ public WeatherChanger(WorldList worldList) {
+ this.worldList = worldList;
+ }
@EventHandler(priority = EventPriority.LOWEST)
public void onGlobalWeatherUpdate(AsyncGlobalWeatherUpdateEvent event) {
+ if (event.isCancelled()) return;
+
Weather weather = event.getWeather();
- DebugLogger.info("Changing weather static", 3);
- mapper.getWorlds().forEach(w -> {
- DebugLogger.info("Changing weather static in world %s", 2, w.getName());
+ DebugLogger.finer("Changing weather static");
+
+ worldList.getIncludedWorlds().forEach(w -> {
if (weather.thundering()) {
+ DebugLogger.finer("Changing weather static in world %s to thunder", w.getName());
w.setClearWeatherDuration(0);
w.setWeatherDuration(120000);
w.setThunderDuration(120000);
} else if (weather.raining() || weather.snowing()) {
+ DebugLogger.finer("Changing weather static in world %s to rain", w.getName());
w.setClearWeatherDuration(0);
w.setWeatherDuration(120000);
w.setThunderDuration(0);
} else {
+ DebugLogger.finer("Changing weather static in world %s to clear", w.getName());
w.setClearWeatherDuration(120000);
w.setWeatherDuration(0);
w.setThunderDuration(0);
@@ -41,14 +54,16 @@ public class WeatherChanger implements Listener {
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerWeatherUpdate(AsyncPlayerWeatherUpdateEvent event) {
+ if (event.isCancelled()) return;
+
Player player = event.getPlayer();
Weather weather = event.getWeather();
- DebugLogger.info("Changing weather for player %s", 2, player.getName());
-
if (weather.thundering() || weather.snowing() || weather.raining()) {
+ DebugLogger.finer("Changing weather for player %s to downfall", player.getName());
player.setPlayerWeather(WeatherType.DOWNFALL);
} else {
+ DebugLogger.finer("Changing weather for player %s to clear", player.getName());
player.setPlayerWeather(WeatherType.CLEAR);
}
}
diff --git a/src/main/java/eu/m724/realweather/weather/WeatherConfig.java b/src/main/java/eu/m724/realweather/weather/WeatherConfig.java
index ffcd497..a76230f 100644
--- a/src/main/java/eu/m724/realweather/weather/WeatherConfig.java
+++ b/src/main/java/eu/m724/realweather/weather/WeatherConfig.java
@@ -1,3 +1,8 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather.weather;
import org.bukkit.configuration.ConfigurationSection;
@@ -5,15 +10,15 @@ import org.bukkit.configuration.ConfigurationSection;
/**
* Configuration of the weather module
*
- * @param enabled Is weather module enabled
- * @param provider The provider name, may or may not exist, if it doesn't, an error is thrown later
- * @param apiKey API key for the provider
+ * @param enabled Whether the weather module is enabled
+ * @param provider The provider name
+ * @param apiKey API key for the provider, null if not necessary
* @param dynamic dynamic mode, weather is per player or global
*/
public record WeatherConfig(
boolean enabled,
String provider,
- String apiKey, // TODO don't expose that, I mean it's only used in one place in init
+ String apiKey, // TODO don't expose that, it's only used in one place in init
boolean dynamic
) {
public static WeatherConfig fromConfiguration(ConfigurationSection configuration) {
diff --git a/src/main/java/eu/m724/realweather/weather/WeatherMaster.java b/src/main/java/eu/m724/realweather/weather/WeatherMaster.java
index 13b13f1..d07a269 100644
--- a/src/main/java/eu/m724/realweather/weather/WeatherMaster.java
+++ b/src/main/java/eu/m724/realweather/weather/WeatherMaster.java
@@ -1,49 +1,74 @@
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
+
package eu.m724.realweather.weather;
-import eu.m724.realweather.Configs;
-import eu.m724.wtapi.provider.exception.NoSuchProviderException;
-import org.bukkit.GameRule;
-import org.bukkit.plugin.Plugin;
-
import eu.m724.realweather.DebugLogger;
-import eu.m724.realweather.GlobalConstants;
-import eu.m724.realweather.mapper.Mapper;
+import eu.m724.realweather.RealWeatherPlugin;
+import eu.m724.wtapi.provider.exception.NoSuchProviderException;
+
import eu.m724.wtapi.provider.Providers;
import eu.m724.wtapi.provider.exception.ProviderException;
import eu.m724.wtapi.provider.weather.WeatherProvider;
public class WeatherMaster {
- private final WeatherConfig config = Configs.weatherConfig();
- private final Mapper mapper = GlobalConstants.getMapper();
-
- /**
+ private final RealWeatherPlugin plugin;
+ private final boolean dynamic;
+
+ private final String providerName;
+ private final String apiKey;
+
+ private final PlayerWeatherStore playerWeatherStore = new PlayerWeatherStore();
+
+ public WeatherMaster(RealWeatherPlugin plugin, WeatherConfig weatherConfig) {
+ this.plugin = plugin;
+ this.dynamic = weatherConfig.dynamic();
+ this.providerName = weatherConfig.provider();
+ this.apiKey = weatherConfig.apiKey();
+ }
+
+ /**
* initializes, tests and starts
* @throws ProviderException if provider initialization failed
* @throws NoSuchProviderException config issue
*/
- public void init(Plugin plugin) throws ProviderException, NoSuchProviderException {
- if (!config.enabled()) {
- DebugLogger.info("weather module is disabled", 1);
- return;
- }
-
- WeatherProvider provider = Providers.getWeatherProvider(config.provider(), config.apiKey());
+ public void init() throws ProviderException, NoSuchProviderException {
+ WeatherProvider provider = Providers.getWeatherProvider(providerName, apiKey);
provider.init();
- if (config.dynamic()) {
- DynamicWeatherRetriever retriever = new DynamicWeatherRetriever(provider);
+ if (dynamic) {
+ DebugLogger.finer("Weather is dynamic");
+
+ DynamicWeatherRetriever retriever = new DynamicWeatherRetriever(plugin, provider, playerWeatherStore);
retriever.runTaskTimerAsynchronously(plugin,0, 200);
- plugin.getServer().getPluginManager().registerEvents(retriever, plugin);
} else {
- StaticWeatherRetriever retriever = new StaticWeatherRetriever(provider);
+ DebugLogger.finer("Weather is static");
+
+ StaticWeatherRetriever retriever = new StaticWeatherRetriever(plugin, provider);
retriever.runTaskTimerAsynchronously(plugin,0, 60000);
}
- plugin.getServer().getPluginManager().registerEvents(new WeatherChanger(), plugin);
-
- mapper.registerWorldLoadConsumer(world -> world.setGameRule(GameRule.DO_WEATHER_CYCLE, false));
- mapper.registerWorldUnloadConsumer(world -> world.setGameRule(GameRule.DO_WEATHER_CYCLE, true));
-
- DebugLogger.info("weather loaded", 1);
+ plugin.getServer().getPluginManager().registerEvents(new WeatherChanger(plugin.getWorldList()), plugin);
+
+ DebugLogger.finer("Done initializing");
+
+ // TODO replace that
+ // coordinatesLocationConverter.registerWorldLoadConsumer(world -> world.setGameRule(GameRule.DO_WEATHER_CYCLE, false));
+ // coordinatesLocationConverter.registerWorldUnloadConsumer(world -> world.setGameRule(GameRule.DO_WEATHER_CYCLE, true));
+ }
+
+ public boolean isDynamic() {
+ return dynamic;
+ }
+
+ // TODO should this be exposed?
+ public String getProviderName() {
+ return providerName;
+ }
+
+ public PlayerWeatherStore getPlayerWeatherStore() {
+ return playerWeatherStore;
}
}
diff --git a/src/main/java/eu/m724/realweather/commands/LocalWeatherCommand.java b/src/main/java/eu/m724/realweather/weather/command/LocalWeatherCommand.java
similarity index 65%
rename from src/main/java/eu/m724/realweather/commands/LocalWeatherCommand.java
rename to src/main/java/eu/m724/realweather/weather/command/LocalWeatherCommand.java
index d810c20..c7b4d8c 100644
--- a/src/main/java/eu/m724/realweather/commands/LocalWeatherCommand.java
+++ b/src/main/java/eu/m724/realweather/weather/command/LocalWeatherCommand.java
@@ -1,35 +1,42 @@
-package eu.m724.realweather.commands;
+/*
+ * Copyright (c) 2025 RealWeather Authors
+ * RealWeather is licensed under the GNU General Public License. See the LICENSE.md file in the project root for the full license text.
+ */
-import java.time.Instant;
-import java.time.ZoneOffset;
+package eu.m724.realweather.weather.command;
+
+import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
+import eu.m724.realweather.weather.PlayerWeatherStore;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
-import eu.m724.realweather.GlobalConstants;
-import eu.m724.realweather.weather.PlayerWeatherCache;
import eu.m724.wtapi.object.Weather;
public class LocalWeatherCommand implements CommandExecutor {
- private final PlayerWeatherCache playerWeatherCache = GlobalConstants.getPlayerWeatherCache();
+ private final PlayerWeatherStore playerWeatherStore;
- @Override
+ public LocalWeatherCommand(PlayerWeatherStore playerWeatherStore) {
+ this.playerWeatherStore = playerWeatherStore;
+ }
+
+ @Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player player)) {
sender.sendMessage("You must be a player to use this command");
return true;
}
- Weather weather = playerWeatherCache.getWeather(player);
+ Weather weather = playerWeatherStore.getWeather(player);
if (weather != null) {
- long lastUpdate = playerWeatherCache.getLastUpdate(player);
+ LocalDateTime lastUpdate = playerWeatherStore.getLastUpdate(player);
colorize(sender, "&6Weather for: &b%f&7, &b%f &7(lat, lon)\n", weather.coordinates().latitude(), weather.coordinates().longitude());
@@ -48,10 +55,9 @@ public class LocalWeatherCommand implements CommandExecutor {
colorize(sender, "&6Cloud cover (cloudiness): &b%.0f&7%%", weather.cloudCoverPercentage() * 100);
colorize(sender, "&6Relative humidity: &b%.0f&7%%", weather.relativeHumidityPercentage() * 100);
- colorize(sender, "&6Last update: &b%s UTC\n", formatTime(lastUpdate));
-
+ colorize(sender, "&6Last update: &b%s UTC\n", lastUpdate.format(DateTimeFormatter.ofPattern("HH:mm:ss")));
} else {
- colorize(sender, "&6No weather for you, try again in a second");
+ colorize(sender, "&6No weather for you yet, try again in a few seconds");
}
return true;
@@ -60,9 +66,4 @@ public class LocalWeatherCommand implements CommandExecutor {
private void colorize(CommandSender sender, String text, Object... format) {
sender.sendMessage(ChatColor.translateAlternateColorCodes('&', text.formatted(format)));
}
-
- private String formatTime(long timestamp) {
- return DateTimeFormatter.ofPattern("HH:mm:ss").format(Instant.ofEpochMilli(timestamp).atZone(ZoneOffset.UTC));
- }
-
}
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index a45d69f..75f7749 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -2,22 +2,11 @@
### GENERAL SETTINGS ###
############################
-# Master switch
-enabled: false
+# Not much to be found here! Explore the other files.
updater:
# Notify players and console about plugin updates
# This also controls automatic checking
# You can still update with /rwadmin update
# Relevant permission node: realweather.update.notify
- notify: true
- # stable for stable releases
- # testing for latest builds (untested hence the name)
- # As there's no release yet, stable will just error
- channel: testing
-
-# 0 - no debug
-# 1 - debug loading modules
-# 2 - also debug processing conditions
-# 3 - also log tasks running, this will spam
-debug: 0
\ No newline at end of file
+ notify: true
\ No newline at end of file
diff --git a/src/main/resources/map.yml b/src/main/resources/map.yml
index b030cc4..dfc8512 100644
--- a/src/main/resources/map.yml
+++ b/src/main/resources/map.yml
@@ -2,21 +2,20 @@
### MAP SETTINGS ###
############################
-# true if the list below is a blacklist, false otherwise
-worldBlacklist: false
worlds:
- world
+isBlacklist: false
dimensions:
- # Blocks per 1 deg, can't be decimal
- # latitude = -Z, longitude = X (to match with F3)
- # The default (111000) assumes 1 block = 1 meter
- latitude: 111000 # -9990000 to 9990000 Z
- longitude: 111000 # -19980000 to 19980000 X
+ # Blocks per 1 deg, must be integer
+ # latitude+ = Z-, longitude+ = X+ (to match with F3)
+ # The default (111,000) assumes 1 block = 1 meter
+ latitude: 111_000 # -9,990,000 to 9,990,000 Z. Wraps beyond that
+ longitude: 111_000 # -19,980,000 to 19,980,000 X
# To make it span world border to world border:
- # latitude: 333333
- # longitude: 166666
+ # latitude: 333_333
+ # longitude: 166_666
# For `static` mode
point:
diff --git a/src/main/resources/modules/thunder.yml b/src/main/resources/modules/thunder.yml
index 5293ffc..755e2ce 100644
--- a/src/main/resources/modules/thunder.yml
+++ b/src/main/resources/modules/thunder.yml
@@ -6,10 +6,7 @@ enabled: false
# Currently only blitzortung
provider: blitzortung
+# No API key needed
-# How often should we poll for updates and spawn lightning
-# This is a synchronous task
-# Exaggerating, if you put it too low you'll have lag,
-# But if you put it too high you'll have lag spikes and weird lightning
-# In ticks, default 100 is 5 seconds so reduce if lightning seems weird, in my testing even 5 ticks is fine
-refreshPeriod: 100
+# Lightning is DYNAMIC.
+# Settings are in map.yml
diff --git a/src/main/resources/modules/time.yml b/src/main/resources/modules/time.yml
index bbc768f..ce6d647 100644
--- a/src/main/resources/modules/time.yml
+++ b/src/main/resources/modules/time.yml
@@ -2,20 +2,19 @@
### TIME SETTINGS ###
############################
-# Warning: this removes sleep
-# No, it's not a bug. It would de-synchronize, and can you skip time IRL?
-# Can you believe that I actually used to consider this a bug?
+# Warning: this removes sleep. No, not a bug, as you can't skip time IRL.
enabled: false
-# How this plugin affects your world:
+# How time is applied:
# - static (false): time is the same across the world
-# - dynamic (true): static + local time for each player, however it's only cosmetic so it will not match mobs spawning etc
+# - dynamic (true): local time based on player's location. However, it's only cosmetic, so it will not match mobs spawning, etc.
# Settings for both are in map.yml
dynamic: true
-# x in game day cycles in 1 irl day cycle
+# Real days per in-game day
# 2.0 - time goes 2x SLOWER
# 0.5 - time goes 2x FASTER
-# If modified, time will no longer be in sync with real life
+# If modified, time will no longer be in sync with real life. Unreal time.
+# The division 72 / scale should equal an integer!
scale: 1.0
\ No newline at end of file
diff --git a/src/main/resources/modules/weather.yml b/src/main/resources/modules/weather.yml
index 84a2ed4..94e906f 100644
--- a/src/main/resources/modules/weather.yml
+++ b/src/main/resources/modules/weather.yml
@@ -13,8 +13,8 @@ enabled: false
provider: openmeteo
# No API key needed
-# How this plugin affects your world:
+# How weather is applied:
# - static (false): weather is the same across the world
-# - dynamic (true): weather is per player, however it's only cosmetic so it will not match mobs spawning etc
-# settings for both are in map.yml
+# - dynamic (true): weather is based on player's location. However, it's only cosmetic, so it will not match mobs spawning, etc.
+# Settings for both are in map.yml
dynamic: true
\ No newline at end of file