Compare commits
14 commits
realweathe
...
master
Author | SHA1 | Date | |
---|---|---|---|
39e1186911 | |||
f35b931f77 | |||
0a4798656f | |||
c51f06d2b5 | |||
1895e90610 | |||
8d1e5d3a3b | |||
0e8c6e5460 | |||
a33022380a | |||
505d8df3d6 | |||
d55939c116 | |||
7927d8ba3e | |||
![]() |
1920d8dcc2 | ||
96454e22ec | |||
![]() |
f61783c1a5 |
56 changed files with 1470 additions and 1206 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
.idea/modules.xml
generated
2
.idea/modules.xml
generated
|
@ -2,7 +2,7 @@
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectModuleManager">
|
<component name="ProjectModuleManager">
|
||||||
<modules>
|
<modules>
|
||||||
<module fileurl="file://$PROJECT_DIR$/realweather.iml" filepath="$PROJECT_DIR$/realweather.iml" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/realweather.iml" filepath="$PROJECT_DIR$/.idea/realweather.iml" />
|
||||||
</modules>
|
</modules>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
13
.idea/realweather.iml
generated
Normal file
13
.idea/realweather.iml
generated
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module version="4">
|
||||||
|
<component name="FacetManager">
|
||||||
|
<facet type="minecraft" name="Minecraft">
|
||||||
|
<configuration>
|
||||||
|
<autoDetectTypes>
|
||||||
|
<platformType>SPIGOT</platformType>
|
||||||
|
</autoDetectTypes>
|
||||||
|
<projectReimportVersion>1</projectReimportVersion>
|
||||||
|
</configuration>
|
||||||
|
</facet>
|
||||||
|
</component>
|
||||||
|
</module>
|
|
@ -2,7 +2,7 @@ If you're using a firewall, you must allow the following hosts:
|
||||||
- updater:
|
- updater:
|
||||||
* git.m724.eu
|
* git.m724.eu
|
||||||
- weather:
|
- weather:
|
||||||
* api.openweathermap.org
|
* api.open-meteo.com
|
||||||
- thunder:
|
- thunder:
|
||||||
* ws1.blitzortung.org
|
* ws1.blitzortung.org
|
||||||
* ws7.blitzortung.org
|
* ws7.blitzortung.org
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# realweather
|
# realweather
|
||||||
|
|
||||||
For MC 1.19.4+ and Java 21+
|
For MC 1.16.5+ and Java 21+
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
To compile, clone this repo and `mvn clean package`. \
|
To compile, clone this repo and `mvn clean package`. \
|
||||||
|
|
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
|
|
||||||
|
|
13
pom.xml
13
pom.xml
|
@ -2,7 +2,7 @@
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>eu.m724</groupId>
|
<groupId>eu.m724</groupId>
|
||||||
<artifactId>realweather</artifactId>
|
<artifactId>realweather</artifactId>
|
||||||
<version>1.0.0-alpha-5</version>
|
<version>1.0.0-alpha-8-SNAPSHOT</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
<maven.compiler.source>21</maven.compiler.source>
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.spigotmc</groupId>
|
<groupId>org.spigotmc</groupId>
|
||||||
<artifactId>spigot-api</artifactId>
|
<artifactId>spigot-api</artifactId>
|
||||||
<version>1.19.4-R0.1-SNAPSHOT</version>
|
<version>1.16.5-R0.1-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
<!-- Fix warning about vulnerabilities of things we don't use -->
|
<!-- Fix warning about vulnerabilities of things we don't use -->
|
||||||
<exclusions>
|
<exclusions>
|
||||||
|
@ -46,13 +46,18 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>eu.m724</groupId>
|
<groupId>eu.m724</groupId>
|
||||||
<artifactId>wtapi</artifactId>
|
<artifactId>wtapi</artifactId>
|
||||||
<version>0.8.3</version>
|
<version>0.9.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>eu.m724</groupId>
|
<groupId>eu.m724</groupId>
|
||||||
<artifactId>jarupdater</artifactId>
|
<artifactId>jarupdater</artifactId>
|
||||||
<version>0.1.8</version>
|
<version>0.1.8</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>eu.m724</groupId>
|
||||||
|
<artifactId>mstats-spigot</artifactId>
|
||||||
|
<version>0.1.2</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
@ -145,6 +150,6 @@
|
||||||
|
|
||||||
<scm>
|
<scm>
|
||||||
<developerConnection>scm:git:git@git.m724.eu:Minecon724/realweather.git</developerConnection>
|
<developerConnection>scm:git:git@git.m724.eu:Minecon724/realweather.git</developerConnection>
|
||||||
<tag>realweather-1.0.0-alpha-5</tag>
|
<tag>HEAD</tag>
|
||||||
</scm>
|
</scm>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -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;
|
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.thunder.ThunderConfig;
|
||||||
import eu.m724.realweather.time.TimeConfig;
|
import eu.m724.realweather.time.TimeConfig;
|
||||||
import eu.m724.realweather.updater.UpdaterConfig;
|
import eu.m724.realweather.weather.WeatherChanger;
|
||||||
import eu.m724.realweather.weather.WeatherConfig;
|
|
||||||
|
|
||||||
// TODO replaces GlobalConstants for configs
|
public record Configs(
|
||||||
public class Configs {
|
WeatherChanger weatherConfig,
|
||||||
static WeatherConfig weatherConfig;
|
TimeConfig timeConfig,
|
||||||
static TimeConfig timeConfig;
|
ThunderConfig thunderConfig,
|
||||||
static ThunderConfig thunderConfig;
|
MapConfig mapConfig
|
||||||
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; }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
package eu.m724.realweather;
|
||||||
|
|
||||||
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
public class DebugLogger {
|
public class DebugLogger {
|
||||||
static Logger baseLogger;
|
private DebugLogger() {}
|
||||||
static int debugLevel;
|
static Logger logger;
|
||||||
|
|
||||||
public static int getDebugLevel() {
|
public static void info(String message, Object... format) {
|
||||||
return debugLevel;
|
log(Level.INFO, message, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void warning(String message, Object... format) {
|
||||||
public static void info(String message, int minDebugLevel, Object... format) {
|
log(Level.WARNING, message, format);
|
||||||
if (debugLevel >= minDebugLevel)
|
|
||||||
baseLogger.info(message.formatted(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;
|
package eu.m724.realweather;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -5,149 +10,208 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import eu.m724.mstats.MStatsPlugin;
|
||||||
|
import eu.m724.realweather.map.WorldList;
|
||||||
|
import eu.m724.realweather.updater.UpdateNotifier;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
|
||||||
|
|
||||||
import eu.m724.realweather.commands.AdminCommand;
|
import eu.m724.realweather.commands.AdminCommand;
|
||||||
import eu.m724.realweather.commands.GeoCommand;
|
import eu.m724.realweather.map.command.GeoCommand;
|
||||||
import eu.m724.realweather.commands.LocalTimeCommand;
|
import eu.m724.realweather.time.command.LocalTimeCommand;
|
||||||
import eu.m724.realweather.commands.LocalWeatherCommand;
|
import eu.m724.realweather.weather.command.LocalWeatherCommand;
|
||||||
import eu.m724.realweather.exception.UserError;
|
import eu.m724.realweather.map.CoordinatesLocationConverter;
|
||||||
import eu.m724.realweather.mapper.Mapper;
|
import eu.m724.realweather.map.MapConfig;
|
||||||
import eu.m724.realweather.mapper.MapperConfig;
|
|
||||||
import eu.m724.realweather.thunder.ThunderConfig;
|
import eu.m724.realweather.thunder.ThunderConfig;
|
||||||
import eu.m724.realweather.thunder.ThunderMaster;
|
import eu.m724.realweather.thunder.ThunderMaster;
|
||||||
import eu.m724.realweather.time.TimeConfig;
|
import eu.m724.realweather.time.TimeConfig;
|
||||||
import eu.m724.realweather.time.TimeMaster;
|
import eu.m724.realweather.time.TimeMaster;
|
||||||
import eu.m724.realweather.updater.PluginUpdater;
|
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.WeatherConfig;
|
||||||
import eu.m724.realweather.weather.WeatherMaster;
|
import eu.m724.realweather.weather.WeatherMaster;
|
||||||
import eu.m724.wtapi.provider.exception.NoSuchProviderException;
|
|
||||||
import eu.m724.wtapi.provider.exception.ProviderException;
|
|
||||||
|
|
||||||
// TODO unmess this too
|
// TODO unmess this too
|
||||||
public class RealWeatherPlugin extends JavaPlugin {
|
public class RealWeatherPlugin extends MStatsPlugin {
|
||||||
|
private Configs configs;
|
||||||
|
private WorldList worldList;
|
||||||
|
|
||||||
private WeatherMaster weatherMaster;
|
private WeatherMaster weatherMaster;
|
||||||
private ThunderMaster thunderMaster;
|
|
||||||
private TimeMaster timeMaster;
|
private TimeMaster timeMaster;
|
||||||
private PluginUpdater updater;
|
private ThunderMaster thunderMaster;
|
||||||
|
|
||||||
private Logger logger;
|
private static RealWeatherPlugin INSTANCE;
|
||||||
|
|
||||||
|
private CoordinatesLocationConverter coordinatesLocationConverter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
logger = getLogger();
|
long start = System.nanoTime();
|
||||||
|
INSTANCE = this;
|
||||||
|
|
||||||
File dataFolder = getDataFolder();
|
File dataFolder = getDataFolder();
|
||||||
File modulesFolder = new File("modules");
|
File modulesFolder = new File("modules");
|
||||||
modulesFolder.mkdir();
|
modulesFolder.mkdir();
|
||||||
|
|
||||||
YamlConfiguration configuration,
|
|
||||||
mapConfiguration, weatherConfiguration,
|
|
||||||
thunderConfiguration, timeConfiguration;
|
|
||||||
|
|
||||||
DebugLogger.info("loading configurations", 1);
|
|
||||||
boolean firstRun = !new File(dataFolder, "config.yml").exists();
|
boolean firstRun = !new File(dataFolder, "config.yml").exists();
|
||||||
|
|
||||||
|
YamlConfiguration configuration;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
configuration = getConfig("config.yml");
|
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) {
|
} catch (IOException e) {
|
||||||
logger.severe("Failed to load config!");
|
DebugLogger.severe("Failed to load configuration:");
|
||||||
throw new RuntimeException(e);
|
DebugLogger.severe(" " + e);
|
||||||
|
|
||||||
|
getServer().getPluginManager().disablePlugin(this);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DebugLogger.logger = getLogger();
|
||||||
|
getLogger().setLevel(configuration.getBoolean("debug") ? Level.FINEST : Level.INFO);
|
||||||
|
|
||||||
if (firstRun) {
|
if (firstRun) {
|
||||||
logger.warning("This is your first time running this plugin.");
|
DebugLogger.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.)");
|
DebugLogger.warning("Please *shut down* the server, review the config files (enable modules, enter API keys, etc.)");
|
||||||
logger.warning("Don't forget to enable the plugin in config.yml");
|
DebugLogger.warning("Don't forget to enable the plugin in config.yml");
|
||||||
|
|
||||||
getServer().getPluginManager().disablePlugin(this);
|
getServer().getPluginManager().disablePlugin(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DebugLogger.baseLogger = logger;
|
if (configuration.getBoolean("disabled")) {
|
||||||
DebugLogger.debugLevel = configuration.getInt("debug");
|
DebugLogger.warning("Plugin disabled per config. Enable it in config.yml");
|
||||||
|
|
||||||
if (!configuration.getBoolean("enabled")) {
|
|
||||||
logger.warning("Plugin disabled by administrator. Enable it in config.yml");
|
|
||||||
getServer().getPluginManager().disablePlugin(this);
|
getServer().getPluginManager().disablePlugin(this);
|
||||||
return;
|
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 {
|
try {
|
||||||
DebugLogger.info("loading weather", 1);
|
loadMapModules();
|
||||||
Configs.weatherConfig = WeatherConfig.fromConfiguration(weatherConfiguration);
|
} catch (Exception e) {
|
||||||
weatherMaster = new WeatherMaster();
|
DebugLogger.severe("Failed to load the Map module:");
|
||||||
weatherMaster.init(this);
|
DebugLogger.severe(" " + e);
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
Configs.updaterConfig = UpdaterConfig.fromConfiguration(configuration.getConfigurationSection("updater"));
|
|
||||||
updater = PluginUpdater.build(this, this.getFile());
|
|
||||||
//updater.init();
|
|
||||||
} catch (UserError | NoSuchProviderException e) {
|
|
||||||
logger.severe("There are errors 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);
|
getServer().getPluginManager().disablePlugin(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCommand("rwadmin").setExecutor(new AdminCommand(updater, thunderMaster));
|
try {
|
||||||
getCommand("geo").setExecutor(new GeoCommand());
|
loadWeatherModule();
|
||||||
|
} catch (Exception e) {
|
||||||
|
DebugLogger.severe("Failed to load the Weather module:");
|
||||||
|
DebugLogger.severe(" " + e);
|
||||||
|
|
||||||
if (Configs.timeConfig.enabled())
|
getServer().getPluginManager().disablePlugin(this);
|
||||||
getCommand("localtime").setExecutor(new LocalTimeCommand(timeMaster.getTimeConverter()));
|
return;
|
||||||
|
|
||||||
if (Configs.weatherConfig.enabled()) {
|
|
||||||
getCommand("localweather").setExecutor(new LocalWeatherCommand());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*Metrics metrics = new Metrics(this, 15020);
|
try {
|
||||||
metrics.addCustomChart(new SimplePie("weather_provider", () ->
|
loadThunderModule();
|
||||||
GlobalConstants.weatherConfig.enabled ? GlobalConstants.weatherConfig.provider : "off"
|
} catch (Exception e) {
|
||||||
));
|
DebugLogger.severe("Failed to load the Thunder module:");
|
||||||
metrics.addCustomChart(new SimplePie("thunder_provider", () ->
|
DebugLogger.severe(" " + e);
|
||||||
GlobalConstants.thunderConfig.enabled ? GlobalConstants.thunderConfig.provider : "off"
|
|
||||||
));
|
|
||||||
metrics.addCustomChart(new SimplePie("real_time", () ->
|
|
||||||
GlobalConstants.timeConfig.enabled() ? "on" : "off"
|
|
||||||
));*/
|
|
||||||
|
|
||||||
DebugLogger.info("ended loading", 1);
|
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.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 {
|
public YamlConfiguration getConfig(String configFilePath) throws IOException {
|
||||||
|
@ -155,15 +219,50 @@ public class RealWeatherPlugin extends JavaPlugin {
|
||||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
||||||
|
|
||||||
if (!configFile.exists()) {
|
if (!configFile.exists()) {
|
||||||
final InputStream defConfigStream = getResource(configFilePath);
|
try (InputStream defConfigStream = getResource(configFilePath)) {
|
||||||
|
if (defConfigStream == null)
|
||||||
if (defConfigStream == null)
|
return null;
|
||||||
return null;
|
|
||||||
|
config = YamlConfiguration.loadConfiguration(new InputStreamReader(defConfigStream, StandardCharsets.UTF_8));
|
||||||
config = YamlConfiguration.loadConfiguration(new InputStreamReader(defConfigStream, StandardCharsets.UTF_8));
|
}
|
||||||
|
|
||||||
config.save(configFile);
|
config.save(configFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +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.api.weather;
|
||||||
|
|
||||||
|
import eu.m724.wtapi.object.Weather;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when weather is <em>updated</em> for the <strong>server</strong>
|
||||||
|
* <br>
|
||||||
|
* This is only used on <em>static</em> mode. For the dynamic mode counterpart, see {@link AsyncPlayerWeatherUpdateEvent}
|
||||||
|
*/
|
||||||
|
public class AsyncGlobalWeatherUpdateEvent extends AsyncWeatherUpdateEvent {
|
||||||
|
public AsyncGlobalWeatherUpdateEvent(Weather weather) {
|
||||||
|
super(weather);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when weather is <em>updated</em> for a player
|
||||||
|
* <br>
|
||||||
|
* This is only used on <em>dynamic</em> mode. For the static mode counterpart, see {@link AsyncGlobalWeatherUpdateEvent}
|
||||||
|
*/
|
||||||
|
public class AsyncPlayerWeatherUpdateEvent extends AsyncWeatherUpdateEvent {
|
||||||
|
private static final HandlerList HANDLERS = new HandlerList();
|
||||||
|
|
||||||
|
private final Player player;
|
||||||
|
|
||||||
|
public AsyncPlayerWeatherUpdateEvent(Player player, Weather weather) {
|
||||||
|
super(weather);
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The player to update weather for
|
||||||
|
*/
|
||||||
|
public Player getPlayer() {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,65 +1,55 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
package eu.m724.realweather.api.weather;
|
||||||
|
|
||||||
import org.bukkit.entity.Player;
|
import eu.m724.wtapi.object.Weather;
|
||||||
import org.bukkit.event.Cancellable;
|
import org.bukkit.event.Cancellable;
|
||||||
import org.bukkit.event.Event;
|
import org.bukkit.event.Event;
|
||||||
import org.bukkit.event.HandlerList;
|
import org.bukkit.event.HandlerList;
|
||||||
|
|
||||||
import eu.m724.wtapi.object.Weather;
|
class AsyncWeatherUpdateEvent extends Event implements Cancellable {
|
||||||
|
private static final HandlerList HANDLERS = new HandlerList();
|
||||||
|
|
||||||
/**
|
private final Weather weather;
|
||||||
* Fired when a weather state is retrieved<br>
|
|
||||||
* It doesn't mean the weather has changed, just that we retrieved the state<br>
|
|
||||||
*/
|
|
||||||
public class AsyncWeatherUpdateEvent extends Event implements Cancellable {
|
|
||||||
private static final HandlerList HANDLERS = new HandlerList();
|
|
||||||
|
|
||||||
private final Player player;
|
|
||||||
private final Weather weather;
|
|
||||||
|
|
||||||
private boolean cancelled;
|
private boolean cancelled;
|
||||||
|
|
||||||
public AsyncWeatherUpdateEvent(Player player, Weather weather) {
|
public AsyncWeatherUpdateEvent(Weather weather) {
|
||||||
super(true);
|
super(true);
|
||||||
this.player = player;
|
this.weather = weather;
|
||||||
this.weather = weather;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return a player that the weather is for, null if worldwide (static mode)
|
* @return The new weather
|
||||||
*/
|
*/
|
||||||
public Player getPlayer() {
|
public Weather getWeather() {
|
||||||
return player;
|
return weather;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static HandlerList getHandlerList() {
|
||||||
* @return the weather state that was just changed
|
return HANDLERS;
|
||||||
*/
|
}
|
||||||
public Weather getWeather() {
|
|
||||||
return weather;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HandlerList getHandlerList() {
|
@Override
|
||||||
return HANDLERS;
|
public HandlerList getHandlers() {
|
||||||
}
|
return HANDLERS;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HandlerList getHandlers() {
|
public boolean isCancelled() {
|
||||||
return HANDLERS;
|
return cancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public boolean isCancelled() {
|
* Cancel the weather change
|
||||||
return cancelled;
|
*
|
||||||
}
|
* @param cancelled Whether to cancel
|
||||||
|
*/
|
||||||
/**
|
@Override
|
||||||
* Cancel weather change<br>
|
public void setCancelled(boolean cancelled) {
|
||||||
* It will only cancel changing the actual weather by the plugin, not retrieving and caching it
|
this.cancelled = cancelled;
|
||||||
* @param cancelled to cancel or not
|
}
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setCancelled(boolean cancelled) {
|
|
||||||
this.cancelled = cancelled;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
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.Command;
|
||||||
import org.bukkit.command.CommandExecutor;
|
import org.bukkit.command.CommandExecutor;
|
||||||
import org.bukkit.command.CommandSender;
|
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.thunder.ThunderMaster;
|
||||||
import eu.m724.realweather.time.TimeConfig;
|
|
||||||
import eu.m724.realweather.updater.PluginUpdater;
|
import eu.m724.realweather.updater.PluginUpdater;
|
||||||
import eu.m724.realweather.weather.WeatherConfig;
|
|
||||||
import net.md_5.bungee.api.ChatColor;
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
|
||||||
// TODO unmess this all
|
|
||||||
public class AdminCommand implements CommandExecutor {
|
public class AdminCommand implements CommandExecutor {
|
||||||
|
private final RealWeatherPlugin plugin;
|
||||||
private final UpdateCommand updateCommand;
|
private final UpdateCommand updateCommand;
|
||||||
private final Plugin plugin = GlobalConstants.getPlugin();
|
|
||||||
|
private final WeatherMaster weatherMaster;
|
||||||
private final WeatherConfig weatherConfig = Configs.weatherConfig();
|
private final TimeMaster timeMaster;
|
||||||
private final TimeConfig timeConfig = Configs.timeConfig();
|
|
||||||
private final ThunderConfig thunderConfig = Configs.thunderConfig();
|
|
||||||
private final MapperConfig mapperConfig = Configs.mapperConfig();
|
|
||||||
|
|
||||||
private final ThunderMaster thunderMaster;
|
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.updateCommand = new UpdateCommand(updater);
|
||||||
this.thunderMaster = thunderMaster;
|
this.weatherMaster = plugin.getWeatherMaster();
|
||||||
|
this.timeMaster = plugin.getTimeMaster();
|
||||||
|
this.thunderMaster = plugin.getThunderMaster();
|
||||||
|
this.coordinatesLocationConverter = plugin.getCoordinatesLocationConverter();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
if (args.length > 0 && args[0].equals("update")) {
|
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, "\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 (weatherMaster != null) {
|
||||||
|
colorize(sender, "&6Weather: %s", weatherMaster.isDynamic() ? "&aYes, dynamic" : "&aYes, static");
|
||||||
if (weatherConfig.enabled()) {
|
colorize(sender, " &6Provider: &b%s", weatherMaster.getProviderName());
|
||||||
colorize(sender, " &6Provider: &b%s", weatherConfig.provider());
|
|
||||||
colorize(sender, " &6/localweather to see current weather");
|
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");
|
sender.sendMessage("");
|
||||||
|
|
||||||
if (timeConfig.enabled()) {
|
if (timeMaster != null) {
|
||||||
colorize(sender, " &6Scale: &b%s&7x", timeConfig.scale());
|
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");
|
colorize(sender, " &6/localtime to see current time");
|
||||||
|
} else {
|
||||||
|
colorize(sender, "&6Time: &cDisabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
colorize(sender, "\n&6Thunder: %s", thunderConfig.enabled() ? "&aYes, dynamic" : "&cDisabled");
|
sender.sendMessage("");
|
||||||
|
|
||||||
if (thunderConfig.enabled()) {
|
if (thunderMaster != null) {
|
||||||
colorize(sender, " &6Provider: &b%s", thunderConfig.provider());
|
colorize(sender, "&6Thunder: &aYes, dynamic");
|
||||||
colorize(sender, " &6Refresh period: &b%d &7ticks", thunderConfig.refreshPeriod());
|
colorize(sender, " &6Provider: &b%s", thunderMaster.getProviderName());
|
||||||
colorize(sender, " &6API latency: &b%d&7ms", thunderMaster.getLatency());
|
colorize(sender, " &6API latency: &b%d&7ms", thunderMaster.getLatency());
|
||||||
|
} else {
|
||||||
|
colorize(sender, "&6Thunder: &cDisabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,106 +0,0 @@
|
||||||
package eu.m724.realweather.commands;
|
|
||||||
|
|
||||||
import net.md_5.bungee.api.chat.*;
|
|
||||||
import net.md_5.bungee.api.chat.hover.content.Content;
|
|
||||||
import net.md_5.bungee.api.chat.hover.content.Text;
|
|
||||||
import org.bukkit.Location;
|
|
||||||
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.weather.PlayerWeatherCache;
|
|
||||||
import eu.m724.wtapi.object.Coordinates;
|
|
||||||
import eu.m724.wtapi.object.Weather;
|
|
||||||
import net.md_5.bungee.api.ChatColor;
|
|
||||||
|
|
||||||
public class GeoCommand implements CommandExecutor {
|
|
||||||
private final PlayerWeatherCache playerWeatherCache = GlobalConstants.getPlayerWeatherCache();
|
|
||||||
private final Mapper mapper = GlobalConstants.getMapper();
|
|
||||||
|
|
||||||
@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);
|
|
||||||
|
|
||||||
colorize(player, "");
|
|
||||||
colorize(player, "&6Geolocation: &b%f&7, %b%f &7(lat, lon)", coordinates.latitude, coordinates.longitude);
|
|
||||||
colorize(player, "&7Position: &3%f&8, %3%f &8(x, z)", location.getX(), location.getZ());
|
|
||||||
colorize(player, "&7City: &3%s", address);
|
|
||||||
colorize(player, "");
|
|
||||||
}
|
|
||||||
} else if (args.length >= 3) {
|
|
||||||
colorize(sender, "&cInvalid arguments, &7make sure it's &a\"/geo lat,lon\" &7or &a\"/geo x z\" &7or just &a\"/geo\"");
|
|
||||||
} else if (args.length == 2) {
|
|
||||||
double x, z;
|
|
||||||
|
|
||||||
try {
|
|
||||||
x = Double.parseDouble(args[0]);
|
|
||||||
z = Double.parseDouble(args[1]);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
colorize(sender, "&cInvalid arguments, &7make sure it's &a\"/geo lat,lon\" &7or &a\"/geo x z\" &7or just &a\"/geo\"");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Location location = new Location(null, x, 0, z);
|
|
||||||
Coordinates coordinates = mapper.locationToCoordinates(location);
|
|
||||||
|
|
||||||
colorize(sender, "");
|
|
||||||
colorize(sender, "&6Position: &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 with a space");
|
|
||||||
colorize(sender, "");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
double latitude, longitude;
|
|
||||||
|
|
||||||
try {
|
|
||||||
String[] split = args[0].split(",");
|
|
||||||
latitude = Double.parseDouble(split[0]);
|
|
||||||
longitude = Double.parseDouble(split[1]);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
colorize(sender, "&cInvalid arguments, &7make sure it's &a\"/geo lat,lon\" &7or &a\"/geo x z\" &7or just &a\"/geo\"");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Coordinates coordinates = new Coordinates(latitude, longitude);
|
|
||||||
Location location = mapper.coordinatesToLocation(null, coordinates);
|
|
||||||
|
|
||||||
colorize(sender, "");
|
|
||||||
colorize(sender, "&6Position: &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, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void colorize(CommandSender sender, String text, Object... format) {
|
|
||||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&', text.formatted(format)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String formatAddress(Weather weather) {
|
|
||||||
if (weather == null) return "Not retrieved yet";
|
|
||||||
Coordinates coordinates = weather.coordinates;
|
|
||||||
|
|
||||||
if (coordinates.country == null && coordinates.city == null)
|
|
||||||
return "Unknown";
|
|
||||||
else if (coordinates.city == null)
|
|
||||||
return "Somewhere in " + coordinates.country;
|
|
||||||
else if (coordinates.country == null)
|
|
||||||
return coordinates.city;
|
|
||||||
return coordinates.city + ", " + coordinates.country;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
package eu.m724.realweather.commands;
|
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.ZoneOffset;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
@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);
|
|
||||||
|
|
||||||
if (weather != null) {
|
|
||||||
long lastUpdate = playerWeatherCache.getLastUpdate(player);
|
|
||||||
colorize(sender, "\n&e" + weather.description);
|
|
||||||
|
|
||||||
if (weather.rainSeverity != null)
|
|
||||||
colorize(sender, "&6Rain: &b%s", weather.rainSeverity.toString());
|
|
||||||
if (weather.drizzleSeverity != null)
|
|
||||||
colorize(sender, "&6Drizzle: &b%s", weather.drizzleSeverity.toString());
|
|
||||||
if (weather.sleetSeverity != null)
|
|
||||||
colorize(sender, "&6Sleet: &b%s", weather.sleetSeverity.toString());
|
|
||||||
if (weather.snowSeverity != null)
|
|
||||||
colorize(sender, "&6Snow: &b%s", weather.snowSeverity.toString());
|
|
||||||
if (weather.thunderstormSeverity != null)
|
|
||||||
colorize(sender, "&6Thunderstorm: &b%s", weather.thunderstormSeverity.toString());
|
|
||||||
if (weather.shower)
|
|
||||||
colorize(sender, "&6Shower");
|
|
||||||
|
|
||||||
colorize(sender, "&6Cloudiness: &b%f%&7%", weather.cloudiness * 100);
|
|
||||||
colorize(sender, "&6Humidity: &b%f%&7%", weather.humidity * 100);
|
|
||||||
colorize(sender, "&6Temperature: &b%f&7°C (feels like %f°C)", weather.temperature - 273.15, weather.temperatureApparent - 273.15);
|
|
||||||
colorize(sender, "&6Wind: &b%f&7m/s (gust %fm/s)", weather.windSpeed, weather.windGust);
|
|
||||||
colorize(sender, "&6Last update: &b%s UTC\n", formatTime(lastUpdate));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
colorize(sender, "&6No weather for you, try again in a second");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
package eu.m724.realweather.exception;
|
|
||||||
|
|
||||||
public class UserError extends Error {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 7152429719832602384L;
|
|
||||||
|
|
||||||
public UserError(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
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 CoordinatesLocationConverter coordinatesLocationConverter;
|
||||||
|
|
||||||
|
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 = 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, "");
|
||||||
|
} else {
|
||||||
|
sender.sendMessage("You can't run this command without arguments as console");
|
||||||
|
}
|
||||||
|
} else if (args.length >= 3) {
|
||||||
|
colorize(sender, "&cInvalid arguments, &7make sure it's &a\"/geo lat,lon\" &7or &a\"/geo x z\" &7or just &a\"/geo\"");
|
||||||
|
} else if (args.length == 2) {
|
||||||
|
double x, z;
|
||||||
|
|
||||||
|
try {
|
||||||
|
x = Double.parseDouble(args[0]);
|
||||||
|
z = Double.parseDouble(args[1]);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
colorize(sender, "&cInvalid arguments, &7make sure it's &a\"/geo lat,lon\" &7or &a\"/geo x z\" &7or just &a\"/geo\"");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Location location = new Location(null, x, 0, z);
|
||||||
|
Coordinates coordinates = coordinatesLocationConverter.locationToCoordinates(location);
|
||||||
|
|
||||||
|
colorize(sender, "");
|
||||||
|
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;
|
||||||
|
|
||||||
|
try {
|
||||||
|
String[] split = args[0].split(",");
|
||||||
|
latitude = Double.parseDouble(split[0]);
|
||||||
|
longitude = Double.parseDouble(split[1]);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
colorize(sender, "&cInvalid arguments, &7make sure it's &a\"/geo lat,lon\" &7or &a\"/geo x z\" &7or just &a\"/geo\"");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Coordinates coordinates = new Coordinates(latitude, longitude);
|
||||||
|
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, comma used as separator");
|
||||||
|
colorize(sender, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void colorize(CommandSender sender, String text, Object... format) {
|
||||||
|
sender.sendMessage(ChatColor.translateAlternateColorCodes('&', text.formatted(format)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,33 +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 int scaleLatitude;
|
|
||||||
public int 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.getInt("dimensions.latitude");
|
|
||||||
mapperConfig.scaleLongitude = configuration.getInt("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,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;
|
package eu.m724.realweather.thunder;
|
||||||
|
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Configuration of the thunder module
|
||||||
*
|
*
|
||||||
* @param enabled is thunder module enabled
|
* @param enabled Whether the thunder module is enabled
|
||||||
* @param provider The provider name, may or may not exist, if it doesn't, an error is thrown later
|
* @param provider The provider name
|
||||||
* @param refreshPeriod how often probe for strikes, in ticks
|
* @param apiKey API key for the provider, null if not necessary
|
||||||
*/
|
*/
|
||||||
public record ThunderConfig(
|
public record ThunderConfig(
|
||||||
boolean enabled,
|
boolean enabled,
|
||||||
String provider,
|
String provider,
|
||||||
int refreshPeriod
|
String apiKey
|
||||||
) {
|
) {
|
||||||
public static ThunderConfig fromConfiguration(ConfigurationSection configuration) {
|
public static ThunderConfig fromConfiguration(ConfigurationSection configuration) {
|
||||||
return new ThunderConfig(
|
return new ThunderConfig(
|
||||||
configuration.getBoolean("enabled"),
|
configuration.getBoolean("enabled"),
|
||||||
configuration.getString("provider"),
|
configuration.getString("provider"),
|
||||||
configuration.getInt("refreshPeriod")
|
configuration.getString("apiKey")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +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;
|
package eu.m724.realweather.thunder;
|
||||||
|
|
||||||
import eu.m724.realweather.Configs;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
|
|
||||||
import eu.m724.realweather.DebugLogger;
|
import eu.m724.realweather.DebugLogger;
|
||||||
import eu.m724.wtapi.provider.Providers;
|
import eu.m724.realweather.RealWeatherPlugin;
|
||||||
import eu.m724.wtapi.provider.exception.NoSuchProviderException;
|
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.exception.ProviderException;
|
||||||
import eu.m724.wtapi.provider.thunder.ThunderProvider;
|
import eu.m724.wtapi.provider.thunder.ThunderProvider;
|
||||||
|
|
||||||
public class ThunderMaster {
|
public class ThunderMaster {
|
||||||
private final ThunderConfig config = Configs.thunderConfig();
|
private final RealWeatherPlugin plugin;
|
||||||
|
private final String providerName;
|
||||||
|
private final String apiKey;
|
||||||
|
|
||||||
private ThunderProvider provider;
|
private ThunderProvider provider;
|
||||||
|
|
||||||
/**
|
public ThunderMaster(RealWeatherPlugin plugin, ThunderConfig config) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.providerName = config.provider();
|
||||||
|
this.apiKey = config.apiKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* initializes, tests and starts
|
* initializes, tests and starts
|
||||||
|
*
|
||||||
* @throws ProviderException if provider initialization failed
|
* @throws ProviderException if provider initialization failed
|
||||||
* @throws NoSuchProviderException config issue
|
* @throws NoSuchProviderException config issue
|
||||||
*/
|
*/
|
||||||
public void init(Plugin plugin) throws ProviderException, NoSuchProviderException {
|
public void init() throws ProviderException, NoSuchProviderException {
|
||||||
if (!config.enabled())
|
provider = Providers.getThunderProvider(providerName, apiKey);
|
||||||
return;
|
|
||||||
|
|
||||||
provider = Providers.getThunderProvider(config.provider(), null);
|
|
||||||
provider.init();
|
|
||||||
|
|
||||||
ThunderTask thunderTask = new ThunderTask(provider);
|
// TODO is this good? Probably not
|
||||||
thunderTask.init();
|
provider.registerStrikeConsumer(strike -> {
|
||||||
thunderTask.runTaskTimer(plugin, 0, config.refreshPeriod());
|
plugin.getServer().getPluginManager().callEvent(
|
||||||
|
new AsyncLightningStrikeEvent(strike)
|
||||||
DebugLogger.info("thunder loaded", 1);
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
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.finer("Done initializing");
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getLatency() {
|
public long getLatency() {
|
||||||
return provider.getLatency();
|
return provider.getLatency();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO should this be exposed?
|
||||||
|
public String getProviderName() {
|
||||||
|
return providerName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
package eu.m724.realweather.thunder;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.scheduler.BukkitRunnable;
|
|
||||||
|
|
||||||
import eu.m724.realweather.DebugLogger;
|
|
||||||
import eu.m724.realweather.GlobalConstants;
|
|
||||||
import eu.m724.realweather.mapper.Mapper;
|
|
||||||
import eu.m724.wtapi.provider.thunder.ThunderProvider;
|
|
||||||
import eu.m724.wtapi.provider.thunder.impl.blitzortung.TimedStrike;
|
|
||||||
|
|
||||||
class ThunderTask extends BukkitRunnable {
|
|
||||||
private final ThunderProvider thunderProvider;
|
|
||||||
private final Mapper mapper = GlobalConstants.getMapper();
|
|
||||||
private final ArrayList<TimedStrike> strikes = new ArrayList<>();
|
|
||||||
|
|
||||||
public ThunderTask(ThunderProvider thunderProvider) {
|
|
||||||
this.thunderProvider = thunderProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init() {
|
|
||||||
thunderProvider.registerStrikeHandler(coords -> {
|
|
||||||
strikes.add(new TimedStrike(System.currentTimeMillis() + thunderProvider.getDelay(), coords));
|
|
||||||
});
|
|
||||||
thunderProvider.start();
|
|
||||||
DebugLogger.info("thunderprovider started", 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
DebugLogger.info("thundertask running", 3);
|
|
||||||
thunderProvider.tick();
|
|
||||||
|
|
||||||
while (!strikes.isEmpty()) {
|
|
||||||
TimedStrike strike = strikes.removeFirst();
|
|
||||||
|
|
||||||
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,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;
|
package eu.m724.realweather.time;
|
||||||
|
|
||||||
|
import eu.m724.realweather.map.WorldList;
|
||||||
import org.bukkit.Server;
|
import org.bukkit.Server;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.scheduler.BukkitRunnable;
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
|
|
||||||
import eu.m724.realweather.DebugLogger;
|
import eu.m724.realweather.DebugLogger;
|
||||||
import eu.m724.realweather.GlobalConstants;
|
import eu.m724.realweather.map.CoordinatesLocationConverter;
|
||||||
import eu.m724.realweather.mapper.Mapper;
|
|
||||||
import eu.m724.wtapi.object.Coordinates;
|
import eu.m724.wtapi.object.Coordinates;
|
||||||
|
|
||||||
public class AsyncPlayerTimeTask extends BukkitRunnable {
|
public class AsyncPlayerTimeTask extends BukkitRunnable {
|
||||||
private final Server server = GlobalConstants.getPlugin().getServer();
|
|
||||||
private final Mapper mapper = GlobalConstants.getMapper();
|
|
||||||
|
|
||||||
private final TimeConverter timeConverter;
|
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.timeConverter = timeConverter;
|
||||||
|
this.server = server;
|
||||||
|
this.coordinatesLocationConverter = coordinatesLocationConverter;
|
||||||
|
this.worldList = worldList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
for (Player player : server.getOnlinePlayers()) {
|
for (Player player : server.getOnlinePlayers()) {
|
||||||
if (!player.hasPermission("realweather.dynamic")) continue;
|
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 time = timeConverter.calculateZoneOffset(coordinates.longitude());
|
||||||
long ticks = timeConverter.millisToTicks(time);
|
long ticks = timeConverter.millisToTicks(time);
|
||||||
|
|
||||||
player.setPlayerTime(ticks, true);
|
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;
|
package eu.m724.realweather.time;
|
||||||
|
|
||||||
|
import eu.m724.realweather.map.WorldList;
|
||||||
import org.bukkit.scheduler.BukkitRunnable;
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
|
|
||||||
import eu.m724.realweather.DebugLogger;
|
import eu.m724.realweather.DebugLogger;
|
||||||
import eu.m724.realweather.GlobalConstants;
|
import eu.m724.realweather.map.CoordinatesLocationConverter;
|
||||||
import eu.m724.realweather.mapper.Mapper;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This does world time, player time is basically offset of this, like timezone +0
|
* This does world time, player time is basically offset of this, like timezone +0
|
||||||
*/
|
*/
|
||||||
public class SyncTimeUpdateTask extends BukkitRunnable {
|
public class SyncTimeUpdateTask extends BukkitRunnable {
|
||||||
private final Mapper mapper = GlobalConstants.getMapper();
|
|
||||||
|
|
||||||
private final TimeConverter timeConverter;
|
private final TimeConverter timeConverter;
|
||||||
|
private final WorldList worldList;
|
||||||
|
|
||||||
private final long zoneOffset;
|
private final long zoneOffset;
|
||||||
|
|
||||||
SyncTimeUpdateTask(TimeConverter timeConverter, boolean dynamic) {
|
SyncTimeUpdateTask(TimeConverter timeConverter, WorldList worldList, CoordinatesLocationConverter coordinatesLocationConverter, boolean dynamic) {
|
||||||
this.timeConverter = timeConverter;
|
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
|
@Override
|
||||||
|
@ -27,10 +34,9 @@ public class SyncTimeUpdateTask extends BukkitRunnable {
|
||||||
|
|
||||||
long ticks = timeConverter.millisToTicks(time + zoneOffset);
|
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));
|
worldList.getIncludedWorlds().forEach(world -> world.setFullTime(ticks));
|
||||||
// TODO add world handlers to mapper and don't calculate time each run
|
// 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;
|
package eu.m724.realweather.time;
|
||||||
|
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
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;
|
package eu.m724.realweather.time;
|
||||||
|
|
||||||
public class TimeConverter {
|
public class TimeConverter {
|
||||||
public final double scale;
|
private final double scale;
|
||||||
|
|
||||||
public TimeConverter(double scale) {
|
public TimeConverter(double scale) {
|
||||||
|
if (scale <= 0.0) {
|
||||||
|
throw new IllegalArgumentException("Scale must be greater than 0.0");
|
||||||
|
}
|
||||||
|
|
||||||
this.scale = scale;
|
this.scale = scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Divides time by predefined scale<br>
|
* Divides time by predefined scale<br>
|
||||||
* ...slowing it down
|
* ...slowing it down (or speeding up)
|
||||||
*
|
*
|
||||||
* @param time unix milliseconds
|
* @param time unix milliseconds
|
||||||
* @return scaled unix milliseconds
|
* @return scaled unix milliseconds
|
||||||
|
@ -48,4 +57,8 @@ public class TimeConverter {
|
||||||
public long calculateZoneOffset(double longitude) {
|
public long calculateZoneOffset(double longitude) {
|
||||||
return (long) (longitude * 240000);
|
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;
|
package eu.m724.realweather.time;
|
||||||
|
|
||||||
import eu.m724.realweather.Configs;
|
import eu.m724.realweather.RealWeatherPlugin;
|
||||||
import org.bukkit.GameRule;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
|
|
||||||
import eu.m724.realweather.DebugLogger;
|
import eu.m724.realweather.DebugLogger;
|
||||||
import eu.m724.realweather.GlobalConstants;
|
import eu.m724.realweather.map.CoordinatesLocationConverter;
|
||||||
import eu.m724.realweather.mapper.Mapper;
|
import eu.m724.realweather.map.WorldList;
|
||||||
|
|
||||||
public class TimeMaster {
|
public class TimeMaster {
|
||||||
private final Mapper mapper = GlobalConstants.getMapper();
|
private final RealWeatherPlugin plugin;
|
||||||
private final Plugin plugin = GlobalConstants.getPlugin();
|
private final TimeConverter timeConverter;
|
||||||
private final TimeConfig timeConfig = Configs.timeConfig();
|
|
||||||
|
|
||||||
// TODO I don't want to initialize this here
|
private final boolean dynamic;
|
||||||
private final TimeConverter timeConverter = new TimeConverter(timeConfig.scale());
|
|
||||||
|
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() {
|
public TimeConverter getTimeConverter() {
|
||||||
return timeConverter;
|
return timeConverter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init() {
|
public boolean isDynamic() {
|
||||||
if (!timeConfig.enabled())
|
return dynamic;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 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.TimeConverter;
|
||||||
|
import eu.m724.realweather.time.TimeMaster;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
import org.bukkit.command.CommandExecutor;
|
import org.bukkit.command.CommandExecutor;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.entity.Player;
|
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 eu.m724.wtapi.object.Coordinates;
|
||||||
import net.md_5.bungee.api.ChatColor;
|
import net.md_5.bungee.api.ChatColor;
|
||||||
import net.md_5.bungee.api.chat.BaseComponent;
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
import net.md_5.bungee.api.chat.ComponentBuilder;
|
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||||
|
|
||||||
public class LocalTimeCommand implements CommandExecutor {
|
public class LocalTimeCommand implements CommandExecutor {
|
||||||
private final Mapper mapper = GlobalConstants.getMapper();
|
private final TimeMaster timeMaster;
|
||||||
private final TimeConfig timeConfig = Configs.timeConfig();
|
|
||||||
private final TimeConverter timeConverter;
|
private final TimeConverter timeConverter;
|
||||||
|
private final CoordinatesLocationConverter coordinatesLocationConverter;
|
||||||
|
|
||||||
public LocalTimeCommand(TimeConverter timeConverter) {
|
public LocalTimeCommand(TimeMaster timeMaster, CoordinatesLocationConverter coordinatesLocationConverter) {
|
||||||
this.timeConverter = timeConverter;
|
this.coordinatesLocationConverter = coordinatesLocationConverter;
|
||||||
|
this.timeMaster = timeMaster;
|
||||||
|
this.timeConverter = timeMaster.getTimeConverter();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -46,10 +51,10 @@ public class LocalTimeCommand implements CommandExecutor {
|
||||||
|
|
||||||
sender.spigot().sendMessage(component);
|
sender.spigot().sendMessage(component);
|
||||||
|
|
||||||
if (timeConfig.dynamic() && player != null && player.hasPermission("realweather.dynamic")) {
|
if (timeMaster.isDynamic() && player != null && player.hasPermission("realweather.dynamic")) {
|
||||||
Coordinates coordinates = mapper.locationToCoordinates(player.getLocation());
|
Coordinates coordinates = coordinatesLocationConverter.locationToCoordinates(player.getLocation());
|
||||||
|
|
||||||
long offsetTime = timeConverter.calculateZoneOffset(coordinates.longitude);
|
long offsetTime = timeConverter.calculateZoneOffset(coordinates.longitude());
|
||||||
long offsetTimeTicks = timeConverter.millisToTicks(offsetTime);
|
long offsetTimeTicks = timeConverter.millisToTicks(offsetTime);
|
||||||
boolean negative = offsetTime < 0;
|
boolean negative = offsetTime < 0;
|
||||||
|
|
|
@ -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;
|
package eu.m724.realweather.updater;
|
||||||
|
|
||||||
import eu.m724.jarupdater.environment.ConstantEnvironment;
|
import eu.m724.jarupdater.environment.ConstantEnvironment;
|
||||||
import eu.m724.jarupdater.updater.Updater;
|
import eu.m724.jarupdater.updater.Updater;
|
||||||
import eu.m724.jarupdater.verify.SignatureVerifier;
|
import eu.m724.jarupdater.verify.SignatureVerifier;
|
||||||
import eu.m724.jarupdater.verify.Verifier;
|
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.Downloader;
|
||||||
import eu.m724.jarupdater.download.SimpleDownloader;
|
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.GiteaMetadataDAO;
|
||||||
import eu.m724.jarupdater.live.MetadataDAO;
|
import eu.m724.jarupdater.live.MetadataDAO;
|
||||||
import eu.m724.jarupdater.live.MetadataFacade;
|
import eu.m724.jarupdater.live.MetadataFacade;
|
||||||
|
import eu.m724.realweather.RealWeatherPlugin;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
public class PluginUpdater extends Updater {
|
public class PluginUpdater extends Updater {
|
||||||
private final UpdaterConfig updaterConfig = Configs.updaterConfig();
|
private PluginUpdater(Environment environment, MetadataFacade metadataProvider, Downloader downloader, Verifier verifier) {
|
||||||
|
|
||||||
final Plugin plugin;
|
|
||||||
|
|
||||||
PluginUpdater(Plugin plugin, Environment environment, MetadataFacade metadataProvider, Downloader downloader, Verifier verifier) {
|
|
||||||
super(environment, metadataProvider, downloader, 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(
|
Environment environment = new ConstantEnvironment(
|
||||||
plugin.getDescription().getVersion(),
|
plugin.getDescription().getVersion(),
|
||||||
Configs.updaterConfig().channel(),
|
channel,
|
||||||
file.toPath()
|
file.toPath()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -46,13 +47,6 @@ public class PluginUpdater extends Updater {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new PluginUpdater(plugin, environment, metadataFacade, downloader, verifier);
|
return new PluginUpdater(environment, metadataFacade, downloader, verifier);
|
||||||
}
|
|
||||||
|
|
||||||
public void init() {
|
|
||||||
if (!updaterConfig.alert()) return;
|
|
||||||
|
|
||||||
UpdateNotifier updateNotifier = new UpdateNotifier(this, (version) -> {});
|
|
||||||
updateNotifier.register();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
package eu.m724.realweather.updater;
|
||||||
|
|
||||||
import java.util.concurrent.CompletionException;
|
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.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
|
@ -14,55 +20,51 @@ import eu.m724.jarupdater.object.Version;
|
||||||
import eu.m724.realweather.DebugLogger;
|
import eu.m724.realweather.DebugLogger;
|
||||||
|
|
||||||
public class UpdateNotifier extends BukkitRunnable implements Listener { // TODO move this to jarupdater
|
public class UpdateNotifier extends BukkitRunnable implements Listener { // TODO move this to jarupdater
|
||||||
|
private final Plugin plugin = RealWeatherPlugin.getInstance();
|
||||||
|
|
||||||
private final PluginUpdater updater;
|
private final PluginUpdater updater;
|
||||||
private final Consumer<Version> updateConsumer;
|
|
||||||
private final Plugin plugin;
|
|
||||||
|
|
||||||
private Version latestVersion;
|
private Version latestVersion;
|
||||||
|
|
||||||
public UpdateNotifier(PluginUpdater updater, Consumer<Version> updateConsumer) {
|
public UpdateNotifier(PluginUpdater updater) {
|
||||||
this.updater = updater;
|
this.updater = updater;
|
||||||
this.updateConsumer = updateConsumer;
|
|
||||||
this.plugin = updater.plugin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void register() {
|
public void register() {
|
||||||
this.runTaskTimerAsynchronously(updater.plugin, 0, 432000); // 6h
|
this.runTaskTimerAsynchronously(plugin, 0, 6 * 3600 * 20); // 6h
|
||||||
plugin.getServer().getPluginManager().registerEvents(this, plugin);
|
plugin.getServer().getPluginManager().registerEvents(this, plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
DebugLogger.info("update task running", 2);
|
DebugLogger.finer("Updater running");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
latestVersion = updater.getLatestVersion().join();
|
latestVersion = updater.getLatestVersion().join();
|
||||||
} catch (CompletionException e) {
|
} catch (CompletionException e) {
|
||||||
Throwable ex = e.getCause();
|
DebugLogger.warning("Error trying to contact update server:");
|
||||||
DebugLogger.info("Error trying to contact update server: %s", 0, ex.getMessage());
|
DebugLogger.warning(" %s", e.getCause().toString());
|
||||||
|
|
||||||
if (DebugLogger.getDebugLevel() >= 1)
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (latestVersion == null) return;
|
if (latestVersion == null) {
|
||||||
DebugLogger.info("RealWeather is outdated. /rwadmin update", 0);
|
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")) {
|
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
|
@EventHandler
|
||||||
public void onPlayerJoin(PlayerJoinEvent e) {
|
public void onPlayerJoin(PlayerJoinEvent e) {
|
||||||
Player player = e.getPlayer();
|
Player player = e.getPlayer();
|
||||||
if (latestVersion != null && player.hasPermission("realweather.update.notify")) {
|
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,137 +1,105 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
package eu.m724.realweather.weather;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.CompletionException;
|
|
||||||
|
|
||||||
import org.bukkit.Server;
|
import eu.m724.realweather.RealWeatherPlugin;
|
||||||
|
import eu.m724.realweather.api.weather.AsyncPlayerWeatherUpdateEvent;
|
||||||
|
import eu.m724.wtapi.provider.weather.WeatherQueryResult;
|
||||||
|
import org.bukkit.World;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.event.player.PlayerJoinEvent;
|
|
||||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
import org.bukkit.scheduler.BukkitRunnable;
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
|
|
||||||
import eu.m724.realweather.DebugLogger;
|
import eu.m724.realweather.DebugLogger;
|
||||||
import eu.m724.realweather.GlobalConstants;
|
import eu.m724.realweather.map.CoordinatesLocationConverter;
|
||||||
import eu.m724.realweather.mapper.Mapper;
|
|
||||||
import eu.m724.realweather.api.weather.AsyncWeatherUpdateEvent;
|
|
||||||
import eu.m724.wtapi.object.Coordinates;
|
import eu.m724.wtapi.object.Coordinates;
|
||||||
import eu.m724.wtapi.object.Weather;
|
import eu.m724.wtapi.object.Weather;
|
||||||
import eu.m724.wtapi.provider.weather.WeatherProvider;
|
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 WeatherProvider weatherProvider;
|
||||||
|
private final PlayerWeatherStore playerWeatherStore;
|
||||||
|
|
||||||
private final Mapper mapper = GlobalConstants.getMapper();
|
private final CoordinatesLocationConverter coordinatesLocationConverter;
|
||||||
private final Plugin plugin = GlobalConstants.getPlugin();
|
private final List<World> worlds;
|
||||||
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<>();
|
|
||||||
|
|
||||||
public DynamicWeatherRetriever(WeatherProvider weatherProvider) {
|
public DynamicWeatherRetriever(RealWeatherPlugin plugin, WeatherProvider weatherProvider, PlayerWeatherStore playerWeatherStore) {
|
||||||
|
this.plugin = plugin;
|
||||||
this.weatherProvider = weatherProvider;
|
this.weatherProvider = weatherProvider;
|
||||||
}
|
this.playerWeatherStore = playerWeatherStore;
|
||||||
|
|
||||||
private record CoordinatesResult(
|
this.coordinatesLocationConverter = plugin.getCoordinatesLocationConverter();
|
||||||
Map<Coordinates, Player[]> coordinatesPlayersMap,
|
this.worlds = plugin.getWorldList().getIncludedWorlds();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
DebugLogger.info("Weather retrieval", 3);
|
DebugLogger.finer("Updating weather");
|
||||||
long now = System.currentTimeMillis();
|
LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
|
||||||
|
|
||||||
CoordinatesResult coordinates;
|
float maxHourlyUpdates = (float) weatherProvider.getQuota().getHourlyQuota() / plugin.getServer().getOnlinePlayers().size();
|
||||||
if (now > nextUpdate) {
|
long updateDelay = Math.max(60, (long) (3600 / maxHourlyUpdates));
|
||||||
coordinates = makeCoordinates(server.getOnlinePlayers());
|
|
||||||
// calculate acceptable request rate based on weather provider quota and active players
|
DebugLogger.finer("Update delay: %d seconds", updateDelay);
|
||||||
float hourly = weatherProvider.getQuotaHourly() / (float)(weatherProvider.getBulkLimit() * coordinates.coordinatesCount());
|
|
||||||
nextUpdate = now + Math.max(60000, (long) (3600000 / hourly));
|
Player[] playersToUpdate = plugin.getServer().getOnlinePlayers().stream().filter(player -> {
|
||||||
DebugLogger.info("Next update in %d", 3, nextUpdate);
|
LocalDateTime lastUpdate = playerWeatherStore.getLastUpdate(player);
|
||||||
} else { // immediate update for those that need it right now
|
|
||||||
if (neededUpdate.isEmpty()) return;
|
if (!player.hasPermission("realweather.dynamic")) return false;
|
||||||
DebugLogger.info("Players in need of update: %d", 2, neededUpdate.size());
|
if (!worlds.contains(player.getWorld())) return false;
|
||||||
coordinates = makeCoordinates(neededUpdate);
|
|
||||||
neededUpdate.clear();
|
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 = coordinates.coordinatesPlayersMap().keySet().toArray(Coordinates[]::new);
|
|
||||||
|
|
||||||
// TODO change to Collection in wtapi? but some ordered kind
|
Coordinates[] coordinatesArray = Arrays.stream(playersToUpdate).map(player ->
|
||||||
CompletableFuture<Weather[]> weathersFuture =
|
coordinatesLocationConverter.locationToCoordinates(player.getLocation())
|
||||||
weatherProvider.getWeatherBulk(coordinatesArray);
|
).toArray(Coordinates[]::new);
|
||||||
|
|
||||||
try {
|
CompletableFuture<WeatherQueryResult> weathersFuture =
|
||||||
Weather[] weathers = weathersFuture.join();
|
weatherProvider.getWeather(coordinatesArray);
|
||||||
for (int i=0; i<weathers.length; i++) {
|
|
||||||
Weather weather = weathers[i];
|
|
||||||
for (Player player : coordinates.coordinatesPlayersMap().get(coordinatesArray[i])) {
|
|
||||||
playerWeatherCache.put(player, weather, now);
|
|
||||||
|
|
||||||
AsyncWeatherUpdateEvent event =
|
WeatherQueryResult result = weathersFuture.join();
|
||||||
new AsyncWeatherUpdateEvent(player, weather);
|
|
||||||
|
|
||||||
server.getPluginManager().callEvent(event);
|
if (result.exception() != null) {
|
||||||
}
|
DebugLogger.severe("An error has occurred retrieving weather data");
|
||||||
}
|
DebugLogger.warning(" " + result.exception());
|
||||||
} catch (CompletionException e) { // TODO handle finer exceptions
|
|
||||||
DebugLogger.info("An error occurred trying to retrieve weather data", 0);
|
|
||||||
|
|
||||||
if (DebugLogger.getDebugLevel() > 0)
|
return;
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DebugLogger.info("dynamic retriever done", 3);
|
Weather[] weathers = result.weathers();
|
||||||
}
|
|
||||||
|
for (int i=0; i<weathers.length; i++) {
|
||||||
@EventHandler
|
Weather weather = weathers[i];
|
||||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
Player player = playersToUpdate[i];
|
||||||
Player player = event.getPlayer();
|
|
||||||
neededUpdate.add(player);
|
playerWeatherStore.put(player, weather);
|
||||||
|
|
||||||
|
AsyncPlayerWeatherUpdateEvent event =
|
||||||
|
new AsyncPlayerWeatherUpdateEvent(player, weather);
|
||||||
|
|
||||||
|
plugin.getServer().getPluginManager().callEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
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,47 +1,53 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
package eu.m724.realweather.weather;
|
||||||
|
|
||||||
import eu.m724.realweather.DebugLogger;
|
import eu.m724.realweather.DebugLogger;
|
||||||
import eu.m724.realweather.GlobalConstants;
|
import eu.m724.realweather.RealWeatherPlugin;
|
||||||
import eu.m724.realweather.api.weather.AsyncWeatherUpdateEvent;
|
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.Coordinates;
|
||||||
import eu.m724.wtapi.object.Weather;
|
import eu.m724.wtapi.object.Weather;
|
||||||
import eu.m724.wtapi.provider.weather.WeatherProvider;
|
import eu.m724.wtapi.provider.weather.WeatherProvider;
|
||||||
import org.bukkit.plugin.Plugin;
|
import eu.m724.wtapi.provider.weather.WeatherQueryResult;
|
||||||
import org.bukkit.scheduler.BukkitRunnable;
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.CompletionException;
|
|
||||||
|
|
||||||
public class StaticWeatherRetriever extends BukkitRunnable {
|
public class StaticWeatherRetriever extends BukkitRunnable {
|
||||||
private final Plugin plugin = GlobalConstants.getPlugin();
|
private final RealWeatherPlugin plugin;
|
||||||
private final Mapper mapper = GlobalConstants.getMapper();
|
|
||||||
|
|
||||||
private final WeatherProvider weatherProvider;
|
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.weatherProvider = weatherProvider;
|
||||||
|
this.coordinatesLocationConverter = plugin.getCoordinatesLocationConverter();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Coordinates point = mapper.getPoint();
|
DebugLogger.finer("Updating weather");
|
||||||
CompletableFuture<Weather> weatherFuture = weatherProvider.getWeather(point);
|
|
||||||
|
|
||||||
try {
|
Coordinates point = coordinatesLocationConverter.getStaticPoint();
|
||||||
Weather weather = weatherFuture.join();
|
|
||||||
|
|
||||||
AsyncWeatherUpdateEvent event =
|
WeatherQueryResult result = weatherProvider.getWeather(point).join();
|
||||||
new AsyncWeatherUpdateEvent(null, weather);
|
|
||||||
|
|
||||||
plugin.getServer().getPluginManager().callEvent(event);
|
if (result.exception() != null) {
|
||||||
} catch (CompletionException e) { // TODO handle finer exceptions
|
DebugLogger.severe("An error has occurred retrieving weather data");
|
||||||
DebugLogger.info("An error occurred trying to retrieve weather data", 0);
|
DebugLogger.warning(" " + result.exception());
|
||||||
|
|
||||||
if (DebugLogger.getDebugLevel() > 0)
|
return;
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DebugLogger.info("static weather retriever is done", 3);
|
Weather weather = result.weathers()[0];
|
||||||
|
|
||||||
|
AsyncGlobalWeatherUpdateEvent event =
|
||||||
|
new AsyncGlobalWeatherUpdateEvent(weather);
|
||||||
|
|
||||||
|
plugin.getServer().getPluginManager().callEvent(event);
|
||||||
|
|
||||||
|
DebugLogger.fine("Done updating weather");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +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;
|
package eu.m724.realweather.weather;
|
||||||
|
|
||||||
import eu.m724.realweather.DebugLogger;
|
import eu.m724.realweather.DebugLogger;
|
||||||
import eu.m724.realweather.GlobalConstants;
|
import eu.m724.realweather.api.weather.AsyncGlobalWeatherUpdateEvent;
|
||||||
import eu.m724.realweather.api.weather.AsyncWeatherUpdateEvent;
|
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 eu.m724.wtapi.object.Weather;
|
||||||
import org.bukkit.WeatherType;
|
import org.bukkit.WeatherType;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
@ -11,41 +16,55 @@ import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
|
|
||||||
// TODO make weather more comprehensive
|
// TODO make weather more intricate
|
||||||
public class WeatherChanger implements Listener {
|
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)
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
public void onWeatherUpdate(AsyncWeatherUpdateEvent event) {
|
public void onGlobalWeatherUpdate(AsyncGlobalWeatherUpdateEvent event) {
|
||||||
|
if (event.isCancelled()) return;
|
||||||
|
|
||||||
|
Weather weather = event.getWeather();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
|
public void onPlayerWeatherUpdate(AsyncPlayerWeatherUpdateEvent event) {
|
||||||
|
if (event.isCancelled()) return;
|
||||||
|
|
||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
Weather weather = event.getWeather();
|
Weather weather = event.getWeather();
|
||||||
|
|
||||||
if (player != null) { // dynamic mode
|
if (weather.thundering() || weather.snowing() || weather.raining()) {
|
||||||
DebugLogger.info("Changing weather for player %s", 2, player.getName());
|
DebugLogger.finer("Changing weather for player %s to downfall", player.getName());
|
||||||
|
player.setPlayerWeather(WeatherType.DOWNFALL);
|
||||||
if (weather.isThundering() || weather.isSnowing() || weather.isRaining()) {
|
} else {
|
||||||
player.setPlayerWeather(WeatherType.DOWNFALL);
|
DebugLogger.finer("Changing weather for player %s to clear", player.getName());
|
||||||
} else {
|
player.setPlayerWeather(WeatherType.CLEAR);
|
||||||
player.setPlayerWeather(WeatherType.CLEAR);
|
|
||||||
}
|
|
||||||
} else { // static mode
|
|
||||||
DebugLogger.info("Changing weather static", 3);
|
|
||||||
mapper.getWorlds().forEach(w -> {
|
|
||||||
DebugLogger.info("Changing weather static in world %s", 2, w.getName());
|
|
||||||
if (weather.isThundering()) {
|
|
||||||
w.setClearWeatherDuration(0);
|
|
||||||
w.setWeatherDuration(120000);
|
|
||||||
w.setThunderDuration(120000);
|
|
||||||
} else if (weather.isRaining() || weather.isSnowing()) {
|
|
||||||
w.setClearWeatherDuration(0);
|
|
||||||
w.setWeatherDuration(120000);
|
|
||||||
w.setThunderDuration(0);
|
|
||||||
} else {
|
|
||||||
w.setClearWeatherDuration(120000);
|
|
||||||
w.setWeatherDuration(0);
|
|
||||||
w.setThunderDuration(0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
package eu.m724.realweather.weather;
|
||||||
|
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
@ -5,15 +10,15 @@ import org.bukkit.configuration.ConfigurationSection;
|
||||||
/**
|
/**
|
||||||
* Configuration of the weather module
|
* Configuration of the weather module
|
||||||
*
|
*
|
||||||
* @param enabled Is weather module enabled
|
* @param enabled Whether the weather module is enabled
|
||||||
* @param provider The provider name, may or may not exist, if it doesn't, an error is thrown later
|
* @param provider The provider name
|
||||||
* @param apiKey API key for the provider
|
* @param apiKey API key for the provider, null if not necessary
|
||||||
* @param dynamic dynamic mode, weather is per player or global
|
* @param dynamic dynamic mode, weather is per player or global
|
||||||
*/
|
*/
|
||||||
public record WeatherConfig(
|
public record WeatherConfig(
|
||||||
boolean enabled,
|
boolean enabled,
|
||||||
String provider,
|
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
|
boolean dynamic
|
||||||
) {
|
) {
|
||||||
public static WeatherConfig fromConfiguration(ConfigurationSection configuration) {
|
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;
|
package eu.m724.realweather.weather;
|
||||||
|
|
||||||
import eu.m724.realweather.Configs;
|
|
||||||
import org.bukkit.GameRule;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
|
|
||||||
import eu.m724.realweather.DebugLogger;
|
import eu.m724.realweather.DebugLogger;
|
||||||
import eu.m724.realweather.GlobalConstants;
|
import eu.m724.realweather.RealWeatherPlugin;
|
||||||
import eu.m724.realweather.mapper.Mapper;
|
|
||||||
import eu.m724.wtapi.provider.Providers;
|
|
||||||
import eu.m724.wtapi.provider.exception.NoSuchProviderException;
|
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.exception.ProviderException;
|
||||||
import eu.m724.wtapi.provider.weather.WeatherProvider;
|
import eu.m724.wtapi.provider.weather.WeatherProvider;
|
||||||
|
|
||||||
public class WeatherMaster {
|
public class WeatherMaster {
|
||||||
private final WeatherConfig config = Configs.weatherConfig();
|
private final RealWeatherPlugin plugin;
|
||||||
private final Mapper mapper = GlobalConstants.getMapper();
|
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
|
* initializes, tests and starts
|
||||||
* @throws ProviderException if provider initialization failed
|
* @throws ProviderException if provider initialization failed
|
||||||
* @throws NoSuchProviderException config issue
|
* @throws NoSuchProviderException config issue
|
||||||
*/
|
*/
|
||||||
public void init(Plugin plugin) throws ProviderException, NoSuchProviderException {
|
public void init() throws ProviderException, NoSuchProviderException {
|
||||||
if (!config.enabled()) {
|
WeatherProvider provider = Providers.getWeatherProvider(providerName, apiKey);
|
||||||
DebugLogger.info("weather module is disabled", 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
WeatherProvider provider = Providers.getWeatherProvider(config.provider(), config.apiKey());
|
|
||||||
provider.init();
|
provider.init();
|
||||||
|
|
||||||
if (config.dynamic()) {
|
if (dynamic) {
|
||||||
DynamicWeatherRetriever retriever = new DynamicWeatherRetriever(provider);
|
DebugLogger.finer("Weather is dynamic");
|
||||||
retriever.runTaskTimerAsynchronously(plugin,0, 1000);
|
|
||||||
plugin.getServer().getPluginManager().registerEvents(retriever, plugin);
|
DynamicWeatherRetriever retriever = new DynamicWeatherRetriever(plugin, provider, playerWeatherStore);
|
||||||
|
retriever.runTaskTimerAsynchronously(plugin,0, 200);
|
||||||
} else {
|
} else {
|
||||||
StaticWeatherRetriever retriever = new StaticWeatherRetriever(provider);
|
DebugLogger.finer("Weather is static");
|
||||||
|
|
||||||
|
StaticWeatherRetriever retriever = new StaticWeatherRetriever(plugin, provider);
|
||||||
retriever.runTaskTimerAsynchronously(plugin,0, 60000);
|
retriever.runTaskTimerAsynchronously(plugin,0, 60000);
|
||||||
}
|
}
|
||||||
|
|
||||||
plugin.getServer().getPluginManager().registerEvents(new WeatherChanger(), plugin);
|
plugin.getServer().getPluginManager().registerEvents(new WeatherChanger(plugin.getWorldList()), plugin);
|
||||||
|
|
||||||
mapper.registerWorldLoadConsumer(world -> world.setGameRule(GameRule.DO_WEATHER_CYCLE, false));
|
DebugLogger.finer("Done initializing");
|
||||||
mapper.registerWorldUnloadConsumer(world -> world.setGameRule(GameRule.DO_WEATHER_CYCLE, true));
|
|
||||||
|
// TODO replace that
|
||||||
DebugLogger.info("weather loaded", 1);
|
// 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* 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.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.wtapi.object.Weather;
|
||||||
|
|
||||||
|
public class LocalWeatherCommand implements CommandExecutor {
|
||||||
|
private final PlayerWeatherStore playerWeatherStore;
|
||||||
|
|
||||||
|
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 = playerWeatherStore.getWeather(player);
|
||||||
|
|
||||||
|
if (weather != null) {
|
||||||
|
LocalDateTime lastUpdate = playerWeatherStore.getLastUpdate(player);
|
||||||
|
|
||||||
|
colorize(sender, "&6Weather for: &b%f&7, &b%f &7(lat, lon)\n", weather.coordinates().latitude(), weather.coordinates().longitude());
|
||||||
|
|
||||||
|
List<String> states = new ArrayList<>(3);
|
||||||
|
|
||||||
|
if (weather.raining()) states.add("Raining");
|
||||||
|
if (weather.thundering()) states.add("Thundering");
|
||||||
|
if (weather.thundering()) states.add("Snowing");
|
||||||
|
|
||||||
|
if (!states.isEmpty()) {
|
||||||
|
colorize(sender, "&6" + String.join(", ", states));
|
||||||
|
}
|
||||||
|
|
||||||
|
colorize(sender, "&6Temperature: &b%.1f&7°C (feels like %.1f°C)", weather.temperatureCelsius(), weather.temperatureApparentCelsius());
|
||||||
|
|
||||||
|
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", lastUpdate.format(DateTimeFormatter.ofPattern("HH:mm:ss")));
|
||||||
|
} else {
|
||||||
|
colorize(sender, "&6No weather for you yet, try again in a few seconds");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void colorize(CommandSender sender, String text, Object... format) {
|
||||||
|
sender.sendMessage(ChatColor.translateAlternateColorCodes('&', text.formatted(format)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,22 +2,11 @@
|
||||||
### GENERAL SETTINGS ###
|
### GENERAL SETTINGS ###
|
||||||
############################
|
############################
|
||||||
|
|
||||||
# Master switch
|
# Not much to be found here! Explore the other files.
|
||||||
enabled: false
|
|
||||||
|
|
||||||
updater:
|
updater:
|
||||||
# Notify players and console about plugin updates
|
# Notify players and console about plugin updates
|
||||||
# This also controls automatic checking
|
# This also controls automatic checking
|
||||||
# You can still update with /rwadmin update
|
# You can still update with /rwadmin update
|
||||||
# Relevant permission node: realweather.update.notify
|
# Relevant permission node: realweather.update.notify
|
||||||
notify: true
|
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
|
|
|
@ -2,21 +2,20 @@
|
||||||
### MAP SETTINGS ###
|
### MAP SETTINGS ###
|
||||||
############################
|
############################
|
||||||
|
|
||||||
# true if the list below is a blacklist, false otherwise
|
|
||||||
worldBlacklist: false
|
|
||||||
worlds:
|
worlds:
|
||||||
- world
|
- world
|
||||||
|
isBlacklist: false
|
||||||
|
|
||||||
dimensions:
|
dimensions:
|
||||||
# Blocks per 1 deg, can't be decimal
|
# Blocks per 1 deg, must be integer
|
||||||
# latitude = -Z, longitude = X (to match with F3)
|
# latitude+ = Z-, longitude+ = X+ (to match with F3)
|
||||||
# The default (111000) assumes 1 block = 1 meter
|
# The default (111,000) assumes 1 block = 1 meter
|
||||||
latitude: 111000 # -9990000 to 9990000 Z
|
latitude: 111_000 # -9,990,000 to 9,990,000 Z. Wraps beyond that
|
||||||
longitude: 111000 # -19980000 to 19980000 X
|
longitude: 111_000 # -19,980,000 to 19,980,000 X
|
||||||
|
|
||||||
# To make it span world border to world border:
|
# To make it span world border to world border:
|
||||||
# latitude: 333333
|
# latitude: 333_333
|
||||||
# longitude: 166666
|
# longitude: 166_666
|
||||||
|
|
||||||
# For `static` mode
|
# For `static` mode
|
||||||
point:
|
point:
|
||||||
|
|
|
@ -6,10 +6,7 @@ enabled: false
|
||||||
|
|
||||||
# Currently only blitzortung
|
# Currently only blitzortung
|
||||||
provider: blitzortung
|
provider: blitzortung
|
||||||
|
# No API key needed
|
||||||
|
|
||||||
# How often should we poll for updates and spawn lightning
|
# Lightning is DYNAMIC.
|
||||||
# This is a synchronous task
|
# Settings are in map.yml
|
||||||
# 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
|
|
||||||
|
|
|
@ -2,20 +2,19 @@
|
||||||
### TIME SETTINGS ###
|
### TIME SETTINGS ###
|
||||||
############################
|
############################
|
||||||
|
|
||||||
# Warning: this removes sleep
|
# Warning: this removes sleep. No, not a bug, as you can't skip time IRL.
|
||||||
# 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?
|
|
||||||
|
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
# How this plugin affects your world:
|
# How time is applied:
|
||||||
# - static (false): time is the same across the world
|
# - 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
|
# Settings for both are in map.yml
|
||||||
dynamic: true
|
dynamic: true
|
||||||
|
|
||||||
# x in game day cycles in 1 irl day cycle
|
# Real days per in-game day
|
||||||
# 2.0 - time goes 2x SLOWER
|
# 2.0 - time goes 2x SLOWER
|
||||||
# 0.5 - time goes 2x FASTER
|
# 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
|
scale: 1.0
|
|
@ -9,13 +9,12 @@
|
||||||
|
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
# Currently only OpenWeatherMap
|
# Currently only OpenMeteo
|
||||||
provider: openweathermap
|
provider: openmeteo
|
||||||
# put your OpenWeatherMap api key
|
# No API key needed
|
||||||
apiKey: REPLACE ME
|
|
||||||
|
|
||||||
# How this plugin affects your world:
|
# How weather is applied:
|
||||||
# - static (false): weather is the same across the world
|
# - 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
|
# - 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
|
# Settings for both are in map.yml
|
||||||
dynamic: true
|
dynamic: true
|
|
@ -4,12 +4,13 @@ version: ${project.version}
|
||||||
author: Minecon724
|
author: Minecon724
|
||||||
website: https://www.spigotmc.org/resources/realweather-realtime.101599/
|
website: https://www.spigotmc.org/resources/realweather-realtime.101599/
|
||||||
|
|
||||||
api-version: 1.19.4
|
api-version: 1.16
|
||||||
load: STARTUP
|
load: STARTUP
|
||||||
main: eu.m724.realweather.RealWeatherPlugin
|
main: eu.m724.realweather.RealWeatherPlugin
|
||||||
|
|
||||||
libraries:
|
libraries:
|
||||||
- org.java-websocket:Java-WebSocket:1.5.7
|
- org.java-websocket:Java-WebSocket:1.6.0
|
||||||
|
- eu.m724:mstats-spigot:0.1.2
|
||||||
|
|
||||||
commands:
|
commands:
|
||||||
rwadmin:
|
rwadmin:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue