Weather Providers
A weather provider answers MythicSeasons' weather questions for a location. See API for setup.
How a question is answered
The WeatherRegistry asks its providers by priority, highest first, and returns the first answer that is not null (false, 0 and 0.0 are answers). When no provider answers, it returns null.
Built-in providers
| Priority | Provider | Answers | Returns null when |
|---|---|---|---|
| 1000 | Permanent weather spots | isRaining, isThundering |
No WorldGuard region with the permanent-weather flag at the middle column of the location's chunk, at the world's sea level, or no WorldGuard support (it needs WorldGuard and WorldEdit). See AtmosphereSpot. |
| 100 | Atmosphere simulation | All six | The simulation is off, does not run in the world or has no weather there yet, or a WorldGuard region sets season-atmosphere to deny (Land Claims). |
| 0 | Your provider, unless it sets a priority | What you answer | You have nothing to say. |
The simulation answers snow only where it also answers rain, and rain, snow and thunder only where its first solid cloud layer (the first in clouds.layers with opacity: 1.0) draws cloud above the location.
WeatherProvider
WeatherProvider (in net.taiyou.mythicseasons.api.weather) has only default methods: the six questions return null and priority() returns 0. Override what you answer, in a class, since it cannot be a lambda.
| Method | Returns | Answers | Unit |
|---|---|---|---|
priority() |
int |
Where the provider ranks. Higher is asked first. Default 0. |
none |
isRaining(Location loc) |
Boolean |
Whether it rains at loc. |
none |
isSnowing(Location loc) |
Boolean |
Whether the precipitation at loc falls as snow. |
none |
isThundering(Location loc) |
Boolean |
Whether a thunderstorm is overhead at loc. |
none |
isInCloud(Location loc) |
Boolean |
Whether loc is inside a cloud. |
none |
airTemperature(Location loc) |
Integer |
The air temperature at loc. |
Whole degrees Celsius |
surfaceWindSpeed(Location loc) |
Double |
The wind speed at the ground at loc. |
Metres per second |
- Return
nullwhenloc.getWorld()isnull. nullpasses a question on,falseanswers it: a provider that returnsfalseforisRainingin a world keeps the simulation from answering that question there.- Conditions test each question on its own, so answer
isSnowingastrueonly whereisRainingis alsotrue, as the simulation does.
Priority
- Above 100, your provider overrides the simulation. Above 1000, it overrides permanent spots.
- Below 100 (the default is 0), it answers only where the simulation has no answer, such as worlds without the simulation.
- Providers with the same priority are asked in the order they registered. MythicSeasons registers the built-in ones after every plugin has enabled, so at 100 or 1000 a provider registered in
onEnableis asked before them. - The registry reads
priority()only when providers register, not on every question, so return a constant.
Registering
Register in your onEnable and unregister in your onDisable:
WeatherRegistry registry = SeasonsAPI.getInstance().getWeatherRegistry();
registry.register(provider);
registry.unregister(provider);
| Method | Returns | Description |
|---|---|---|
register(WeatherProvider provider) |
void |
Adds the provider unless an equal one is registered, then sorts all providers by priority(). |
unregister(WeatherProvider provider) |
void |
Removes the provider, if registered. |
Both methods are safe from any thread and never block a question being answered. Registrations survive /ms reload, but not MythicSeasons being disabled and enabled again.
Reading the answers
The registry has one query method per question, returning null when no provider answered.
| Method | Returns |
|---|---|
isRaining(Location loc) |
Boolean |
isSnowing(Location loc) |
Boolean |
isThundering(Location loc) |
Boolean |
isInCloud(Location loc) |
Boolean |
airTemperature(Location loc) |
Integer |
surfaceWindSpeed(Location loc) |
Double |
Boolean raining = SeasonsAPI.getInstance().getWeatherRegistry().isRaining(player.getLocation());
Use
WeatherStateto get a fallback.
WeatherState
WeatherState is a final class of static methods that ask the registry and fall back to the server when no provider answers, like MythicSeasons' own conditions and placeholders. The first argument is MythicSeasons.getInstance() (from net.taiyou.mythicseasons). Call it only after MythicSeasons has finished starting.
| Method | Returns | When no provider answers |
|---|---|---|
isRaining(MythicSeasons main, Location loc) |
boolean |
world.hasStorm(), or false when the location has no world. |
isSnowing(MythicSeasons main, Location loc) |
boolean |
world.hasStorm() and a biome temperature below 0.15 at the block, or false when the location has no world. |
isThundering(MythicSeasons main, Location loc) |
boolean |
world.isThundering(), or false when the location has no world. |
isInCloud(MythicSeasons main, Location loc) |
boolean |
false. |
airTemperature(MythicSeasons main, Location loc) |
Integer |
The temperature system's air temperature, or null when that system is off (enabled: false in temperature.yml). |
airTemperature(MythicSeasons main, Entity e) |
Integer |
Whether or not a provider answers: the Location form at the entity's position, plus, for a player, the warmth of their blockheat lines with air=true while the temperature system is on. null when the Location form is null. |
surfaceWindSpeed(MythicSeasons main, Location loc) |
double |
0.0. |
With WorldGuard support loaded, isRaining and isThundering throw a NullPointerException for a location without a world, because the permanent spot provider is asked first.
boolean raining = WeatherState.isRaining(MythicSeasons.getInstance(), player.getLocation());
Integer air = WeatherState.airTemperature(MythicSeasons.getInstance(), player.getLocation());
Where the answers are used
| Question | Used by |
|---|---|
isRaining |
The raining test of the Atmosphere condition, the MythicMobs conditions raining and sunny, the weather each player sees, and the atmosphere-rain-start and atmosphere-rain-stop triggers. |
isSnowing |
The snowing test of atmosphere, the MythicMobs condition snowing, and the atmosphere-snow-start and atmosphere-snow-stop triggers. |
isThundering |
The storm test of atmosphere, the MythicMobs conditions thundering and stormy, the placeholders <mythicseasons.storm> and %ms_storm%, the thunder each player sees, and the atmosphere-storm-start and atmosphere-storm-stop triggers. |
isInCloud |
The incloud test of atmosphere, the placeholders <mythicseasons.incloud> and %ms_in_cloud%, and the atmosphere-cloud-enter and atmosphere-cloud-exit triggers. |
airTemperature |
The AtmosphereTemp condition and the placeholders <mythicseasons.airtemperature> and %ms_atmosphere_temperature%. |
surfaceWindSpeed |
The AtmosphereWind condition and the placeholders <mythicseasons.windspeed> and %ms_wind_speed%. |
Three of these differ:
- The weather a player sees follows the providers only while the atmosphere is on (
enabled: trueinatmosphere.yml) and a skill with theatmosphereweathermechanic, such as the bundledMS_Atmosphere, runs for the player. Thunder shows only while it rains. Without anisRaininganswer the player gets the server weather back, and with the atmosphere off the mechanic restores it for every player. - The skill triggers read the registry without the vanilla fallback of
WeatherState. Without an answer, rain, snow, storm and cloud count as absent, so a player who was in one gets its stop or exit trigger. - The MythicMobs conditions
raining,sunny,snowingandthunderingfollow the providers only on a MythicMobs build that lets plugins replace its built-in conditions. On other builds they read the vanilla weather, with a warning at startup.
While the simulation still loads the area around a player, the first two keep that player's last state for up to 30 seconds.
These skip the providers and read the simulation or the temperature system directly: wind direction, humidity, precipitation, cloud cover, cloud base and top, the wind push of the atmospherewind mechanic, and %ms_air_temperature%.
Threads
A provider is called on the thread that asks, never moved to the main thread:
- The main thread, for the weather skill triggers.
- The thread MythicMobs runs a skill on, for its conditions, placeholders and the
atmosphereweathermechanic. - The thread PlaceholderAPI is called on.
The weather triggers and the skills under skill-triggers.player-tick, such as MS_Atmosphere, ask several times for each player in a world with a season, every performance.skill-trigger-interval ticks (default 5).
So keep it thread-safe and fast: no blocking, no file or network access, and no reading of blocks, chunks or entities through the Bukkit API. Answer from a field you replace as a whole, such as a volatile reference to an immutable object updated on the main thread.
Example
A provider that fixes the weather in chosen worlds, ranked between the simulation and permanent spots:
import net.taiyou.mythicseasons.api.weather.WeatherProvider;
import org.bukkit.Location;
import org.bukkit.World;
import java.util.Set;
public final class FixedWeatherProvider implements WeatherProvider {
private final Set<String> worlds;
public FixedWeatherProvider(Set<String> worlds) {
this.worlds = Set.copyOf(worlds);
}
@Override
public int priority() {
return 200;
}
@Override
public Boolean isRaining(Location loc) {
return fixed(loc) ? Boolean.FALSE : null;
}
@Override
public Boolean isSnowing(Location loc) {
return fixed(loc) ? Boolean.FALSE : null;
}
@Override
public Boolean isThundering(Location loc) {
return fixed(loc) ? Boolean.FALSE : null;
}
@Override
public Integer airTemperature(Location loc) {
return fixed(loc) ? Integer.valueOf(22) : null;
}
@Override
public Double surfaceWindSpeed(Location loc) {
return fixed(loc) ? Double.valueOf(0.0) : null;
}
private boolean fixed(Location loc) {
World world = loc.getWorld();
return world != null && worlds.contains(world.getName());
}
}
Other worlds, and
isInCloudeverywhere, keep the simulation's answers.
import net.taiyou.mythicseasons.api.SeasonsAPI;
import net.taiyou.mythicseasons.api.weather.WeatherProvider;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.Set;
public final class MyPlugin extends JavaPlugin {
private WeatherProvider provider;
@Override
public void onEnable() {
provider = new FixedWeatherProvider(Set.of("minigame"));
SeasonsAPI.getInstance().getWeatherRegistry().register(provider);
}
@Override
public void onDisable() {
SeasonsAPI.getInstance().getWeatherRegistry().unregister(provider);
}
}
The plugin names MythicSeasons in
dependin itsplugin.yml.