Update from Git Manager GUI
This commit is contained in:
@@ -5,15 +5,13 @@ import net.kyori.adventure.text.format.NamedTextColor;
|
|||||||
import net.kyori.adventure.text.format.TextDecoration;
|
import net.kyori.adventure.text.format.TextDecoration;
|
||||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Block;
|
|
||||||
import org.bukkit.command.*;
|
import org.bukkit.command.*;
|
||||||
import org.bukkit.configuration.file.FileConfiguration;
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
import org.bukkit.entity.EntityType;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.entity.Snowman;
|
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.block.BlockFormEvent;
|
||||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||||
import org.bukkit.event.inventory.InventoryCloseEvent;
|
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||||
@@ -34,6 +32,7 @@ import java.time.ZoneId;
|
|||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import dev.viper.weathertime.MetricsManager;
|
import dev.viper.weathertime.MetricsManager;
|
||||||
import dev.viper.weathertime.UpdateChecker;
|
import dev.viper.weathertime.UpdateChecker;
|
||||||
@@ -47,16 +46,22 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
private String apiKey;
|
private String apiKey;
|
||||||
private int updateInterval;
|
private int updateInterval;
|
||||||
private final Map<String, WorldConfig> worldConfigs = new HashMap<>();
|
private final Map<String, WorldConfig> worldConfigs = new HashMap<>();
|
||||||
private final Map<String, WeatherTimeData> worldWeatherData = new HashMap<>();
|
private final Map<String, WeatherTimeData> worldWeatherData = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
// FIX: Scoreboard standardmäßig AUS – nur aktiv wenn der Spieler es in der GUI aktiviert hat
|
||||||
private final Map<UUID, Set<String>> playersWithDisplay = new HashMap<>();
|
private final Map<UUID, Set<String>> playersWithDisplay = new HashMap<>();
|
||||||
private final Map<UUID, String> playerLocations = new HashMap<>();
|
private final Map<UUID, String> playerLocations = new HashMap<>();
|
||||||
private final Map<UUID, Inventory> playerGUIs = new HashMap<>();
|
private final Map<UUID, Inventory> playerGUIs = new HashMap<>();
|
||||||
private final Map<UUID, Scoreboard> playerScoreboards = new HashMap<>();
|
private final Map<UUID, Scoreboard> playerScoreboards = new HashMap<>();
|
||||||
private final Map<UUID, DisplayMode> playerDisplayModes = new HashMap<>();
|
private final Map<UUID, DisplayMode> playerDisplayModes = new HashMap<>();
|
||||||
|
|
||||||
|
// FIX: Gecachte Wetterdaten pro Spieler (custom location) – vermeidet Blocking auf dem Main-Thread
|
||||||
|
private final Map<UUID, WeatherTimeData> playerWeatherCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private BukkitAudiences audiences;
|
private BukkitAudiences audiences;
|
||||||
private BukkitRunnable weatherUpdateTask;
|
private BukkitRunnable weatherUpdateTask;
|
||||||
private BukkitRunnable syncTask;
|
private BukkitRunnable syncTask;
|
||||||
private final Set<String> processedLocations = new HashSet<>();
|
private final Set<String> processedLocations = ConcurrentHashMap.newKeySet();
|
||||||
private FileConfiguration langConfig;
|
private FileConfiguration langConfig;
|
||||||
|
|
||||||
private MetricsManager metricsManager;
|
private MetricsManager metricsManager;
|
||||||
@@ -74,22 +79,26 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
|
|
||||||
updateChecker = new UpdateChecker(this, 127846);
|
updateChecker = new UpdateChecker(this, 127846);
|
||||||
updateChecker.getLatestVersion(version -> {
|
updateChecker.getLatestVersion(version -> {
|
||||||
|
// FIX: NumberFormatException absichern falls Version kein valides Format hat
|
||||||
String cleanVersion = version.replaceAll("[^0-9.]", "").trim();
|
String cleanVersion = version.replaceAll("[^0-9.]", "").trim();
|
||||||
String currentVersion = getDescription().getVersion();
|
String currentVersion = getDescription().getVersion();
|
||||||
|
|
||||||
if (isVersionNewer(currentVersion, cleanVersion)) {
|
try {
|
||||||
latestVersion = cleanVersion;
|
if (isVersionNewer(currentVersion, cleanVersion)) {
|
||||||
|
latestVersion = cleanVersion;
|
||||||
|
getLogger().info("Neue Version verfügbar: " + cleanVersion + " (aktuell: " + currentVersion + ")");
|
||||||
|
getLogger().info("Download: https://www.spigotmc.org/resources/127846/");
|
||||||
|
|
||||||
getLogger().info("Neue Version verfügbar: " + cleanVersion + " (aktuell: " + currentVersion + ")");
|
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||||
getLogger().info("Download: https://www.spigotmc.org/resources/127846/");
|
if (player.isOp()) {
|
||||||
|
player.sendMessage("§aEine neue Version von §eRealTimeWeather §aist verfügbar: §e"
|
||||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
+ cleanVersion + " §7(aktuell: " + currentVersion + ")");
|
||||||
if (player.isOp()) {
|
player.sendMessage("§eDownload: §bhttps://www.spigotmc.org/resources/127846/");
|
||||||
player.sendMessage("§aEine neue Version von §eRealTimeWeather §aist verfügbar: §e"
|
}
|
||||||
+ cleanVersion + " §7(aktuell: " + currentVersion + ")");
|
|
||||||
player.sendMessage("§eDownload: §bhttps://www.spigotmc.org/resources/127846/");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
getLogger().warning("Versionsvergleich fehlgeschlagen: " + e.getMessage());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -104,28 +113,39 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
startWeatherUpdateTask();
|
startWeatherUpdateTask();
|
||||||
startSyncTask();
|
startSyncTask();
|
||||||
initializePlayerDisplays();
|
initializePlayerDisplays();
|
||||||
|
|
||||||
getLogger().info("==============================================");
|
getLogger().info("==============================================");
|
||||||
getLogger().info("RealTimeWeather nutzt SCOREBOARD-Anzeige!");
|
getLogger().info("RealTimeWeather nutzt SCOREBOARD-Anzeige!");
|
||||||
getLogger().info("Die Wetteranzeige erscheint rechts am Bildschirm.");
|
getLogger().info("Das Scoreboard ist standardmäßig AUSGEBLENDET.");
|
||||||
|
getLogger().info("Spieler können es per /wetter gui oder /toggleweather aktivieren.");
|
||||||
getLogger().info("==============================================");
|
getLogger().info("==============================================");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIX: NumberFormatException durch try-catch abgesichert.
|
||||||
|
* Vergleicht semantische Versionen (z.B. "1.3.0" vs "1.4").
|
||||||
|
*/
|
||||||
private boolean isVersionNewer(String current, String latest) {
|
private boolean isVersionNewer(String current, String latest) {
|
||||||
|
if (current == null || current.isEmpty() || latest == null || latest.isEmpty()) return false;
|
||||||
String[] curParts = current.split("\\.");
|
String[] curParts = current.split("\\.");
|
||||||
String[] latParts = latest.split("\\.");
|
String[] latParts = latest.split("\\.");
|
||||||
int length = Math.max(curParts.length, latParts.length);
|
int length = Math.max(curParts.length, latParts.length);
|
||||||
|
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
int curNum = i < curParts.length ? Integer.parseInt(curParts[i]) : 0;
|
int curNum, latNum;
|
||||||
int latNum = i < latParts.length ? Integer.parseInt(latParts[i]) : 0;
|
try {
|
||||||
|
curNum = i < curParts.length ? Integer.parseInt(curParts[i].trim()) : 0;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
curNum = 0;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
latNum = i < latParts.length ? Integer.parseInt(latParts[i].trim()) : 0;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
latNum = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (latNum > curNum) {
|
if (latNum > curNum) return true;
|
||||||
return true;
|
if (latNum < curNum) return false;
|
||||||
}
|
|
||||||
if (latNum < curNum) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -168,10 +188,6 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
cfg.getString("defaults.location", "Berlin,de"),
|
cfg.getString("defaults.location", "Berlin,de"),
|
||||||
cfg.getString("defaults.units", "metric"),
|
cfg.getString("defaults.units", "metric"),
|
||||||
cfg.getString("defaults.time-format", "24h"),
|
cfg.getString("defaults.time-format", "24h"),
|
||||||
cfg.getBoolean("defaults.display-actionbar", true),
|
|
||||||
cfg.getBoolean("defaults.display-weather-icon", true),
|
|
||||||
cfg.getString("defaults.display-position", "top-right"),
|
|
||||||
cfg.getInt("defaults.padding-right", 100),
|
|
||||||
cfg.getBoolean("defaults.sync-in-game-weather", true)
|
cfg.getBoolean("defaults.sync-in-game-weather", true)
|
||||||
);
|
);
|
||||||
worldConfigs.put("defaults", defaultConfig);
|
worldConfigs.put("defaults", defaultConfig);
|
||||||
@@ -183,10 +199,6 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
cfg.getString("worlds." + worldName + ".location", defaultConfig.location),
|
cfg.getString("worlds." + worldName + ".location", defaultConfig.location),
|
||||||
cfg.getString("worlds." + worldName + ".units", defaultConfig.units),
|
cfg.getString("worlds." + worldName + ".units", defaultConfig.units),
|
||||||
cfg.getString("worlds." + worldName + ".time-format", defaultConfig.timeFormat),
|
cfg.getString("worlds." + worldName + ".time-format", defaultConfig.timeFormat),
|
||||||
cfg.getBoolean("worlds." + worldName + ".display-actionbar", defaultConfig.displayActionbar),
|
|
||||||
cfg.getBoolean("worlds." + worldName + ".display-weather-icon", defaultConfig.displayWeatherIcon),
|
|
||||||
cfg.getString("worlds." + worldName + ".display-position", defaultConfig.displayPosition),
|
|
||||||
cfg.getInt("worlds." + worldName + ".padding-right", defaultConfig.paddingRight),
|
|
||||||
cfg.getBoolean("worlds." + worldName + ".sync-in-game-weather", defaultConfig.syncInGameWeather)
|
cfg.getBoolean("worlds." + worldName + ".sync-in-game-weather", defaultConfig.syncInGameWeather)
|
||||||
);
|
);
|
||||||
worldConfigs.put(worldName, worldConfig);
|
worldConfigs.put(worldName, worldConfig);
|
||||||
@@ -218,25 +230,33 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
for (World world : Bukkit.getWorlds()) {
|
for (World world : Bukkit.getWorlds()) {
|
||||||
WorldConfig config = worldConfigs.getOrDefault(world.getName(), worldConfigs.get("defaults"));
|
WorldConfig config = worldConfigs.getOrDefault(world.getName(), worldConfigs.get("defaults"));
|
||||||
if (config.enabled) {
|
if (config.enabled) {
|
||||||
|
// Daylight-Cycle deaktivieren – Zeit wird vom Plugin gesetzt
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
GameRule<Boolean> doDaylightCycle = (GameRule<Boolean>) GameRule.getByName("doDaylightCycle");
|
GameRule<Boolean> doDaylightCycle = (GameRule<Boolean>) GameRule.getByName("doDaylightCycle");
|
||||||
if (doDaylightCycle != null) {
|
if (doDaylightCycle != null) {
|
||||||
world.setGameRule(doDaylightCycle, false);
|
world.setGameRule(doDaylightCycle, false);
|
||||||
}
|
}
|
||||||
|
// Weather-Cycle deaktivieren – verhindert dass Minecraft das Wetter
|
||||||
|
// nach ein paar Minuten eigenständig zurücksetzt
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
GameRule<Boolean> doWeatherCycle = (GameRule<Boolean>) GameRule.getByName("doWeatherCycle");
|
||||||
|
if (doWeatherCycle != null) {
|
||||||
|
world.setGameRule(doWeatherCycle, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIX: Scoreboard wird standardmäßig NICHT aktiviert.
|
||||||
|
* playersWithDisplay wird nur mit leeren Sets initialisiert.
|
||||||
|
* Kein createScoreboardForPlayer() beim Start.
|
||||||
|
*/
|
||||||
private void initializePlayerDisplays() {
|
private void initializePlayerDisplays() {
|
||||||
playersWithDisplay.clear();
|
playersWithDisplay.clear();
|
||||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||||
Set<String> worlds = new HashSet<>();
|
// Leeres Set – Spieler hat noch kein Scoreboard aktiviert
|
||||||
for (World world : Bukkit.getWorlds()) {
|
playersWithDisplay.put(player.getUniqueId(), new HashSet<>());
|
||||||
WorldConfig config = worldConfigs.getOrDefault(world.getName(), worldConfigs.get("defaults"));
|
|
||||||
if (config.enabled && config.displayActionbar) worlds.add(world.getName());
|
|
||||||
}
|
|
||||||
playersWithDisplay.put(player.getUniqueId(), worlds);
|
|
||||||
createScoreboardForPlayer(player);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,7 +292,7 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
private void updateWeatherDataForAllWorlds() {
|
private void updateWeatherDataForAllWorlds() {
|
||||||
for (World world : Bukkit.getWorlds()) {
|
for (World world : Bukkit.getWorlds()) {
|
||||||
WorldConfig config = worldConfigs.getOrDefault(world.getName(), worldConfigs.get("defaults"));
|
WorldConfig config = worldConfigs.getOrDefault(world.getName(), worldConfigs.get("defaults"));
|
||||||
|
|
||||||
if (!config.enabled) {
|
if (!config.enabled) {
|
||||||
worldWeatherData.remove(world.getName());
|
worldWeatherData.remove(world.getName());
|
||||||
continue;
|
continue;
|
||||||
@@ -301,26 +321,7 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
public void run() {
|
public void run() {
|
||||||
syncMinecraftTime(world, weatherData.getDateTime());
|
syncMinecraftTime(world, weatherData.getDateTime());
|
||||||
if (config.syncInGameWeather) {
|
if (config.syncInGameWeather) {
|
||||||
switch (weatherData.getWeatherMain().toLowerCase()) {
|
syncInGameWeather(world, weatherData);
|
||||||
case "rain":
|
|
||||||
case "drizzle":
|
|
||||||
world.setStorm(true);
|
|
||||||
world.setThundering(false);
|
|
||||||
break;
|
|
||||||
case "thunderstorm":
|
|
||||||
world.setStorm(true);
|
|
||||||
world.setThundering(true);
|
|
||||||
break;
|
|
||||||
case "snow":
|
|
||||||
world.setStorm(true);
|
|
||||||
world.setThundering(false);
|
|
||||||
spawnTemporarySnowInWorld(world, WeatherTimeSyncPlugin.this);
|
|
||||||
spawnTemporarySnowmen(world, 25, WeatherTimeSyncPlugin.this);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
world.setStorm(false);
|
|
||||||
world.setThundering(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.runTask(this);
|
}.runTask(this);
|
||||||
@@ -329,78 +330,153 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
getLogger().warning("Fehler beim Abrufen von Wetter/Zeit für Welt " + world.getName() + ": " + e.getMessage());
|
getLogger().warning("Fehler beim Abrufen von Wetter/Zeit für Welt " + world.getName() + ": " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Custom-Location-Wetterdaten für Spieler asynchron aktualisieren (kein Blocking auf dem Main-Thread)
|
||||||
|
refreshPlayerWeatherCaches();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void spawnTemporarySnowInWorld(World world, JavaPlugin plugin) {
|
/**
|
||||||
List<Block> snowBlocks = new ArrayList<>();
|
* Synchronisiert das Minecraft-Ingame-Wetter mit den echten API-Daten.
|
||||||
|
*
|
||||||
|
* Mapping der OpenWeatherMap-Wettertypen auf Minecraft:
|
||||||
|
*
|
||||||
|
* Thunderstorm → Gewitter (Sturm + Donner)
|
||||||
|
* Squall → Gewitter (böiger Wind/Sturm)
|
||||||
|
* Tornado → Gewitter (stärkste Stufe)
|
||||||
|
* Rain → Regen (Sturm, kein Donner)
|
||||||
|
* Drizzle → Nieselregen (Sturm, kein Donner)
|
||||||
|
* Snow → Schnee (Sturm; in kalten Biomen schneit es automatisch)
|
||||||
|
* Mist → Nebel/Dunst → klares Wetter (Minecraft hat keinen Nebel-Zustand)
|
||||||
|
* Smoke → Rauch → klares Wetter
|
||||||
|
* Haze → Dunst → klares Wetter
|
||||||
|
* Dust → Staub → klares Wetter
|
||||||
|
* Sand → Sandsturm → klares Wetter
|
||||||
|
* Ash → Asche → klares Wetter
|
||||||
|
* Fog → Nebel → klares Wetter
|
||||||
|
* Clouds → Bewölkt → klares Wetter (Minecraft hat keine reine Bewölkungsstufe)
|
||||||
|
* Clear → Sonnig → klares Wetter
|
||||||
|
*
|
||||||
|
* Wichtig: doWeatherCycle muss false sein (wird in setDoDaylightCycleForWorlds gesetzt),
|
||||||
|
* damit Minecraft das Wetter nicht nach wenigen Minuten eigenständig zurücksetzt.
|
||||||
|
*/
|
||||||
|
private void syncInGameWeather(World world, WeatherTimeData data) {
|
||||||
|
// Wetter-Duration etwas länger als das Update-Interval setzen,
|
||||||
|
// damit kein Flickern zwischen den API-Abfragen entsteht.
|
||||||
|
// Auch bei deaktiviertem doWeatherCycle empfohlen für maximale Stabilität.
|
||||||
|
int durationTicks = (updateInterval + 120) * 20;
|
||||||
|
|
||||||
for (org.bukkit.Chunk chunk : world.getLoadedChunks()) {
|
switch (data.getWeatherMain().toLowerCase()) {
|
||||||
int chunkX = chunk.getX() << 4;
|
|
||||||
int chunkZ = chunk.getZ() << 4;
|
|
||||||
|
|
||||||
for (int x = 0; x < 16; x++) {
|
// --- Gewitter-Gruppe ---
|
||||||
for (int z = 0; z < 16; z++) {
|
case "thunderstorm":
|
||||||
if (Math.random() > 0.7) continue;
|
// Klassisches Gewitter mit Blitz und Donner
|
||||||
int worldX = chunkX + x;
|
world.setStorm(true);
|
||||||
int worldZ = chunkZ + z;
|
world.setThundering(true);
|
||||||
int worldY = world.getHighestBlockYAt(worldX, worldZ);
|
world.setWeatherDuration(durationTicks);
|
||||||
|
world.setThunderDuration(durationTicks);
|
||||||
|
getLogger().fine("Wetter [" + world.getName() + "]: Gewitter (Thunderstorm)");
|
||||||
|
break;
|
||||||
|
|
||||||
Block ground = world.getBlockAt(worldX, worldY - 1, worldZ);
|
case "squall":
|
||||||
Block above = world.getBlockAt(worldX, worldY, worldZ);
|
// Bö / Sturmböe – starker Wind mit Gewittercharakter
|
||||||
|
world.setStorm(true);
|
||||||
|
world.setThundering(true);
|
||||||
|
world.setWeatherDuration(durationTicks);
|
||||||
|
world.setThunderDuration(durationTicks);
|
||||||
|
getLogger().fine("Wetter [" + world.getName() + "]: Sturmböe (Squall)");
|
||||||
|
break;
|
||||||
|
|
||||||
if (ground.getType().isSolid() && above.getType() == Material.AIR) {
|
case "tornado":
|
||||||
above.setType(Material.SNOW);
|
// Tornado – stärkste Wetterstufe, Gewitter als Annäherung
|
||||||
snowBlocks.add(above);
|
world.setStorm(true);
|
||||||
}
|
world.setThundering(true);
|
||||||
}
|
world.setWeatherDuration(durationTicks);
|
||||||
}
|
world.setThunderDuration(durationTicks);
|
||||||
|
getLogger().fine("Wetter [" + world.getName() + "]: Tornado");
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --- Regen-Gruppe ---
|
||||||
|
case "rain":
|
||||||
|
// Normaler Regen
|
||||||
|
world.setStorm(true);
|
||||||
|
world.setThundering(false);
|
||||||
|
world.setWeatherDuration(durationTicks);
|
||||||
|
getLogger().fine("Wetter [" + world.getName() + "]: Regen (Rain)");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "drizzle":
|
||||||
|
// Nieselregen – in Minecraft gleich wie Regen
|
||||||
|
world.setStorm(true);
|
||||||
|
world.setThundering(false);
|
||||||
|
world.setWeatherDuration(durationTicks);
|
||||||
|
getLogger().fine("Wetter [" + world.getName() + "]: Nieselregen (Drizzle)");
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --- Schnee ---
|
||||||
|
case "snow":
|
||||||
|
// Schneefall – Minecraft lässt es in kalten Biomen bei storm=true automatisch schneien.
|
||||||
|
// Kein Snowmen/Snow-Block-Hack nötig – das Spiel kümmert sich selbst darum.
|
||||||
|
world.setStorm(true);
|
||||||
|
world.setThundering(false);
|
||||||
|
world.setWeatherDuration(durationTicks);
|
||||||
|
getLogger().fine("Wetter [" + world.getName() + "]: Schnee (Snow)");
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --- Atmosphärische Zustände (Nebel, Dunst, Staub, etc.) ---
|
||||||
|
// Minecraft hat keinen echten Nebel-Wetterzustand.
|
||||||
|
// Klares Wetter ist die sinnvollste Annäherung.
|
||||||
|
case "mist":
|
||||||
|
case "smoke":
|
||||||
|
case "haze":
|
||||||
|
case "dust":
|
||||||
|
case "sand":
|
||||||
|
case "ash":
|
||||||
|
case "fog":
|
||||||
|
world.setStorm(false);
|
||||||
|
world.setThundering(false);
|
||||||
|
world.setWeatherDuration(durationTicks);
|
||||||
|
getLogger().fine("Wetter [" + world.getName() + "]: Atmosphärisch (" + data.getWeatherMain() + ") → klar");
|
||||||
|
break;
|
||||||
|
|
||||||
|
// --- Bewölkt & Klar ---
|
||||||
|
case "clouds":
|
||||||
|
// Minecraft unterscheidet nicht zwischen klar und bewölkt → kein Sturm
|
||||||
|
world.setStorm(false);
|
||||||
|
world.setThundering(false);
|
||||||
|
world.setWeatherDuration(durationTicks);
|
||||||
|
getLogger().fine("Wetter [" + world.getName() + "]: Bewölkt (Clouds) → klar");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "clear":
|
||||||
|
default:
|
||||||
|
// Klarer Himmel oder unbekannter Typ → sicherer Fallback auf klar
|
||||||
|
world.setStorm(false);
|
||||||
|
world.setThundering(false);
|
||||||
|
world.setWeatherDuration(durationTicks);
|
||||||
|
getLogger().fine("Wetter [" + world.getName() + "]: Klar (Clear / unbekannt: " + data.getWeatherMain() + ")");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
new BukkitRunnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
for (Block b : snowBlocks) {
|
|
||||||
if (b.getType() == Material.SNOW) {
|
|
||||||
b.setType(Material.AIR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.runTaskLater(plugin, 180 * 20L);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void spawnTemporarySnowmen(World world, int count, JavaPlugin plugin) {
|
/**
|
||||||
List<Snowman> snowmen = new ArrayList<>();
|
* Spieler mit eigener Location bekommen ihre Wetterdaten asynchron gecacht.
|
||||||
Random random = new Random();
|
* updateScoreboardForWorld() nutzt dann nur noch den Cache – kein API-Call auf dem Main-Thread.
|
||||||
|
*/
|
||||||
|
private void refreshPlayerWeatherCaches() {
|
||||||
|
for (Map.Entry<UUID, String> entry : playerLocations.entrySet()) {
|
||||||
|
UUID playerId = entry.getKey();
|
||||||
|
String location = entry.getValue();
|
||||||
|
if (location == null || location.isEmpty()) continue;
|
||||||
|
|
||||||
List<org.bukkit.Chunk> loadedChunks = Arrays.asList(world.getLoadedChunks());
|
// Läuft bereits asynchron (aufgerufen aus weatherUpdateTask)
|
||||||
|
try {
|
||||||
for (int i = 0; i < count; i++) {
|
WeatherFetcher fetcher = new WeatherFetcher(apiKey, location, "metric", getLogger());
|
||||||
if (loadedChunks.isEmpty()) break;
|
WeatherTimeData data = fetcher.fetchAsWeatherTimeData();
|
||||||
org.bukkit.Chunk chunk = loadedChunks.get(random.nextInt(loadedChunks.size()));
|
playerWeatherCache.put(playerId, data);
|
||||||
|
} catch (Exception e) {
|
||||||
int chunkX = chunk.getX() << 4;
|
getLogger().warning("Fehler beim Aktualisieren des Wetter-Caches für Spieler " + playerId + ": " + e.getMessage());
|
||||||
int chunkZ = chunk.getZ() << 4;
|
|
||||||
int x = chunkX + random.nextInt(16);
|
|
||||||
int z = chunkZ + random.nextInt(16);
|
|
||||||
int y = world.getHighestBlockYAt(x, z);
|
|
||||||
|
|
||||||
if (world.getBlockAt(x, y - 1, z).getType().isSolid()) {
|
|
||||||
Snowman snowman = (Snowman) world.spawnEntity(
|
|
||||||
new Location(world, x + 0.5, y, z + 0.5),
|
|
||||||
EntityType.SNOW_GOLEM
|
|
||||||
);
|
|
||||||
snowmen.add(snowman);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new BukkitRunnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
for (Snowman s : snowmen) {
|
|
||||||
if (!s.isDead()) s.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.runTaskLater(plugin, 180 * 20L);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void syncMinecraftTime(World world, ZonedDateTime dateTime) {
|
private void syncMinecraftTime(World world, ZonedDateTime dateTime) {
|
||||||
@@ -416,32 +492,44 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erstellt ein neues Scoreboard für den Spieler und weist es zu.
|
||||||
|
* Wird nur aufgerufen wenn der Spieler das Scoreboard explizit aktiviert.
|
||||||
|
*/
|
||||||
private void createScoreboardForPlayer(Player player) {
|
private void createScoreboardForPlayer(Player player) {
|
||||||
Scoreboard scoreboard = Bukkit.getScoreboardManager().getNewScoreboard();
|
Scoreboard scoreboard = Bukkit.getScoreboardManager().getNewScoreboard();
|
||||||
Objective objective = scoreboard.registerNewObjective("weather", "dummy",
|
Objective objective = scoreboard.registerNewObjective("weather", "dummy",
|
||||||
ChatColor.GOLD + "" + ChatColor.BOLD + "⛅ Wetter Info");
|
ChatColor.GOLD + "" + ChatColor.BOLD + "⛅ Wetter Info");
|
||||||
objective.setDisplaySlot(DisplaySlot.SIDEBAR);
|
objective.setDisplaySlot(DisplaySlot.SIDEBAR);
|
||||||
|
|
||||||
player.setScoreboard(scoreboard);
|
player.setScoreboard(scoreboard);
|
||||||
playerScoreboards.put(player.getUniqueId(), scoreboard);
|
playerScoreboards.put(player.getUniqueId(), scoreboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entfernt das Scoreboard vom Spieler (setzt auf Main-Scoreboard zurück).
|
||||||
|
*/
|
||||||
|
private void removeScoreboardFromPlayer(Player player) {
|
||||||
|
player.setScoreboard(Bukkit.getScoreboardManager().getMainScoreboard());
|
||||||
|
playerScoreboards.remove(player.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIX: API-Calls nur noch aus dem Cache (playerWeatherCache) lesen.
|
||||||
|
* Kein blockierender fetchAsWeatherTimeData()-Aufruf auf dem Main-Thread mehr.
|
||||||
|
*/
|
||||||
private void updateScoreboardForWorld(World world, WeatherTimeData data) {
|
private void updateScoreboardForWorld(World world, WeatherTimeData data) {
|
||||||
WorldConfig config = worldConfigs.getOrDefault(world.getName(), worldConfigs.get("defaults"));
|
WorldConfig config = worldConfigs.getOrDefault(world.getName(), worldConfigs.get("defaults"));
|
||||||
|
|
||||||
if (!config.enabled || !config.displayActionbar) {
|
if (!config.enabled) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Player player : world.getPlayers()) {
|
for (Player player : world.getPlayers()) {
|
||||||
Set<String> displayWorlds = playersWithDisplay.getOrDefault(player.getUniqueId(), new HashSet<>());
|
Set<String> displayWorlds = playersWithDisplay.getOrDefault(player.getUniqueId(), new HashSet<>());
|
||||||
|
|
||||||
if (!displayWorlds.contains(world.getName())) {
|
if (!displayWorlds.contains(world.getName())) {
|
||||||
// Scoreboard entfernen wenn disabled
|
// Scoreboard entfernen wenn für diese Welt deaktiviert
|
||||||
Scoreboard scoreboard = playerScoreboards.get(player.getUniqueId());
|
if (playerScoreboards.containsKey(player.getUniqueId())) {
|
||||||
if (scoreboard != null) {
|
removeScoreboardFromPlayer(player);
|
||||||
player.setScoreboard(Bukkit.getScoreboardManager().getMainScoreboard());
|
|
||||||
playerScoreboards.remove(player.getUniqueId());
|
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -456,19 +544,19 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
if (objective == null) continue;
|
if (objective == null) continue;
|
||||||
|
|
||||||
// Alte Scores löschen
|
// Alte Scores löschen
|
||||||
for (String entry : scoreboard.getEntries()) {
|
for (String entry : new HashSet<>(scoreboard.getEntries())) {
|
||||||
scoreboard.resetScores(entry);
|
scoreboard.resetScores(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIX: Wetterdaten aus Cache holen statt API-Call auf Main-Thread
|
||||||
WeatherTimeData playerData = data;
|
WeatherTimeData playerData = data;
|
||||||
String location = playerLocations.getOrDefault(player.getUniqueId(), "");
|
String location = playerLocations.getOrDefault(player.getUniqueId(), "");
|
||||||
if (!location.isEmpty()) {
|
if (!location.isEmpty()) {
|
||||||
try {
|
WeatherTimeData cached = playerWeatherCache.get(player.getUniqueId());
|
||||||
WeatherFetcher fetcher = new WeatherFetcher(apiKey, location, config.units, getLogger());
|
if (cached != null) {
|
||||||
playerData = fetcher.fetchAsWeatherTimeData();
|
playerData = cached;
|
||||||
} catch (Exception e) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
// Kein else – bei fehlendem Cache einfach Welt-Daten verwenden
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display Mode des Spielers
|
// Display Mode des Spielers
|
||||||
@@ -491,8 +579,8 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
ChatColor windColor = WeatherColors.getWindSpeedColor(playerData.getWindSpeed());
|
ChatColor windColor = WeatherColors.getWindSpeedColor(playerData.getWindSpeed());
|
||||||
|
|
||||||
// Trennlinie
|
// Trennlinie
|
||||||
String separator = ChatColor.DARK_GRAY + "~" + ChatColor.GRAY + "~" + ChatColor.DARK_GRAY + "~" +
|
String separator = ChatColor.DARK_GRAY + "~" + ChatColor.GRAY + "~" + ChatColor.DARK_GRAY + "~" +
|
||||||
ChatColor.GRAY + "~" + ChatColor.DARK_GRAY + "~" + ChatColor.GRAY + "~" +
|
ChatColor.GRAY + "~" + ChatColor.DARK_GRAY + "~" + ChatColor.GRAY + "~" +
|
||||||
ChatColor.DARK_GRAY + "~" + ChatColor.GRAY + "~" + ChatColor.DARK_GRAY + "~";
|
ChatColor.DARK_GRAY + "~" + ChatColor.GRAY + "~" + ChatColor.DARK_GRAY + "~";
|
||||||
|
|
||||||
int line = 20;
|
int line = 20;
|
||||||
@@ -517,20 +605,17 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
objective.getScore(weatherColor + weatherIcon + " " + ChatColor.GRAY + weatherMain).setScore(line--);
|
objective.getScore(weatherColor + weatherIcon + " " + ChatColor.GRAY + weatherMain).setScore(line--);
|
||||||
objective.getScore(tempColor + "🌡 " + String.format("%.1f%s", temp, tempUnit)).setScore(line--);
|
objective.getScore(tempColor + "🌡 " + String.format("%.1f%s", temp, tempUnit)).setScore(line--);
|
||||||
objective.getScore(humidityColor + "💧 " + playerData.getHumidity() + "%").setScore(line--);
|
objective.getScore(humidityColor + "💧 " + playerData.getHumidity() + "%").setScore(line--);
|
||||||
|
|
||||||
// Windrichtung mit Pfeil
|
|
||||||
String windDir = WindDirection.getDirection(playerData.getWindDeg());
|
String windDir = WindDirection.getDirection(playerData.getWindDeg());
|
||||||
String windArrow = WindDirection.getArrow(playerData.getWindDeg());
|
String windArrow = WindDirection.getArrow(playerData.getWindDeg());
|
||||||
objective.getScore(windColor + "🌬 " + String.format("%.1f", playerData.getWindSpeed()) +
|
objective.getScore(windColor + "🌬 " + String.format("%.1f", playerData.getWindSpeed()) +
|
||||||
(config.units.equalsIgnoreCase("metric") ? " m/s " : " mph ") + windArrow + " " + windDir).setScore(line--);
|
(config.units.equalsIgnoreCase("metric") ? " m/s " : " mph ") + windArrow + " " + windDir).setScore(line--);
|
||||||
|
|
||||||
objective.getScore(separator + "2").setScore(line--);
|
objective.getScore(separator + "2").setScore(line--);
|
||||||
objective.getScore(ChatColor.GOLD + "🌅 " + playerData.getSunrise().format(DateTimeFormatter.ofPattern("HH:mm"))).setScore(line--);
|
objective.getScore(ChatColor.GOLD + "🌅 " + playerData.getSunrise().format(DateTimeFormatter.ofPattern("HH:mm"))).setScore(line--);
|
||||||
objective.getScore(ChatColor.DARK_RED + "🌇 " + playerData.getSunset().format(DateTimeFormatter.ofPattern("HH:mm"))).setScore(line--);
|
objective.getScore(ChatColor.DARK_RED + "🌇 " + playerData.getSunset().format(DateTimeFormatter.ofPattern("HH:mm"))).setScore(line--);
|
||||||
|
|
||||||
// Mondphase
|
|
||||||
objective.getScore(ChatColor.LIGHT_PURPLE + MoonPhase.getEmoji(world) + " " + MoonPhase.getName(world)).setScore(line--);
|
objective.getScore(ChatColor.LIGHT_PURPLE + MoonPhase.getEmoji(world) + " " + MoonPhase.getName(world)).setScore(line--);
|
||||||
|
|
||||||
objective.getScore(separator + "3").setScore(line--);
|
objective.getScore(separator + "3").setScore(line--);
|
||||||
objective.getScore(ChatColor.DARK_AQUA + "📍 " + cityName).setScore(line--);
|
objective.getScore(ChatColor.DARK_AQUA + "📍 " + cityName).setScore(line--);
|
||||||
objective.getScore(" ").setScore(line--);
|
objective.getScore(" ").setScore(line--);
|
||||||
@@ -543,42 +628,33 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
objective.getScore(separator + "1").setScore(line--);
|
objective.getScore(separator + "1").setScore(line--);
|
||||||
objective.getScore(weatherColor + weatherIcon + " " + ChatColor.GRAY + weatherMain).setScore(line--);
|
objective.getScore(weatherColor + weatherIcon + " " + ChatColor.GRAY + weatherMain).setScore(line--);
|
||||||
objective.getScore(tempColor + "🌡 " + String.format("%.1f%s", temp, tempUnit)).setScore(line--);
|
objective.getScore(tempColor + "🌡 " + String.format("%.1f%s", temp, tempUnit)).setScore(line--);
|
||||||
|
|
||||||
// Gefühlte Temperatur
|
|
||||||
double feelsLike = playerData.getFeelsLike(config.units.equalsIgnoreCase("metric") ? "C" : "F");
|
double feelsLike = playerData.getFeelsLike(config.units.equalsIgnoreCase("metric") ? "C" : "F");
|
||||||
ChatColor feelsLikeColor = WeatherColors.getTemperatureColor(playerData.getFeelsLike("C"));
|
ChatColor feelsLikeColor = WeatherColors.getTemperatureColor(playerData.getFeelsLike("C"));
|
||||||
objective.getScore(feelsLikeColor + "🌡️ Gefühlt: " + String.format("%.1f%s", feelsLike, tempUnit)).setScore(line--);
|
objective.getScore(feelsLikeColor + "🌡️ Gefühlt: " + String.format("%.1f%s", feelsLike, tempUnit)).setScore(line--);
|
||||||
|
|
||||||
objective.getScore(humidityColor + "💧 " + playerData.getHumidity() + "%").setScore(line--);
|
objective.getScore(humidityColor + "💧 " + playerData.getHumidity() + "%").setScore(line--);
|
||||||
|
|
||||||
// Windrichtung mit Pfeil
|
|
||||||
String windDir = WindDirection.getDirection(playerData.getWindDeg());
|
String windDir = WindDirection.getDirection(playerData.getWindDeg());
|
||||||
String windArrow = WindDirection.getArrow(playerData.getWindDeg());
|
String windArrow = WindDirection.getArrow(playerData.getWindDeg());
|
||||||
objective.getScore(windColor + "🌬 " + String.format("%.1f", playerData.getWindSpeed()) +
|
objective.getScore(windColor + "🌬 " + String.format("%.1f", playerData.getWindSpeed()) +
|
||||||
(config.units.equalsIgnoreCase("metric") ? " m/s " : " mph ") + windArrow + " " + windDir).setScore(line--);
|
(config.units.equalsIgnoreCase("metric") ? " m/s " : " mph ") + windArrow + " " + windDir).setScore(line--);
|
||||||
|
|
||||||
// Luftdruck
|
|
||||||
objective.getScore(ChatColor.GRAY + "📊 " + playerData.getPressure() + " hPa").setScore(line--);
|
objective.getScore(ChatColor.GRAY + "📊 " + playerData.getPressure() + " hPa").setScore(line--);
|
||||||
|
|
||||||
// Wolkendichte
|
|
||||||
objective.getScore(ChatColor.WHITE + "☁️ Wolken: " + playerData.getClouds() + "%").setScore(line--);
|
objective.getScore(ChatColor.WHITE + "☁️ Wolken: " + playerData.getClouds() + "%").setScore(line--);
|
||||||
|
|
||||||
// Sichtweite
|
|
||||||
int visKm = playerData.getVisibility() / 1000;
|
int visKm = playerData.getVisibility() / 1000;
|
||||||
objective.getScore(ChatColor.BLUE + "👁️ Sicht: " + visKm + " km").setScore(line--);
|
objective.getScore(ChatColor.BLUE + "👁️ Sicht: " + visKm + " km").setScore(line--);
|
||||||
|
|
||||||
// UV-Index
|
|
||||||
int uvIndex = playerData.getUVIndex();
|
int uvIndex = playerData.getUVIndex();
|
||||||
ChatColor uvColor = WeatherColors.getUVIndexColor(uvIndex);
|
ChatColor uvColor = WeatherColors.getUVIndexColor(uvIndex);
|
||||||
objective.getScore(uvColor + "☀️ UV: " + uvIndex).setScore(line--);
|
objective.getScore(uvColor + "☀️ UV: " + uvIndex).setScore(line--);
|
||||||
|
|
||||||
objective.getScore(separator + "2").setScore(line--);
|
objective.getScore(separator + "2").setScore(line--);
|
||||||
objective.getScore(ChatColor.GOLD + "🌅 " + playerData.getSunrise().format(DateTimeFormatter.ofPattern("HH:mm"))).setScore(line--);
|
objective.getScore(ChatColor.GOLD + "🌅 " + playerData.getSunrise().format(DateTimeFormatter.ofPattern("HH:mm"))).setScore(line--);
|
||||||
objective.getScore(ChatColor.DARK_RED + "🌇 " + playerData.getSunset().format(DateTimeFormatter.ofPattern("HH:mm"))).setScore(line--);
|
objective.getScore(ChatColor.DARK_RED + "🌇 " + playerData.getSunset().format(DateTimeFormatter.ofPattern("HH:mm"))).setScore(line--);
|
||||||
|
|
||||||
// Mondphase
|
|
||||||
objective.getScore(ChatColor.LIGHT_PURPLE + MoonPhase.getPhase(world)).setScore(line--);
|
objective.getScore(ChatColor.LIGHT_PURPLE + MoonPhase.getPhase(world)).setScore(line--);
|
||||||
|
|
||||||
objective.getScore(separator + "3").setScore(line--);
|
objective.getScore(separator + "3").setScore(line--);
|
||||||
objective.getScore(ChatColor.DARK_AQUA + "📍 " + cityName).setScore(line--);
|
objective.getScore(ChatColor.DARK_AQUA + "📍 " + cityName).setScore(line--);
|
||||||
objective.getScore(" ").setScore(line--);
|
objective.getScore(" ").setScore(line--);
|
||||||
@@ -598,30 +674,81 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
String localizedWeather = getLocalizedWeatherMain(data.getWeatherMain(), countryCode);
|
String localizedWeather = getLocalizedWeatherMain(data.getWeatherMain(), countryCode);
|
||||||
String tempUnit = config.units.equalsIgnoreCase("metric") ? "°C" : "°F";
|
String tempUnit = config.units.equalsIgnoreCase("metric") ? "°C" : "°F";
|
||||||
String city = config.location.split(",")[0];
|
String city = config.location.split(",")[0];
|
||||||
String message = String.format("%s: %s, %.1f%s", city, localizedWeather, data.getTemp(config.units.equalsIgnoreCase("metric") ? "C" : "F"), tempUnit);
|
String message = String.format("%s: %s, %.1f%s", city, localizedWeather,
|
||||||
|
data.getTemp(config.units.equalsIgnoreCase("metric") ? "C" : "F"), tempUnit);
|
||||||
player.sendMessage(ChatColor.GREEN + message);
|
player.sendMessage(ChatColor.GREEN + message);
|
||||||
|
|
||||||
|
// Scoreboard-Status prüfen: wenn in neuer Welt nicht aktiviert → entfernen
|
||||||
|
Set<String> displayWorlds = playersWithDisplay.getOrDefault(player.getUniqueId(), new HashSet<>());
|
||||||
|
if (!displayWorlds.contains(world.getName()) && playerScoreboards.containsKey(player.getUniqueId())) {
|
||||||
|
removeScoreboardFromPlayer(player);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIX: Beim Join wird das Scoreboard NICHT automatisch aktiviert.
|
||||||
|
* Spieler starten mit leerem Display-Set.
|
||||||
|
*/
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerJoinForDisplay(PlayerJoinEvent event) {
|
public void onPlayerJoinForDisplay(PlayerJoinEvent event) {
|
||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
Set<String> worlds = new HashSet<>();
|
// Nur leeres Set initialisieren – kein automatisches Scoreboard
|
||||||
for (World world : Bukkit.getWorlds()) {
|
playersWithDisplay.put(player.getUniqueId(), new HashSet<>());
|
||||||
WorldConfig config = worldConfigs.getOrDefault(world.getName(), worldConfigs.get("defaults"));
|
|
||||||
if (config.enabled && config.displayActionbar) {
|
// Gespeicherte Location laden (falls vorhanden)
|
||||||
worlds.add(world.getName());
|
PlayerConfig playerConfig = new PlayerConfig(player.getUniqueId());
|
||||||
}
|
String savedLocation = playerConfig.getLocation();
|
||||||
|
if (savedLocation != null && !savedLocation.isEmpty()) {
|
||||||
|
playerLocations.put(player.getUniqueId(), savedLocation);
|
||||||
}
|
}
|
||||||
playersWithDisplay.put(player.getUniqueId(), worlds);
|
|
||||||
createScoreboardForPlayer(player);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verhindert dass Schnee ingame liegenbleibt wenn das Plugin Schneewetter setzt.
|
||||||
|
*
|
||||||
|
* Minecraft akkumuliert Schneelagen (SNOW) und Eis (ICE / FROSTED_ICE) über Zeit
|
||||||
|
* in kalten Biomen sobald storm=true gesetzt ist. Da wir das Wetter dauerhaft
|
||||||
|
* steuern, würde Schnee sonst endlos wachsen.
|
||||||
|
*
|
||||||
|
* Lösung: BlockFormEvent abfangen und canceln wenn:
|
||||||
|
* - Die Welt vom Plugin verwaltet wird (enabled + syncInGameWeather)
|
||||||
|
* - Das aktuelle API-Wetter "Snow" ist
|
||||||
|
* - Der entstehende Block eine Schneelage oder Eis ist
|
||||||
|
*/
|
||||||
|
@EventHandler(ignoreCancelled = true)
|
||||||
|
public void onBlockForm(BlockFormEvent event) {
|
||||||
|
World world = event.getBlock().getWorld();
|
||||||
|
WorldConfig config = worldConfigs.getOrDefault(world.getName(), worldConfigs.get("defaults"));
|
||||||
|
|
||||||
|
// Nur in vom Plugin verwalteten Welten eingreifen
|
||||||
|
if (!config.enabled || !config.syncInGameWeather) return;
|
||||||
|
|
||||||
|
WeatherTimeData data = worldWeatherData.get(world.getName());
|
||||||
|
if (data == null) return;
|
||||||
|
|
||||||
|
// Nur bei Schnee-Wetter aktiv
|
||||||
|
if (!data.getWeatherMain().equalsIgnoreCase("snow")) return;
|
||||||
|
|
||||||
|
Material formed = event.getNewState().getType();
|
||||||
|
if (formed == Material.SNOW || formed == Material.ICE || formed == Material.FROSTED_ICE) {
|
||||||
|
event.setCancelled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIX: instanceof-Check vor Cast auf Player hinzugefügt.
|
||||||
|
* FIX: GUI-Toggle korrekt mit createScoreboardForPlayer verknüpft.
|
||||||
|
*/
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onInventoryClick(InventoryClickEvent event) {
|
public void onInventoryClick(InventoryClickEvent event) {
|
||||||
|
// FIX: Sicherheitscheck – nur Player können die GUI bedienen
|
||||||
|
if (!(event.getWhoClicked() instanceof Player)) return;
|
||||||
Player player = (Player) event.getWhoClicked();
|
Player player = (Player) event.getWhoClicked();
|
||||||
|
|
||||||
Inventory expectedGUI = playerGUIs.get(player.getUniqueId());
|
Inventory expectedGUI = playerGUIs.get(player.getUniqueId());
|
||||||
if (expectedGUI == null || event.getInventory() != expectedGUI) return;
|
if (expectedGUI == null || event.getInventory() != expectedGUI) return;
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
|
|
||||||
ItemStack clickedItem = event.getCurrentItem();
|
ItemStack clickedItem = event.getCurrentItem();
|
||||||
if (clickedItem == null || clickedItem.getType() == Material.AIR) return;
|
if (clickedItem == null || clickedItem.getType() == Material.AIR) return;
|
||||||
|
|
||||||
@@ -632,24 +759,35 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
player.closeInventory();
|
player.closeInventory();
|
||||||
audiences.player(player).sendMessage(Component.text(
|
audiences.player(player).sendMessage(Component.text(
|
||||||
getLocalizedMessage("gui_setlocation_prompt", countryCode), NamedTextColor.YELLOW));
|
getLocalizedMessage("gui_setlocation_prompt", countryCode), NamedTextColor.YELLOW));
|
||||||
|
|
||||||
} else if (clickedItem.getType() == Material.REDSTONE_TORCH) {
|
} else if (clickedItem.getType() == Material.REDSTONE_TORCH) {
|
||||||
|
// FIX: Scoreboard korrekt aktivieren/deaktivieren
|
||||||
String worldName = player.getWorld().getName();
|
String worldName = player.getWorld().getName();
|
||||||
Set<String> worlds = playersWithDisplay.computeIfAbsent(player.getUniqueId(), k -> new HashSet<>());
|
Set<String> worlds = playersWithDisplay.computeIfAbsent(player.getUniqueId(), k -> new HashSet<>());
|
||||||
if (worlds.contains(worldName)) {
|
if (worlds.contains(worldName)) {
|
||||||
worlds.remove(worldName);
|
worlds.remove(worldName);
|
||||||
|
removeScoreboardFromPlayer(player);
|
||||||
audiences.player(player).sendMessage(Component.text(
|
audiences.player(player).sendMessage(Component.text(
|
||||||
getLocalizedMessage("toggle_disabled", countryCode, "world", worldName), NamedTextColor.RED));
|
ChatColor.RED + "⛅ Scoreboard-Wetteranzeige deaktiviert.", NamedTextColor.RED));
|
||||||
} else {
|
} else {
|
||||||
worlds.add(worldName);
|
worlds.add(worldName);
|
||||||
|
createScoreboardForPlayer(player);
|
||||||
audiences.player(player).sendMessage(Component.text(
|
audiences.player(player).sendMessage(Component.text(
|
||||||
getLocalizedMessage("toggle_enabled", countryCode, "world", worldName), NamedTextColor.GREEN));
|
ChatColor.GREEN + "⛅ Scoreboard-Wetteranzeige aktiviert!", NamedTextColor.GREEN));
|
||||||
}
|
}
|
||||||
|
// GUI offen lassen damit Spieler Status sehen kann
|
||||||
|
player.closeInventory();
|
||||||
|
player.openInventory(createWeatherGUI(player));
|
||||||
|
|
||||||
} else if (clickedItem.getType() == Material.PAPER) {
|
} else if (clickedItem.getType() == Material.PAPER) {
|
||||||
player.closeInventory();
|
player.closeInventory();
|
||||||
new WeatherForecastCommand().onCommand(player, null, "weatherforecast", new String[]{});
|
new WeatherForecastCommand().onCommand(player, null, "weatherforecast", new String[]{});
|
||||||
|
|
||||||
} else if (clickedItem.getType() == Material.OAK_SIGN) {
|
} else if (clickedItem.getType() == Material.OAK_SIGN) {
|
||||||
player.closeInventory();
|
player.closeInventory();
|
||||||
String infoMessage = "RealTimeWeather Plugin\nVersion: 1.3\nAutor: M_Viper\nGetestete Minecraft-Version: 1.21.1 - 1.21.11";
|
String infoMessage = getDescription().getName() + " Plugin"
|
||||||
|
+ "\nVersion: " + getDescription().getVersion()
|
||||||
|
+ "\nAutor: " + String.join(", ", getDescription().getAuthors());
|
||||||
audiences.player(player).sendMessage(Component.text(infoMessage, NamedTextColor.AQUA));
|
audiences.player(player).sendMessage(Component.text(infoMessage, NamedTextColor.AQUA));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -699,36 +837,55 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
return countryCode;
|
return countryCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIX: GUI-Toggle-Button zeigt korrekt "Scoreboard" an (statt "Bossbar").
|
||||||
|
* Toggle-Button aktualisiert seinen Status live (✅ aktiv / ❌ inaktiv).
|
||||||
|
*/
|
||||||
private Inventory createWeatherGUI(Player player) {
|
private Inventory createWeatherGUI(Player player) {
|
||||||
String countryCode = getCountryCode(worldConfigs.getOrDefault(player.getWorld().getName(), worldConfigs.get("defaults")).location);
|
String countryCode = getCountryCode(worldConfigs.getOrDefault(player.getWorld().getName(), worldConfigs.get("defaults")).location);
|
||||||
Inventory inv = Bukkit.createInventory(null, 9, getLocalizedMessage("gui_title", countryCode));
|
Inventory inv = Bukkit.createInventory(null, 9, getLocalizedMessage("gui_title", countryCode));
|
||||||
ItemStack setLocation = new ItemStack(Material.BOOK);
|
|
||||||
ItemStack toggleWeather = new ItemStack(Material.REDSTONE_TORCH);
|
|
||||||
ItemStack forecast = new ItemStack(Material.PAPER);
|
|
||||||
ItemStack info = new ItemStack(Material.OAK_SIGN);
|
|
||||||
|
|
||||||
|
// --- Standort setzen ---
|
||||||
|
ItemStack setLocation = new ItemStack(Material.BOOK);
|
||||||
ItemMeta setLocationMeta = setLocation.getItemMeta();
|
ItemMeta setLocationMeta = setLocation.getItemMeta();
|
||||||
setLocationMeta.setDisplayName(getLocalizedMessage("gui_setlocation", countryCode));
|
setLocationMeta.setDisplayName(ChatColor.YELLOW + "📍 Standort setzen");
|
||||||
setLocationMeta.setLore(Collections.singletonList(getLocalizedMessage("gui_setlocation_lore", countryCode)));
|
setLocationMeta.setLore(Collections.singletonList(ChatColor.GRAY + "Klicke um deinen Standort zu ändern"));
|
||||||
setLocation.setItemMeta(setLocationMeta);
|
setLocation.setItemMeta(setLocationMeta);
|
||||||
|
|
||||||
ItemMeta toggleWeatherMeta = toggleWeather.getItemMeta();
|
// --- Scoreboard umschalten ---
|
||||||
toggleWeatherMeta.setDisplayName(getLocalizedMessage("gui_toggleweather", countryCode));
|
// FIX: Korrekte Beschriftung als "Scoreboard" (nicht mehr "Bossbar")
|
||||||
toggleWeatherMeta.setLore(Collections.singletonList(getLocalizedMessage("gui_toggleweather_lore", countryCode)));
|
boolean scoreboardActive = playersWithDisplay
|
||||||
toggleWeather.setItemMeta(toggleWeatherMeta);
|
.getOrDefault(player.getUniqueId(), new HashSet<>())
|
||||||
|
.contains(player.getWorld().getName());
|
||||||
|
|
||||||
|
ItemStack toggleScoreboard = new ItemStack(Material.REDSTONE_TORCH);
|
||||||
|
ItemMeta toggleMeta = toggleScoreboard.getItemMeta();
|
||||||
|
toggleMeta.setDisplayName(ChatColor.GOLD + "⛅ Scoreboard-Wetteranzeige");
|
||||||
|
List<String> toggleLore = new ArrayList<>();
|
||||||
|
toggleLore.add(ChatColor.GRAY + "Zeigt Wetterdaten im Scoreboard (rechts)");
|
||||||
|
toggleLore.add(scoreboardActive
|
||||||
|
? ChatColor.GREEN + "✅ Aktuell: Aktiv"
|
||||||
|
: ChatColor.RED + "❌ Aktuell: Inaktiv");
|
||||||
|
toggleLore.add(ChatColor.GRAY + "Klicke zum Umschalten");
|
||||||
|
toggleMeta.setLore(toggleLore);
|
||||||
|
toggleScoreboard.setItemMeta(toggleMeta);
|
||||||
|
|
||||||
|
// --- Vorhersage ---
|
||||||
|
ItemStack forecast = new ItemStack(Material.PAPER);
|
||||||
ItemMeta forecastMeta = forecast.getItemMeta();
|
ItemMeta forecastMeta = forecast.getItemMeta();
|
||||||
forecastMeta.setDisplayName(getLocalizedMessage("gui_forecast", countryCode));
|
forecastMeta.setDisplayName(ChatColor.AQUA + "📋 Wettervorhersage");
|
||||||
forecastMeta.setLore(Collections.singletonList(getLocalizedMessage("gui_forecast_lore", countryCode)));
|
forecastMeta.setLore(Collections.singletonList(ChatColor.GRAY + "5-Tage-Vorhersage anzeigen"));
|
||||||
forecast.setItemMeta(forecastMeta);
|
forecast.setItemMeta(forecastMeta);
|
||||||
|
|
||||||
|
// --- Info ---
|
||||||
|
ItemStack info = new ItemStack(Material.OAK_SIGN);
|
||||||
ItemMeta infoMeta = info.getItemMeta();
|
ItemMeta infoMeta = info.getItemMeta();
|
||||||
infoMeta.setDisplayName(getLocalizedMessage("gui_info", countryCode));
|
infoMeta.setDisplayName(ChatColor.WHITE + "ℹ Plugin-Info");
|
||||||
infoMeta.setLore(Collections.singletonList(getLocalizedMessage("gui_info_lore", countryCode)));
|
infoMeta.setLore(Collections.singletonList(ChatColor.GRAY + "Plugin-Informationen anzeigen"));
|
||||||
info.setItemMeta(infoMeta);
|
info.setItemMeta(infoMeta);
|
||||||
|
|
||||||
inv.setItem(2, setLocation);
|
inv.setItem(2, setLocation);
|
||||||
inv.setItem(3, toggleWeather);
|
inv.setItem(3, toggleScoreboard);
|
||||||
inv.setItem(4, forecast);
|
inv.setItem(4, forecast);
|
||||||
inv.setItem(5, info);
|
inv.setItem(5, info);
|
||||||
|
|
||||||
@@ -739,15 +896,18 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
private class PlayerConfig {
|
private class PlayerConfig {
|
||||||
private final File configFile;
|
private final File configFile;
|
||||||
private final FileConfiguration config;
|
private final FileConfiguration config;
|
||||||
|
|
||||||
public PlayerConfig(UUID playerId) {
|
public PlayerConfig(UUID playerId) {
|
||||||
File playerDir = new File(getDataFolder(), "players");
|
File playerDir = new File(getDataFolder(), "players");
|
||||||
playerDir.mkdirs();
|
playerDir.mkdirs();
|
||||||
configFile = new File(playerDir, playerId.toString() + ".yml");
|
configFile = new File(playerDir, playerId.toString() + ".yml");
|
||||||
config = YamlConfiguration.loadConfiguration(configFile);
|
config = YamlConfiguration.loadConfiguration(configFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLocation() {
|
public String getLocation() {
|
||||||
return config.getString("location", "");
|
return config.getString("location", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLocation(String location) {
|
public void setLocation(String location) {
|
||||||
config.set("location", location);
|
config.set("location", location);
|
||||||
try {
|
try {
|
||||||
@@ -815,10 +975,8 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
processedLocations.clear();
|
processedLocations.clear();
|
||||||
initializePlayerDisplays();
|
initializePlayerDisplays();
|
||||||
setDoDaylightCycleForWorlds();
|
setDoDaylightCycleForWorlds();
|
||||||
startWeatherUpdateTask();
|
startWeatherUpdateTask(); // startet async und ruft updateWeatherDataForAllWorlds() intern auf
|
||||||
startSyncTask();
|
startSyncTask();
|
||||||
processedLocations.clear();
|
|
||||||
updateWeatherDataForAllWorlds();
|
|
||||||
audiences.sender(sender).sendMessage(Component.text(getLocalizedMessage("reload_success", countryCode), NamedTextColor.GREEN));
|
audiences.sender(sender).sendMessage(Component.text(getLocalizedMessage("reload_success", countryCode), NamedTextColor.GREEN));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -838,10 +996,12 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
WeatherFetcher fetcher = new WeatherFetcher(apiKey, location, "metric", getLogger());
|
WeatherFetcher fetcher = new WeatherFetcher(apiKey, location, "metric", getLogger());
|
||||||
fetcher.fetchAsWeatherTimeData();
|
WeatherTimeData newData = fetcher.fetchAsWeatherTimeData();
|
||||||
PlayerConfig playerConfig = new PlayerConfig(player.getUniqueId());
|
PlayerConfig playerConfig = new PlayerConfig(player.getUniqueId());
|
||||||
playerConfig.setLocation(location);
|
playerConfig.setLocation(location);
|
||||||
playerLocations.put(player.getUniqueId(), location);
|
playerLocations.put(player.getUniqueId(), location);
|
||||||
|
// FIX: Sofort in Cache speichern
|
||||||
|
playerWeatherCache.put(player.getUniqueId(), newData);
|
||||||
String cc = getCountryCode(location);
|
String cc = getCountryCode(location);
|
||||||
audiences.player(player).sendMessage(Component.text(getLocalizedMessage("location_set", cc, "location", location), NamedTextColor.GREEN));
|
audiences.player(player).sendMessage(Component.text(getLocalizedMessage("location_set", cc, "location", location), NamedTextColor.GREEN));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -873,7 +1033,8 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
String localizedWeather = getLocalizedWeatherMain(data.getWeatherMain(), cc);
|
String localizedWeather = getLocalizedWeatherMain(data.getWeatherMain(), cc);
|
||||||
String tempUnit = config.units.equalsIgnoreCase("metric") ? "°C" : "°F";
|
String tempUnit = config.units.equalsIgnoreCase("metric") ? "°C" : "°F";
|
||||||
String city = queryLocation.split(",")[0];
|
String city = queryLocation.split(",")[0];
|
||||||
String message = String.format("%s: %s, %.1f%s", city, localizedWeather, data.getTemp(config.units.equalsIgnoreCase("metric") ? "C" : "F"), tempUnit);
|
String message = String.format("%s: %s, %.1f%s", city, localizedWeather,
|
||||||
|
data.getTemp(config.units.equalsIgnoreCase("metric") ? "C" : "F"), tempUnit);
|
||||||
audiences.player(queryPlayer).sendMessage(Component.text(message, NamedTextColor.GREEN));
|
audiences.player(queryPlayer).sendMessage(Component.text(message, NamedTextColor.GREEN));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
audiences.player(queryPlayer).sendMessage(Component.text(getLocalizedMessage("forecast_error", countryCode, "location", queryLocation, "error", e.getMessage()), NamedTextColor.RED));
|
audiences.player(queryPlayer).sendMessage(Component.text(getLocalizedMessage("forecast_error", countryCode, "location", queryLocation, "error", e.getMessage()), NamedTextColor.RED));
|
||||||
@@ -883,7 +1044,11 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case "info": {
|
case "info": {
|
||||||
String infoMessage = "RealTimeWeather Plugin\nVersion: 1.3\nAutor: M_Viper\nGetestete Minecraft-Version: 1.21.1 - 1.21.11\n\nDie Wetteranzeige erscheint als SCOREBOARD rechts am Bildschirm.";
|
String infoMessage = getDescription().getName() + " Plugin"
|
||||||
|
+ "\nVersion: " + getDescription().getVersion()
|
||||||
|
+ "\nAutor: " + String.join(", ", getDescription().getAuthors())
|
||||||
|
+ "\n\nDas Scoreboard ist standardmäßig ausgeblendet."
|
||||||
|
+ "\nAktiviere es per /wetter gui oder /toggleweather.";
|
||||||
audiences.sender(sender).sendMessage(Component.text(infoMessage, NamedTextColor.AQUA));
|
audiences.sender(sender).sendMessage(Component.text(infoMessage, NamedTextColor.AQUA));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -908,7 +1073,7 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Player modePlayer = (Player) sender;
|
Player modePlayer = (Player) sender;
|
||||||
|
|
||||||
if (args.length != 2) {
|
if (args.length != 2) {
|
||||||
DisplayMode currentMode = playerDisplayModes.getOrDefault(modePlayer.getUniqueId(), DisplayMode.STANDARD);
|
DisplayMode currentMode = playerDisplayModes.getOrDefault(modePlayer.getUniqueId(), DisplayMode.STANDARD);
|
||||||
modePlayer.sendMessage(ChatColor.YELLOW + "Aktueller Anzeigemodus: " + ChatColor.GREEN + currentMode.getDisplayName());
|
modePlayer.sendMessage(ChatColor.YELLOW + "Aktueller Anzeigemodus: " + ChatColor.GREEN + currentMode.getDisplayName());
|
||||||
@@ -918,13 +1083,12 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
modePlayer.sendMessage(ChatColor.GRAY + "- " + ChatColor.AQUA + "detailed" + ChatColor.GRAY + ": Alle verfügbaren Daten");
|
modePlayer.sendMessage(ChatColor.GRAY + "- " + ChatColor.AQUA + "detailed" + ChatColor.GRAY + ": Alle verfügbaren Daten");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DisplayMode newMode = DisplayMode.fromString(args[1]);
|
DisplayMode newMode = DisplayMode.fromString(args[1]);
|
||||||
playerDisplayModes.put(modePlayer.getUniqueId(), newMode);
|
playerDisplayModes.put(modePlayer.getUniqueId(), newMode);
|
||||||
|
|
||||||
modePlayer.sendMessage(ChatColor.GREEN + "Anzeigemodus geändert zu: " + ChatColor.GOLD + newMode.getDisplayName());
|
modePlayer.sendMessage(ChatColor.GREEN + "Anzeigemodus geändert zu: " + ChatColor.GOLD + newMode.getDisplayName());
|
||||||
modePlayer.sendMessage(ChatColor.GRAY + "Das Scoreboard wird beim nächsten Update aktualisiert.");
|
modePlayer.sendMessage(ChatColor.GRAY + "Das Scoreboard wird beim nächsten Update aktualisiert.");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -971,13 +1135,13 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
String weatherMain = forecast.getJSONArray("weather").getJSONObject(0).getString("main");
|
String weatherMain = forecast.getJSONArray("weather").getJSONObject(0).getString("main");
|
||||||
double temp = forecast.getJSONObject("main").getDouble("temp");
|
double temp = forecast.getJSONObject("main").getDouble("temp");
|
||||||
ZonedDateTime dateTime = Instant.ofEpochSecond(dt).atZone(ZoneId.of("UTC"));
|
ZonedDateTime dateTime = Instant.ofEpochSecond(dt).atZone(ZoneId.of("UTC"));
|
||||||
|
|
||||||
String localizedWeatherMain = getLocalizedWeatherMain(weatherMain, countryCode);
|
String localizedWeatherMain = getLocalizedWeatherMain(weatherMain, countryCode);
|
||||||
|
|
||||||
message = message.append(Component.newline())
|
message = message.append(Component.newline())
|
||||||
.append(Component.text(
|
.append(Component.text(
|
||||||
dateTime.format(DateTimeFormatter.ofPattern("dd.MM.yyyy")) + ": " +
|
dateTime.format(DateTimeFormatter.ofPattern("dd.MM.yyyy")) + ": " +
|
||||||
localizedWeatherMain + ", " + String.format("%.1f%s", temp, config.units.equalsIgnoreCase("metric") ? "°C" : "°F"),
|
localizedWeatherMain + ", " + String.format("%.1f%s", temp,
|
||||||
|
config.units.equalsIgnoreCase("metric") ? "°C" : "°F"),
|
||||||
NamedTextColor.GREEN));
|
NamedTextColor.GREEN));
|
||||||
}
|
}
|
||||||
audiences.player(player).sendMessage(message);
|
audiences.player(player).sendMessage(message);
|
||||||
@@ -1013,14 +1177,11 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
Set<String> worlds = playersWithDisplay.computeIfAbsent(player.getUniqueId(), k -> new HashSet<>());
|
Set<String> worlds = playersWithDisplay.computeIfAbsent(player.getUniqueId(), k -> new HashSet<>());
|
||||||
if (worlds.contains(worldName)) {
|
if (worlds.contains(worldName)) {
|
||||||
worlds.remove(worldName);
|
worlds.remove(worldName);
|
||||||
// Scoreboard entfernen
|
removeScoreboardFromPlayer(player);
|
||||||
player.setScoreboard(Bukkit.getScoreboardManager().getMainScoreboard());
|
|
||||||
playerScoreboards.remove(player.getUniqueId());
|
|
||||||
audiences.player(player).sendMessage(Component.text(
|
audiences.player(player).sendMessage(Component.text(
|
||||||
getLocalizedMessage("toggle_disabled", countryCode, "world", worldName), NamedTextColor.RED));
|
getLocalizedMessage("toggle_disabled", countryCode, "world", worldName), NamedTextColor.RED));
|
||||||
} else {
|
} else {
|
||||||
worlds.add(worldName);
|
worlds.add(worldName);
|
||||||
// Scoreboard erstellen
|
|
||||||
createScoreboardForPlayer(player);
|
createScoreboardForPlayer(player);
|
||||||
audiences.player(player).sendMessage(Component.text(
|
audiences.player(player).sendMessage(Component.text(
|
||||||
getLocalizedMessage("toggle_enabled", countryCode, "world", worldName), NamedTextColor.GREEN));
|
getLocalizedMessage("toggle_enabled", countryCode, "world", worldName), NamedTextColor.GREEN));
|
||||||
@@ -1034,24 +1195,15 @@ public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener {
|
|||||||
String location;
|
String location;
|
||||||
String units;
|
String units;
|
||||||
String timeFormat;
|
String timeFormat;
|
||||||
boolean displayActionbar;
|
|
||||||
boolean displayWeatherIcon;
|
|
||||||
String displayPosition;
|
|
||||||
int paddingRight;
|
|
||||||
boolean syncInGameWeather;
|
boolean syncInGameWeather;
|
||||||
|
|
||||||
public WorldConfig(boolean enabled, String location, String units, String timeFormat,
|
public WorldConfig(boolean enabled, String location, String units, String timeFormat,
|
||||||
boolean displayActionbar, boolean displayWeatherIcon,
|
boolean syncInGameWeather) {
|
||||||
String displayPosition, int paddingRight, boolean syncInGameWeather) {
|
|
||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
this.location = location;
|
this.location = location;
|
||||||
this.units = units;
|
this.units = units;
|
||||||
this.timeFormat = timeFormat;
|
this.timeFormat = timeFormat;
|
||||||
this.displayActionbar = displayActionbar;
|
|
||||||
this.displayWeatherIcon = displayWeatherIcon;
|
|
||||||
this.displayPosition = displayPosition;
|
|
||||||
this.paddingRight = paddingRight;
|
|
||||||
this.syncInGameWeather = syncInGameWeather;
|
this.syncInGameWeather = syncInGameWeather;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,39 +1,61 @@
|
|||||||
# OpenWeatherMap API-Key – hier eintragen
|
# ============================================================
|
||||||
|
# RealTimeWeather – config.yml
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# OpenWeatherMap API-Key
|
||||||
|
# Kostenlos erhältlich unter: https://openweathermap.org/api
|
||||||
api-key: "DEIN_API_KEY_HIER"
|
api-key: "DEIN_API_KEY_HIER"
|
||||||
|
|
||||||
# Update-Intervall in Sekunden (Standard: 60)
|
# Wie oft Wetter & Zeit von der API abgerufen werden (in Sekunden)
|
||||||
|
# Empfohlen: 60–300 (zu niedrige Werte können das API-Limit überschreiten)
|
||||||
update-interval: 60
|
update-interval: 60
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# Standard-Einstellungen (gelten wenn keine Welt-spezifischen
|
||||||
|
# Einstellungen definiert sind)
|
||||||
|
# ------------------------------------------------------------
|
||||||
defaults:
|
defaults:
|
||||||
|
# Plugin für diese Welt aktiv?
|
||||||
enabled: true
|
enabled: true
|
||||||
location: "Berlin,de" # Stadt,Land
|
|
||||||
units: "metric" # metric = °C, imperial = °F
|
# Standort für Wetter & Zeit – Format: "Stadt,Länderkürzel"
|
||||||
time-format: "24h" # oder "12h"
|
# Beispiele: "Berlin,de" | "London,gb" | "New York,us"
|
||||||
display-actionbar: true
|
location: "Berlin,de"
|
||||||
display-weather-icon: true
|
|
||||||
display-position: "top-right"
|
# Temperatureinheit: "metric" (°C) oder "imperial" (°F)
|
||||||
padding-right: 100
|
units: "metric"
|
||||||
|
|
||||||
|
# Uhrzeitformat: "24h" oder "12h"
|
||||||
|
time-format: "24h"
|
||||||
|
|
||||||
|
# Soll das Ingame-Wetter mit der echten API synchronisiert werden?
|
||||||
|
# (Regen, Gewitter, Schnee, Klar etc.)
|
||||||
sync-in-game-weather: true
|
sync-in-game-weather: true
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# Welt-spezifische Einstellungen
|
||||||
|
# Nicht definierte Felder erben vom "defaults"-Block oben.
|
||||||
|
# ------------------------------------------------------------
|
||||||
worlds:
|
worlds:
|
||||||
world:
|
world:
|
||||||
enabled: true
|
enabled: true
|
||||||
location: "Berlin,de"
|
location: "Berlin,de"
|
||||||
units: "metric"
|
units: "metric"
|
||||||
time-format: "24h"
|
time-format: "24h"
|
||||||
display-actionbar: true
|
|
||||||
display-weather-icon: true
|
|
||||||
display-position: "top-left"
|
|
||||||
padding-right: 50
|
|
||||||
sync-in-game-weather: true
|
sync-in-game-weather: true
|
||||||
|
|
||||||
world_nether:
|
world_nether:
|
||||||
|
# Nether hat kein Wetter/Tageslicht – Plugin hier deaktivieren
|
||||||
|
enabled: false
|
||||||
|
location: "Berlin,de"
|
||||||
|
units: "metric"
|
||||||
|
time-format: "24h"
|
||||||
|
sync-in-game-weather: false
|
||||||
|
|
||||||
|
world_the_end:
|
||||||
|
# End hat kein Wetter/Tageslicht – Plugin hier deaktivieren
|
||||||
enabled: false
|
enabled: false
|
||||||
location: "Berlin,de"
|
location: "Berlin,de"
|
||||||
units: "metric"
|
units: "metric"
|
||||||
time-format: "24h"
|
time-format: "24h"
|
||||||
display-actionbar: true
|
|
||||||
display-weather-icon: false
|
|
||||||
display-position: "top-right"
|
|
||||||
padding-right: 150
|
|
||||||
sync-in-game-weather: false
|
sync-in-game-weather: false
|
||||||
@@ -1,32 +1,43 @@
|
|||||||
name: RealTimeWeather
|
name: RealTimeWeather
|
||||||
version: 1.4
|
version: 1.5
|
||||||
main: dev.viper.weathertime.WeatherTimeSyncPlugin
|
main: dev.viper.weathertime.WeatherTimeSyncPlugin
|
||||||
api-version: 1.21
|
api-version: 1.21
|
||||||
|
authors: [M_Viper]
|
||||||
|
description: Synchronizes real-time weather and time from OpenWeatherMap with your Minecraft world.
|
||||||
|
|
||||||
commands:
|
commands:
|
||||||
wetter:
|
wetter:
|
||||||
description: Manages real-time weather and time synchronization
|
description: Manages real-time weather and time synchronization
|
||||||
usage: /<command> reload | setlocation <city,country> | query | info | gui | help
|
usage: /<command> <reload|setlocation|query|mode|info|gui|help>
|
||||||
permission: realtimeweather.use
|
permission: realtimeweather.use
|
||||||
weatherforecast:
|
weatherforecast:
|
||||||
description: Shows the weather forecast for the player's location
|
description: Shows the 5-day weather forecast for the player's location
|
||||||
usage: /<command>
|
usage: /<command>
|
||||||
permission: realtimeweather.forecast
|
permission: realtimeweather.forecast
|
||||||
toggleweather:
|
toggleweather:
|
||||||
description: Toggles the weather display for the current world
|
description: Toggles the scoreboard weather display for the current world
|
||||||
usage: /<command>
|
usage: /<command>
|
||||||
permission: realtimeweather.toggle
|
permission: realtimeweather.toggle
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
realtimeweather.use:
|
realtimeweather.use:
|
||||||
description: Allows usage of the /wetter command
|
description: Allows usage of the /wetter command (query, info, gui, mode, help)
|
||||||
default: true
|
default: true
|
||||||
|
children:
|
||||||
|
realtimeweather.setlocation: true
|
||||||
|
|
||||||
|
realtimeweather.setlocation:
|
||||||
|
description: Allows setting a custom weather location via /wetter setlocation
|
||||||
|
default: true
|
||||||
|
|
||||||
realtimeweather.forecast:
|
realtimeweather.forecast:
|
||||||
description: Allows usage of the /weatherforecast command
|
description: Allows usage of the /weatherforecast command
|
||||||
default: true
|
default: true
|
||||||
|
|
||||||
realtimeweather.toggle:
|
realtimeweather.toggle:
|
||||||
description: Allows usage of the /toggleweather command
|
description: Allows toggling the scoreboard weather display via /toggleweather
|
||||||
default: true
|
default: true
|
||||||
|
|
||||||
realtimeweather.reload:
|
realtimeweather.reload:
|
||||||
description: Allows reloading the plugin configuration
|
description: Allows reloading the plugin configuration via /wetter reload
|
||||||
default: op
|
default: op
|
||||||
Reference in New Issue
Block a user