@@ -222,7 +222,7 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
private final Map < String , Long > fullChestLocationCache = new HashMap < > ( ) ;
private static final long FULL_CHEST_CACHE_DURATION = 10_000L ; // 10 Sekunden
private static final String CONFIG_VERSION = " 2.1 " ; // BungeeCord NEU : 2.0 → 2.1
private static final String CONFIG_VERSION = " 2.2 " ; // Multi-Rest : 2.1 → 2.2
private boolean updateAvailable = false ;
private String latestVersion = " " ;
@@ -343,14 +343,29 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
mysqlManager . setTargetChest ( uuidString , item , world , x , y , z , isPublic ) ;
}
}
String restPath = " players. " + uuidString + " .rest-chest " ;
if ( playerData . contains ( restPath ) ) {
String world = playerData . getStr ing ( restPath + " .world " ) ;
int x = playerData . getInt ( restPath + " .x " ) ;
int y = playerData . getInt ( restPath + " .y " ) ;
int z = playerData . getInt ( rest Path + " .z " ) ;
boolean isPublic = playerData . getBoolean ( rest Path + " .public " , false ) ;
mysqlManager . s etRestChest ( uuidString , world , x , y , z , isPublic ) ;
// BUG FIX: Mehrere Rest-Truhen migrieren (neues Format + Legacy)
String restBasePath = " players. " + uuidString + " .rest-chests " ;
if ( playerData . conta ins ( restBase Path ) ) {
for ( Str ing slotKey : playerData . getConfigurationSection ( restBase Path ) . getKeys ( false ) ) {
Str ing rPath = restBase Path + " . " + slotKey ;
Str ing world = playerData . getString ( rPath + " .world " ) ;
int x = playerData . getInt ( rPath + " .x " ) ;
int y = playerData . g etInt ( rPath + " .y " ) ;
int z = playerData . getInt ( rPath + " .z " ) ;
boolean isPublic = playerData . getBoolean ( rPath + " .public " , false ) ;
mysqlManager . setRestChest ( uuidString , world , x , y , z , isPublic ) ;
}
} else {
// Legacy: altes single rest-chest Format
String restPath = " players. " + uuidString + " .rest-chest " ;
if ( playerData . contains ( restPath ) ) {
String world = playerData . getString ( restPath + " .world " ) ;
int x = playerData . getInt ( restPath + " .x " ) ;
int y = playerData . getInt ( restPath + " .y " ) ;
int z = playerData . getInt ( restPath + " .z " ) ;
boolean isPublic = playerData . getBoolean ( restPath + " .public " , false ) ;
mysqlManager . setRestChest ( uuidString , world , x , y , z , isPublic ) ;
}
}
}
getLogger ( ) . info ( " Migration der YAML-Daten nach MySQL abgeschlossen. " ) ;
@@ -405,9 +420,11 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
exportData . set ( path + " .public " , chest . get ( " public " ) ) ;
}
Map < String , Object > restChest = mysqlManager . getRestChest ( uuidString ) ;
if ( restChest ! = null ) {
String path = " players. " + uuidString + " . rest-c hest" ;
// BUG FIX: Alle Rest-Truhen exportieren
List < Map < String , Object > > restChests = mysqlManager . getRestChests ( uuidString ) ;
for ( Map < String , Object > restChest : restC hests ) {
int restSlot = ( int ) restChest . get ( " slot " ) ;
String path = " players. " + uuidString + " .rest-chests. " + restSlot ;
exportData . set ( path + " .world " , restChest . get ( " world " ) ) ;
exportData . set ( path + " .x " , restChest . get ( " x " ) ) ;
exportData . set ( path + " .y " , restChest . get ( " y " ) ) ;
@@ -689,10 +706,8 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
if ( ! config . contains ( " messages.rest-chest-set " ) ) config . set ( " messages.rest-chest-set " , defaultConfig . getString ( " messages.rest-chest-set " , " &aRest-Truhe (Fallback) erfolgreich gesetzt! " ) ) ;
if ( ! config . contains ( " messages.target-chest-missing " ) ) config . set ( " messages.target-chest-missing " , defaultConfig . getString ( " messages.target-chest-missing " , " &cZieltruhe für %item% fehlt! " ) ) ;
String targetChestFull = config . getStr ing ( " messages.target-chest-full " , " " ) ;
String defaultT argetC hestF ull = defaultConfig . getString ( " messages.target-chest-full " , " &cZieltruhe für %item% ist voll! Koordinaten: (%x%, %y%, %z%) " ) ;
if ( ! config . contains ( " messages.target-chest-full " ) | | ! targetChestFull . equals ( defaultTargetChestFull ) ) {
config . set ( " messages.target-chest-full " , defaultTargetChestFull ) ;
if ( ! config . conta ins ( " messages.target-chest-full " ) ) {
config . set ( " messages.t arget-c hest-f ull " , defaultConfig . getString ( " messages.target-chest-full " , " &cZieltruhe für %item% ist voll! Koordinaten: (%x%, %y%, %z%) " ) ) ;
}
if ( ! config . contains ( " messages.mode-changed " ) ) config . set ( " messages.mode-changed " , defaultConfig . getString ( " messages.mode-changed " , " &aModus gewechselt: &e%mode% " ) ) ;
@@ -825,9 +840,9 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
}
}
String restPath = basePath + " .rest-chest " ;
if ( playerData . contains ( restPath ) ) {
Location chestLocation = ge tLocationFromPath ( restPath ) ;
// BUG FIX: Alle Rest-Truhen iterieren (neues Multi-Format + Legacy)
List < Location > restLocations = getRestChestLocations ( playerUUID ) ;
for ( Location chestLocation : res tLocations ) {
if ( chestLocation = = null ) continue ;
Block chestBlock = chestLocation . getBlock ( ) ;
if ( ! ( chestBlock . getState ( ) instanceof Chest ) ) continue ;
@@ -928,13 +943,27 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
}
}
}
String restPath = " players. " + uuid + " .rest-chest " ;
if ( playerData . contains ( restPath + " .world " ) ) {
if ( world . equals ( playerData. getStr ing ( restPath + " .world " ) )
& & x = = playerData . getInt ( restPath + " .x " )
& & y = = playerData . getInt ( restPath + " .y " )
& & z = = playerData . getInt ( rest Path + " .z " ) ) {
if ( playerData . getBoolean ( rest Path + " .public " , false ) ) return true ;
// BUG FIX: Neues Multi-Format für Rest-Truhen
String restBase Path = " players. " + uuid + " .rest-chests " ;
if ( playerData . conta ins ( restBase Path ) ) {
for ( String slotKey : playerData . getConfigurationSection ( restBase Path ) . getKeys ( false ) ) {
String rPath = restBase Path + " . " + slotKey ;
if ( world . equals ( playerData . getString ( rPath + " .world " ) )
& & x = = playerData . getInt ( rPath + " .x " )
& & y = = playerData . getInt ( rPath + " .y " )
& & z = = playerData . getInt ( rPath + " .z " ) ) {
if ( playerData . getBoolean ( rPath + " .public " , false ) ) return true ;
}
}
}
// Legacy
String legacyRestPath = " players. " + uuid + " .rest-chest " ;
if ( playerData . contains ( legacyRestPath + " .world " ) ) {
if ( world . equals ( playerData . getString ( legacyRestPath + " .world " ) )
& & x = = playerData . getInt ( legacyRestPath + " .x " )
& & y = = playerData . getInt ( legacyRestPath + " .y " )
& & z = = playerData . getInt ( legacyRestPath + " .z " ) ) {
if ( playerData . getBoolean ( legacyRestPath + " .public " , false ) ) return true ;
}
}
}
@@ -987,7 +1016,9 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
String chestId = null ;
List < Map < String , Object > > chests = mysqlManager . getInputChests ( playerUUID . toString ( ) ) ;
for ( Map < String , Object > chest : chests ) {
if ( chest . get ( " world ") . equals ( location . getWorld ( ) . getName ( ) )
String chestWorld = ( String ) chest . get ( " world " ) ;
if ( chestWorld = = null ) continue ; // NPE-Schutz: korrupter DB-Eintrag
if ( chestWorld . equals ( location . getWorld ( ) . getName ( ) )
& & ( ( int ) chest . get ( " x " ) ) = = location . getBlockX ( )
& & ( ( int ) chest . get ( " y " ) ) = = location . getBlockY ( )
& & ( ( int ) chest . get ( " z " ) ) = = location . getBlockZ ( ) ) {
@@ -1075,13 +1106,34 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
private void setRestChestLocation ( UUID playerUUID , Location location , boolean isPublic ) {
if ( mysqlEnabled & & mysqlManager ! = null ) {
// ── BungeeCord NEU : serverName mitgeben ────────── ─────────────────
// ── BungeeCord + Multi-Rest : serverName und auto-slot ─────────────────
mysqlManager . setRestChest ( playerUUID . toString ( ) ,
location . getWorld ( ) . getName ( ) , location . getBlockX ( ) , location . getBlockY ( ) , location . getBlockZ ( ) ,
isPublic , serverName ) ;
mysqlManager . savePlayer ( playerUUID . toString ( ) , Bukkit . getOfflinePlayer ( playerUUID ) . getName ( ) ) ;
} else {
String path = " players. " + playerUUID + " . rest-chest" ;
// YAML: mehrere Rest-Truhen unter rest-chests.<slot>
String basePath = " players. " + playerUUID + " .rest-chests " ;
// Prüfen ob diese Location schon registriert ist (Update)
if ( playerData . contains ( basePath ) ) {
for ( String slotKey : playerData . getConfigurationSection ( basePath ) . getKeys ( false ) ) {
String path = basePath + " . " + slotKey ;
if ( location . getWorld ( ) . getName ( ) . equals ( playerData . getString ( path + " .world " ) )
& & location . getBlockX ( ) = = playerData . getInt ( path + " .x " )
& & location . getBlockY ( ) = = playerData . getInt ( path + " .y " )
& & location . getBlockZ ( ) = = playerData . getInt ( path + " .z " ) ) {
playerData . set ( path + " .public " , isPublic ) ;
savePlayerData ( ) ;
return ;
}
}
}
// Neuen Slot anlegen
int nextSlot = 0 ;
if ( playerData . contains ( basePath ) ) {
nextSlot = playerData . getConfigurationSection ( basePath ) . getKeys ( false ) . size ( ) ;
}
String path = basePath + " . " + nextSlot ;
playerData . set ( path + " .world " , location . getWorld ( ) . getName ( ) ) ;
playerData . set ( path + " .x " , location . getBlockX ( ) ) ;
playerData . set ( path + " .y " , location . getBlockY ( ) ) ;
@@ -1091,23 +1143,48 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
}
}
private Location getRestChestLocation ( UUID playerUUID ) {
/** Gibt alle Rest-Truhen-Locations zurück (für mehrere Rest-Truhen). */
private List < Location > getRestChestLocations ( UUID playerUUID ) {
List < Location > result = new ArrayList < > ( ) ;
if ( mysqlEnabled & & mysqlManager ! = null ) {
Map < String , Object > map = mysqlManager . getRestChest ( playerUUID . toString ( ) ) ;
i f ( m ap = = null ) return null ;
// ── BungeeCord NEU: Remote-Truhen nicht lokal auflösen ─────────────
if ( isRemoteChest ( map ) ) return null ;
World w = Bukkit . getWorld ( ( String ) map . get ( " world " ) ) ;
if ( w = = null ) return null ;
return new Location ( w , ( int ) map . get ( " x " ) , ( int ) map . get ( " y " ) , ( int ) map . get ( " z " ) ) ;
List < Map< String , Object > > maps = mysqlManager . getRestChests ( playerUUID . toString ( ) ) ;
for ( M ap< String , Object > map : maps ) {
if ( isRemoteChest ( map ) ) continue ;
World w = Bukkit . getWorld ( ( String ) map . get ( " world " ) ) ;
if ( w = = null ) continue ;
result . add ( new Location ( w , ( int ) map . get ( " x " ) , ( int ) map . get ( " y " ) , ( int ) map . get ( " z " ) ) ) ;
}
} else {
String path = " players. " + playerUUID + " .rest-chest " ;
if ( ! playerData . contains ( path ) ) return null ;
String worldName = playerData . getStr ing ( path + " .world " ) ;
World world = getServer ( ) . getWorld ( worldName ) ;
if ( world = = null ) return null ;
return new Location ( world , playerData . getInt ( path + " .x " ) , playerData . getInt ( path + " .y " ) , playerData . getInt ( path + " .z " ) ) ;
// Neues Multi-Format
String basePath = " players. " + playerUUID + " .rest-chests " ;
if ( playerData . conta ins ( basePath ) ) {
for ( String slotKey : playerData . getConfigurationSection ( basePath ) . getKeys ( false ) ) {
String path = basePath + " . " + slotKey ;
String worldName = playerData . getString ( path + " .world " ) ;
World world = getServer ( ) . getWorld ( worldName ) ;
if ( world = = null ) continue ;
result . add ( new Location ( world , playerData . getInt ( path + " .x " ) ,
playerData . getInt ( path + " .y " ) , playerData . getInt ( path + " .z " ) ) ) ;
}
}
// Legacy-Fallback: altes single rest-chest Format
String legacyPath = " players. " + playerUUID + " .rest-chest " ;
if ( result . isEmpty ( ) & & playerData . contains ( legacyPath ) ) {
String worldName = playerData . getString ( legacyPath + " .world " ) ;
World world = getServer ( ) . getWorld ( worldName ) ;
if ( world ! = null ) {
result . add ( new Location ( world , playerData . getInt ( legacyPath + " .x " ) ,
playerData . getInt ( legacyPath + " .y " ) , playerData . getInt ( legacyPath + " .z " ) ) ) ;
}
}
}
return result ;
}
/** Legacy: Gibt die erste Rest-Truhe zurück (oder null). */
private Location getRestChestLocation ( UUID playerUUID ) {
List < Location > locs = getRestChestLocations ( playerUUID ) ;
return locs . isEmpty ( ) ? null : locs . get ( 0 ) ;
}
private Location getTargetChestLocation ( UUID playerUUID , Material itemType ) {
@@ -1171,15 +1248,25 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
@Override
public boolean onCommand ( CommandSender sender , Command command , String label , String [ ] args ) {
if ( ! command . getName ( ) . equalsIgnoreCase ( " asc " ) ) return false ;
if ( ! ( sender instanceof Player ) ) {
sender . sendMessage ( ChatColor . RED + " Dieser Befehl ist nur für Spieler! " ) ;
return true ;
}
Player player = ( Player ) sender ;
String lang = config . getString ( " language " , " de " ) ;
String lang = config ! = null ? config . getString ( " language " , " de " ) : " de " ;
if ( lang = = null ) lang = " de " ;
// reload, import und export sind auch von der Konsole erlaubt
boolean isPlayer = sender instanceof Player ;
if ( ! isPlayer ) {
if ( args . length = = 0 | | ( ! args [ 0 ] . equalsIgnoreCase ( " reload " )
& & ! args [ 0 ] . equalsIgnoreCase ( " import " )
& & ! args [ 0 ] . equalsIgnoreCase ( " export " ) ) ) {
sender . sendMessage ( ChatColor . RED + " Dieser Befehl ist nur für Spieler! (Konsole: reload, import, export) " ) ;
return true ;
}
}
Player player = isPlayer ? ( Player ) sender : null ;
if ( args . length = = 0 | | args [ 0 ] . equalsIgnoreCase ( " help " ) ) {
if ( player = = null ) { sender . sendMessage ( ChatColor . RED + " Verwendung: /asc [reload|import|export] " ) ; return true ; }
String helpMessage = lang . equalsIgnoreCase ( " en " ) ? HELP_EN : HELP_DE ;
helpMessage = ChatColor . translateAlternateColorCodes ( '&' , helpMessage ) ;
player . sendMessage ( helpMessage . split ( " \ n " ) ) ;
@@ -1194,13 +1281,13 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
. replace ( " %server_name% " , serverName . isEmpty ( ) ? " (nicht gesetzt) " : serverName ) // BungeeCord NEU
. replace ( " %author% " , String . join ( " , " , getDescription ( ) . getAuthors ( ) ) ) ;
infoMessage = ChatColor . translateAlternateColorCodes ( '&' , infoMessage ) ;
play er. sendMessage ( infoMessage . split ( " \ n " ) ) ;
send er. sendMessage ( infoMessage . split ( " \ n " ) ) ;
return true ;
}
if ( args [ 0 ] . equalsIgnoreCase ( " reload " ) ) {
if ( ! play er. hasPermission ( " autosortchest.reload " ) ) {
play er. sendMessage ( getMessage ( " no-permission " ) ) ;
if ( ! send er. hasPermission ( " autosortchest.reload " ) ) {
send er. sendMessage ( getMessage ( " no-permission " ) ) ;
return true ;
}
@@ -1240,8 +1327,8 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
migrateInputChests ( ) ;
startTasks ( ) ;
play er. sendMessage ( getMessage ( " reload-success " ) ) ;
getLogger ( ) . info ( " Plugin erfolgreich neu geladen von " + play er. getName ( )
send er. sendMessage ( getMessage ( " reload-success " ) ) ;
getLogger ( ) . info ( " Plugin erfolgreich neu geladen von " + send er. getName ( )
+ " (sort_interval_ticks= " + sortIntervalTicks + " , server_name= \" " + serverName + " \" ) " ) ;
return true ;
}
@@ -1250,22 +1337,22 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
// /asc import – YAML → MySQL
// -------------------------------------------------------
if ( args [ 0 ] . equalsIgnoreCase ( " import " ) ) {
if ( ! play er. hasPermission ( " autosortchest.import " ) ) {
play er. sendMessage ( getMessage ( " no-permission " ) ) ;
if ( ! send er. hasPermission ( " autosortchest.import " ) ) {
send er. sendMessage ( getMessage ( " no-permission " ) ) ;
return true ;
}
if ( ! mysqlEnabled | | mysqlManager = = null ) {
play er. sendMessage ( ChatColor . RED + " MySQL ist nicht aktiviert! Aktiviere MySQL in der config.yml zuerst. " ) ;
send er. sendMessage ( ChatColor . RED + " MySQL ist nicht aktiviert! Aktiviere MySQL in der config.yml zuerst. " ) ;
return true ;
}
if ( playerData = = null | | playerData . getConfigurationSection ( " players " ) = = null
| | playerData . getConfigurationSection ( " players " ) . getKeys ( false ) . isEmpty ( ) ) {
play er. sendMessage ( ChatColor . RED + " Die players.yml ist leer oder enthält keine Spielerdaten! " ) ;
send er. sendMessage ( ChatColor . RED + " Die players.yml ist leer oder enthält keine Spielerdaten! " ) ;
return true ;
}
play er. sendMessage ( ChatColor . YELLOW + " Importiere Daten aus players.yml nach MySQL... " ) ;
play er. sendMessage ( ChatColor . GRAY + " Bestehende MySQL-Daten werden nicht überschrieben (REPLACE INTO). " ) ;
send er. sendMessage ( ChatColor . YELLOW + " Importiere Daten aus players.yml nach MySQL... " ) ;
send er. sendMessage ( ChatColor . GRAY + " Bestehende MySQL-Daten werden nicht überschrieben (REPLACE INTO). " ) ;
new BukkitRunnable ( ) {
@Override
@@ -1311,26 +1398,41 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
}
}
String restPath = " players. " + uuidString + " .rest-chest " ;
if ( playerData . contains ( restPath + " .world " ) ) {
String world = playerData . getStr ing ( restPath + " .world " ) ;
int x = playerData . getInt ( restPath + " .x " ) ;
int y = playerData . getInt ( restPath + " .y " ) ;
int z = playerData . getInt ( rest Path + " .z " ) ;
boolean isPublic = playerData . getBoolean ( rest Path + " .public " , false ) ;
mysqlManager . s etRestChest ( uuidString , world , x , y , z , isPublic ) ;
restCount + + ;
// BUG FIX: Mehrere Rest-Truhen importieren (neues Format + Legacy)
String restBasePath2 = " players. " + uuidString + " .rest-chests " ;
if ( playerData . conta ins ( restBasePath2 ) ) {
for ( Str ing slotKey : playerData . getConfigurationSection ( restBasePath2 ) . getKeys ( false ) ) {
Str ing rPath = restBase Path2 + " . " + slotKey ;
Str ing world = playerData . getString ( rPath + " .world " ) ;
int x = playerData . getInt ( rPath + " .x " ) ;
int y = playerData . g etInt ( rPath + " .y " ) ;
int z = playerData . getInt ( rPath + " .z " ) ;
boolean isPublic = playerData . getBoolean ( rPath + " .public " , false ) ;
mysqlManager . setRestChest ( uuidString , world , x , y , z , isPublic ) ;
restCount + + ;
}
} else {
String restPath = " players. " + uuidString + " .rest-chest " ;
if ( playerData . contains ( restPath + " .world " ) ) {
String world = playerData . getString ( restPath + " .world " ) ;
int x = playerData . getInt ( restPath + " .x " ) ;
int y = playerData . getInt ( restPath + " .y " ) ;
int z = playerData . getInt ( restPath + " .z " ) ;
boolean isPublic = playerData . getBoolean ( restPath + " .public " , false ) ;
mysqlManager . setRestChest ( uuidString , world , x , y , z , isPublic ) ;
restCount + + ;
}
}
}
final int fp = playerCount , fi = inputCount , ft = targetCount , fr = restCount ;
Bukkit . getScheduler ( ) . runTask ( Main . this , ( ) - > {
play er. sendMessage ( ChatColor . GREEN + " Import erfolgreich abgeschlossen! " ) ;
play er. sendMessage ( ChatColor . GRAY + " Spieler: " + ChatColor . WHITE + fp ) ;
play er. sendMessage ( ChatColor . GRAY + " Eingangstruhen: " + ChatColor . WHITE + fi ) ;
play er. sendMessage ( ChatColor . GRAY + " Zieltruhen: " + ChatColor . WHITE + ft ) ;
play er. sendMessage ( ChatColor . GRAY + " Rest-Truhen: " + ChatColor . WHITE + fr ) ;
getLogger ( ) . info ( " Import durch " + play er. getName ( ) + " abgeschlossen: "
send er. sendMessage ( ChatColor . GREEN + " Import erfolgreich abgeschlossen! " ) ;
send er. sendMessage ( ChatColor . GRAY + " Spieler: " + ChatColor . WHITE + fp ) ;
send er. sendMessage ( ChatColor . GRAY + " Eingangstruhen: " + ChatColor . WHITE + fi ) ;
send er. sendMessage ( ChatColor . GRAY + " Zieltruhen: " + ChatColor . WHITE + ft ) ;
send er. sendMessage ( ChatColor . GRAY + " Rest-Truhen: " + ChatColor . WHITE + fr ) ;
getLogger ( ) . info ( " Import durch " + send er. getName ( ) + " abgeschlossen: "
+ fp + " Spieler, " + fi + " Input, " + ft + " Target, " + fr + " Rest. " ) ;
} ) ;
}
@@ -1342,17 +1444,17 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
// /asc export – MySQL → YAML
// -------------------------------------------------------
if ( args [ 0 ] . equalsIgnoreCase ( " export " ) ) {
if ( ! play er. hasPermission ( " autosortchest.export " ) ) {
play er. sendMessage ( getMessage ( " no-permission " ) ) ;
if ( ! send er. hasPermission ( " autosortchest.export " ) ) {
send er. sendMessage ( getMessage ( " no-permission " ) ) ;
return true ;
}
if ( ! mysqlEnabled | | mysqlManager = = null ) {
play er. sendMessage ( ChatColor . RED + " MySQL ist nicht aktiviert! Der Export benötigt eine aktive MySQL-Verbindung. " ) ;
send er. sendMessage ( ChatColor . RED + " MySQL ist nicht aktiviert! Der Export benötigt eine aktive MySQL-Verbindung. " ) ;
return true ;
}
play er. sendMessage ( ChatColor . YELLOW + " Exportiere Daten aus MySQL nach players.yml... " ) ;
play er. sendMessage ( ChatColor . GRAY + " Ein Backup der aktuellen players.yml wird erstellt. " ) ;
send er. sendMessage ( ChatColor . YELLOW + " Exportiere Daten aus MySQL nach players.yml... " ) ;
send er. sendMessage ( ChatColor . GRAY + " Ein Backup der aktuellen players.yml wird erstellt. " ) ;
new BukkitRunnable ( ) {
@Override
@@ -1370,7 +1472,7 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
java . nio . file . Files . copy ( playerDataFile . toPath ( ) , backupFile . toPath ( ) ) ;
} catch ( IOException e ) {
Bukkit . getScheduler ( ) . runTask ( Main . this , ( ) - >
play er. sendMessage ( ChatColor . RED + " Backup fehlgeschlagen: " + e . getMessage ( ) ) ) ;
send er. sendMessage ( ChatColor . RED + " Backup fehlgeschlagen: " + e . getMessage ( ) ) ) ;
return ;
}
}
@@ -1410,9 +1512,11 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
targetCount + + ;
}
Map < String , Object > restChest = mysqlManager . getRestChest ( uuidString ) ;
if ( restChest ! = null ) {
String path = " players. " + uuidString + " . rest-c hest" ;
// BUG FIX: Alle Rest-Truhen exportieren
List < Map < String , Object > > restChests = mysqlManager . getRestChests ( uuidString ) ;
for ( Map < String , Object > restChest : restC hests ) {
int restSlot = ( int ) restChest . get ( " slot " ) ;
String path = " players. " + uuidString + " .rest-chests. " + restSlot ;
exportData . set ( path + " .world " , restChest . get ( " world " ) ) ;
exportData . set ( path + " .x " , restChest . get ( " x " ) ) ;
exportData . set ( path + " .y " , restChest . get ( " y " ) ) ;
@@ -1430,24 +1534,24 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
Bukkit . getScheduler ( ) . runTask ( Main . this , ( ) - > {
playerData = finalExport ;
play er. sendMessage ( ChatColor . GREEN + " Export erfolgreich abgeschlossen! " ) ;
send er. sendMessage ( ChatColor . GREEN + " Export erfolgreich abgeschlossen! " ) ;
if ( finalBackupName ! = null ) {
play er. sendMessage ( ChatColor . GRAY + " Backup: " + ChatColor . WHITE + finalBackupName ) ;
send er. sendMessage ( ChatColor . GRAY + " Backup: " + ChatColor . WHITE + finalBackupName ) ;
} else {
play er. sendMessage ( ChatColor . GRAY + " Backup: " + ChatColor . DARK_GRAY + " Übersprungen (players.yml war leer) " ) ;
send er. sendMessage ( ChatColor . GRAY + " Backup: " + ChatColor . DARK_GRAY + " Übersprungen (players.yml war leer) " ) ;
}
play er. sendMessage ( ChatColor . GRAY + " Spieler: " + ChatColor . WHITE + fp ) ;
play er. sendMessage ( ChatColor . GRAY + " Eingangstruhen: " + ChatColor . WHITE + fi ) ;
play er. sendMessage ( ChatColor . GRAY + " Zieltruhen: " + ChatColor . WHITE + ft ) ;
play er. sendMessage ( ChatColor . GRAY + " Rest-Truhen: " + ChatColor . WHITE + fr ) ;
getLogger ( ) . info ( " Export durch " + play er. getName ( ) + " abgeschlossen: "
send er. sendMessage ( ChatColor . GRAY + " Spieler: " + ChatColor . WHITE + fp ) ;
send er. sendMessage ( ChatColor . GRAY + " Eingangstruhen: " + ChatColor . WHITE + fi ) ;
send er. sendMessage ( ChatColor . GRAY + " Zieltruhen: " + ChatColor . WHITE + ft ) ;
send er. sendMessage ( ChatColor . GRAY + " Rest-Truhen: " + ChatColor . WHITE + fr ) ;
getLogger ( ) . info ( " Export durch " + send er. getName ( ) + " abgeschlossen: "
+ fp + " Spieler, " + fi + " Input, " + ft + " Target, " + fr + " Rest. "
+ ( finalBackupName ! = null ? " Backup: " + finalBackupName : " Kein Backup. " ) ) ;
} ) ;
} catch ( Exception e ) {
Bukkit . getScheduler ( ) . runTask ( Main . this , ( ) - >
play er. sendMessage ( ChatColor . RED + " Export fehlgeschlagen: " + e . getMessage ( ) ) ) ;
send er. sendMessage ( ChatColor . RED + " Export fehlgeschlagen: " + e . getMessage ( ) ) ) ;
getLogger ( ) . warning ( " Export fehlgeschlagen: " + e . getMessage ( ) ) ;
}
}
@@ -1455,6 +1559,11 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
return true ;
}
// Unbekannter Befehl → Hilfe (nur für Spieler)
if ( player = = null ) {
sender . sendMessage ( ChatColor . RED + " Verwendung: /asc [reload|import|export] " ) ;
return true ;
}
String helpMessage = lang . equalsIgnoreCase ( " en " ) ? HELP_EN : HELP_DE ;
helpMessage = ChatColor . translateAlternateColorCodes ( '&' , helpMessage ) ;
player . sendMessage ( helpMessage . split ( " \ n " ) ) ;
@@ -1486,14 +1595,40 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
// ── Limit-Pruefung: Rest-Truhe ─────────────────────────────────────
if ( chestLimitsEnabled ) {
int maxRest = getChestLimitForPlayer ( player , " rest " ) ;
boolean hasRestAlready = false ;
int currentRest = 0 ;
final Block finalChestBlockRest = chestBlock ;
boolean alreadyRest = false ;
if ( mysqlEnabled & & mysqlManager ! = null ) {
hasRestAlready = mysqlManager . ge tRestChest( playerUUID . toString ( ) ) ! = null ;
currentRest = mysqlManager . coun tRestChests ( playerUUID . toString ( ) ) ;
// Prüfen ob diese Truhe bereits als Rest-Truhe registriert ist (Update erlaubt)
alreadyRest = mysqlManager . getRestSlotForLocation (
playerUUID . toString ( ) ,
finalChestBlockRest . getWorld ( ) . getName ( ) ,
finalChestBlockRest . getX ( ) ,
finalChestBlockRest . getY ( ) ,
finalChestBlockRest . getZ ( ) ) > = 0 ;
} else {
hasRestAlready = playerData . contains ( " players. " + playerUUID + " .rest-chest " ) ;
String basePath = " players. " + playerUUID + " .rest-chests " ;
if ( playerData . contains ( basePath ) ) {
for ( String slotKey : playerData . getConfigurationSection ( basePath ) . getKeys ( false ) ) {
String rPath = basePath + " . " + slotKey ;
if ( finalChestBlockRest . getWorld ( ) . getName ( ) . equals ( playerData . getString ( rPath + " .world " ) )
& & finalChestBlockRest . getX ( ) = = playerData . getInt ( rPath + " .x " )
& & finalChestBlockRest . getY ( ) = = playerData . getInt ( rPath + " .y " )
& & finalChestBlockRest . getZ ( ) = = playerData . getInt ( rPath + " .z " ) ) {
alreadyRest = true ;
break ;
}
}
if ( ! alreadyRest ) currentRest = playerData . getConfigurationSection ( basePath ) . getKeys ( false ) . size ( ) ;
}
// Legacy fallback
if ( ! alreadyRest & & currentRest = = 0 & & playerData . contains ( " players. " + playerUUID + " .rest-chest " ) ) {
currentRest = 1 ;
}
}
if ( hasRestA lready & & max Rest < = 1 ) {
player . sendMessage ( ChatColor . RED + " Du hast bereits eine Rest-Truhe! (Limit: " + maxRest + " ) " ) ;
if ( ! a lreadyRest & & current Rest > = maxRest ) {
player . sendMessage ( ChatColor . RED + " Du hast das Limit d einer Rest-Truhen erreicht! ( " + maxRest + " ) " ) ;
event . setCancelled ( true ) ;
return ;
}
@@ -1530,11 +1665,14 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
final Block finalChestBlock = chestBlock ;
boolean alreadyInput = false ;
if ( mysqlEnabled & & mysqlManager ! = null ) {
alreadyInput = mysqlManager . getInputChests ( playerUUID . toString ( ) ) . stream ( ) . anyMatch ( c - >
c . get ( " world ") . equals ( finalChestBlock . getWorld ( ) . getName ( ) )
& & ( int ) c . get ( " x " ) = = finalChestBlock . getX ( )
& & ( int ) c . get ( " y " ) = = finalChestBlock . getY ( )
& & ( int ) c . get ( " z " ) = = finalChestBlock . getZ ( ) ) ;
alreadyInput = mysqlManager . getInputChests ( playerUUID . toString ( ) ) . stream ( ) . anyMatch ( c - > {
String cWorld = ( String ) c. get ( " world " ) ;
return cWorld ! = null
& & cWorld . equals ( finalChestBlock . getWorld ( ) . getName ( ) )
& & ( int ) c . get ( " x " ) = = finalChestBlock . getX ( )
& & ( int ) c . get ( " y " ) = = finalChestBlock . getY ( )
& & ( int ) c . get ( " z " ) = = finalChestBlock . getZ ( ) ;
} ) ;
}
if ( ! alreadyInput & & currentInput > = maxInput ) {
player . sendMessage ( ChatColor . RED + " Du hast das Limit deiner Eingangstruhen erreicht! ( " + maxInput + " ) " ) ;
@@ -1564,8 +1702,8 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
if ( attachedBlock . getState ( ) instanceof Chest ) chestBlock = attachedBlock ;
}
if ( chestBlock = = null ) { player . sendMessage ( getMessage ( " no-chest-near-sign " ) ) ; return ; }
event . setLine ( 0 , " [asc] " ) ;
event . setLine ( 1 , " ziel " ) ;
event . setLine ( 0 , getSignColor ( " target " , " line1 " ) + " [asc] " ) ;
event . setLine ( 1 , getSignColor ( " target " , " line2 " ) + " ziel " ) ;
event . setLine ( 2 , " " ) ;
event . setLine ( 3 , " " ) ;
}
@@ -2017,7 +2155,8 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
event . setCancelled ( true ) ;
return ;
}
if ( ! player . isSneaking ( ) & & ! isAdmin ( player ) ) {
// BUG FIX: autosortchest.bypass erlaubt Abbau ohne Shift-Taste
if ( ! player . isSneaking ( ) & & ! isAdmin ( player ) & & ! player . hasPermission ( " autosortchest.bypass " ) ) {
player . sendMessage ( getMessage ( " sign-break-denied " ) ) ;
event . setCancelled ( true ) ;
return ;
@@ -2052,8 +2191,10 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
}
}
if ( ownerUUID = = null ) {
Location restLoc = getRestChestLocation ( uuid ) ;
i f ( chestLoc . equals ( restLoc ) ) ownerUUID = uuid ;
// BUG FIX: Alle Rest-Truhen des Spielers prüfen (nicht nur erste)
for ( Location restLoc : getRestChestLocations ( uuid ) ) {
if ( chestLoc . equals ( restLoc ) ) { ownerUUID = uuid ; break ; }
}
}
if ( ownerUUID ! = null ) break ;
}
@@ -2062,9 +2203,33 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
UUID uuidToDelete = ( ownerUUID ! = null ) ? ownerUUID : player . getUniqueId ( ) ;
if ( line1 . equalsIgnoreCase ( " rest " ) ) {
playerData . set ( " players. " + uuidToDelete + " .rest-chest " , null ) ;
savePlayerData ( ) ;
if ( mysqlEnabled & & mysqlManager ! = null ) mysqlManager . removeRestChest ( uuidToDelete . toString ( ) ) ;
if ( mysqlEnabled & & mysqlManager ! = null ) {
// BUG FIX: Nur die spezifische Location löschen, nicht alle Rest-Truhen
mysqlManager . removeRestChestByLocation ( uuidToDelete . toString ( ) ,
chestLoc . getWorld ( ) . getName ( ) , chestLoc . getBlockX ( ) , chestLoc . getBlockY ( ) , chestLoc . getBlockZ ( ) ) ;
} else {
// YAML: Spezifische Location aus rest-chests entfernen
boolean removedFromNew = false ;
String basePath = " players. " + uuidToDelete + " .rest-chests " ;
if ( playerData . contains ( basePath ) ) {
for ( String slotKey : new ArrayList < > ( playerData . getConfigurationSection ( basePath ) . getKeys ( false ) ) ) {
String rPath = basePath + " . " + slotKey ;
if ( chestLoc . getWorld ( ) . getName ( ) . equals ( playerData . getString ( rPath + " .world " ) )
& & chestLoc . getBlockX ( ) = = playerData . getInt ( rPath + " .x " )
& & chestLoc . getBlockY ( ) = = playerData . getInt ( rPath + " .y " )
& & chestLoc . getBlockZ ( ) = = playerData . getInt ( rPath + " .z " ) ) {
playerData . set ( rPath , null ) ;
removedFromNew = true ;
break ;
}
}
}
// Legacy-Fallback
if ( ! removedFromNew ) {
playerData . set ( " players. " + uuidToDelete + " .rest-chest " , null ) ;
}
savePlayerData ( ) ;
}
} else if ( line1 . equalsIgnoreCase ( " input " ) ) {
removeInputChestByLocation ( uuidToDelete , chestLoc ) ;
} else if ( line1 . equalsIgnoreCase ( " ziel " ) ) {
@@ -2072,11 +2237,24 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
if ( ! line2 . isEmpty ( ) ) {
Material mat = Material . matchMaterial ( line2 ) ;
if ( mat ! = null ) {
Location savedLoc = getTargetChestLocation ( uuidToDelete , mat ) ;
if ( savedLoc ! = null & & savedLoc . equals ( chestLoc ) ) {
playerData . set ( " players. " + uuidToDelete + " .target-chests. " + mat . name ( ) , null );
savePlayerData ( ) ;
if ( mysqlEnabled & & mysqlManager ! = null ) mysqlManager . removeTargetChest ( uuidToDelete . toString ( ) , mat . name ( ) ) ;
if ( mysqlEnabled & & mysqlManager ! = null ) {
// BUG FIX: Nur bei MySQL → kein playerData-Zugriff nötig
mysqlManager . removeTargetChest ( uuidToDelete . toString ( ) , mat . name ( ) ) ;
} else {
// YAML: Slot mit passender Location finden und löschen
String basePath = " players. " + uuidToDelete + " .target-chests " ;
if ( playerData . contains ( basePath ) ) {
String path = basePath + " . " + mat . name ( ) ;
if ( playerData . contains ( path ) ) {
Location savedLoc = getLocationFromPath ( path ) ;
if ( savedLoc ! = null & & savedLoc . getBlockX ( ) = = chestLoc . getBlockX ( )
& & savedLoc . getBlockY ( ) = = chestLoc . getBlockY ( )
& & savedLoc . getBlockZ ( ) = = chestLoc . getBlockZ ( ) ) {
playerData . set ( path , null ) ;
savePlayerData ( ) ;
}
}
}
}
}
}
@@ -2144,16 +2322,36 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
}
private void removeOldTargetEntry ( UUID uuid , Location loc , String newItemType ) {
if ( mysqlEnabled & & mysqlManager ! = null ) {
// MySQL: Alle Zieltruhen dieser Location löschen, die ein anderes Item haben
List < Map < String , Object > > existing = mysqlManager . getTargetChests ( uuid . toString ( ) ) ;
for ( Map < String , Object > e : existing ) {
String existingItem = ( String ) e . get ( " item " ) ;
if ( existingItem . equalsIgnoreCase ( newItemType ) ) continue ;
String w = ( String ) e . get ( " world " ) ;
if ( w . equals ( loc . getWorld ( ) . getName ( ) )
& & ( int ) e . get ( " x " ) = = loc . getBlockX ( )
& & ( int ) e . get ( " y " ) = = loc . getBlockY ( )
& & ( int ) e . get ( " z " ) = = loc . getBlockZ ( ) ) {
mysqlManager . removeTargetChest ( uuid . toString ( ) , existingItem ) ;
}
}
return ;
}
// YAML-Modus
String basePath = " players. " + uuid + " .target-chests " ;
if ( ! playerData . contains ( basePath ) ) return ;
for ( String existingType : playerData . getConfigurationSection ( basePath ) . getKeys ( false ) ) {
if ( existingType . equalsIgnoreCase ( newItemType ) ) continue ;
String path = basePath + " . " + existingType ;
if ( playerData . getString ( path + " .world " ) . equals ( loc . getWorld ( ) . getName ( ) )
String savedWorld = playerData . getString ( path + " .world " ) ;
if ( savedWorld = = null ) continue ; // NPE-Schutz: korrupter Eintrag überspringen
if ( savedWorld . equals ( loc . getWorld ( ) . getName ( ) )
& & playerData . getInt ( path + " .x " ) = = loc . getBlockX ( )
& & playerData . getInt ( path + " .y " ) = = loc . getBlockY ( )
& & playerData . getInt ( path + " .z " ) = = loc . getBlockZ ( ) ) {
playerData . set ( path , null ) ;
savePlayerData ( ) ; // Änderung direkt auf Disk schreiben
break ;
}
}
@@ -2352,7 +2550,8 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
} ) ;
// Preload Zieltruhen+Rest nur wenn es lokale Input-Truhen gibt
List < Map < String , Object > > preTargets = anyLocal ? mysqlManager . getTargetChests ( uuidString ) : new ArrayList < > ( ) ;
Map < String , Object > preRest = anyLocal ? mysqlManager . getRestChest ( uuidString ) : null ;
// BUG FIX: Alle Rest-Truhen vorladen (nicht nur eine)
List < Map < String , Object > > preRests = anyLocal ? mysqlManager . getRestChests ( uuidString ) : new ArrayList < > ( ) ;
for ( Map < String , Object > chest : chests ) {
if ( crosslink ) {
@@ -2361,7 +2560,7 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
}
String worldName = ( String ) chest . get ( " world " ) ;
boolean isLocal = Bukkit . getWorld ( worldName ) ! = null ;
jobs . add ( new Object [ ] { ownerUUID , chest , isLocal , preTargets , preRest } ) ;
jobs . add ( new Object [ ] { ownerUUID , chest , isLocal , preTargets , preRests } ) ;
}
}
} catch ( Exception e ) {
@@ -2383,7 +2582,8 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
@SuppressWarnings ( " unchecked " )
List < Map < String , Object > > preTargets = ( List < Map < String , Object > > ) job [ 3 ] ;
@SuppressWarnings ( " unchecked " )
Map < String , Object > preRest = ( Map < String , Object > ) job [ 4 ] ;
// BUG FIX: preRests ist jetzt eine Liste
List < Map < String , Object > > preRests = ( List < Map < String , Object > > ) job [ 4 ] ;
if ( isLocal ) {
World world = Bukkit . getWorld ( ( String ) chest . get ( " world " ) ) ;
@@ -2400,14 +2600,17 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
targetLocs . computeIfAbsent ( ( String ) tc . get ( " item " ) , k - > new ArrayList < > ( ) )
. add ( new Location ( w , ( int ) tc . get ( " x " ) , ( int ) tc . get ( " y " ) , ( int ) tc . get ( " z " ) ) ) ;
}
Location restLoc = null ;
if ( preRest ! = null & & ! isRemoteChest ( preRest ) ) {
World w = Bukkit . getWorld ( ( String ) preRest . get ( " world " ) ) ;
if ( w ! = null ) restLoc = new Location ( w ,
( int ) preRest . get ( " x " ) , ( int ) preRest . get ( " y " ) , ( int ) preRest . get ( " z " ) ) ;
// BUG FIX: Alle Rest-Truhen als Liste konvertieren
List < Location > restLocs = new ArrayList < > ( ) ;
for ( Map < String , Object > preRest : preRests ) {
if ( preRest ! = null & & ! isRemoteChest ( preRest ) ) {
World w = Bukkit . getWorld ( ( Str ing ) preRest . get ( " world " ) ) ;
if ( w ! = null ) restLocs . add ( new Location ( w ,
( int ) preRest . get ( " x " ) , ( int ) preRest . get ( " y " ) , ( int ) preRest . get ( " z " ) ) ) ;
}
}
checkSingleInputChest ( ownerUUID , loc , ( String ) chest . get ( " chest_id " ) , false , targetLocs , restLoc ) ;
checkSingleInputChest ( ownerUUID , loc , ( String ) chest . get ( " chest_id " ) , false , targetLocs , restLocs ) ;
} else if ( crosslink ) {
checkRemoteInputChest ( ownerUUID , chest ) ;
}
@@ -2457,20 +2660,21 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
. add ( new Location ( world , ( int ) tc . get ( " x " ) , ( int ) tc . get ( " y " ) , ( int ) tc . get ( " z " ) ) ) ;
}
Location loc alRestChest = getRestChestLocation ( ownerUUID ) ;
// BUG FIX: Alle lok alen Rest-Truhen laden
List < Location > localRestChests = getRestChestLocations ( ownerUUID ) ;
if ( localTargets . isEmpty ( ) & & localRestChest = = null ) return ;
if ( localTargets . isEmpty ( ) & & localRestChests . isEmpty ( ) ) return ;
OfflinePlayer op = Bukkit . getOfflinePlayer ( ownerUUID ) ;
String ownerName = op . getName ( ) ! = null ? op . getName ( ) : ownerUUID . toString ( ) ;
distributeFromRemoteInputChest ( ownerUUID , ownerName , inputChestData , localTargets , localRestChest ) ;
distributeFromRemoteInputChest ( ownerUUID , ownerName , inputChestData , localTargets , localRestChests ) ;
}
private void distributeFromRemoteInputChest ( UUID ownerUUID , String ownerName ,
Map < String , Object > inputChestData ,
Map < String , List < Location > > localTargets ,
Location localRestChest ) {
List < Location> localRestChests ) {
if ( ! mysqlEnabled | | mysqlManager = = null ) return ;
@@ -2495,7 +2699,12 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
for ( Location l : slots ) {
if ( l ! = null & & ! isChestCachedFull ( l ) ) { targetLoc = l ; break ; }
}
if ( targetLoc = = null ) targetLoc = loc alRestChest ;
// BUG FIX: Alle lok alen Rest-Truhen als Fallback versuchen
if ( targetLoc = = null ) {
for ( Location restLoc : localRestChests ) {
if ( restLoc ! = null & & ! isChestCachedFull ( restLoc ) ) { targetLoc = restLoc ; break ; }
}
}
if ( targetLoc = = null ) continue ;
if ( ! ( targetLoc . getBlock ( ) . getState ( ) instanceof Chest ) ) continue ;
@@ -2523,7 +2732,7 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
/** Legacy-Ueberladung: laedt Truhen-Locations synchron (nur fuer InventoryMoveItemEvent). */
private void distributeItemsForOwner ( UUID ownerUUID , Player ownerPlayer , Inventory sourceInventory ,
String ownerNameOverride , Location sourceLocation ) {
Location restLoc = getRestChestLocation ( ownerUUID ) ;
List < Location> restLocs = getRestChestLocations ( ownerUUID ) ;
Map < String , List < Location > > targets = new HashMap < > ( ) ;
if ( mysqlEnabled & & mysqlManager ! = null ) {
for ( Map < String , Object > tc : mysqlManager . getTargetChests ( ownerUUID . toString ( ) ) ) {
@@ -2546,7 +2755,7 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
}
}
}
distributeItemsForOwner ( ownerUUID , ownerPlayer , sourceInventory , ownerNameOverride , sourceLocation , targets , restLoc ) ;
distributeItemsForOwner ( ownerUUID , ownerPlayer , sourceInventory , ownerNameOverride , sourceLocation , targets , restLocs ) ;
}
private boolean checkSingleInputChest ( UUID ownerUUID , Location location , String debugId , boolean crosslinkMode ) {
@@ -2554,7 +2763,7 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
}
private boolean checkSingleInputChest ( UUID ownerUUID , Location location , String debugId , boolean crosslinkMode ,
Map < String , List < Location > > preloadedTargets , Location preloadedRest ) {
Map < String , List < Location > > preloadedTargets , List < Location> preloadedRests ) {
if ( isWorldBlacklisted ( location . getWorld ( ) ) ) return true ;
if ( ! ( location . getBlock ( ) . getState ( ) instanceof Chest ) ) return false ;
@@ -2599,7 +2808,7 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
// Wenn preloadedTargets null: synchron laden (Fallback)
Map < String , List < Location > > effectiveTargets = preloadedTargets ;
Location effectiveRest = preloadedRest ;
List < Location> effectiveRests = preloadedRests ;
if ( effectiveTargets = = null ) {
effectiveTargets = new HashMap < > ( ) ;
if ( mysqlEnabled & & mysqlManager ! = null ) {
@@ -2625,22 +2834,23 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
}
}
}
if ( effectiveRest = = null & & pr elo adedTargets = = null ) {
effectiveRest = getRestChestLocation ( ownerUUID ) ;
// BUG FIX: Rest-Truhen als Liste laden wenn nicht vorg eladen
if ( effectiveRests = = null ) {
effectiveRests = getRestChestLocations ( ownerUUID ) ;
}
distributeItemsForOwner ( ownerUUID , ownerPlayer , chest . getInventory ( ) , ownerName , location , effectiveTargets , effectiveRest ) ;
distributeItemsForOwner ( ownerUUID , ownerPlayer , chest . getInventory ( ) , ownerName , location , effectiveTargets , effectiveRests ) ;
return true ;
}
/**
* Oeffentliche Variante mit vorgeladenen Truhen-Locations (kein DB-Hit im Main Thread).
* targetChestMap: Material-Name → Location (lokale Zieltruhen)
* restChestLoc: lokale Rest-Truhe oder null
* restChestLocs : lokale Rest-Truhen oder leere Liste
*/
private void distributeItemsForOwner ( UUID ownerUUID , Player ownerPlayer , Inventory sourceInventory ,
String ownerNameOverride , Location sourceLocation ,
Map < String , List < Location > > targetChestMap , Location restChestLoc ) {
Map < String , List < Location > > targetChestMap , List < Location> restChestLocs ) {
boolean hasItems = false ;
for ( ItemStack item : sourceInventory . getContents ( ) ) {
@@ -2658,8 +2868,11 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
if ( offlinePlayer . hasPlayedBefore ( ) ) ownerName = offlinePlayer . getName ( ) ;
}
Location restChestLocation = restChestLoc ;
boolean restChestKnownFull = ( restChestLocation ! = null ) & & is ChestCachedFull ( restChestLocation ) ;
// BUG FIX: Rest-Truhen als Liste verwalten
List < Location > restChestLocations = ( restChestLocs ! = null ) ? rest ChestLocs : new ArrayList < > ( ) ;
// Alle vollen Rest-Truhen ermitteln
boolean allRestChestsFull = ! restChestLocations . isEmpty ( )
& & restChestLocations . stream ( ) . allMatch ( l - > l ! = null & & isChestCachedFull ( l ) ) ;
for ( int slot = 0 ; slot < sourceInventory . getSize ( ) ; slot + + ) {
ItemStack item = sourceInventory . getItem ( slot ) ;
@@ -2676,12 +2889,16 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
}
boolean isRestChest = false ;
boolean isCrosslink = false ;
// Aktive Rest-Truhe für dieses Item (erste nicht-volle)
Location activeRestChestLocation = null ;
for ( Location loc : restChestLocations ) {
if ( loc ! = null & & ! isChestCachedFull ( loc ) ) { activeRestChestLocation = loc ; break ; }
}
if ( targetChestLocation = = null ) {
if ( serverCrosslink & & mysqlEnabled & & mysqlManager ! = null ) {
Map < String , Object > raw = mysqlManager . getTargetChest ( ownerUUID . toString ( ) , item . getType ( ) . name ( ) ) ;
if ( raw ! = null & & isRemoteChest ( raw ) ) {
// ── BungeeCord NEU: expliziten Ziel-Server mitgeben ───────────
String targetServerName = getChestServer ( raw ) ;
isCrosslink = true ;
mysqlManager . setupTransferTable ( ) ;
@@ -2698,27 +2915,31 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
}
if ( ! isCrosslink ) {
if ( restChestKnownFull ) continue ;
// BUG FIX: Alle Rest-Truhen prüfen, nicht nur eine
if ( allRestChestsFull ) continue ;
if ( r estChestLocation ! = null ) {
targetChestLocation = r estChestLocation;
if ( activeR estChestLocation ! = null ) {
targetChestLocation = activeR estChestLocation;
isRestChest = true ;
} else if ( serverCrosslink & & mysqlEnabled & & mysqlManager ! = null ) {
Map < String , Object > raw = mysqlManager . getRestChest ( ownerUUID . toString ( ) ) ;
i f ( raw ! = null & & isRemote Chest( raw ) ) {
// ── BungeeCord NEU: expliziten Ziel-Server mitgeben ─────
String targetServerName = getChestServer ( raw ) ;
mysqlManager . setupTransferTable ( ) ;
mysqlManager . addTransfer ( ownerUUID . toString ( ) , item . getType ( ) . name ( ) , item . getAmount ( ) ,
( String ) raw . get ( " world " ) , targetServerName , serverName ) ;
sourceInventory . setItem ( slot , null ) ;
if ( isDebug ( ) ) {
getLogger ( ) . info ( " [CrossLink] " + item . getAmount ( ) + " x " + item . getType ( ) . name ( )
+ " → Server:' " + ( targetServerName . isEmpty ( ) ? " ? " : targetServerName )
+ " ' Welt:' " + raw . get ( " world " ) + " ' (Rest-Transfer-DB) " ) ;
// Crosslink: Remote Rest-Truhe suchen
for ( Map < String , Object > raw : mysqlManager . getRest Chests ( ownerUUID . toString ( ) ) ) {
if ( raw ! = null & & isRemoteChest ( raw ) ) {
String targetServerName = getChestServer ( raw ) ;
mysqlManager . setupTransferTable ( ) ;
mysqlManager . addTransfer ( ownerUUID . toString ( ) , item . getType ( ) . name ( ) , item . getAmount ( ) ,
( String ) raw . get ( " world " ) , targetServerName , serverName ) ;
sourceInventory . setItem ( slot , null ) ;
if ( isDebug ( ) ) {
getLogger ( ) . info ( " [CrossLink] " + item . getAmount ( ) + " x " + item . getType ( ) . name ( )
+ " → Server:' " + ( targetServerName . isEmpty ( ) ? " ? " : targetServerName )
+ " ' Welt:' " + raw . get ( " world " ) + " ' (Rest-Transfer-DB) " ) ;
}
isCrosslink = true ;
break ;
}
continue ;
}
if ( isCrosslink ) continue ;
}
}
} else {
@@ -2741,9 +2962,36 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
if ( ownerPlayer ! = null & & canSendFullChestMessage ( ownerUUID , item . getType ( ) ) ) {
ownerPlayer . sendMessage ( getMessage ( " target-chest-missing " ) . replace ( " %item% " , isRestChest ? " Rest-Truhe " : item . getType ( ) . name ( ) ) ) ;
}
if ( isRestChest ) playerData . set ( " players. " + ownerUUID + " .rest-chest " , null ) ;
else playerData . set ( " players. " + ownerUUID + " .target-chests. " + item . getType ( ) . name ( ) , null ) ;
savePlayerData ( ) ;
// BUG FIX: Beim MySQL-Modus playerData nicht anfassen; bei YAML korrekte Pfade
if ( isRestChest ) {
if ( mysqlEnabled & & mysqlManager ! = null ) {
mysqlManager . removeRestChestByLocation ( ownerUUID . toString ( ) ,
targetChestLocation . getWorld ( ) . getName ( ) ,
targetChestLocation . getBlockX ( ) , targetChestLocation . getBlockY ( ) , targetChestLocation . getBlockZ ( ) ) ;
} else {
String basePath = " players. " + ownerUUID + " .rest-chests " ;
if ( playerData . contains ( basePath ) ) {
for ( String slotKey : new ArrayList < > ( playerData . getConfigurationSection ( basePath ) . getKeys ( false ) ) ) {
String rPath = basePath + " . " + slotKey ;
if ( targetChestLocation . getWorld ( ) . getName ( ) . equals ( playerData . getString ( rPath + " .world " ) )
& & targetChestLocation . getBlockX ( ) = = playerData . getInt ( rPath + " .x " )
& & targetChestLocation . getBlockY ( ) = = playerData . getInt ( rPath + " .y " )
& & targetChestLocation . getBlockZ ( ) = = playerData . getInt ( rPath + " .z " ) ) {
playerData . set ( rPath , null ) ;
break ;
}
}
}
// Legacy
playerData . set ( " players. " + ownerUUID + " .rest-chest " , null ) ;
savePlayerData ( ) ;
}
} else {
if ( ! mysqlEnabled ) {
playerData . set ( " players. " + ownerUUID + " .target-chests. " + item . getType ( ) . name ( ) , null ) ;
savePlayerData ( ) ;
}
}
continue ;
}
@@ -2782,7 +3030,6 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
if ( isFull ) {
markChestFull ( targetChestLocation ) ;
if ( isRestChest ) restChestKnownFull = true ;
}
if ( signBlock ! = null ) {
@@ -2829,19 +3076,41 @@ public class Main extends JavaPlugin implements Listener, CommandExecutor {
}
}
if ( isFull & & isRestChest ) {
if ( ownerPlayer ! = null & & canSendFullChestMessage ( ownerUUID , item . getType ( ) ) ) {
String message = getMessage ( " target-chest-full " )
. replace ( " %item% " , item . getType ( ) . name ( ) )
. replace ( " %x% " , String . valueOf ( targetChestLocation . getBlockX ( ) ))
. replace ( " %y% " , String . valueOf ( targetChestLocation . getBlockY ( ) ) )
. replace ( " %z% " , String . valueOf ( targetChestLocation . getBlockZ ( ) ) ) ;
ownerPlayer . sendMessage ( message ) ;
// BUG FIX: Nächste Rest-Truhe versuchen
Location nextRestLoc = null ;
for ( Location loc : restChestLocations ) {
if ( loc ! = null & & ! isChestCachedFull ( loc ) & & ! loc . equals ( targetChestLocation ) ) {
nextRestLoc = loc ; break ;
}
}
for ( ItemStack leftoverItem : leftover . values ( ) ) {
if ( leftoverItem ! = null & & leftoverItem . getType ( ) = = item . getTyp e ( ) ) {
item . setAmount ( leftoverItem . getAmount ( ) ) ;
i f ( nextRestLoc ! = null & & nextRestLoc . getBlock ( ) . getState ( ) instanceof Chest ) {
Chest nextRestChest = ( Chest ) nextRestLoc . getBlock ( ) . getStat e ( ) ;
Map < Integer , ItemStack > leftover2 = nextRestChest . getInventory ( ) . addItem (
leftover . isEmpty ( ) ? new ItemStack ( item . getType ( ) , 0 ) : leftover . get ( 0 ) . clone ( ) ) ;
if ( leftover2 . isEmpty ( ) ) {
sourceInventory . setItem ( slot , null ) ;
spawnTransferParticles ( null , nextRestLoc ) ;
continue ;
} else {
item . setAmount ( leftover2 . get ( 0 ) . getAmount ( ) ) ;
sourceInventory . setItem ( slot , item ) ;
break ;
}
} else {
// Alle Rest-Truhen voll: Nachricht senden
if ( ownerPlayer ! = null & & canSendFullChestMessage ( ownerUUID , item . getType ( ) ) ) {
String message = getMessage ( " target-chest-full " )
. replace ( " %item% " , item . getType ( ) . name ( ) )
. replace ( " %x% " , String . valueOf ( targetChestLocation . getBlockX ( ) ) )
. replace ( " %y% " , String . valueOf ( targetChestLocation . getBlockY ( ) ) )
. replace ( " %z% " , String . valueOf ( targetChestLocation . getBlockZ ( ) ) ) ;
ownerPlayer . sendMessage ( message ) ;
}
for ( ItemStack leftoverItem : leftover . values ( ) ) {
if ( leftoverItem ! = null & & leftoverItem . getType ( ) = = item . getType ( ) ) {
item . setAmount ( leftoverItem . getAmount ( ) ) ;
sourceInventory . setItem ( slot , item ) ;
break ;
}
}
}
}