parent
0e8c6e5460
commit
8d1e5d3a3b
49 changed files with 1184 additions and 919 deletions
6
.idea/copyright/gpl3.xml
generated
Normal file
6
.idea/copyright/gpl3.xml
generated
Normal file
|
@ -0,0 +1,6 @@
|
|||
<component name="CopyrightManager">
|
||||
<copyright>
|
||||
<option name="notice" value="Copyright (c) &#36;originalComment.match("Copyright \(c\) (\d+)", 1, "-", "&#36;today.year")&#36;today.year 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." />
|
||||
<option name="myName" value="gpl3" />
|
||||
</copyright>
|
||||
</component>
|
7
.idea/copyright/profiles_settings.xml
generated
Normal file
7
.idea/copyright/profiles_settings.xml
generated
Normal file
|
@ -0,0 +1,7 @@
|
|||
<component name="CopyrightManager">
|
||||
<settings default="gpl3">
|
||||
<module2copyright>
|
||||
<element module="All" copyright="gpl3" />
|
||||
</module2copyright>
|
||||
</settings>
|
||||
</component>
|
|
@ -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
|
||||
|
|
73
notes.txt
73
notes.txt
|
@ -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
|
||||
|
2
pom.xml
2
pom.xml
|
@ -46,7 +46,7 @@
|
|||
<dependency>
|
||||
<groupId>eu.m724</groupId>
|
||||
<artifactId>wtapi</artifactId>
|
||||
<version>0.9.2</version>
|
||||
<version>0.9.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>eu.m724</groupId>
|
||||
|
|
|
@ -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
|
||||
) {
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Version> 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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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}<br>
|
||||
* 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}<br>
|
||||
* 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;
|
||||
}
|
||||
}
|
38
src/main/java/eu/m724/realweather/map/MapConfig.java
Normal file
38
src/main/java/eu/m724/realweather/map/MapConfig.java
Normal file
|
@ -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<String> worldNames,
|
||||
boolean worldNamesIsBlacklist,
|
||||
|
||||
double scaleLatitude,
|
||||
double scaleLongitude,
|
||||
|
||||
Coordinates point
|
||||
) {
|
||||
public static MapConfig fromConfiguration(ConfigurationSection configuration) {
|
||||
List<String> 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);
|
||||
}
|
||||
}
|
48
src/main/java/eu/m724/realweather/map/WorldList.java
Normal file
48
src/main/java/eu/m724/realweather/map/WorldList.java
Normal file
|
@ -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<String> worldNames;
|
||||
private final boolean isBlacklist;
|
||||
|
||||
private final List<World> worlds = new ArrayList<>();
|
||||
|
||||
public WorldList(List<String> 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<World> getIncludedWorlds() {
|
||||
return Collections.unmodifiableList(this.worlds);
|
||||
}
|
||||
|
||||
public boolean isWorldIncluded(World world) {
|
||||
return worlds.contains(world);
|
||||
}
|
||||
}
|
|
@ -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, "");
|
||||
}
|
||||
|
|
@ -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<World> worlds = new ArrayList<>();
|
||||
|
||||
private final List<Consumer<World>> worldLoadConsumers = new ArrayList<>();
|
||||
private final List<Consumer<World>> 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<World> 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<World> consumer) {
|
||||
this.worldUnloadConsumers.add(consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers events handled by mapper.<br>
|
||||
* 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}<br>
|
||||
* 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}<br>
|
||||
* 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<World> 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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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<String> 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;
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<br>
|
||||
* ...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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Version> updateConsumer;
|
||||
private final Plugin plugin;
|
||||
|
||||
private Version latestVersion;
|
||||
|
||||
public UpdateNotifier(PluginUpdater updater, Consumer<Version> 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
|
||||
}
|
|
@ -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<Player> neededUpdate = new HashSet<>();
|
||||
private final CoordinatesLocationConverter coordinatesLocationConverter;
|
||||
private final List<World> 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<Coordinates, Player[]> coordinatesPlayersMap,
|
||||
int coordinatesCount,
|
||||
int playerCount
|
||||
) {}
|
||||
|
||||
private CoordinatesResult makeCoordinates(Collection<? extends Player> players) {
|
||||
Map<Coordinates, Player[]> 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<WeatherQueryResult> 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.length; i++) {
|
||||
Weather weather = weathers[i];
|
||||
for (Player player : coordinates.coordinatesPlayersMap().get(coordinatesArray[i])) {
|
||||
playerWeatherCache.put(player, weather, weather.timestamp().toEpochSecond(ZoneOffset.UTC));
|
||||
Player player = playersToUpdate[i];
|
||||
|
||||
AsyncPlayerWeatherUpdateEvent event =
|
||||
new AsyncPlayerWeatherUpdateEvent(player, weather);
|
||||
playerWeatherStore.put(player, weather);
|
||||
|
||||
server.getPluginManager().callEvent(event);
|
||||
}
|
||||
AsyncPlayerWeatherUpdateEvent event =
|
||||
new AsyncPlayerWeatherUpdateEvent(player, weather);
|
||||
|
||||
plugin.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
DebugLogger.info("dynamic retriever done", 3);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
neededUpdate.add(player);
|
||||
DebugLogger.fine("Done updating weather");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
package eu.m724.realweather.weather;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import eu.m724.wtapi.object.Weather;
|
||||
|
||||
/**
|
||||
* Stores player weathers and when they were last updated
|
||||
*/
|
||||
public class PlayerWeatherCache {
|
||||
HashMap<Player, Weather> weathers = new HashMap<>();
|
||||
HashMap<Player, Long> 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);
|
||||
}
|
||||
}
|
|
@ -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<Player, Weather> 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;
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
notify: true
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue