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