Dateien nach "src/main/java/de/viper/survivalplus/listeners" hochladen

This commit is contained in:
2025-08-03 15:46:12 +00:00
parent d561581e3a
commit 6f8f7c2d39
15 changed files with 1610 additions and 0 deletions

View File

@@ -0,0 +1,120 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus;
import de.viper.survivalplus.tasks.AFKManager;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.event.player.*;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.scheduler.BukkitRunnable;
public class AFKListener implements Listener {
private final SurvivalPlus plugin;
private AFKManager afkManager;
private boolean afkEnabled;
private FileConfiguration config;
private BukkitRunnable afkTask;
public AFKListener(SurvivalPlus plugin) {
this.plugin = plugin;
this.config = plugin.getConfig();
loadSettings();
if (afkEnabled) {
startTask();
}
}
private void loadSettings() {
this.afkEnabled = config.getBoolean("afk.enabled", true);
long afkAfter = config.getLong("afk.afk-after-seconds", 300);
long kickAfter = config.getLong("afk.kick-after-seconds", 0);
this.afkManager = new AFKManager(plugin, afkAfter, kickAfter);
}
private void startTask() {
if (afkTask != null) {
afkTask.cancel();
}
afkTask = new BukkitRunnable() {
@Override
public void run() {
for (Player player : Bukkit.getOnlinePlayers()) {
afkManager.checkAFKStatus(player);
}
}
};
afkTask.runTaskTimer(plugin, 20L, 100L);
}
private void handleActivity(Player player) {
if (!afkEnabled) return;
afkManager.updateActivity(player);
}
@org.bukkit.event.EventHandler
public void onMove(PlayerMoveEvent event) {
if (!event.getFrom().toVector().equals(event.getTo().toVector())) {
handleActivity(event.getPlayer());
}
}
@org.bukkit.event.EventHandler
public void onChat(AsyncPlayerChatEvent event) {
handleActivity(event.getPlayer());
}
@org.bukkit.event.EventHandler
public void onInteract(PlayerInteractEvent event) {
handleActivity(event.getPlayer());
}
@org.bukkit.event.EventHandler
public void onInventoryClick(InventoryClickEvent event) {
if (event.getWhoClicked() instanceof Player player) {
handleActivity(player);
}
}
@org.bukkit.event.EventHandler
public void onBlockPlace(BlockPlaceEvent event) {
handleActivity(event.getPlayer());
}
@org.bukkit.event.EventHandler
public void onBlockBreak(BlockBreakEvent event) {
handleActivity(event.getPlayer());
}
@org.bukkit.event.EventHandler
public void onJoin(PlayerJoinEvent event) {
afkManager.updateActivity(event.getPlayer());
}
@org.bukkit.event.EventHandler
public void onQuit(PlayerQuitEvent event) {
afkManager.reset(event.getPlayer());
}
public void reloadConfig(FileConfiguration config) {
this.config = config;
loadSettings();
if (afkTask != null) {
afkTask.cancel();
afkTask = null;
}
if (afkEnabled) {
startTask();
}
}
}

View File

@@ -0,0 +1,27 @@
package de.viper.survivalplus.listeners;
import org.bukkit.ChatColor;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
public class ArmorStandDestroyListener implements Listener {
@EventHandler
public void onArmorStandInteract(PlayerInteractAtEntityEvent event) {
if (!(event.getRightClicked() instanceof ArmorStand armorStand)) return;
Player player = event.getPlayer();
if (!player.hasPermission("survivalplus.armorstand.destroy")) {
player.sendMessage(ChatColor.RED + "Du hast keine Berechtigung, diesen ArmorStand zu entfernen.");
return;
}
armorStand.remove();
player.sendMessage(ChatColor.YELLOW + "ArmorStand wurde erfolgreich entfernt.");
event.setCancelled(true);
}
}

View File

@@ -0,0 +1,111 @@
package de.viper.survivalplus.listeners;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
import java.util.logging.Logger;
public class BackpackListener implements Listener {
private final FileConfiguration backpackConfig;
private final FileConfiguration langConfig;
private final Logger logger;
private final File backpackFile;
public BackpackListener(FileConfiguration backpackConfig, FileConfiguration langConfig, Logger logger, File backpackFile) {
this.backpackConfig = backpackConfig;
this.langConfig = langConfig;
this.logger = logger;
this.backpackFile = backpackFile;
}
@EventHandler
public void onRightClick(PlayerInteractEvent event) {
if (!event.hasItem() || !event.getAction().toString().contains("RIGHT")) return;
Player player = event.getPlayer();
ItemStack item = event.getItem();
if (item == null || item.getType() != Material.CHEST) {
logger.fine("Kein Rucksack-Item: " + (item != null ? item.getType() : "null"));
return;
}
if (!item.hasItemMeta()) {
logger.fine("Item hat kein ItemMeta: " + item.getType());
return;
}
ItemMeta meta = item.getItemMeta();
if (!meta.hasDisplayName()) {
logger.fine("Item hat keinen Display-Namen: " + item.getType());
return;
}
String expectedName = ChatColor.translateAlternateColorCodes('&', langConfig.getString("backpack.name", "&eRucksack"));
if (!meta.getDisplayName().equals(expectedName)) {
logger.fine("Item hat falschen Namen: " + meta.getDisplayName() + ", erwartet: " + expectedName);
return;
}
event.setCancelled(true);
UUID uuid = player.getUniqueId();
String inventoryTitle = ChatColor.translateAlternateColorCodes('&', langConfig.getString("backpack.inventory-title", "&eDein Rucksack"));
Inventory backpackInv = Bukkit.createInventory(null, 27, inventoryTitle);
logger.info("Öffne Rucksack-Inventar für Spieler " + player.getName() + " (" + uuid + ")");
for (int i = 0; i < 27; i++) {
String path = uuid + ".slot" + i;
if (backpackConfig.contains(path)) {
backpackInv.setItem(i, backpackConfig.getItemStack(path));
}
}
player.openInventory(backpackInv);
}
@EventHandler
public void onClose(InventoryCloseEvent event) {
String expectedTitle = ChatColor.translateAlternateColorCodes('&', langConfig.getString("backpack.inventory-title", "&eDein Rucksack"));
if (!event.getView().getTitle().equals(expectedTitle)) return;
Player player = (Player) event.getPlayer();
UUID uuid = player.getUniqueId();
Inventory inv = event.getInventory();
logger.info("Speichere Rucksack-Inventar für Spieler " + player.getName() + " (" + uuid + ")");
for (int i = 0; i < inv.getSize(); i++) {
String path = uuid + ".slot" + i;
ItemStack item = inv.getItem(i);
backpackConfig.set(path, item);
}
try {
if (!backpackFile.exists()) {
backpackFile.createNewFile();
logger.info("backpacks.yml wurde erstellt.");
}
backpackConfig.save(backpackFile);
logger.info("Rucksack-Inventar gespeichert für " + player.getName());
} catch (IOException e) {
player.sendMessage(ChatColor.RED + "Fehler beim Speichern deines Rucksacks.");
logger.severe("Fehler beim Speichern der backpacks.yml: " + e.getMessage());
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,46 @@
package de.viper.survivalplus.listeners;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
public class DebugArmorStandListener implements Listener {
private static final double MAX_DISTANCE = 3.0;
@EventHandler
public void onPlayerInteractAtEntity(PlayerInteractAtEntityEvent event) {
if (event.getRightClicked() instanceof ArmorStand armorStand) {
Player player = event.getPlayer();
if (armorStand.getScoreboardTags().contains("debugArmorStand")) {
if (armorStand.getLocation().distance(player.getLocation()) <= MAX_DISTANCE) {
armorStand.remove();
player.sendMessage("§aDebug-ArmorStand wurde entfernt.");
event.setCancelled(true);
} else {
player.sendMessage("§cDieser Debug-ArmorStand ist zu weit entfernt.");
}
}
}
}
@EventHandler
public void onEntityDamageByEntity(EntityDamageByEntityEvent event) {
if (event.getEntity() instanceof ArmorStand armorStand) {
if (armorStand.getScoreboardTags().contains("debugArmorStand")) {
if (event.getDamager() instanceof Player player) {
if (armorStand.getLocation().distance(player.getLocation()) <= MAX_DISTANCE) {
armorStand.remove();
player.sendMessage("§aDebug-ArmorStand wurde zerstört.");
event.setCancelled(true);
} else {
player.sendMessage("§cDieser Debug-ArmorStand ist zu weit entfernt.");
}
}
}
}
}
}

View File

@@ -0,0 +1,174 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.Chest;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
public class GraveListener implements Listener {
private final SurvivalPlus plugin;
private final HashMap<Location, UUID> graveOwners = new HashMap<>();
private long despawnTime;
public GraveListener(SurvivalPlus plugin) {
this.plugin = plugin;
reloadConfig(plugin.getConfig());
}
public void reloadConfig(FileConfiguration config) {
this.despawnTime = config.getLong("graves.despawn-time", 1800);
}
@EventHandler
public void onPlayerDeath(PlayerDeathEvent event) {
if (event.getKeepInventory()) return;
Player player = event.getEntity();
UUID playerId = player.getUniqueId();
FileConfiguration gravesConfig = plugin.getGravesConfig();
FileConfiguration lang = plugin.getLangConfig();
Location deathLocation = player.getLocation().clone();
Block block = deathLocation.getBlock();
// Save to graves.yml
gravesConfig.set("graves." + playerId + ".world", deathLocation.getWorld().getName());
gravesConfig.set("graves." + playerId + ".x", deathLocation.getX());
gravesConfig.set("graves." + playerId + ".y", deathLocation.getY());
gravesConfig.set("graves." + playerId + ".z", deathLocation.getZ());
plugin.saveGravesConfig();
// Suche Platz für Truhe
if (block.isEmpty() || block.getType().isAir()) {
block.setType(Material.CHEST);
} else {
for (int i = 1; i <= 10; i++) {
Block above = block.getRelative(0, i, 0);
if (above.isEmpty() || above.getType().isAir()) {
above.setType(Material.CHEST);
block = above;
deathLocation = block.getLocation();
break;
}
}
}
if (block.getType() == Material.CHEST) {
Chest chest = (Chest) block.getState();
for (ItemStack item : event.getDrops()) {
if (item != null && item.getType() != Material.AIR) {
chest.getInventory().addItem(item);
}
}
event.getDrops().clear();
graveOwners.put(block.getLocation(), playerId);
player.sendMessage(String.format(
lang.getString("graves.created", "§aDein Grab wurde bei x=%.2f, y=%.2f, z=%.2f erstellt!"),
deathLocation.getX(), deathLocation.getY(), deathLocation.getZ()
));
// ArmorStand mit "Grab von <Name>" in Rot über der Truhe
Location standLocation = deathLocation.clone().add(0.5, 1.2, 0.5);
ArmorStand stand = deathLocation.getWorld().spawn(standLocation, ArmorStand.class);
stand.setVisible(false);
stand.setCustomName("§cGrab von " + player.getName());
stand.setCustomNameVisible(true);
stand.setMarker(true);
stand.setGravity(false);
stand.setInvulnerable(true);
stand.setSilent(true);
stand.setSmall(true);
// Entfernen nach Zeit
if (despawnTime >= 0) {
final Block graveBlock = block;
final Location graveLocation = graveBlock.getLocation();
final Player deadPlayer = player;
final FileConfiguration langConfigFinal = lang;
new BukkitRunnable() {
@Override
public void run() {
if (graveBlock.getType() == Material.CHEST) {
graveBlock.setType(Material.AIR);
graveOwners.remove(graveLocation);
removeArmorStand(graveLocation);
deadPlayer.sendMessage(String.format(
langConfigFinal.getString("graves.despawned", "§cDein Grab bei x=%.2f, y=%.2f, z=%.2f ist verschwunden!"),
graveLocation.getX(), graveLocation.getY(), graveLocation.getZ()
));
}
}
}.runTaskLater(plugin, despawnTime * 20L);
}
// Überwache, ob Truhe geleert wurde
final Block chestBlockFinal = block;
final Player playerFinal = player;
final FileConfiguration langFinal = lang;
new BukkitRunnable() {
@Override
public void run() {
if (chestBlockFinal.getType() != Material.CHEST) {
cancel();
return;
}
Chest c = (Chest) chestBlockFinal.getState();
boolean empty = true;
for (ItemStack item : c.getInventory().getContents()) {
if (item != null && item.getType() != Material.AIR) {
empty = false;
break;
}
}
if (empty) {
c.getBlock().setType(Material.AIR);
graveOwners.remove(c.getLocation());
removeArmorStand(c.getLocation());
playerFinal.sendMessage(String.format(
langFinal.getString("graves.emptied", "§7Dein Grab bei x=%.2f, y=%.2f, z=%.2f wurde geleert."),
c.getX(), c.getY(), c.getZ()
));
cancel();
}
}
}.runTaskTimer(plugin, 20L, 60L);
}
}
private void removeArmorStand(Location chestLocation) {
List<Entity> entities = chestLocation.getWorld().getNearbyEntities(chestLocation.clone().add(0.5, 1.2, 0.5), 0.5, 1, 0.5)
.stream().filter(e -> e instanceof ArmorStand).toList();
for (Entity entity : entities) {
entity.remove();
}
}
public boolean isOwner(Player player, Location location) {
return graveOwners.getOrDefault(location, null) != null &&
graveOwners.get(location).equals(player.getUniqueId());
}
}

View File

@@ -0,0 +1,69 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.configuration.file.FileConfiguration;
import java.util.logging.Level;
public class InventoryClickListener implements Listener {
private final SurvivalPlus plugin;
public InventoryClickListener(SurvivalPlus plugin) {
this.plugin = plugin;
}
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
FileConfiguration lang = plugin.getLangConfig();
String guiTitle = event.getView().getTitle();
String homeGuiTitle = lang.getString("homelist.gui-title", "Deine Homes");
if (!guiTitle.equals(homeGuiTitle)) {
plugin.getLogger().log(Level.FINE, "Ignoriere Klick in GUI mit Titel: " + guiTitle);
return;
}
plugin.getLogger().log(Level.FINE, "Verarbeite Klick in Home-Liste GUI für Spieler: " + event.getWhoClicked().getName());
event.setCancelled(true);
if (!(event.getWhoClicked() instanceof Player) || event.getCurrentItem() == null) {
return;
}
Player player = (Player) event.getWhoClicked();
String homeName = event.getCurrentItem().getItemMeta().getDisplayName().replace("§a", "");
String path = "homes." + player.getUniqueId() + "." + homeName.toLowerCase();
FileConfiguration homes = plugin.getHomesConfig();
if (!homes.contains(path)) {
player.sendMessage(lang.getString("homelist.home-deleted", "§cDieses Home existiert nicht mehr!"));
player.closeInventory();
return;
}
String worldName = homes.getString(path + ".world");
World world = plugin.getServer().getWorld(worldName);
if (world == null) {
player.sendMessage(lang.getString("homelist.world-not-found", "§cDie Welt dieses Homes existiert nicht!"));
player.closeInventory();
return;
}
Location loc = new Location(
world,
homes.getDouble(path + ".x"),
homes.getDouble(path + ".y"),
homes.getDouble(path + ".z"),
(float) homes.getDouble(path + ".yaw"),
(float) homes.getDouble(path + ".pitch")
);
player.teleport(loc);
player.sendMessage(lang.getString("homelist.teleported", "§aZum Home %name% teleportiert!")
.replace("%name%", homeName));
player.closeInventory();
}
}

View File

@@ -0,0 +1,53 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.ChatColor;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
public class LoginListener implements Listener {
private final SurvivalPlus plugin;
public LoginListener(SurvivalPlus plugin) {
this.plugin = plugin;
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
if (!player.hasPlayedBefore()) {
// Erster Login
sendWelcomeMessage(player, true);
} else {
// Wiederkehrer
sendWelcomeMessage(player, false);
}
}
private void sendWelcomeMessage(Player player, boolean firstJoin) {
String messageKey = firstJoin ? "welcome.first-join-message" : "welcome.return-message";
String soundKey = firstJoin ? "welcome.first-join-sound" : "welcome.return-sound";
String volumeKey = firstJoin ? "welcome.first-join-sound-volume" : "welcome.return-sound-volume";
String pitchKey = firstJoin ? "welcome.first-join-sound-pitch" : "welcome.return-sound-pitch";
String rawMessage = plugin.getLangConfig().getString(messageKey, "&6Willkommen, &e{player}&6!");
String message = ChatColor.translateAlternateColorCodes('&', rawMessage.replace("{player}", player.getName()));
player.sendMessage(message);
String soundName = plugin.getLangConfig().getString(soundKey, "ENTITY_PLAYER_LEVELUP");
float volume = (float) plugin.getLangConfig().getDouble(volumeKey, 1.0);
float pitch = (float) plugin.getLangConfig().getDouble(pitchKey, 1.0);
try {
Sound sound = Sound.valueOf(soundName);
player.playSound(player.getLocation(), sound, volume, pitch);
} catch (IllegalArgumentException e) {
plugin.getLogger().warning("Ungültiger Sound-Name in lang.yml: " + soundName);
}
}
}

View File

@@ -0,0 +1,181 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.Chunk;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Animals;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Entity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class MobCapListener implements Listener {
private final SurvivalPlus plugin;
private boolean enabled;
private int maxAnimalsPerChunk;
private final Map<String, Map<UUID, UUID>> chunkAnimalMap = new HashMap<>(); // Chunk -> Tier-UUID -> Spieler-UUID
public MobCapListener(SurvivalPlus plugin, FileConfiguration config) {
this.plugin = plugin;
reloadConfig(config);
}
public void reloadConfig(FileConfiguration config) {
this.enabled = config.getBoolean("mob-cap.enabled", true);
this.maxAnimalsPerChunk = config.getInt("mob-cap.max-animals-per-chunk", 10);
plugin.getLogger().info("MobCapListener: enabled=" + enabled + ", maxAnimalsPerChunk=" + maxAnimalsPerChunk);
// Bereinige bestehende Daten
chunkAnimalMap.clear();
if (!enabled) {
plugin.getMobCapConfig().set("mobcap", null); // Bereinige mobcap.yml
plugin.saveMobCapConfig();
plugin.getLogger().info("MobCapListener: Daten bereinigt, da enabled=false");
return;
}
// Lade Zuordnungen aus mobcap.yml
FileConfiguration mobCapConfig = plugin.getMobCapConfig();
if (mobCapConfig.contains("mobcap")) {
for (String chunkKey : mobCapConfig.getConfigurationSection("mobcap").getKeys(false)) {
Map<UUID, UUID> animalToPlayer = new HashMap<>();
for (String animalIdStr : mobCapConfig.getConfigurationSection("mobcap." + chunkKey).getKeys(false)) {
try {
UUID animalId = UUID.fromString(animalIdStr);
String playerIdStr = mobCapConfig.getString("mobcap." + chunkKey + "." + animalIdStr);
if (playerIdStr != null) {
UUID playerId = UUID.fromString(playerIdStr);
Entity entity = plugin.getServer().getEntity(animalId);
if (entity instanceof Animals) {
animalToPlayer.put(animalId, playerId);
} else {
// Entferne ungültige Einträge
mobCapConfig.set("mobcap." + chunkKey + "." + animalIdStr, null);
}
}
} catch (IllegalArgumentException e) {
plugin.getLogger().warning("Ungültige UUID in mobcap.yml: " + animalIdStr);
}
}
chunkAnimalMap.put(chunkKey, animalToPlayer);
}
}
// Scanne alle Welten nach Tieren
for (org.bukkit.World world : plugin.getServer().getWorlds()) {
for (Chunk chunk : world.getLoadedChunks()) {
String chunkKey = getChunkKey(chunk);
Map<UUID, UUID> animalToPlayer = chunkAnimalMap.computeIfAbsent(chunkKey, k -> new HashMap<>());
for (Entity entity : chunk.getEntities()) {
if (entity instanceof Animals) {
UUID id = entity.getUniqueId();
if (!animalToPlayer.containsKey(id)) {
animalToPlayer.put(id, null); // Kein Spieler zugeordnet
}
}
}
}
}
// Speichere aktualisierte mobcap.yml
saveChunkAnimalMap();
plugin.getLogger().info("MobCapListener: Nach Reload " + chunkAnimalMap.size() + " Chunks mit Tieren gefunden.");
}
@EventHandler
public void onCreatureSpawn(CreatureSpawnEvent event) {
if (!enabled) return;
LivingEntity entity = event.getEntity();
if (!(entity instanceof Animals)) return;
Chunk chunk = entity.getLocation().getChunk();
String chunkKey = getChunkKey(chunk);
Map<UUID, UUID> animalToPlayer = chunkAnimalMap.computeIfAbsent(chunkKey, k -> new HashMap<>());
int animalCount = animalToPlayer.size();
if (animalCount >= maxAnimalsPerChunk) {
event.setCancelled(true);
plugin.getLogger().info("Spawn von " + entity.getType() + " in Chunk " + chunkKey + " verhindert: Limit von " + maxAnimalsPerChunk + " erreicht.");
// HINWEIS: Wenn du dem Züchter eine Nachricht schicken willst, nutze EntityBreedEvent
// Hier ist das nicht möglich, weil man beim Spawn keinen Spielerzugriff hat
} else {
animalToPlayer.put(entity.getUniqueId(), null); // Kein Spieler zugeordnet
saveChunkAnimalMap();
}
}
@EventHandler
public void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
if (!enabled) return;
if (!(event.getRightClicked() instanceof Animals)) return;
Player player = event.getPlayer();
Entity entity = event.getRightClicked();
Chunk chunk = entity.getLocation().getChunk();
String chunkKey = getChunkKey(chunk);
Map<UUID, UUID> animalToPlayer = chunkAnimalMap.computeIfAbsent(chunkKey, k -> new HashMap<>());
int animalCount = animalToPlayer.size();
if (animalCount >= maxAnimalsPerChunk) {
event.setCancelled(true);
player.sendMessage(plugin.getMessage("mob-cap.limit-reached").replace("%max%", String.valueOf(maxAnimalsPerChunk)));
plugin.getLogger().info("Interaktion mit " + entity.getType() + " in Chunk " + chunkKey + " verhindert: Limit von " + maxAnimalsPerChunk + " erreicht.");
} else {
animalToPlayer.put(entity.getUniqueId(), player.getUniqueId());
saveChunkAnimalMap();
}
}
@EventHandler
public void onEntityDeath(EntityDeathEvent event) {
if (!enabled) return;
if (!(event.getEntity() instanceof Animals)) return;
LivingEntity entity = event.getEntity();
Chunk chunk = entity.getLocation().getChunk();
String chunkKey = getChunkKey(chunk);
Map<UUID, UUID> animalToPlayer = chunkAnimalMap.get(chunkKey);
if (animalToPlayer != null) {
UUID animalId = entity.getUniqueId();
animalToPlayer.remove(animalId);
if (animalToPlayer.isEmpty()) {
chunkAnimalMap.remove(chunkKey);
}
saveChunkAnimalMap();
plugin.getLogger().info("Tier " + entity.getType() + " in Chunk " + chunkKey + " gestorben. Verbleibende Tiere: " + animalToPlayer.size());
}
}
private String getChunkKey(Chunk chunk) {
return chunk.getWorld().getName() + "," + chunk.getX() + "," + chunk.getZ();
}
private void saveChunkAnimalMap() {
FileConfiguration mobCapConfig = plugin.getMobCapConfig();
mobCapConfig.set("mobcap", null); // Bereinige vorherige Daten
for (Map.Entry<String, Map<UUID, UUID>> entry : chunkAnimalMap.entrySet()) {
String chunkKey = entry.getKey();
Map<UUID, UUID> animalToPlayer = entry.getValue();
for (Map.Entry<UUID, UUID> animalEntry : animalToPlayer.entrySet()) {
mobCapConfig.set("mobcap." + chunkKey + "." + animalEntry.getKey().toString(),
animalEntry.getValue() != null ? animalEntry.getValue().toString() : null);
}
}
plugin.saveMobCapConfig();
}
}

View File

@@ -0,0 +1,253 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.Material;
import java.util.*;
public class MobLeashLimitListener implements Listener {
private final SurvivalPlus plugin;
private boolean enabled;
private int maxLeashCount;
private final Map<UUID, Set<UUID>> leashedEntities = new HashMap<>();
private final Map<UUID, Integer> leashCounts = new HashMap<>();
private final Map<UUID, UUID> entityToPlayer = new HashMap<>();
public MobLeashLimitListener(SurvivalPlus plugin, FileConfiguration config) {
this.plugin = plugin;
reloadConfig(config);
}
public void reloadConfig(FileConfiguration config) {
this.enabled = config.getBoolean("mob-leash-limit.enabled", true);
this.maxLeashCount = config.getInt("mob-leash-limit.max-leash-count", 5);
plugin.getLogger().info("MobLeashLimitListener: enabled=" + enabled + ", maxLeashCount=" + maxLeashCount);
// Bereinige bestehende Daten
leashedEntities.clear();
leashCounts.clear();
entityToPlayer.clear();
if (!enabled) {
plugin.getLeashesConfig().set("leashes", null); // Bereinige leashes.yml
plugin.saveLeashesConfig();
plugin.getLogger().info("MobLeashLimitListener: Daten bereinigt, da enabled=false");
return;
}
// Lade Zuordnungen aus leashes.yml
FileConfiguration leashesConfig = plugin.getLeashesConfig();
if (leashesConfig.contains("leashes")) {
for (String entityIdStr : leashesConfig.getConfigurationSection("leashes").getKeys(false)) {
UUID entityId;
try {
entityId = UUID.fromString(entityIdStr);
} catch (IllegalArgumentException e) {
plugin.getLogger().warning("Ungültige UUID in leashes.yml: " + entityIdStr);
continue;
}
String playerIdStr = leashesConfig.getString("leashes." + entityIdStr);
if (playerIdStr != null) {
try {
UUID playerId = UUID.fromString(playerIdStr);
LivingEntity entity = (LivingEntity) plugin.getServer().getEntity(entityId);
if (entity != null && entity.isLeashed()) {
Set<UUID> playerLeashedEntities = leashedEntities.computeIfAbsent(playerId, k -> new HashSet<>());
playerLeashedEntities.add(entityId);
leashCounts.put(playerId, playerLeashedEntities.size());
entityToPlayer.put(entityId, playerId);
} else {
// Entferne ungültige Einträge aus leashes.yml
leashesConfig.set("leashes." + entityIdStr, null);
}
} catch (IllegalArgumentException e) {
plugin.getLogger().warning("Ungültige Spieler-UUID in leashes.yml für Entity " + entityIdStr + ": " + playerIdStr);
}
}
}
}
// Scanne alle Welten nach angeleinten Tieren, die noch nicht in leashes.yml sind
for (org.bukkit.World world : plugin.getServer().getWorlds()) {
for (LivingEntity entity : world.getLivingEntities()) {
if (entity.isLeashed() && !entityToPlayer.containsKey(entity.getUniqueId())) {
// Nur Tiere zählen, die von einem Spieler gehalten werden (für neue Leashes)
if (entity.getLeashHolder() instanceof Player) {
Player player = (Player) entity.getLeashHolder();
UUID playerId = player.getUniqueId();
UUID entityId = entity.getUniqueId();
Set<UUID> playerLeashedEntities = leashedEntities.computeIfAbsent(playerId, k -> new HashSet<>());
playerLeashedEntities.add(entityId);
leashCounts.put(playerId, playerLeashedEntities.size());
entityToPlayer.put(entityId, playerId);
// Speichere in leashes.yml
leashesConfig.set("leashes." + entityId.toString(), playerId.toString());
}
}
}
}
// Prüfe, ob das neue Limit überschritten wird
for (Map.Entry<UUID, Set<UUID>> entry : leashedEntities.entrySet()) {
UUID playerId = entry.getKey();
Set<UUID> playerLeashedEntities = entry.getValue();
if (playerLeashedEntities.size() > maxLeashCount) {
Player player = plugin.getServer().getPlayer(playerId);
if (player != null && player.isOnline()) {
player.sendMessage(plugin.getMessage("mob-leash-limit.limit-adjusted")
.replace("%count%", String.valueOf(playerLeashedEntities.size()))
.replace("%max%", String.valueOf(maxLeashCount)));
}
}
}
// Entferne leere Einträge
leashedEntities.entrySet().removeIf(entry -> entry.getValue().isEmpty());
leashCounts.entrySet().removeIf(entry -> entry.getValue() == 0);
// Speichere aktualisierte leashes.yml
plugin.saveLeashesConfig();
plugin.getLogger().info("MobLeashLimitListener: Nach Reload " + leashedEntities.size() + " Spieler mit angeleinten Tieren gefunden.");
}
@EventHandler
public void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
if (!enabled) return;
Player player = event.getPlayer();
ItemStack itemInHand = player.getInventory().getItemInMainHand();
UUID playerId = player.getUniqueId();
LivingEntity entity = (LivingEntity) event.getRightClicked();
UUID entityId = entity.getUniqueId();
if (itemInHand != null && itemInHand.getType() == Material.LEAD) {
Set<UUID> playerLeashedEntities = leashedEntities.computeIfAbsent(playerId, k -> new HashSet<>());
int currentCount = playerLeashedEntities.size();
plugin.getLogger().info("PlayerInteractEntity: player=" + player.getName() + ", currentCount=" + currentCount + ", maxLeashCount=" + maxLeashCount);
if (playerLeashedEntities.contains(entityId)) {
// Ableinen
playerLeashedEntities.remove(entityId);
leashCounts.put(playerId, currentCount - 1);
entityToPlayer.remove(entityId);
plugin.getLeashesConfig().set("leashes." + entityId.toString(), null);
plugin.saveLeashesConfig();
player.sendMessage(plugin.getMessage("mob-leash-limit.unleashed")
.replace("%count%", String.valueOf(currentCount - 1)));
// Entferne leere Einträge
if (playerLeashedEntities.isEmpty()) {
leashedEntities.remove(playerId);
leashCounts.remove(playerId);
}
} else {
// Anleinen
if (currentCount >= maxLeashCount) {
player.sendMessage(plugin.getMessage("mob-leash-limit.max-reached")
.replace("%count%", String.valueOf(maxLeashCount)));
event.setCancelled(true);
} else {
playerLeashedEntities.add(entityId);
leashCounts.put(playerId, currentCount + 1);
entityToPlayer.put(entityId, playerId);
plugin.getLeashesConfig().set("leashes." + entityId.toString(), playerId.toString());
plugin.saveLeashesConfig();
player.sendMessage(plugin.getMessage("mob-leash-limit.leashed")
.replace("%count%", String.valueOf(currentCount + 1)));
}
}
}
}
@EventHandler
public void onEntityDeath(EntityDeathEvent event) {
if (!enabled) {
UUID entityId = event.getEntity().getUniqueId();
entityToPlayer.remove(entityId);
plugin.getLeashesConfig().set("leashes." + entityId.toString(), null);
plugin.saveLeashesConfig();
return;
}
LivingEntity entity = event.getEntity();
UUID entityId = entity.getUniqueId();
UUID playerId = entityToPlayer.remove(entityId);
if (playerId != null) {
Set<UUID> playerLeashedEntities = leashedEntities.getOrDefault(playerId, new HashSet<>());
playerLeashedEntities.remove(entityId);
int newCount = playerLeashedEntities.size();
leashCounts.put(playerId, newCount);
// Entferne aus leashes.yml
plugin.getLeashesConfig().set("leashes." + entityId.toString(), null);
plugin.saveLeashesConfig();
// Entferne leere Einträge
if (playerLeashedEntities.isEmpty()) {
leashedEntities.remove(playerId);
leashCounts.remove(playerId);
}
Player player = plugin.getServer().getPlayer(playerId);
if (player != null && player.isOnline()) {
player.sendMessage(plugin.getMessage("mob-leash-limit.entity-died")
.replace("%count%", String.valueOf(newCount)));
}
}
}
public void updateLeashCount(Player player) {
UUID playerId = player.getUniqueId();
Set<UUID> playerLeashedEntities = leashedEntities.getOrDefault(playerId, new HashSet<>());
// Entferne nicht existierende oder nicht angeleinte Entities
playerLeashedEntities.removeIf(entityId -> {
LivingEntity entity = (LivingEntity) plugin.getServer().getEntity(entityId);
if (entity == null || !entity.isLeashed()) {
plugin.getLeashesConfig().set("leashes." + entityId.toString(), null);
return true;
}
return false;
});
int currentCount = playerLeashedEntities.size();
leashCounts.put(playerId, currentCount);
// Aktualisiere entityToPlayer-Map
entityToPlayer.entrySet().removeIf(entry -> entry.getValue().equals(playerId) && !playerLeashedEntities.contains(entry.getKey()));
// Entferne leere Einträge
if (playerLeashedEntities.isEmpty()) {
leashedEntities.remove(playerId);
leashCounts.remove(playerId);
}
// Falls disabled, bereinige die Daten für diesen Spieler
if (!enabled) {
playerLeashedEntities.clear();
leashedEntities.remove(playerId);
leashCounts.remove(playerId);
entityToPlayer.entrySet().removeIf(entry -> entry.getValue().equals(playerId));
plugin.getLeashesConfig().set("leashes", null);
plugin.saveLeashesConfig();
}
plugin.getLogger().info("updateLeashCount: player=" + player.getName() + ", currentCount=" + currentCount);
}
public int getLeashCount(Player player) {
updateLeashCount(player);
return leashCounts.getOrDefault(player.getUniqueId(), 0);
}
}

View File

@@ -0,0 +1,139 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class OreAlarmListener implements Listener {
private static final long TIME_FRAME_MILLIS = 30 * 1000;
private final SurvivalPlus plugin;
private final Map<UUID, Map<Material, OreData>> playerOreData = new HashMap<>();
private FileConfiguration config;
// Schwellenwert wird jetzt aus der Config geladen (Fallback DEFAULT_THRESHOLD)
private int threshold;
public OreAlarmListener(SurvivalPlus plugin) {
this.plugin = plugin;
this.config = plugin.getConfig();
loadSettings();
}
private void loadSettings() {
this.threshold = config.getInt("ore-alarm.threshold", 10);
}
/**
* Reloadet die Config, lädt die Einstellungen neu und leert gespeicherte Daten.
*/
public void reloadConfig(FileConfiguration newConfig) {
this.config = newConfig;
loadSettings();
playerOreData.clear();
}
@EventHandler
public void onBlockBreak(BlockBreakEvent event) {
Player player = event.getPlayer();
Material blockType = event.getBlock().getType();
if (!isMonitoredOre(blockType)) {
return;
}
UUID playerId = player.getUniqueId();
long now = System.currentTimeMillis();
playerOreData.putIfAbsent(playerId, new HashMap<>());
Map<Material, OreData> oreMap = playerOreData.get(playerId);
oreMap.putIfAbsent(blockType, new OreData(0, now));
OreData data = oreMap.get(blockType);
if (now - data.startTime > TIME_FRAME_MILLIS) {
data.count = 1;
data.startTime = now;
} else {
data.count++;
}
oreMap.put(blockType, data);
int threshold = calculateDynamicThreshold(playerId, blockType);
if (data.count >= threshold) {
String oreName = prettifyMaterialName(blockType);
String rawMessage = plugin.getLangConfig().getString("orealarm.message",
"§c[Erz-Alarm] Spieler §e%player% §chat in %seconds% Sekunden ungewöhnlich viele %ore% abgebaut: §e%count%");
String message = rawMessage
.replace("%player%", player.getName())
.replace("%ore%", oreName)
.replace("%count%", Integer.toString(data.count))
.replace("%seconds%", Long.toString(TIME_FRAME_MILLIS / 1000));
Bukkit.getOnlinePlayers().stream()
.filter(p -> p.isOp() || p.hasPermission("survivalplus.orealarm"))
.forEach(p -> p.sendMessage(message));
data.count = 0;
data.startTime = now;
}
}
private boolean isMonitoredOre(Material material) {
switch (material) {
case DIAMOND_ORE:
case DEEPSLATE_DIAMOND_ORE:
case REDSTONE_ORE:
case DEEPSLATE_REDSTONE_ORE:
case GOLD_ORE:
case DEEPSLATE_GOLD_ORE:
case IRON_ORE:
case DEEPSLATE_IRON_ORE:
case LAPIS_ORE:
case DEEPSLATE_LAPIS_ORE:
case EMERALD_ORE:
case DEEPSLATE_EMERALD_ORE:
return true;
default:
return false;
}
}
private int calculateDynamicThreshold(UUID playerId, Material ore) {
// Nutzt jetzt das geladene threshold, kann später erweitert werden
return threshold;
}
private String prettifyMaterialName(Material material) {
String name = material.name().toLowerCase();
name = name.replace("_ore", " Erz");
name = name.replace("deepslate ", "Tiefgeschichtetes ");
name = Character.toUpperCase(name.charAt(0)) + name.substring(1);
return name;
}
private static class OreData {
int count;
long startTime;
OreData(int count, long startTime) {
this.count = count;
this.startTime = startTime;
}
}
}

View File

@@ -0,0 +1,85 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus;
import net.md_5.bungee.api.ChatColor; // Bungee ChatColor für RGB/Hex Unterstützung
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.SignChangeEvent;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SignColorListener implements Listener {
private final SurvivalPlus plugin;
// Muster für Hex-Farbcodes (#FFFFFF)
private static final Pattern HEX_PATTERN = Pattern.compile("#[a-fA-F0-9]{6}");
// Muster für RGB-Farben rgb(255,255,255)
private static final Pattern RGB_PATTERN = Pattern.compile("rgb\\((\\d{1,3}),(\\d{1,3}),(\\d{1,3})\\)");
public SignColorListener(SurvivalPlus plugin) {
this.plugin = plugin;
}
@EventHandler
public void onSignChange(SignChangeEvent event) {
for (int i = 0; i < event.getLines().length; i++) {
String line = event.getLine(i);
if (line != null && !line.isEmpty()) {
// 1. &-Codes übersetzen (z.B. &a -> §a)
String translated = ChatColor.translateAlternateColorCodes('&', line);
// 2. Hex-Farben ersetzen
translated = replaceHexColors(translated);
// 3. RGB-Farben ersetzen
translated = replaceRgbColors(translated);
event.setLine(i, translated);
}
}
}
private String replaceHexColors(String input) {
Matcher matcher = HEX_PATTERN.matcher(input);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
String hexCode = matcher.group();
// ChatColor.of akzeptiert Hex-Farbcode mit #
String color = ChatColor.of(hexCode).toString();
matcher.appendReplacement(sb, color);
}
matcher.appendTail(sb);
return sb.toString();
}
private String replaceRgbColors(String input) {
Matcher matcher = RGB_PATTERN.matcher(input);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
int r = clampColorValue(matcher.group(1));
int g = clampColorValue(matcher.group(2));
int b = clampColorValue(matcher.group(3));
String hex = String.format("#%02X%02X%02X", r, g, b);
String color = ChatColor.of(hex).toString();
matcher.appendReplacement(sb, color);
}
matcher.appendTail(sb);
return sb.toString();
}
private int clampColorValue(String val) {
int v;
try {
v = Integer.parseInt(val);
} catch (NumberFormatException e) {
v = 0;
}
return Math.min(255, Math.max(0, v));
}
}

View File

@@ -0,0 +1,128 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.event.block.Action;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
public class SitListener implements Listener {
private final SurvivalPlus plugin;
private final Map<UUID, ArmorStand> sittingPlayers = new HashMap<>();
public SitListener(SurvivalPlus plugin) {
this.plugin = plugin;
}
public boolean isSitting(Player player) {
return sittingPlayers.containsKey(player.getUniqueId());
}
public void sitPlayer(Player player, Location location) {
if (sittingPlayers.containsKey(player.getUniqueId())) {
return;
}
// Erstelle einen unsichtbaren ArmorStand als Sitz
ArmorStand armorStand = player.getWorld().spawn(location, ArmorStand.class);
armorStand.setGravity(false);
armorStand.setMarker(true);
armorStand.setInvisible(true);
armorStand.setInvulnerable(true);
armorStand.addPassenger(player);
sittingPlayers.put(player.getUniqueId(), armorStand);
FileConfiguration lang = plugin.getLangConfig();
player.sendMessage(lang.getString("sit.success", "§aDu hast dich hingesetzt!"));
plugin.getLogger().log(Level.FINE, "Spieler " + player.getName() + " sitzt bei " + locationToString(location));
}
public void standUp(Player player) {
UUID playerId = player.getUniqueId();
ArmorStand armorStand = sittingPlayers.remove(playerId);
if (armorStand != null) {
armorStand.remove();
FileConfiguration lang = plugin.getLangConfig();
player.sendMessage(lang.getString("sit.stand-up", "§aDu bist aufgestanden!"));
plugin.getLogger().log(Level.FINE, "Spieler " + player.getName() + " ist aufgestanden");
}
}
@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
if (event.getHand() != EquipmentSlot.HAND || event.getAction() != Action.RIGHT_CLICK_BLOCK) {
return;
}
Player player = event.getPlayer();
FileConfiguration lang = plugin.getLangConfig();
if (!player.hasPermission("survivalplus.sit")) {
player.sendMessage(lang.getString("no-permission", "§cDu hast keine Berechtigung für diesen Befehl!"));
return;
}
Block block = event.getClickedBlock();
if (block == null || !isStair(block.getType())) {
return;
}
// Wenn der Spieler bereits sitzt, stehe auf
if (sittingPlayers.containsKey(player.getUniqueId())) {
standUp(player);
event.setCancelled(true);
return;
}
// Setze den Spieler genau auf der Treppenstufe
Location location = block.getLocation();
location.setX(location.getX() + 0.5);
location.setY(location.getY() + 0.5); // Genau auf der Treppenstufe (halbe Blockhöhe)
location.setZ(location.getZ() + 0.5);
sitPlayer(player, location);
event.setCancelled(true); // Verhindere andere Interaktionen mit der Treppe
}
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
Player player = event.getPlayer();
UUID playerId = player.getUniqueId();
if (!sittingPlayers.containsKey(playerId)) {
return;
}
// Prüfe, ob der Spieler sich bewegt hat (nur Positionsänderung, nicht Kopfbewegung)
Location from = event.getFrom();
Location to = event.getTo();
if (from.getX() != to.getX() || from.getY() != to.getY() || from.getZ() != to.getZ()) {
standUp(player);
}
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
standUp(player);
}
private boolean isStair(Material material) {
return material.name().endsWith("_STAIRS");
}
private String locationToString(Location loc) {
return String.format("x=%.2f, y=%.2f, z=%.2f, world=%s", loc.getX(), loc.getY(), loc.getZ(), loc.getWorld().getName());
}
}

View File

@@ -0,0 +1,68 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerBedEnterEvent;
import org.bukkit.event.player.PlayerBedLeaveEvent;
public class SleepListener implements Listener {
private final SurvivalPlus plugin;
private double requiredPercentage;
public SleepListener(SurvivalPlus plugin) {
this.plugin = plugin;
reloadConfig(plugin.getConfig());
}
public void reloadConfig(FileConfiguration config) {
this.requiredPercentage = config.getDouble("sleep.required-percentage", 50.0);
}
@EventHandler
public void onPlayerBedEnter(PlayerBedEnterEvent event) {
Bukkit.getScheduler().runTaskLater(plugin, () -> checkSleepProgress(event.getPlayer().getWorld()), 1L);
}
@EventHandler
public void onPlayerBedLeave(PlayerBedLeaveEvent event) {
Bukkit.getScheduler().runTaskLater(plugin, () -> checkSleepProgress(event.getPlayer().getWorld()), 1L);
}
private void checkSleepProgress(World world) {
long time = world.getTime();
if (time < 12541 || time > 23458) return; // Nur bei Nacht prüfen
int sleeping = 0;
int total = 0;
for (Player player : world.getPlayers()) {
if (player.isSleepingIgnored()) continue;
total++;
if (player.isSleeping()) sleeping++;
}
if (total == 0) return;
double percent = (sleeping * 100.0) / total;
if (percent >= requiredPercentage) {
world.setTime(0);
world.setStorm(false);
world.setThundering(false);
Bukkit.broadcastMessage(plugin.getMessage("sleep.skipped")
.replace("%sleeping%", String.valueOf(sleeping))
.replace("%total%", String.valueOf(total)));
} else {
Bukkit.broadcastMessage(plugin.getMessage("sleep.progress")
.replace("%sleeping%", String.valueOf(sleeping))
.replace("%total%", String.valueOf(total))
.replace("%required%", String.valueOf(requiredPercentage)));
}
}
}

View File

@@ -0,0 +1,75 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
public class SpawnProtectionListener implements Listener {
private final SurvivalPlus plugin;
private final int radius;
private final boolean protectBlockBreak;
private final boolean protectBlockPlace;
private final boolean protectPvP;
private final String bypassPermission;
public SpawnProtectionListener(SurvivalPlus plugin) {
this.plugin = plugin;
this.radius = plugin.getConfig().getInt("spawnprotection.radius", 20);
this.protectBlockBreak = plugin.getConfig().getBoolean("spawnprotection.protect-block-break", true);
this.protectBlockPlace = plugin.getConfig().getBoolean("spawnprotection.protect-block-place", true);
this.protectPvP = plugin.getConfig().getBoolean("spawnprotection.protect-pvp", true);
this.bypassPermission = plugin.getConfig().getString("spawnprotection.bypass-permission", "survivalplus.spawnprotection.bypass");
}
private boolean isInSpawnProtection(Location loc) {
if (loc == null) return false;
Location spawn = loc.getWorld().getSpawnLocation();
return spawn.distance(loc) <= radius;
}
private boolean canBypass(Player player) {
// Nur OPs oder Spieler mit spezieller Berechtigung dürfen bypassen
return player.isOp() || player.hasPermission(bypassPermission);
}
@EventHandler
public void onBlockBreak(BlockBreakEvent event) {
if (!protectBlockBreak) return;
Player player = event.getPlayer();
if (isInSpawnProtection(event.getBlock().getLocation()) && !canBypass(player)) {
player.sendMessage(plugin.getMessage("spawnprotection.blockbreak-denied"));
event.setCancelled(true);
}
}
@EventHandler
public void onBlockPlace(BlockPlaceEvent event) {
if (!protectBlockPlace) return;
Player player = event.getPlayer();
if (isInSpawnProtection(event.getBlock().getLocation()) && !canBypass(player)) {
player.sendMessage(plugin.getMessage("spawnprotection.blockplace-denied"));
event.setCancelled(true);
}
}
@EventHandler
public void onEntityDamageByEntity(EntityDamageByEntityEvent event) {
if (!protectPvP) return;
if (!(event.getEntity() instanceof Player)) return;
if (!(event.getDamager() instanceof Player)) return;
Player damaged = (Player) event.getEntity();
Player damager = (Player) event.getDamager();
if (isInSpawnProtection(damaged.getLocation()) && !canBypass(damager)) {
damager.sendMessage(plugin.getMessage("spawnprotection.pvp-denied"));
event.setCancelled(true);
}
}
}

