Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
26d1f27f79 | |||
4e0195f7ab | |||
9856ad0e54 | |||
de4f24c368 | |||
7ae5eea24a | |||
ac318f4f78 | |||
fe3e1c1534 | |||
c7416baed5 | |||
98cad69efc | |||
64c994bc8b | |||
a1e29bc89e | |||
ede9d372f0 | |||
7761b5a1be | |||
032ada193a |
205
README.md
205
README.md
@@ -1,141 +1,108 @@
|
||||
# RealTimeWeather
|
||||
|
||||
Ein Minecraft-Plugin, das Echtzeit-Wetter und -Zeit in deine Welten bringt! **RealTimeWeather** synchronisiert das In-Game-Wetter und die Tageszeit mit Daten von OpenWeatherMap und bietet eine benutzerfreundliche GUI, Wettervorhersagen und anpassbare Anzeigeoptionen. Entwickelt für Minecraft 1.21.1.
|
||||
**RealTimeWeather** ist ein Bukkit/Spigot/Paper-Plugin, das echte Wetter- und Zeitdaten aus der realen Welt in deine Minecraft-Welt synchronisiert und live auf der Actionbar anzeigt – inklusive Temperatur, Wettericon, Luftfeuchtigkeit, Windstärke und Sonnenauf-/untergang.
|
||||
|
||||
## Funktionen
|
||||
## ⭐ Features
|
||||
|
||||
- **Echtzeit-Wetter**: Synchronisiert Regen, Gewitter oder klares Wetter mit realen Wetterdaten eines konfigurierbaren Orts.
|
||||
- **Echtzeit-Zeit**: Passt die Minecraft-Tageszeit an die aktuelle Uhrzeit an (deaktiviert den Tageszyklus).
|
||||
- **Actionbar-Anzeige**: Zeigt Datum, Uhrzeit, Wetter-Symbol (z. B. 🌧️) und Temperatur in der Actionbar an, mit Optionen für Position (oben links/rechts) und Symbolanzeige.
|
||||
- **Wettervorhersage**: 5-Tage-Wettervorhersage über den Befehl `/weatherforecast`.
|
||||
- **GUI**: Intuitive Benutzeroberfläche zum Festlegen von Orten, Umschalten der Anzeige und Abrufen von Informationen.
|
||||
- **Mehrsprachenunterstützung**: Unterstützt Deutsch (`de`) und Englisch (`en`) über `lang.yml`, mit einfacher Erweiterung für weitere Sprachen.
|
||||
- **Weltspezifische Konfiguration**: Unterschiedliche Einstellungen für jede Welt in `config.yml`.
|
||||
- **Spielerindividuelle Orte**: Spieler können ihren eigenen Ort mit `/wetter setlocation` festlegen.
|
||||
- **Berechtigungen**: Admin-Befehle sind durch Berechtigungen geschützt.
|
||||
- Echte Wetter- und Uhrzeit-Synchronisation pro Welt
|
||||
- Anzeige auf der Actionbar:
|
||||
Datum, Uhrzeit, Wetter-Icon, Temperatur, Luftfeuchtigkeit 💧, Windgeschwindigkeit 🌬️, Sonnenaufgang 🌅 & Sonnenuntergang 🌇
|
||||
- Eigene Location pro Spieler möglich (GUI & Befehl)
|
||||
- Ingame-Vorhersage (5 Tage) als Command
|
||||
- Wetterumschaltung auf Server synchron mit echten Wetterdaten (optional)
|
||||
- Volle Mehrsprachigkeit über lang.yml
|
||||
- Kompatibel mit Minecraft 1.19.x – 1.21.x
|
||||
|
||||
## Installation
|
||||
## 🔧 Installation
|
||||
|
||||
1. **Download**: Lade die `RealTimeWeather.jar` aus den [Releases](https://github.com/M_Viper/RealTimeWeather/releases) herunter.
|
||||
2. **Plugin-Ordner**: Kopiere die JAR-Datei in den `plugins`-Ordner deines Minecraft-Servers.
|
||||
3. **API-Schlüssel**: Registriere dich bei [OpenWeatherMap](https://openweathermap.org/) für einen kostenlosen API-Schlüssel.
|
||||
4. **Konfiguration**: Bearbeite die `config.yml` im Ordner `plugins/RealTimeWeather/` und füge deinen API-Schlüssel ein.
|
||||
5. **Server starten**: Starte deinen Server, um das Plugin zu laden. Die `lang.yml` wird automatisch generiert.
|
||||
1. **Kopiere die Dateien:**<br>
|
||||
Die JAR nach `/plugins/` kopieren, Server neu starten.
|
||||
2. **OpenWeatherMap API-Key** im `/plugins/RealTimeWeather/config.yml` eintragen:
|
||||
|
||||
## Konfiguration
|
||||
api-key: "DEIN_OPENWEATHERMAP_API_KEY"
|
||||
|
||||
Die Konfigurationsdateien befinden sich im Ordner `plugins/RealTimeWeather/`.
|
||||
Registriere gratis unter https://openweathermap.org.
|
||||
|
||||
### config.yml
|
||||
3. **Plugin starten/neu laden:**<br>
|
||||
`reload` Befehl (`/wetter reload`) oder Server-Neustart.
|
||||
|
||||
## ⚙️ Kurzes Setup
|
||||
|
||||
- Die wichtigsten Einstellungen sind in der `config.yml` dokumentiert (siehe unten).
|
||||
- Du kannst für jede Welt eigene Locations, Anzeige-Modi oder Wettertypen setzen.
|
||||
|
||||
## 🕹️ Kommandos
|
||||
|
||||
| Befehl | Beschreibung |
|
||||
|-----------------------|-----------------------------------------------|
|
||||
| `/wetter help` | Alle Befehle und Kurzinfos anzeigen |
|
||||
| `/wetter reload` | Konfiguration und Sprache neu laden |
|
||||
| `/wetter setlocation <city,country>` | Eigene Stadt für Anzeige wählen |
|
||||
| `/wetter query` | Zeigt das aktuelle Wetter für dich an |
|
||||
| `/wetter info` | Infos zum Plugin |
|
||||
| `/wetter gui` | Öffnet das GUI mit Schnell-Optionen |
|
||||
| `/weatherforecast` | 5-Tage-Wetterprognose deiner Location |
|
||||
| `/toggleweather` | Actionbar-Wetteranzeige für Welt (de)aktivieren |
|
||||
|
||||
## 📊 Live-Anzeige (Actionbar)
|
||||
|
||||
Beispiel:
|
||||
|
||||
08.08.2025 18:31 ☁️ 21.7°C | 💧 68% | 🌬️ 2.5 m/s | 🌅 05:48 | 🌇 21:34
|
||||
|
||||
```yml
|
||||
## 📝 Beispiel-config.yml
|
||||
|
||||
```yaml
|
||||
api-key: "DEIN_API_KEY_HIER"
|
||||
update-interval: 60 # Sekunden
|
||||
update-interval: 60
|
||||
|
||||
defaults:
|
||||
enabled: true # Plugin für Welten ohne spezifische Konfiguration aktivieren
|
||||
location: "Berlin,de" # Stadt,Ländercode
|
||||
units: "metric" # metric = Celsius, imperial = Fahrenheit
|
||||
time-format: "24h" # "24h" oder "12h"
|
||||
display-actionbar: true
|
||||
display-weather-icon: true
|
||||
display-position: "top-right" # top-left oder top-right
|
||||
padding-right: 100 # Anzahl der Leerzeichen für top-right
|
||||
enabled: true
|
||||
location: "Berlin,de"
|
||||
units: "metric"
|
||||
time-format: "24h"
|
||||
display-actionbar: true
|
||||
display-weather-icon: true
|
||||
|
||||
display-position / padding-right werden zentriert ignoriert
|
||||
display-position: "top-right"
|
||||
padding-right: 100
|
||||
sync-in-game-weather: true
|
||||
|
||||
worlds:
|
||||
world:
|
||||
enabled: true
|
||||
location: "London,uk"
|
||||
units: "imperial"
|
||||
time-format: "12h"
|
||||
display-actionbar: true
|
||||
display-weather-icon: true
|
||||
display-position: "top-left"
|
||||
padding-right: 50
|
||||
world_nether:
|
||||
enabled: false
|
||||
location: "Tokyo,jp"
|
||||
units: "metric"
|
||||
time-format: "24h"
|
||||
display-actionbar: true
|
||||
display-weather-icon: false
|
||||
display-position: "top-right"
|
||||
padding-right: 150
|
||||
world:
|
||||
enabled: true
|
||||
location: "Berlin,de"
|
||||
units: "metric"
|
||||
time-format: "24h"
|
||||
display-actionbar: true
|
||||
display-weather-icon: true
|
||||
display-position: "top-left"
|
||||
padding-right: 50
|
||||
sync-in-game-weather: true
|
||||
world_nether:
|
||||
enabled: false
|
||||
location: "Berlin,de"
|
||||
units: "metric"
|
||||
time-format: "24h"
|
||||
display-actionbar: true
|
||||
display-weather-icon: false
|
||||
display-position: "top-right"
|
||||
padding-right: 150
|
||||
sync-in-game-weather: false
|
||||
```
|
||||
|
||||
# RealTimeWeather
|
||||
|
||||
Ein Minecraft-Plugin, das Echtzeit-Wetter und -Zeit in deine Welten bringt. Synchronisiert Wetter und Tageszeit mit OpenWeatherMap-Daten. Entwickelt für Minecraft 1.21.1 - 1.21.8.
|
||||
## 🧑💻 Erweiterungen
|
||||
|
||||
## Konfiguration
|
||||
- Anzeige von Feuchtigkeit, Wind & Sonnenzeiten kann leicht deaktiviert/angepasst werden (siehe Actionbar-Code).
|
||||
- Ländercodes und Städte werden in OpenWeatherMap-Syntax verwendet, z. B. `London,gb`, `Frankfurt,de`.
|
||||
|
||||
## ❤️ Lizenz
|
||||
|
||||
### lang.yml
|
||||
[MIT License](LICENSE)
|
||||
|
||||
Enthält Übersetzungen für Deutsch (`de`) und Englisch (`en`). Beispiel für Deutsch:
|
||||
---
|
||||
|
||||
```yaml
|
||||
languages:
|
||||
de:
|
||||
help_header: "RealTimeWeather-Befehle"
|
||||
help_help: "Diese Hilfenachricht anzeigen"
|
||||
help_reload: "Die Plugin-Konfiguration neu laden (Berechtigung: realtimeweather.reload)"
|
||||
help_setlocation: "Eigenen Wetterort für den Spieler festlegen (z.B. Berlin,de)"
|
||||
help_query: "Aktuelles Wetter für den Spielerort oder die Welt anzeigen"
|
||||
help_info: "Plugin-Informationen anzeigen"
|
||||
help_gui: "GUI für Wettersteuerung öffnen"
|
||||
help_weatherforecast: "Wettervorhersage für 5 Tage anzeigen"
|
||||
help_toggleweather: "Wetteranzeige in der Actionbar ein-/ausschalten"
|
||||
usage: "Verwendung: /wetter reload | setlocation <Ort> | query | info | gui | help"
|
||||
usage_setlocation: "Verwendung: /wetter setlocation <Stadt,Land>"
|
||||
no_permission: "Du hast keine Berechtigung für diesen Befehl!"
|
||||
reload_success: "Konfiguration erfolgreich neu geladen!"
|
||||
only_players: "Dieser Befehl ist nur für Spieler!"
|
||||
plugin_disabled: "Plugin ist für die Welt {world} deaktiviert!"
|
||||
location_set: "Ort auf {location} gesetzt!"
|
||||
invalid_location: "Ungültiger Ort: {location}"
|
||||
forecast_error: "Fehler beim Abrufen der Wetterdaten für {location}: {error}"
|
||||
gui_title: "Wettersteuerung"
|
||||
gui_setlocation: "Ort festlegen"
|
||||
gui_setlocation_lore: "Eigenen Wetterort festlegen"
|
||||
gui_toggleweather: "Wetteranzeige umschalten"
|
||||
gui_toggleweather_lore: "Wetter-Actionbar ein-/ausschalten"
|
||||
gui_forecast: "Wettervorhersage"
|
||||
gui_forecast_lore: "5-Tage-Wettervorhersage anzeigen"
|
||||
gui_info: "Info"
|
||||
gui_info_lore: "Plugin-Informationen anzeigen"
|
||||
gui_setlocation_prompt: "Gib den Ort ein (z.B. Berlin,de)"
|
||||
toggle_enabled: "Wetteranzeige für {world} aktiviert!"
|
||||
toggle_disabled: "Wetteranzeige für {world} deaktiviert!"
|
||||
forecast_header: "Wettervorhersage für {location}"
|
||||
weather:
|
||||
clear: "Klar"
|
||||
clouds: "Bewölkt"
|
||||
rain: "Regen"
|
||||
thunderstorm: "Gewitter"
|
||||
snow: "Schnee"
|
||||
mist: "Nebel"
|
||||
fog: "Nebel"
|
||||
haze: "Dunst"
|
||||
en:
|
||||
# Ähnliche Schlüssel wie oben, auf Englisch
|
||||
```
|
||||
|
||||
# RealTimeWeather
|
||||
|
||||
Ein Minecraft-Plugin, das Echtzeit-Wetter und -Zeit in deine Welten bringt. Entwickelt für Minecraft 1.21.1.
|
||||
|
||||
## Befehle
|
||||
|
||||
- `/wetter help`: Zeigt die Hilfeübersicht an.
|
||||
- `/wetter reload`: Lädt die Konfiguration neu (Berechtigung: `realtimeweather.reload`).
|
||||
- `/wetter setlocation <city,country>`: Legt einen individuellen Ort für den Spieler fest.
|
||||
- `/wetter query`: Zeigt aktuelles Wetter für den Spielerort oder die Welt.
|
||||
- `/wetter info`: Zeigt Plugin-Informationen an.
|
||||
- `/wetter gui`: Öffnet die Wetter-GUI.
|
||||
- `/weatherforecast`: Zeigt die 5-Tage-Wettervorhersage.
|
||||
- `/toggleweather`: Schaltet die Wetteranzeige in der Actionbar ein/aus.
|
||||
|
||||
## Berechtigungen
|
||||
|
||||
- `realtimeweather.reload`: Erlaubt `/wetter reload`.
|
||||
- `realtimeweather.admin`: Erlaubt Admin-Benachrichtigungen bei Wetterabfrage-Fehlern.
|
||||
> Fragen, Feature-Wünsche oder Bugreports gerne als Gitea-Issue!
|
||||
|
||||
|
17
pom.xml
17
pom.xml
@@ -7,7 +7,7 @@
|
||||
|
||||
<groupId>dev.viper</groupId>
|
||||
<artifactId>RealTimeWeather</artifactId>
|
||||
<version>1.0</version>
|
||||
<version>1.2</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>RealTimeWeather</name>
|
||||
@@ -59,6 +59,14 @@
|
||||
<artifactId>adventure-platform-bukkit</artifactId>
|
||||
<version>4.3.3</version>
|
||||
</dependency>
|
||||
|
||||
<!-- bStats Bukkit Library -->
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bukkit</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -97,11 +105,14 @@
|
||||
<pattern>net.kyori.platform.bukkit</pattern>
|
||||
<shadedPattern>dev.viper.shaded.net.kyori.platform.bukkit</shadedPattern>
|
||||
</relocation>
|
||||
<!-- Shade bStats -->
|
||||
<relocation>
|
||||
<pattern>org.bstats</pattern>
|
||||
<shadedPattern>dev.viper.shaded.org.bstats</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<!-- Minimieren, um die JAR-Größe zu reduzieren (optional) -->
|
||||
<minimizeJar>true</minimizeJar>
|
||||
<!-- Filter, um unnötige Klassen auszuschließen -->
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
|
21
src/main/java/dev/viper/weathertime/MetricsManager.java
Normal file
21
src/main/java/dev/viper/weathertime/MetricsManager.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package dev.viper.weathertime;
|
||||
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class MetricsManager {
|
||||
|
||||
private final Metrics metrics;
|
||||
|
||||
public MetricsManager(JavaPlugin plugin) {
|
||||
int pluginId = 26865; // Deine Plugin-ID von bStats eintragen
|
||||
this.metrics = new Metrics(plugin, pluginId);
|
||||
|
||||
// Optional: Eigene Charts hinzufügen
|
||||
// metrics.addCustomChart(new Metrics.SingleLineChart("custom_chart", () -> 1));
|
||||
}
|
||||
|
||||
public Metrics getMetrics() {
|
||||
return metrics;
|
||||
}
|
||||
}
|
50
src/main/java/dev/viper/weathertime/UpdateChecker.java
Normal file
50
src/main/java/dev/viper/weathertime/UpdateChecker.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package dev.viper.weathertime;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.Scanner;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class UpdateChecker {
|
||||
private final JavaPlugin plugin;
|
||||
private final int resourceId;
|
||||
|
||||
public UpdateChecker(JavaPlugin plugin, int resourceId) {
|
||||
this.plugin = plugin;
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
|
||||
public void getLatestVersion(Consumer<String> consumer) {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
HttpURLConnection connection = (HttpURLConnection)
|
||||
new URL("https://api.spiget.org/v2/resources/" + resourceId + "/versions/latest").openConnection();
|
||||
|
||||
connection.setRequestMethod("GET");
|
||||
connection.addRequestProperty("User-Agent", "Mozilla/5.0");
|
||||
|
||||
try (InputStream inputStream = connection.getInputStream();
|
||||
Scanner scanner = new Scanner(inputStream)) {
|
||||
String response = scanner.useDelimiter("\\A").next();
|
||||
|
||||
JSONObject json = new JSONObject(response);
|
||||
String versionName = json.optString("name");
|
||||
|
||||
// Nur Zahlen + Punkte behalten (z.B. "version 1.1" -> "1.1")
|
||||
String cleanVersion = versionName.replaceAll("[^0-9.]", "").trim();
|
||||
|
||||
consumer.accept(cleanVersion.isEmpty() ? versionName : cleanVersion);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().warning("Konnte keine Update-Info abrufen: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
package dev.viper.weathertime;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONArray;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
@@ -11,6 +11,11 @@ import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Holt Wetter und Zeitdaten asynchron von OpenWeatherMap.
|
||||
* Liefert WeatherTimeData zurück, mit konsistentem Modell und Fehlerhandling.
|
||||
* Erweiterung: Erfasst Luftfeuchtigkeit, Windgeschwindigkeit, Sonnenauf- und -untergang.
|
||||
*/
|
||||
public class WeatherFetcher {
|
||||
|
||||
private final String apiKey;
|
||||
@@ -19,105 +24,135 @@ public class WeatherFetcher {
|
||||
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.apiKey = apiKey == null ? "" : apiKey.trim();
|
||||
this.location = location == null ? "Berlin,de" : location.trim();
|
||||
this.units = units == null ? "metric" : units.trim().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.");
|
||||
logger.warning("Ungültige Einheit für " + this.location + ": " + this.units + ". Verwende 'metric' als Fallback.");
|
||||
}
|
||||
}
|
||||
|
||||
public WeatherData fetch() throws Exception {
|
||||
String effectiveUnits = units.equals("metric") || units.equals("imperial") ? units : "metric";
|
||||
|
||||
/**
|
||||
* Holt die aktuellen Wetter- und Zeitdaten inklusive Zusatzinfos.
|
||||
* Wirft RuntimeExceptions mit klaren Messages bei Fehlern.
|
||||
*/
|
||||
public WeatherTimeData fetchAsWeatherTimeData() throws Exception {
|
||||
if (apiKey.isEmpty()) throw new RuntimeException("API-Key fehlt.");
|
||||
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");
|
||||
JSONObject json;
|
||||
try (BufferedReader in = new BufferedReader(new InputStreamReader(openConnectionStream(urlString)))) {
|
||||
StringBuilder content = new StringBuilder();
|
||||
String inputLine;
|
||||
while ((inputLine = in.readLine()) != null) {
|
||||
content.append(inputLine);
|
||||
}
|
||||
json = new JSONObject(content.toString());
|
||||
}
|
||||
|
||||
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?");
|
||||
}
|
||||
// Fehlerfall: Antwort-Code ungleich 200
|
||||
if (json.has("cod") && json.getInt("cod") != 200) {
|
||||
String msg = json.has("message") ? json.getString("message") : "Unbekannter Fehler";
|
||||
logger.warning("API Error für " + location + ": " + msg);
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
|
||||
return new WeatherData(dateTime, weatherMain, temperature);
|
||||
long dt = json.getLong("dt");
|
||||
int timezoneShift = json.optInt("timezone", 0); // Zeitzonen-Offset in Sekunden
|
||||
|
||||
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?");
|
||||
}
|
||||
|
||||
// Temperatur immer in Celsius für WeatherTimeData speichern
|
||||
double tempCelsius = effectiveUnits.equals("imperial") ? (temperature - 32) * 5 / 9 : temperature;
|
||||
|
||||
// NEUE FELDER AUSLESEN
|
||||
int humidity = json.getJSONObject("main").getInt("humidity");
|
||||
double windSpeed = json.getJSONObject("wind").optDouble("speed", 0.0);
|
||||
|
||||
ZonedDateTime sunrise = Instant.ofEpochSecond(json.getJSONObject("sys").getLong("sunrise"))
|
||||
.atZone(ZoneId.of("UTC"))
|
||||
.plusSeconds(timezoneShift);
|
||||
|
||||
ZonedDateTime sunset = Instant.ofEpochSecond(json.getJSONObject("sys").getLong("sunset"))
|
||||
.atZone(ZoneId.of("UTC"))
|
||||
.plusSeconds(timezoneShift);
|
||||
|
||||
return new WeatherTimeData(dateTime, weatherMain, tempCelsius,
|
||||
humidity, windSpeed, sunrise, sunset);
|
||||
|
||||
} catch (RuntimeException ru) {
|
||||
throw ru;
|
||||
} catch (Exception ex) {
|
||||
logger.warning("Fehler beim Abrufen von OpenWeatherMap: " + ex.getMessage());
|
||||
throw new RuntimeException("Verbindung oder API-Fehler: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holt das Wetter-Vorhersage-JSON (5 Tage, 3-Stunden-Takt).
|
||||
* Muss im Command asynchron ausgewertet werden!
|
||||
*/
|
||||
public JSONObject fetchForecast() throws Exception {
|
||||
String effectiveUnits = units.equals("metric") || units.equals("imperial") ? units : "metric";
|
||||
|
||||
if (apiKey.isEmpty()) throw new RuntimeException("API-Key fehlt.");
|
||||
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);
|
||||
|
||||
try (BufferedReader in = new BufferedReader(new InputStreamReader(openConnectionStream(urlString)))) {
|
||||
StringBuilder content = new StringBuilder();
|
||||
String inputLine;
|
||||
while ((inputLine = in.readLine()) != null) {
|
||||
content.append(inputLine);
|
||||
}
|
||||
JSONObject json = new JSONObject(content.toString());
|
||||
if (json.has("cod") && (!"200".equals(json.get("cod").toString()))) {
|
||||
String msg = json.has("message") ? json.getString("message") : "Unbekannter Fehler";
|
||||
logger.warning("API Error (Forecast) für " + location + ": " + msg);
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
return json;
|
||||
} catch (Exception e) {
|
||||
logger.warning("Fehler beim Abrufen der Wettervorhersage: " + e.getMessage());
|
||||
throw new RuntimeException("Fehler beim Abrufen der Wettervorhersage: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Öffnet eine HTTP-GET-Verbindung und liefert den InputStream der Response zurück (Timeouts gesetzt). */
|
||||
private java.io.InputStream openConnectionStream(String urlString) throws Exception {
|
||||
URL url = new URL(urlString);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setConnectTimeout(5000);
|
||||
connection.setReadTimeout(5000);
|
||||
connection.setConnectTimeout(6000);
|
||||
connection.setReadTimeout(6000);
|
||||
|
||||
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;
|
||||
String err = "(keine Fehlerdetails)";
|
||||
try (BufferedReader errIn = new BufferedReader(new InputStreamReader(connection.getErrorStream()))) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String l;
|
||||
while ((l = errIn.readLine()) != null) sb.append(l);
|
||||
err = sb.toString();
|
||||
} catch (Exception suppress) {}
|
||||
throw new RuntimeException("HTTP-Fehlercode: " + status + " – " + err);
|
||||
}
|
||||
return connection.getInputStream();
|
||||
}
|
||||
}
|
@@ -1,57 +1,87 @@
|
||||
package dev.viper.weathertime;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
/**
|
||||
* Zentrale Datenklasse für Wetter- und Zeitinformationen.
|
||||
* Erweiterung: enthält jetzt auch Luftfeuchtigkeit, Windgeschwindigkeit und Sonnenauf/untergang.
|
||||
*/
|
||||
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;
|
||||
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 final int humidity; // Luftfeuchtigkeit in %
|
||||
private final double windSpeed; // Windgeschwindigkeit (m/s oder mph)
|
||||
private final ZonedDateTime sunrise; // Sonnenaufgang
|
||||
private final ZonedDateTime sunset; // Sonnenuntergang
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public boolean isRain() {
|
||||
return rain;
|
||||
public ZonedDateTime getDateTime() {
|
||||
return dateTime;
|
||||
}
|
||||
|
||||
public boolean isThunder() {
|
||||
return thunder;
|
||||
public String getWeatherMain() {
|
||||
return weatherMain;
|
||||
}
|
||||
|
||||
public double getTemp(String unit) {
|
||||
return unit.equalsIgnoreCase("F") ? (tempCelsius * 9 / 5) + 32 : tempCelsius;
|
||||
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) {
|
||||
LocalTime time = Instant.ofEpochSecond(unixTime)
|
||||
.atOffset(ZoneOffset.UTC)
|
||||
.toLocalTime();
|
||||
|
||||
int hour = dateTime.getHour();
|
||||
int minute = dateTime.getMinute();
|
||||
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());
|
||||
return String.format("%02d:%02d %s", hour, minute, ampm);
|
||||
}
|
||||
return String.format("%02d:%02d", hour, minute);
|
||||
}
|
||||
|
||||
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));
|
||||
int hour = dateTime.getHour();
|
||||
int minute = dateTime.getMinute();
|
||||
int second = dateTime.getSecond();
|
||||
return ((hour + 18) % 24) * 1000L
|
||||
+ (minute * 1000L / 60)
|
||||
+ (second * 1000L / 3600);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,20 @@
|
||||
# OpenWeatherMap API-Key – hier eintragen
|
||||
api-key: "DEIN_API_KEY_HIER"
|
||||
|
||||
# Update-Intervall in Sekunden (Standard: 60)
|
||||
update-interval: 60
|
||||
|
||||
defaults:
|
||||
enabled: true
|
||||
location: "Berlin,de"
|
||||
units: "metric"
|
||||
time-format: "24h"
|
||||
location: "Berlin,de" # Stadt,Land
|
||||
units: "metric" # metric = °C, imperial = °F
|
||||
time-format: "24h" # oder "12h"
|
||||
display-actionbar: true
|
||||
display-weather-icon: true
|
||||
display-position: "top-right"
|
||||
padding-right: 100
|
||||
sync-in-game-weather: true
|
||||
|
||||
worlds:
|
||||
world:
|
||||
enabled: true
|
||||
@@ -19,6 +25,8 @@ worlds:
|
||||
display-weather-icon: true
|
||||
display-position: "top-left"
|
||||
padding-right: 50
|
||||
sync-in-game-weather: true
|
||||
|
||||
world_nether:
|
||||
enabled: false
|
||||
location: "Berlin,de"
|
||||
@@ -28,3 +36,4 @@ worlds:
|
||||
display-weather-icon: false
|
||||
display-position: "top-right"
|
||||
padding-right: 150
|
||||
sync-in-game-weather: false
|
||||
|
@@ -1,7 +1,8 @@
|
||||
name: RealTimeWeather
|
||||
version: 1.0
|
||||
version: 1.3
|
||||
main: dev.viper.weathertime.WeatherTimeSyncPlugin
|
||||
api-version: 1.21
|
||||
|
||||
commands:
|
||||
wetter:
|
||||
description: Manages real-time weather and time synchronization
|
||||
@@ -15,6 +16,7 @@ commands:
|
||||
description: Toggles the weather display for the current world
|
||||
usage: /<command>
|
||||
permission: realtimeweather.toggle
|
||||
|
||||
permissions:
|
||||
realtimeweather.use:
|
||||
description: Allows usage of the /wetter command
|
||||
|
Reference in New Issue
Block a user