From 6297f62c44338b2313c6a319adab9b100e11368e Mon Sep 17 00:00:00 2001 From: M_Viper Date: Thu, 7 Aug 2025 17:06:01 +0000 Subject: [PATCH] Dateien nach "src/main/java/dev/viper/weathertime" hochladen --- .../dev/viper/weathertime/WeatherFetcher.java | 123 +++ .../viper/weathertime/WeatherTimeData.java | 57 ++ .../weathertime/WeatherTimeSyncPlugin.java | 863 ++++++++++++++++++ 3 files changed, 1043 insertions(+) create mode 100644 src/main/java/dev/viper/weathertime/WeatherFetcher.java create mode 100644 src/main/java/dev/viper/weathertime/WeatherTimeData.java create mode 100644 src/main/java/dev/viper/weathertime/WeatherTimeSyncPlugin.java diff --git a/src/main/java/dev/viper/weathertime/WeatherFetcher.java b/src/main/java/dev/viper/weathertime/WeatherFetcher.java new file mode 100644 index 0000000..797ade7 --- /dev/null +++ b/src/main/java/dev/viper/weathertime/WeatherFetcher.java @@ -0,0 +1,123 @@ +package dev.viper.weathertime; + +import org.json.JSONObject; +import org.json.JSONArray; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.logging.Logger; + +public class WeatherFetcher { + + private final String apiKey; + private final String location; + private final String units; + private final Logger logger; + + public WeatherFetcher(String apiKey, String location, String units, Logger logger) { + this.apiKey = apiKey; + this.location = location; + this.units = units.toLowerCase(); + this.logger = logger; + if (!this.units.equals("metric") && !this.units.equals("imperial")) { + logger.warning("Ungültige Einheit für " + location + ": " + units + ". Verwende 'metric' als Fallback."); + } + } + + public WeatherData fetch() throws Exception { + String effectiveUnits = units.equals("metric") || units.equals("imperial") ? units : "metric"; + + String urlString = String.format( + "https://api.openweathermap.org/data/2.5/weather?q=%s&appid=%s&units=%s", + location, apiKey, effectiveUnits); + + URL url = new URL(urlString); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setConnectTimeout(5000); + connection.setReadTimeout(5000); + + int status = connection.getResponseCode(); + if (status != 200) { + logger.warning("HTTP-Fehlercode: " + status + " für Ort: " + location); + throw new RuntimeException("HTTP-Fehlercode: " + status + " für Ort: " + location); + } + + BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); + StringBuilder content = new StringBuilder(); + String inputLine; + + while ((inputLine = in.readLine()) != null) { + content.append(inputLine); + } + in.close(); + connection.disconnect(); + + JSONObject json = new JSONObject(content.toString()); + long dt = json.getLong("dt"); + int timezoneShift; + try { + timezoneShift = json.getInt("timezone"); + } catch (Exception e) { + logger.warning("Fehler beim Abrufen der Zeitzone für Ort: " + location + ": " + e.getMessage()); + throw new RuntimeException("Fehler beim Abrufen der Zeitzone für Ort: " + location, e); + } + ZonedDateTime dateTime = Instant.ofEpochSecond(dt).atZone(ZoneId.of("UTC")).plusSeconds(timezoneShift); + String weatherMain = json.getJSONArray("weather").getJSONObject(0).getString("main"); + double temperature = json.getJSONObject("main").getDouble("temp"); + + if (effectiveUnits.equals("imperial") && (temperature < -50 || temperature > 150)) { + logger.warning("Unrealistische Temperatur für " + location + ": " + temperature + "°F. API-Fehler?"); + } else if (effectiveUnits.equals("metric") && (temperature < -50 || temperature > 60)) { + logger.warning("Unrealistische Temperatur für " + location + ": " + temperature + "°C. API-Fehler?"); + } + + return new WeatherData(dateTime, weatherMain, temperature); + } + + public JSONObject fetchForecast() throws Exception { + String effectiveUnits = units.equals("metric") || units.equals("imperial") ? units : "metric"; + + String urlString = String.format( + "https://api.openweathermap.org/data/2.5/forecast?q=%s&appid=%s&units=%s", + location, apiKey, effectiveUnits); + URL url = new URL(urlString); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setConnectTimeout(5000); + connection.setReadTimeout(5000); + + int status = connection.getResponseCode(); + if (status != 200) { + logger.warning("HTTP-Fehlercode: " + status + " für Ort: " + location); + throw new RuntimeException("HTTP-Fehlercode: " + status + " für Ort: " + location); + } + + BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); + StringBuilder content = new StringBuilder(); + String inputLine; + while ((inputLine = in.readLine()) != null) { + content.append(inputLine); + } + in.close(); + connection.disconnect(); + + return new JSONObject(content.toString()); + } + + public static class WeatherData { + ZonedDateTime dateTime; + String weatherMain; + double temperature; + + public WeatherData(ZonedDateTime dateTime, String weatherMain, double temperature) { + this.dateTime = dateTime; + this.weatherMain = weatherMain; + this.temperature = temperature; + } + } +} \ No newline at end of file diff --git a/src/main/java/dev/viper/weathertime/WeatherTimeData.java b/src/main/java/dev/viper/weathertime/WeatherTimeData.java new file mode 100644 index 0000000..b137ff4 --- /dev/null +++ b/src/main/java/dev/viper/weathertime/WeatherTimeData.java @@ -0,0 +1,57 @@ +package dev.viper.weathertime; + +import java.time.Instant; +import java.time.LocalTime; +import java.time.ZoneOffset; + +public class WeatherTimeData { + private final boolean rain; + private final boolean thunder; + private final long unixTime; + private final double tempCelsius; + + public WeatherTimeData(boolean rain, boolean thunder, long unixTime, double tempCelsius) { + this.rain = rain; + this.thunder = thunder; + this.unixTime = unixTime; + this.tempCelsius = tempCelsius; + } + + public boolean isRain() { + return rain; + } + + public boolean isThunder() { + return thunder; + } + + public double getTemp(String unit) { + return unit.equalsIgnoreCase("F") ? (tempCelsius * 9 / 5) + 32 : tempCelsius; + } + + public String getFormattedTime(String format) { + LocalTime time = Instant.ofEpochSecond(unixTime) + .atOffset(ZoneOffset.UTC) + .toLocalTime(); + + if ("12h".equalsIgnoreCase(format)) { + int hour = time.getHour(); + String ampm = hour >= 12 ? "PM" : "AM"; + hour = hour % 12; + if (hour == 0) hour = 12; + return String.format("%02d:%02d %s", hour, time.getMinute(), ampm); + } else { + return String.format("%02d:%02d", time.getHour(), time.getMinute()); + } + } + + public long toMinecraftTime() { + LocalTime time = Instant.ofEpochSecond(unixTime) + .atOffset(ZoneOffset.UTC) + .toLocalTime(); + + int hour = time.getHour(); + int minute = time.getMinute(); + return (hour * 1000L / 24) + (minute * 1000L / (24 * 60)); + } +} \ No newline at end of file diff --git a/src/main/java/dev/viper/weathertime/WeatherTimeSyncPlugin.java b/src/main/java/dev/viper/weathertime/WeatherTimeSyncPlugin.java new file mode 100644 index 0000000..e233c0b --- /dev/null +++ b/src/main/java/dev/viper/weathertime/WeatherTimeSyncPlugin.java @@ -0,0 +1,863 @@ +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.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +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.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.UUID; + +public class WeatherTimeSyncPlugin extends JavaPlugin implements Listener { + + private String apiKey; + private int updateInterval; + private final HashMap worldConfigs = new HashMap<>(); + private final HashMap worldWeatherData = new HashMap<>(); + private final HashMap> playersWithDisplay = new HashMap<>(); + private final HashMap playerLocations = new HashMap<>(); + private final HashMap playerGUIs = new HashMap<>(); + private BukkitAudiences audiences; + private BukkitRunnable weatherUpdateTask; + private BukkitRunnable syncTask; + private final HashSet processedLocations = new HashSet<>(); + private FileConfiguration langConfig; + + @Override + public void onEnable() { + saveDefaultConfig(); + loadLanguageConfig(); // Zuerst laden, um sicherzustellen, dass Übersetzungen verfügbar sind + loadConfig(); + this.audiences = BukkitAudiences.create(this); + + getCommand("wetter").setExecutor(new WetterCommand()); + getCommand("weatherforecast").setExecutor(new WeatherForecastCommand()); + getCommand("toggleweather").setExecutor(new ToggleWeatherCommand()); + + getServer().getPluginManager().registerEvents(this, this); + + setDoDaylightCycleForWorlds(); + startWeatherUpdateTask(); + startSyncTask(); + + initializePlayerDisplays(); + } + + @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(); + } + } + } + + private void loadConfig() { + FileConfiguration cfg = getConfig(); + apiKey = cfg.getString("api-key"); + 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 == null || apiKey.trim().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); + getLogger().info("lang.yml wurde aus den Ressourcen kopiert."); + langConfig = YamlConfiguration.loadConfiguration(langFile); + } else if (langConfig.getConfigurationSection("languages") == null) { + getLogger().warning("lang.yml enthält keinen 'languages'-Abschnitt. Standardwerte werden verwendet."); + saveResource("lang.yml", true); // Überschreibe mit Standardwerten + langConfig = YamlConfiguration.loadConfiguration(langFile); + } + // Debugging: Verfügbare Sprachen protokollieren + getLogger().info("Verfügbare Sprachen in lang.yml: " + langConfig.getConfigurationSection("languages").getKeys(false)); + } catch (Exception e) { + getLogger().severe("Fehler beim Laden von lang.yml: " + e.getMessage()); + saveResource("lang.yml", true); // Überschreibe bei Fehler mit Standardwerten + langConfig = YamlConfiguration.loadConfiguration(langFile); + getLogger().info("lang.yml wurde nach Fehler mit Standardwerten überschrieben."); + } + } + + 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 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); + if (message == null) { + // Nur Warnung für fehlende Übersetzungen, keine Debugging-Info für jede Suche + getLogger().warning("Übersetzung für Schlüssel '" + key + "' in Sprache '" + countryCode + "' und Fallback 'en' nicht gefunden!"); + return key; + } + } + for (int i = 0; i < placeholders.length - 1; i += 2) { + message = message.replace("{" + placeholders[i] + "}", placeholders[i + 1]); + } + return message; + } + + 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; + WeatherData data = worldWeatherData.get(world.getName()); + if (data != null) { + syncMinecraftTime(world, data.dateTime); + 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)) { + WeatherData 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()); + WeatherFetcher.WeatherData weatherData = fetcher.fetch(); + WeatherData newData = new WeatherData( + weatherData.dateTime, + weatherData.weatherMain, + weatherData.temperature + ); + worldWeatherData.put(world.getName(), newData); + processedLocations.add(config.location); + + new BukkitRunnable() { + @Override + public void run() { + syncMinecraftTime(world, weatherData.dateTime); + if (config.syncInGameWeather) { + int durationTicks = 20 * 60 * 5; // 5 Minuten + switch (weatherData.weatherMain.toLowerCase()) { + case "rain": + case "drizzle": + world.setStorm(true); + world.setThundering(false); + world.setWeatherDuration(durationTicks); + break; + case "thunderstorm": + world.setStorm(true); + world.setThundering(true); + world.setWeatherDuration(durationTicks); + break; + case "snow": + world.setStorm(true); + world.setThundering(false); + world.setWeatherDuration(durationTicks); + break; + default: + world.setStorm(false); + world.setThundering(false); + world.setWeatherDuration(durationTicks); + break; + } + } + } + }.runTask(this); + } catch (Exception e) { + getLogger().warning("Fehler beim Abrufen von Wetter/Zeit für Welt " + world.getName() + ": " + e.getMessage()); + String countryCode = getCountryCode(config.location); + String errorMsg = getLocalizedMessage("forecast_error", countryCode, "location", config.location, "error", e.getMessage()); + for (Player player : Bukkit.getOnlinePlayers()) { + if (player.hasPermission("realtimeweather.admin")) { + audiences.player(player).sendMessage(Component.text(errorMsg, NamedTextColor.RED)); + } + } + } + } + } + + private void initializePlayerDisplays() { + playersWithDisplay.clear(); + for (Player player : Bukkit.getOnlinePlayers()) { + HashSet 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 syncMinecraftTime(World world, ZonedDateTime dateTime) { + int hour = dateTime.getHour(); + int minute = dateTime.getMinute(); + int second = dateTime.getSecond(); + int minecraftTime = ((hour + 18) % 24) * 1000 + (minute * 1000 / 60) + (second * 1000 / 3600); + world.setTime(minecraftTime); + world.setGameRuleValue("doDaylightCycle", "false"); + } + + private void updateActionbarForWorld(World world, WeatherData 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; + } + WeatherData playerData = data; + String location = playerLocations.getOrDefault(player.getUniqueId(), ""); + if (!location.isEmpty()) { + try { + WeatherFetcher fetcher = new WeatherFetcher(apiKey, location, config.units, getLogger()); + WeatherFetcher.WeatherData fetched = fetcher.fetch(); + playerData = new WeatherData(fetched.dateTime, fetched.weatherMain, fetched.temperature); + } 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.dateTime.format(DateTimeFormatter.ofPattern("dd.MM.yyyy")); + String timeStr = formatTime(playerData.dateTime, config.timeFormat); + String tempUnit = config.units.equalsIgnoreCase("metric") ? "°C" : "°F"; + String weatherIcon = config.displayWeatherIcon ? getWeatherSymbol(playerData.weatherMain) + " " : ""; + String tempStr = String.format("%.1f%s", playerData.temperature, tempUnit); + + 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)) + .build(); + + Component paddedMessage; + if ("top-right".equalsIgnoreCase(config.displayPosition)) { + paddedMessage = Component.text() + .append(Component.text(" ".repeat(config.paddingRight))) + .append(message) + .build(); + } else { + paddedMessage = Component.text() + .append(message) + .append(Component.text(" ".repeat(20))) + .build(); + } + + audiences.player(player).sendActionBar(paddedMessage); + } + } + + private String formatTime(ZonedDateTime dateTime, String timeFormat) { + int hour = dateTime.getHour(); + int minute = dateTime.getMinute(); + int second = dateTime.getSecond(); + + if ("12h".equalsIgnoreCase(timeFormat)) { + String ampm = hour >= 12 ? "PM" : "AM"; + int hour12 = hour % 12; + if (hour12 == 0) hour12 = 12; + return String.format("%d:%02d:%02d %s", hour12, minute, second, ampm); + } else { + return String.format("%02d:%02d:%02d", hour, minute, second); + } + } + + private String getWeatherSymbol(String weatherMain) { + String w = weatherMain.toLowerCase(); + switch (w) { + 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(",")) { + getLogger().warning("Ungültiges Location-Format: " + location + ". Fallback auf 'en'."); + return "en"; + } + String[] parts = location.split(","); + String countryCode = parts.length > 1 ? parts[1].trim().toLowerCase() : "en"; + // Überprüfe, ob der Sprachcode in lang.yml existiert + if (!langConfig.contains("languages." + countryCode)) { + getLogger().warning("Sprache '" + countryCode + "' nicht in lang.yml gefunden. Fallback auf 'en'."); + 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(Arrays.asList(getLocalizedMessage("gui_setlocation_lore", countryCode))); + setLocation.setItemMeta(setLocationMeta); + + ItemMeta toggleWeatherMeta = toggleWeather.getItemMeta(); + toggleWeatherMeta.setDisplayName(getLocalizedMessage("gui_toggleweather", countryCode)); + toggleWeatherMeta.setLore(Arrays.asList(getLocalizedMessage("gui_toggleweather_lore", countryCode))); + toggleWeather.setItemMeta(toggleWeatherMeta); + + ItemMeta forecastMeta = forecast.getItemMeta(); + forecastMeta.setDisplayName(getLocalizedMessage("gui_forecast", countryCode)); + forecastMeta.setLore(Arrays.asList(getLocalizedMessage("gui_forecast_lore", countryCode))); + forecast.setItemMeta(forecastMeta); + + ItemMeta infoMeta = info.getItemMeta(); + infoMeta.setDisplayName(getLocalizedMessage("gui_info", countryCode)); + infoMeta.setLore(Arrays.asList(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; + } + + @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; + WeatherData data = worldWeatherData.get(world.getName()); + if (data == null) return; + + String countryCode = getCountryCode(config.location); + String localizedWeather = getLocalizedWeatherMain(data.weatherMain, 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.temperature, tempUnit); + audiences.player(player).sendActionBar(Component.text(message, NamedTextColor.GREEN)); + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + Player player = (Player) event.getWhoClicked(); + if (!playerGUIs.containsKey(player.getUniqueId()) || event.getInventory() != playerGUIs.get(player.getUniqueId())) { + 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(); + HashSet 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) { + Player player = (Player) event.getPlayer(); + playerGUIs.remove(player.getUniqueId()); + } + + private static class WeatherData { + ZonedDateTime dateTime; + String weatherMain; + double temperature; + + public WeatherData(ZonedDateTime dateTime, String weatherMain, double temperature) { + this.dateTime = dateTime; + this.weatherMain = weatherMain; + this.temperature = temperature; + } + } + + 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; + } + } + + 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()); + } + } + } + + private class WetterCommand implements CommandExecutor { + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (args.length == 0) { + String countryCode = getCountryCode(worldConfigs.get("defaults").location); + audiences.sender(sender).sendMessage(Component.text( + getLocalizedMessage("usage", countryCode), NamedTextColor.RED)); + return true; + } + + if (args[0].equalsIgnoreCase("help")) { + String countryCode = getCountryCode(worldConfigs.get("defaults").location); + 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; + } + + if (args[0].equalsIgnoreCase("reload")) { + if (!sender.hasPermission("realtimeweather.reload")) { + String countryCode = getCountryCode(worldConfigs.get("defaults").location); + 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(); + + String countryCode = getCountryCode(worldConfigs.get("defaults").location); + audiences.sender(sender).sendMessage(Component.text( + getLocalizedMessage("reload_success", countryCode), NamedTextColor.GREEN)); + return true; + } + + if (args[0].equalsIgnoreCase("setlocation")) { + 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; + } + if (args.length != 2) { + String countryCode = getCountryCode(worldConfigs.get("defaults").location); + 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.fetch(); // Teste, ob der Ort gültig ist + PlayerConfig playerConfig = new PlayerConfig(player.getUniqueId()); + playerConfig.setLocation(location); + playerLocations.put(player.getUniqueId(), location); + String countryCode = getCountryCode(location); + audiences.player(player).sendMessage(Component.text( + getLocalizedMessage("location_set", countryCode, "location", location), + NamedTextColor.GREEN)); + } catch (Exception e) { + String countryCode = getCountryCode(worldConfigs.get("defaults").location); + audiences.player(player).sendMessage(Component.text( + getLocalizedMessage("invalid_location", countryCode, "location", location), + NamedTextColor.RED)); + } + } + }.runTaskAsynchronously(WeatherTimeSyncPlugin.this); + return true; + } + + if (args[0].equalsIgnoreCase("query")) { + 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()); + WeatherFetcher.WeatherData data = fetcher.fetch(); + String countryCode = getCountryCode(location); + String localizedWeather = getLocalizedWeatherMain(data.weatherMain, countryCode); + String tempUnit = config.units.equalsIgnoreCase("metric") ? "°C" : "°F"; + String city = location.split(",")[0]; + String message = String.format("%s: %s, %.1f%s", city, localizedWeather, data.temperature, tempUnit); + audiences.player(player).sendMessage(Component.text(message, NamedTextColor.GREEN)); + } 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; + } + + if (args[0].equalsIgnoreCase("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; + } + + if (args[0].equalsIgnoreCase("gui")) { + 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; + } + player.openInventory(createWeatherGUI(player)); + return true; + } + + String countryCode = getCountryCode(worldConfigs.get("defaults").location); + 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); + HashSet 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; + } + } +} \ No newline at end of file