From fe3e1c1534da954ea6c69bf12c84242cc37b3515 Mon Sep 17 00:00:00 2001 From: M_Viper Date: Sat, 9 Aug 2025 20:58:15 +0000 Subject: [PATCH] src/main/java/dev/viper/weathertime/WeatherTimeSyncPlugin.java aktualisiert --- .../weathertime/WeatherTimeSyncPlugin.java | 782 ++++++++++++++++-- 1 file changed, 712 insertions(+), 70 deletions(-) diff --git a/src/main/java/dev/viper/weathertime/WeatherTimeSyncPlugin.java b/src/main/java/dev/viper/weathertime/WeatherTimeSyncPlugin.java index 7ed4f9d..ac431ec 100644 --- a/src/main/java/dev/viper/weathertime/WeatherTimeSyncPlugin.java +++ b/src/main/java/dev/viper/weathertime/WeatherTimeSyncPlugin.java @@ -1,87 +1,729 @@ package dev.viper.weathertime; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.adventure.platform.bukkit.BukkitAudiences; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.command.*; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.player.PlayerChangedWorldEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitRunnable; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.io.File; +import java.io.IOException; +import java.time.Instant; +import java.time.ZoneId; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; -/** - * Zentrale Datenklasse für Wetter- und Zeitinformationen. - * Erweiterung: enthält jetzt auch Luftfeuchtigkeit, Windgeschwindigkeit und Sonnenauf/untergang. - */ -public class WeatherTimeData { +public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener { - private final ZonedDateTime dateTime; // Lokale Zeit am Standort - private final String weatherMain; // Hauptwetterbeschreibung (z. B. "Clear", "Rain") - private final double tempCelsius; // Temperatur in °C + private String apiKey; + private int updateInterval; + private final Map worldConfigs = new HashMap<>(); + private final Map worldWeatherData = new HashMap<>(); + private final Map> playersWithDisplay = new HashMap<>(); + private final Map playerLocations = new HashMap<>(); + private final Map playerGUIs = new HashMap<>(); + private BukkitAudiences audiences; + private BukkitRunnable weatherUpdateTask; + private BukkitRunnable syncTask; + private final Set processedLocations = new HashSet<>(); + private FileConfiguration langConfig; - private final int humidity; // Luftfeuchtigkeit in % - private final double windSpeed; // Windgeschwindigkeit (m/s oder mph) - private final ZonedDateTime sunrise; // Sonnenaufgang - private final ZonedDateTime sunset; // Sonnenuntergang + @Override + public void onEnable() { + saveDefaultConfig(); + loadLanguageConfig(); + loadConfig(); + audiences = BukkitAudiences.create(this); - public WeatherTimeData( - ZonedDateTime dateTime, - String weatherMain, - double tempCelsius, - int humidity, - double windSpeed, - ZonedDateTime sunrise, - ZonedDateTime sunset) { - this.dateTime = dateTime; - this.weatherMain = weatherMain; - this.tempCelsius = tempCelsius; - this.humidity = humidity; - this.windSpeed = windSpeed; - this.sunrise = sunrise; - this.sunset = sunset; + Objects.requireNonNull(getCommand("wetter")).setExecutor(new WetterCommand()); + Objects.requireNonNull(getCommand("weatherforecast")).setExecutor(new WeatherForecastCommand()); + Objects.requireNonNull(getCommand("toggleweather")).setExecutor(new ToggleWeatherCommand()); + + getServer().getPluginManager().registerEvents(this, this); + + setDoDaylightCycleForWorlds(); + startWeatherUpdateTask(); + startSyncTask(); + initializePlayerDisplays(); } - public ZonedDateTime getDateTime() { - return dateTime; - } - - public String getWeatherMain() { - return weatherMain; - } - - public double getTemp(String unit) { - return "F".equalsIgnoreCase(unit) - ? (tempCelsius * 9 / 5) + 32 - : tempCelsius; - } - - public int getHumidity() { - return humidity; - } - - public double getWindSpeed() { - return windSpeed; - } - - public ZonedDateTime getSunrise() { - return sunrise; - } - - public ZonedDateTime getSunset() { - return sunset; - } - - public String getFormattedTime(String format) { - int hour = dateTime.getHour(); - int minute = dateTime.getMinute(); - if ("12h".equalsIgnoreCase(format)) { - String ampm = hour >= 12 ? "PM" : "AM"; - hour = hour % 12; - if (hour == 0) hour = 12; - return String.format("%02d:%02d %s", hour, minute, ampm); + @Override + public void onDisable() { + if (weatherUpdateTask != null) weatherUpdateTask.cancel(); + if (syncTask != null) syncTask.cancel(); + if (audiences != null) audiences.close(); + for (Player player : Bukkit.getOnlinePlayers()) { + if (playerGUIs.containsKey(player.getUniqueId())) player.closeInventory(); } - return String.format("%02d:%02d", hour, minute); } - public long toMinecraftTime() { + // =============== Config & Sprache =============== + private void loadConfig() { + FileConfiguration cfg = getConfig(); + apiKey = cfg.getString("api-key", "").trim(); + updateInterval = cfg.getInt("update-interval", 60); + worldConfigs.clear(); + + WorldConfig defaultConfig = new WorldConfig( + cfg.getBoolean("defaults.enabled", true), + cfg.getString("defaults.location", "Berlin,de"), + cfg.getString("defaults.units", "metric"), + 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) + ); + worldConfigs.put("defaults", defaultConfig); + + if (cfg.isConfigurationSection("worlds")) { + for (String worldName : cfg.getConfigurationSection("worlds").getKeys(false)) { + WorldConfig worldConfig = new WorldConfig( + cfg.getBoolean("worlds." + worldName + ".enabled", defaultConfig.enabled), + cfg.getString("worlds." + worldName + ".location", defaultConfig.location), + cfg.getString("worlds." + worldName + ".units", defaultConfig.units), + 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) + ); + worldConfigs.put(worldName, worldConfig); + } + } + if (apiKey.isEmpty()) { + getLogger().severe("API-Key ist nicht gesetzt! Bitte in config.yml eintragen."); + } + } + + private void loadLanguageConfig() { + File langFile = new File(getDataFolder(), "lang.yml"); + try { + langConfig = YamlConfiguration.loadConfiguration(langFile); + if (!langFile.exists()) { + saveResource("lang.yml", false); + langConfig = YamlConfiguration.loadConfiguration(langFile); + } else if (langConfig.getConfigurationSection("languages") == null) { + saveResource("lang.yml", true); + langConfig = YamlConfiguration.loadConfiguration(langFile); + } + } catch (Exception e) { + saveResource("lang.yml", true); + langConfig = YamlConfiguration.loadConfiguration(langFile); + } + } + + private void setDoDaylightCycleForWorlds() { + for (World world : Bukkit.getWorlds()) { + WorldConfig config = worldConfigs.getOrDefault(world.getName(), worldConfigs.get("defaults")); + if (config.enabled) world.setGameRuleValue("doDaylightCycle", "false"); + } + } + + private void initializePlayerDisplays() { + playersWithDisplay.clear(); + for (Player player : Bukkit.getOnlinePlayers()) { + Set worlds = new HashSet<>(); + for (World world : Bukkit.getWorlds()) { + WorldConfig config = worldConfigs.getOrDefault(world.getName(), worldConfigs.get("defaults")); + if (config.enabled && config.displayActionbar) worlds.add(world.getName()); + } + playersWithDisplay.put(player.getUniqueId(), worlds); + } + } + + private void startWeatherUpdateTask() { + weatherUpdateTask = new BukkitRunnable() { + @Override + public void run() { + processedLocations.clear(); + updateWeatherDataForAllWorlds(); + } + }; + weatherUpdateTask.runTaskTimerAsynchronously(this, 0L, updateInterval * 20L); + } + + private void startSyncTask() { + syncTask = new BukkitRunnable() { + @Override + public void run() { + for (World world : Bukkit.getWorlds()) { + WorldConfig config = worldConfigs.getOrDefault(world.getName(), worldConfigs.get("defaults")); + if (!config.enabled) continue; + WeatherTimeData data = worldWeatherData.get(world.getName()); + if (data != null) { + syncMinecraftTime(world, data.getDateTime()); + updateActionbarForWorld(world, data); + } + } + } + }; + syncTask.runTaskTimer(this, 0L, 20L); + } + + private void updateWeatherDataForAllWorlds() { + for (World world : Bukkit.getWorlds()) { + WorldConfig config = worldConfigs.getOrDefault(world.getName(), worldConfigs.get("defaults")); + if (!config.enabled) { + worldWeatherData.remove(world.getName()); + continue; + } + if (processedLocations.contains(config.location)) { + for (String otherWorld : worldWeatherData.keySet()) { + WorldConfig otherConfig = worldConfigs.getOrDefault(otherWorld, worldConfigs.get("defaults")); + if (otherConfig.location.equals(config.location)) { + WeatherTimeData existingData = worldWeatherData.get(otherWorld); + if (existingData != null) { + worldWeatherData.put(world.getName(), existingData); + break; + } + } + } + continue; + } + try { + WeatherFetcher fetcher = new WeatherFetcher(apiKey, config.location, config.units, getLogger()); + WeatherTimeData weatherData = fetcher.fetchAsWeatherTimeData(); + worldWeatherData.put(world.getName(), weatherData); + processedLocations.add(config.location); + + new BukkitRunnable() { + @Override + public void run() { + syncMinecraftTime(world, weatherData.getDateTime()); + if (config.syncInGameWeather) { + switch (weatherData.getWeatherMain().toLowerCase()) { + 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); + break; + default: + world.setStorm(false); + world.setThundering(false); + } + } + } + }.runTask(this); + } catch (Exception e) { + getLogger().warning("Fehler beim Abrufen von Wetter/Zeit für Welt " + world.getName() + ": " + e.getMessage()); + } + } + } + + private void syncMinecraftTime(World world, ZonedDateTime dateTime) { int hour = dateTime.getHour(); int minute = dateTime.getMinute(); int second = dateTime.getSecond(); - return ((hour + 18) % 24) * 1000L - + (minute * 1000L / 60) - + (second * 1000L / 3600); + int minecraftTime = ((hour + 18) % 24) * 1000 + (minute * 1000 / 60) + (second * 1000 / 3600); + world.setTime(minecraftTime); + world.setGameRuleValue("doDaylightCycle", "false"); + } + + // === Anzeige (Actionbar, jetzt mit Luftfeuchtigkeit, Wind, Sonne) === + private void updateActionbarForWorld(World world, WeatherTimeData data) { + WorldConfig config = worldConfigs.getOrDefault(world.getName(), worldConfigs.get("defaults")); + if (!config.enabled || !config.displayActionbar) return; + + for (Player player : world.getPlayers()) { + if (!playersWithDisplay.getOrDefault(player.getUniqueId(), new HashSet<>()).contains(world.getName())) + continue; + + WeatherTimeData playerData = data; + String location = playerLocations.getOrDefault(player.getUniqueId(), ""); + if (!location.isEmpty()) { + try { + WeatherFetcher fetcher = new WeatherFetcher(apiKey, location, config.units, getLogger()); + playerData = fetcher.fetchAsWeatherTimeData(); + } catch (Exception e) { + String countryCode = getCountryCode(config.location); + audiences.player(player).sendMessage(Component.text( + getLocalizedMessage("forecast_error", countryCode, + "location", location, + "error", e.getMessage()), + NamedTextColor.RED)); + continue; + } + } + + String dateStr = playerData.getDateTime().format(DateTimeFormatter.ofPattern("dd.MM.yyyy")); + String timeStr = playerData.getFormattedTime(config.timeFormat); + String tempUnit = config.units.equalsIgnoreCase("metric") ? "°C" : "°F"; + String weatherIcon = config.displayWeatherIcon ? getWeatherSymbol(playerData.getWeatherMain()) + " " : ""; + String tempStr = String.format("%.1f%s", playerData.getTemp(config.units.equalsIgnoreCase("metric") ? "C" : "F"), tempUnit); + + String humidityStr = "💧 " + playerData.getHumidity() + "%"; + String windStr = "🌬️ " + String.format("%.1f", playerData.getWindSpeed()) + (config.units.equalsIgnoreCase("metric") ? " m/s" : " mph"); + String sunriseStr = "🌅 " + playerData.getSunrise().format(DateTimeFormatter.ofPattern("HH:mm")); + String sunsetStr = "🌇 " + playerData.getSunset().format(DateTimeFormatter.ofPattern("HH:mm")); + + Component message = Component.text() + .append(Component.text(dateStr + " ", NamedTextColor.AQUA, TextDecoration.BOLD)) + .append(Component.text("| " + timeStr + " ", NamedTextColor.YELLOW)) + .append(Component.text("| " + weatherIcon, NamedTextColor.WHITE)) + .append(Component.text("| " + tempStr + " ", NamedTextColor.GREEN)) + .append(Component.text("| " + humidityStr + " ", NamedTextColor.AQUA)) + .append(Component.text("| " + windStr + " ", NamedTextColor.GRAY)) + .append(Component.text("| " + sunriseStr + " ", NamedTextColor.GOLD)) + .append(Component.text("| " + sunsetStr, NamedTextColor.DARK_RED)) + .build(); + + audiences.player(player).sendActionBar(message); + } + } + + // === Events (z.B. Weltwechsel) === + @EventHandler + public void onPlayerChangedWorld(PlayerChangedWorldEvent event) { + Player player = event.getPlayer(); + World world = player.getWorld(); + WorldConfig config = worldConfigs.getOrDefault(world.getName(), worldConfigs.get("defaults")); + if (!config.enabled) return; + WeatherTimeData data = worldWeatherData.get(world.getName()); + if (data == null) return; + String countryCode = getCountryCode(config.location); + String localizedWeather = getLocalizedWeatherMain(data.getWeatherMain(), countryCode); + String tempUnit = config.units.equalsIgnoreCase("metric") ? "°C" : "°F"; + 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); + audiences.player(player).sendActionBar(Component.text(message, NamedTextColor.GREEN)); + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + Player player = (Player) event.getWhoClicked(); + Inventory expectedGUI = playerGUIs.get(player.getUniqueId()); + if (expectedGUI == null || event.getInventory() != expectedGUI) return; + event.setCancelled(true); + ItemStack clickedItem = event.getCurrentItem(); + if (clickedItem == null || clickedItem.getType() == Material.AIR) return; + + WorldConfig config = worldConfigs.getOrDefault(player.getWorld().getName(), worldConfigs.get("defaults")); + String countryCode = getCountryCode(config.location); + + if (clickedItem.getType() == Material.BOOK) { + player.closeInventory(); + audiences.player(player).sendMessage(Component.text( + getLocalizedMessage("gui_setlocation_prompt", countryCode), NamedTextColor.YELLOW)); + } else if (clickedItem.getType() == Material.REDSTONE_TORCH) { + String worldName = player.getWorld().getName(); + Set worlds = playersWithDisplay.computeIfAbsent(player.getUniqueId(), k -> new HashSet<>()); + if (worlds.contains(worldName)) { + worlds.remove(worldName); + audiences.player(player).sendMessage(Component.text( + getLocalizedMessage("toggle_disabled", countryCode, "world", worldName), NamedTextColor.RED)); + } else { + worlds.add(worldName); + audiences.player(player).sendMessage(Component.text( + getLocalizedMessage("toggle_enabled", countryCode, "world", worldName), NamedTextColor.GREEN)); + } + } else if (clickedItem.getType() == Material.PAPER) { + player.closeInventory(); + new WeatherForecastCommand().onCommand(player, null, "weatherforecast", new String[]{}); + } else if (clickedItem.getType() == Material.OAK_SIGN) { + player.closeInventory(); + String infoMessage = "RealTimeWeather Plugin\nVersion: 1.0\nAutor: M_Viper\nGetestete Minecraft-Version: 1.21.1 - 1.21.8"; + audiences.player(player).sendMessage(Component.text(infoMessage, NamedTextColor.AQUA)); + } + } + + @EventHandler + public void onInventoryClose(InventoryCloseEvent event) { + playerGUIs.remove(event.getPlayer().getUniqueId()); + } + + // === Hilfsmethoden & Modellklassen === + + private String getLocalizedMessage(String key, String countryCode, String... placeholders) { + String path = "languages." + (countryCode.isEmpty() ? "en" : countryCode.toLowerCase()) + "." + key; + String message = langConfig.getString(path); + if (message == null) { + path = "languages.en." + key; + message = langConfig.getString(path, key); + } + for (int i = 0; i < placeholders.length - 1; i += 2) { + message = message.replace("{" + placeholders[i] + "}", placeholders[i + 1]); + } + return message; + } + + private String getWeatherSymbol(String weatherMain) { + switch (weatherMain.toLowerCase()) { + case "clear": return "☀️"; + case "clouds": return "☁️"; + case "rain": return "🌧️"; + case "thunderstorm": return "⛈️"; + case "snow": return "❄️"; + case "mist": + case "fog": + case "haze": return "🌫️"; + default: return weatherMain; + } + } + + private String getLocalizedWeatherMain(String weatherMain, String countryCode) { + String key = "weather." + weatherMain.toLowerCase(); + return getLocalizedMessage(key, countryCode); + } + + private String getCountryCode(String location) { + if (location == null || !location.contains(",")) return "en"; + String[] parts = location.split(","); + String countryCode = parts.length > 1 ? parts[1].trim().toLowerCase() : "en"; + if (!langConfig.contains("languages." + countryCode)) return "en"; + return countryCode; + } + + private Inventory createWeatherGUI(Player player) { + String countryCode = getCountryCode(worldConfigs.getOrDefault(player.getWorld().getName(), worldConfigs.get("defaults")).location); + 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); + + ItemMeta setLocationMeta = setLocation.getItemMeta(); + setLocationMeta.setDisplayName(getLocalizedMessage("gui_setlocation", countryCode)); + setLocationMeta.setLore(Collections.singletonList(getLocalizedMessage("gui_setlocation_lore", countryCode))); + setLocation.setItemMeta(setLocationMeta); + + ItemMeta toggleWeatherMeta = toggleWeather.getItemMeta(); + toggleWeatherMeta.setDisplayName(getLocalizedMessage("gui_toggleweather", countryCode)); + toggleWeatherMeta.setLore(Collections.singletonList(getLocalizedMessage("gui_toggleweather_lore", countryCode))); + toggleWeather.setItemMeta(toggleWeatherMeta); + + ItemMeta forecastMeta = forecast.getItemMeta(); + forecastMeta.setDisplayName(getLocalizedMessage("gui_forecast", countryCode)); + forecastMeta.setLore(Collections.singletonList(getLocalizedMessage("gui_forecast_lore", countryCode))); + forecast.setItemMeta(forecastMeta); + + ItemMeta infoMeta = info.getItemMeta(); + infoMeta.setDisplayName(getLocalizedMessage("gui_info", countryCode)); + infoMeta.setLore(Collections.singletonList(getLocalizedMessage("gui_info_lore", countryCode))); + info.setItemMeta(infoMeta); + + inv.setItem(2, setLocation); + inv.setItem(3, toggleWeather); + inv.setItem(4, forecast); + inv.setItem(5, info); + + playerGUIs.put(player.getUniqueId(), inv); + return inv; + } + + // ==== PlayerConfig etc. wie gehabt ==== + private class PlayerConfig { + private final File configFile; + private final FileConfiguration config; + public PlayerConfig(UUID playerId) { + File playerDir = new File(getDataFolder(), "players"); + playerDir.mkdirs(); + configFile = new File(playerDir, playerId.toString() + ".yml"); + config = YamlConfiguration.loadConfiguration(configFile); + } + public String getLocation() { + return config.getString("location", ""); + } + public void setLocation(String location) { + config.set("location", location); + try { + config.save(configFile); + } catch (IOException e) { + getLogger().warning("Fehler beim Speichern der Spieler-Konfiguration: " + e.getMessage()); + } + } + } + + // ==== Commands ==== + private class WetterCommand implements CommandExecutor { + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + String countryCode = getCountryCode(worldConfigs.get("defaults").location); + if (args.length == 0) { + audiences.sender(sender).sendMessage(Component.text(getLocalizedMessage("usage", countryCode), NamedTextColor.RED)); + return true; + } + switch (args[0].toLowerCase(Locale.ROOT)) { + case "help": { + Component helpMessage = Component.text() + .append(Component.text(getLocalizedMessage("help_header", countryCode), NamedTextColor.AQUA, TextDecoration.BOLD)) + .append(Component.newline()) + .append(Component.text("/wetter help", NamedTextColor.YELLOW)) + .append(Component.text(" - " + getLocalizedMessage("help_help", countryCode), NamedTextColor.WHITE)) + .append(Component.newline()) + .append(Component.text("/wetter reload", NamedTextColor.YELLOW)) + .append(Component.text(" - " + getLocalizedMessage("help_reload", countryCode), NamedTextColor.WHITE)) + .append(Component.newline()) + .append(Component.text("/wetter setlocation ", NamedTextColor.YELLOW)) + .append(Component.text(" - " + getLocalizedMessage("help_setlocation", countryCode), NamedTextColor.WHITE)) + .append(Component.newline()) + .append(Component.text("/wetter query", NamedTextColor.YELLOW)) + .append(Component.text(" - " + getLocalizedMessage("help_query", countryCode), NamedTextColor.WHITE)) + .append(Component.newline()) + .append(Component.text("/wetter info", NamedTextColor.YELLOW)) + .append(Component.text(" - " + getLocalizedMessage("help_info", countryCode), NamedTextColor.WHITE)) + .append(Component.newline()) + .append(Component.text("/wetter gui", NamedTextColor.YELLOW)) + .append(Component.text(" - " + getLocalizedMessage("help_gui", countryCode), NamedTextColor.WHITE)) + .append(Component.newline()) + .append(Component.text("/weatherforecast", NamedTextColor.YELLOW)) + .append(Component.text(" - " + getLocalizedMessage("help_weatherforecast", countryCode), NamedTextColor.WHITE)) + .append(Component.newline()) + .append(Component.text("/toggleweather", NamedTextColor.YELLOW)) + .append(Component.text(" - " + getLocalizedMessage("help_toggleweather", countryCode), NamedTextColor.WHITE)) + .build(); + audiences.sender(sender).sendMessage(helpMessage); + return true; + } + case "reload": { + if (!sender.hasPermission("realtimeweather.reload")) { + audiences.sender(sender).sendMessage(Component.text(getLocalizedMessage("no_permission", countryCode), NamedTextColor.RED)); + return true; + } + if (weatherUpdateTask != null) weatherUpdateTask.cancel(); + if (syncTask != null) syncTask.cancel(); + reloadConfig(); + loadConfig(); + loadLanguageConfig(); + worldWeatherData.clear(); + processedLocations.clear(); + initializePlayerDisplays(); + setDoDaylightCycleForWorlds(); + startWeatherUpdateTask(); + startSyncTask(); + processedLocations.clear(); + updateWeatherDataForAllWorlds(); + audiences.sender(sender).sendMessage(Component.text(getLocalizedMessage("reload_success", countryCode), NamedTextColor.GREEN)); + return true; + } + case "setlocation": { + if (!(sender instanceof Player)) { + audiences.sender(sender).sendMessage(Component.text(getLocalizedMessage("only_players", countryCode), NamedTextColor.RED)); + return true; + } + if (args.length != 2) { + audiences.sender(sender).sendMessage(Component.text(getLocalizedMessage("usage_setlocation", countryCode), NamedTextColor.RED)); + return true; + } + Player player = (Player) sender; + String location = args[1]; + new BukkitRunnable() { + @Override + public void run() { + try { + WeatherFetcher fetcher = new WeatherFetcher(apiKey, location, "metric", getLogger()); + fetcher.fetchAsWeatherTimeData(); + PlayerConfig playerConfig = new PlayerConfig(player.getUniqueId()); + playerConfig.setLocation(location); + playerLocations.put(player.getUniqueId(), location); + String cc = getCountryCode(location); + audiences.player(player).sendMessage(Component.text(getLocalizedMessage("location_set", cc, "location", location), NamedTextColor.GREEN)); + } catch (Exception e) { + audiences.player(player).sendMessage(Component.text(getLocalizedMessage("invalid_location", countryCode, "location", location), NamedTextColor.RED)); + } + } + }.runTaskAsynchronously(WeatherTimeSyncPlugin.this); + return true; + } + case "query": { + if (!(sender instanceof Player)) { + audiences.sender(sender).sendMessage(Component.text(getLocalizedMessage("only_players", countryCode), NamedTextColor.RED)); + return true; + } + Player queryPlayer = (Player) sender; + WorldConfig config = worldConfigs.getOrDefault(queryPlayer.getWorld().getName(), worldConfigs.get("defaults")); + if (!config.enabled) { + audiences.player(queryPlayer).sendMessage(Component.text(getLocalizedMessage("plugin_disabled", countryCode, "world", queryPlayer.getWorld().getName()), NamedTextColor.RED)); + return true; + } + String queryLocation = playerLocations.getOrDefault(queryPlayer.getUniqueId(), config.location); + new BukkitRunnable() { + @Override + public void run() { + try { + WeatherFetcher fetcher = new WeatherFetcher(apiKey, queryLocation, config.units, getLogger()); + WeatherTimeData data = fetcher.fetchAsWeatherTimeData(); + String cc = getCountryCode(queryLocation); + String localizedWeather = getLocalizedWeatherMain(data.getWeatherMain(), cc); + String tempUnit = config.units.equalsIgnoreCase("metric") ? "°C" : "°F"; + String city = queryLocation.split(",")[0]; + 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)); + } catch (Exception e) { + audiences.player(queryPlayer).sendMessage(Component.text(getLocalizedMessage("forecast_error", countryCode, "location", queryLocation, "error", e.getMessage()), NamedTextColor.RED)); + } + } + }.runTaskAsynchronously(WeatherTimeSyncPlugin.this); + return true; + } + case "info": { + String infoMessage = "RealTimeWeather Plugin\nVersion: 1.0\nAutor: M_Viper\nGetestete Minecraft-Version: 1.21.1 - 1.21.8"; + audiences.sender(sender).sendMessage(Component.text(infoMessage, NamedTextColor.AQUA)); + return true; + } + case "gui": { + if (!(sender instanceof Player)) { + audiences.sender(sender).sendMessage(Component.text(getLocalizedMessage("only_players", countryCode), NamedTextColor.RED)); + return true; + } + Player guiPlayer = (Player) sender; + WorldConfig guiConfig = worldConfigs.getOrDefault(guiPlayer.getWorld().getName(), worldConfigs.get("defaults")); + if (!guiConfig.enabled) { + String cc = getCountryCode(guiConfig.location); + audiences.player(guiPlayer).sendMessage(Component.text(getLocalizedMessage("plugin_disabled", cc, "world", guiPlayer.getWorld().getName()), NamedTextColor.RED)); + return true; + } + guiPlayer.openInventory(createWeatherGUI(guiPlayer)); + return true; + } + default: + audiences.sender(sender).sendMessage(Component.text(getLocalizedMessage("usage", countryCode), NamedTextColor.RED)); + return true; + } + } + } + + private class WeatherForecastCommand implements CommandExecutor { + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + String countryCode = getCountryCode(worldConfigs.get("defaults").location); + audiences.sender(sender).sendMessage(Component.text(getLocalizedMessage("only_players", countryCode), NamedTextColor.RED)); + return true; + } + Player player = (Player) sender; + WorldConfig config = worldConfigs.getOrDefault(player.getWorld().getName(), worldConfigs.get("defaults")); + if (!config.enabled) { + String countryCode = getCountryCode(config.location); + audiences.player(player).sendMessage(Component.text(getLocalizedMessage("plugin_disabled", countryCode, "world", player.getWorld().getName()), NamedTextColor.RED)); + return true; + } + String location = playerLocations.getOrDefault(player.getUniqueId(), config.location); + new BukkitRunnable() { + @Override + public void run() { + try { + WeatherFetcher fetcher = new WeatherFetcher(apiKey, location, config.units, getLogger()); + JSONObject json = fetcher.fetchForecast(); + JSONArray list = json.getJSONArray("list"); + + String[] locationParts = location.split(","); + String countryCode = locationParts.length > 1 ? locationParts[1].trim() : ""; + String city = locationParts[0].trim(); + + String forecastMessage = getLocalizedMessage("forecast_header", countryCode, "location", city); + Component message = Component.text(forecastMessage, NamedTextColor.AQUA); + + for (int i = 0; i < list.length() && i < 40; i += 8) { + JSONObject forecast = list.getJSONObject(i); + long dt = forecast.getLong("dt"); + String weatherMain = forecast.getJSONArray("weather").getJSONObject(0).getString("main"); + double temp = forecast.getJSONObject("main").getDouble("temp"); + ZonedDateTime dateTime = Instant.ofEpochSecond(dt).atZone(ZoneId.of("UTC")); + + String localizedWeatherMain = getLocalizedWeatherMain(weatherMain, countryCode); + + message = message.append(Component.newline()) + .append(Component.text( + dateTime.format(DateTimeFormatter.ofPattern("dd.MM.yyyy")) + ": " + + localizedWeatherMain + ", " + String.format("%.1f%s", temp, config.units.equalsIgnoreCase("metric") ? "°C" : "°F"), + NamedTextColor.GREEN)); + } + audiences.player(player).sendMessage(message); + } catch (Exception e) { + String countryCode = getCountryCode(config.location); + audiences.player(player).sendMessage(Component.text( + getLocalizedMessage("forecast_error", countryCode, "location", location, "error", e.getMessage()), + NamedTextColor.RED)); + } + } + }.runTaskAsynchronously(WeatherTimeSyncPlugin.this); + return true; + } + } + + private class ToggleWeatherCommand implements CommandExecutor { + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + String countryCode = getCountryCode(worldConfigs.get("defaults").location); + audiences.sender(sender).sendMessage(Component.text(getLocalizedMessage("only_players", countryCode), NamedTextColor.RED)); + return true; + } + Player player = (Player) sender; + String worldName = player.getWorld().getName(); + WorldConfig config = worldConfigs.getOrDefault(worldName, worldConfigs.get("defaults")); + if (!config.enabled) { + String countryCode = getCountryCode(config.location); + audiences.player(player).sendMessage(Component.text(getLocalizedMessage("plugin_disabled", countryCode, "world", worldName), NamedTextColor.RED)); + return true; + } + String countryCode = getCountryCode(config.location); + Set worlds = playersWithDisplay.computeIfAbsent(player.getUniqueId(), k -> new HashSet<>()); + if (worlds.contains(worldName)) { + worlds.remove(worldName); + audiences.player(player).sendMessage(Component.text(getLocalizedMessage("toggle_disabled", countryCode, "world", worldName), NamedTextColor.RED)); + } else { + worlds.add(worldName); + audiences.player(player).sendMessage(Component.text(getLocalizedMessage("toggle_enabled", countryCode, "world", worldName), NamedTextColor.GREEN)); + } + return true; + } + } + + // Modellklasse WorldConfig (nur als Speicher für die Welten-Settings) + private static class WorldConfig { + boolean enabled; + String location; + String units; + String timeFormat; + boolean displayActionbar; + boolean displayWeatherIcon; + String displayPosition; + int paddingRight; + boolean syncInGameWeather; + + public WorldConfig(boolean enabled, String location, String units, String timeFormat, + boolean displayActionbar, boolean displayWeatherIcon, + String displayPosition, int paddingRight, boolean syncInGameWeather) { + this.enabled = enabled; + this.location = location; + this.units = units; + this.timeFormat = timeFormat; + this.displayActionbar = displayActionbar; + this.displayWeatherIcon = displayWeatherIcon; + this.displayPosition = displayPosition; + this.paddingRight = paddingRight; + this.syncInGameWeather = syncInGameWeather; + } } }