View File

@@ -0,0 +1,81 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.Manager.StatsManager;
import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class StatsListener implements Listener {
private final SurvivalPlus plugin;
private final StatsManager statsManager;
// Spielzeit-Tracking
private final Map<UUID, Long> joinTimes = new HashMap<>();
public StatsListener(SurvivalPlus plugin, StatsManager statsManager) {
this.plugin = plugin;
this.statsManager = statsManager;
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
joinTimes.put(event.getPlayer().getUniqueId(), System.currentTimeMillis());
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
UUID uuid = event.getPlayer().getUniqueId();
long joinTime = joinTimes.getOrDefault(uuid, System.currentTimeMillis());
long now = System.currentTimeMillis();
long secondsPlayed = (now - joinTime) / 1000;
statsManager.addPlayTime(uuid, secondsPlayed);
statsManager.saveStats();
joinTimes.remove(uuid);
}
@EventHandler
public void onPlayerDeath(PlayerDeathEvent event) {
Player player = event.getEntity();
UUID uuid = player.getUniqueId();
statsManager.addDeaths(uuid, 1);
statsManager.saveStats();
// Optional: Kill-Stat beim Killer erhöhen
if (player.getKiller() != null) {
Player killer = player.getKiller();
statsManager.addKills(killer.getUniqueId(), 1);
statsManager.saveStats();
}
}
@EventHandler
public void onBlockBreak(BlockBreakEvent event) {
Player player = event.getPlayer();
UUID uuid = player.getUniqueId();
statsManager.addBlocksBroken(uuid, 1);
statsManager.saveStats();
}
@EventHandler
public void onBlockPlace(BlockPlaceEvent event) {
Player player = event.getPlayer();
UUID uuid = player.getUniqueId();
statsManager.addBlocksPlaced(uuid, 1);
statsManager.saveStats();
}
}