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