Update from Git Manager GUI
This commit is contained in:
312
src/main/java/de/nexuslobby/modules/ball/SoccerModule.java
Normal file
312
src/main/java/de/nexuslobby/modules/ball/SoccerModule.java
Normal file
@@ -0,0 +1,312 @@
|
||||
package de.nexuslobby.modules.ball;
|
||||
|
||||
import de.nexuslobby.NexusLobby;
|
||||
import de.nexuslobby.api.Module;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.entity.ArmorStand;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.SkullMeta;
|
||||
import org.bukkit.profile.PlayerProfile;
|
||||
import org.bukkit.profile.PlayerTextures;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.UUID;
|
||||
import java.util.Objects;
|
||||
|
||||
public class SoccerModule implements Module, Listener, CommandExecutor {
|
||||
|
||||
private ArmorStand ball;
|
||||
private Location spawnLocation;
|
||||
private long lastMoveTime;
|
||||
private final String TEXTURE_URL = "http://textures.minecraft.net/texture/451f8cfcfb85d77945dc6a3618414093e70436b46d2577b28c727f1329b7265e";
|
||||
private final String BALL_TAG = "nexusball_entity"; // Eindeutiges Tag zur Identifizierung
|
||||
private final String BALL_NAME = "§x§N§e§x§u§s§B§a§l§l"; // Zusätzliche Erkennung
|
||||
|
||||
@Override
|
||||
public String getName() { return "Soccer"; }
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
Bukkit.getPluginManager().registerEvents(this, NexusLobby.getInstance());
|
||||
if (NexusLobby.getInstance().getCommand("nexuslobby") != null) {
|
||||
Objects.requireNonNull(NexusLobby.getInstance().getCommand("nexuslobby")).setExecutor(this);
|
||||
}
|
||||
|
||||
loadConfigLocation();
|
||||
|
||||
// AGGRESSIVES MEHRFACHES CLEANUP-SYSTEM
|
||||
// 1. Sofort beim Enable
|
||||
removeAllOldBallsGlobal();
|
||||
|
||||
// 2. Nach 0.5 Sekunden
|
||||
Bukkit.getScheduler().runTaskLater(NexusLobby.getInstance(), this::removeAllOldBallsGlobal, 10L);
|
||||
|
||||
// 3. Nach 1 Sekunde
|
||||
Bukkit.getScheduler().runTaskLater(NexusLobby.getInstance(), this::removeAllOldBallsGlobal, 20L);
|
||||
|
||||
// 4. Nach 2 Sekunden - cleanup und dann spawnen
|
||||
Bukkit.getScheduler().runTaskLater(NexusLobby.getInstance(), () -> {
|
||||
removeAllOldBallsGlobal();
|
||||
spawnBall();
|
||||
}, 40L);
|
||||
|
||||
// 5. Nach 3 Sekunden - finales Cleanup für hartnäckige Duplikate
|
||||
Bukkit.getScheduler().runTaskLater(NexusLobby.getInstance(), this::removeAllOldBallsGlobal, 60L);
|
||||
|
||||
// 6. Nach 5 Sekunden - letzter Check
|
||||
Bukkit.getScheduler().runTaskLater(NexusLobby.getInstance(), this::removeAllOldBallsGlobal, 100L);
|
||||
|
||||
// Haupt-Physik & Anti-Klon Tick
|
||||
Bukkit.getScheduler().runTaskTimer(NexusLobby.getInstance(), () -> {
|
||||
|
||||
// ANTI-KLON-SYSTEM: Prüfe die Umgebung des Spawns auf illegale Kopien
|
||||
if (spawnLocation != null && spawnLocation.getWorld() != null) {
|
||||
for (Entity entity : spawnLocation.getWorld().getNearbyEntities(spawnLocation, 50, 50, 50)) {
|
||||
if (entity instanceof ArmorStand stand) {
|
||||
// Wenn der ArmorStand unser Tag hat, aber nicht unsere aktive Instanz ist -> Löschen
|
||||
if (stand.getScoreboardTags().contains(BALL_TAG)) {
|
||||
if (ball == null || !stand.getUniqueId().equals(ball.getUniqueId())) {
|
||||
stand.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ball == null || !ball.isValid()) return;
|
||||
|
||||
Vector vel = ball.getVelocity();
|
||||
double speed = vel.length();
|
||||
|
||||
handleWallBounce(vel);
|
||||
handleParticles(speed);
|
||||
|
||||
// Dribbel-Logik
|
||||
for (Entity nearby : ball.getNearbyEntities(0.7, 0.5, 0.7)) {
|
||||
if (nearby instanceof Player p) {
|
||||
Vector direction = ball.getLocation().toVector().subtract(p.getLocation().toVector());
|
||||
if (direction.lengthSquared() > 0) {
|
||||
direction.normalize();
|
||||
direction.setY(0.12);
|
||||
ball.setVelocity(direction.multiply(0.35));
|
||||
}
|
||||
lastMoveTime = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
// Automatischer Respawn bei Inaktivität oder Void
|
||||
long delay = NexusLobby.getInstance().getConfig().getLong("ball.respawn_delay", 60) * 1000;
|
||||
if (System.currentTimeMillis() - lastMoveTime > delay || ball.getLocation().getY() < -5) {
|
||||
respawnBall();
|
||||
}
|
||||
}, 1L, 1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scannt ALLE Welten nach Entities mit dem BALL_TAG und entfernt sie.
|
||||
* Nutzt mehrere Erkennungsmethoden für maximale Sicherheit.
|
||||
*/
|
||||
private void removeAllOldBallsGlobal() {
|
||||
int removed = 0;
|
||||
// Alle Welten durchsuchen
|
||||
for (World world : Bukkit.getWorlds()) {
|
||||
for (Entity entity : world.getEntities()) {
|
||||
if (entity instanceof ArmorStand stand) {
|
||||
boolean shouldRemove = false;
|
||||
|
||||
// Methode 1: Tag-basiert
|
||||
if (stand.getScoreboardTags().contains(BALL_TAG)) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
|
||||
// Methode 2: Name-basiert (falls Tags verloren gehen)
|
||||
if (stand.getCustomName() != null && stand.getCustomName().equals(BALL_NAME)) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
|
||||
// Methode 3: Kopf-Textur-basiert (prüfe ob es ein Soccer-Ball Kopf ist)
|
||||
if (stand.getEquipment() != null && stand.getEquipment().getHelmet() != null) {
|
||||
ItemStack helmet = stand.getEquipment().getHelmet();
|
||||
if (helmet.getType() == Material.PLAYER_HEAD && helmet.hasItemMeta()) {
|
||||
SkullMeta meta = (SkullMeta) helmet.getItemMeta();
|
||||
if (meta.hasOwner() && meta.getOwnerProfile() != null) {
|
||||
PlayerProfile profile = meta.getOwnerProfile();
|
||||
if (profile.getName() != null && profile.getName().equals("SoccerBall")) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Methode 4: Position-basiert - Entferne alle unsichtbaren, kleinen ArmorStands in der Nähe des Spawns
|
||||
if (spawnLocation != null && spawnLocation.getWorld() != null &&
|
||||
stand.getWorld().equals(spawnLocation.getWorld()) &&
|
||||
stand.isSmall() && stand.isInvisible() && !stand.hasBasePlate()) {
|
||||
|
||||
double distance = stand.getLocation().distance(spawnLocation);
|
||||
// Wenn innerhalb von 5 Blöcken vom Spawn und hat einen Kopf
|
||||
if (distance < 5.0 && stand.getEquipment() != null &&
|
||||
stand.getEquipment().getHelmet() != null &&
|
||||
stand.getEquipment().getHelmet().getType() == Material.PLAYER_HEAD) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Nur entfernen wenn es NICHT unsere aktuelle Ball-Instanz ist
|
||||
if (shouldRemove && (ball == null || !stand.getUniqueId().equals(ball.getUniqueId()))) {
|
||||
stand.remove();
|
||||
removed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removed > 0) {
|
||||
Bukkit.getLogger().info("[NexusLobby] " + removed + " alte Ball-Entities entfernt.");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleWallBounce(Vector vel) {
|
||||
if (vel.lengthSquared() < 0.001) return;
|
||||
Location loc = ball.getLocation();
|
||||
Block nextX = loc.clone().add(vel.getX() * 1.3, 0.5, 0).getBlock();
|
||||
Block nextZ = loc.clone().add(0, 0.5, vel.getZ() * 1.3).getBlock();
|
||||
|
||||
boolean bounced = false;
|
||||
if (nextX.getType().isSolid()) { vel.setX(-vel.getX() * 0.75); bounced = true; }
|
||||
if (nextZ.getType().isSolid()) { vel.setZ(-vel.getZ() * 0.75); bounced = true; }
|
||||
|
||||
if (bounced) {
|
||||
ball.setVelocity(vel);
|
||||
ball.getWorld().playSound(ball.getLocation(), Sound.BLOCK_WOOD_BREAK, 0.6f, 1.3f);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleParticles(double speed) {
|
||||
if (speed < 0.05) return;
|
||||
Location loc = ball.getLocation().add(0, 0.2, 0);
|
||||
World world = loc.getWorld();
|
||||
if (world == null) return;
|
||||
|
||||
if (speed > 0.85) world.spawnParticle(Particle.SONIC_BOOM, loc, 1, 0, 0, 0, 0);
|
||||
else if (speed > 0.45) world.spawnParticle(Particle.CRIT, loc, 3, 0.1, 0.1, 0.1, 0.08);
|
||||
else world.spawnParticle(Particle.SMOKE, loc, 1, 0.05, 0, 0.05, 0.02);
|
||||
}
|
||||
|
||||
private void spawnBall() {
|
||||
if (spawnLocation == null || (ball != null && ball.isValid())) return;
|
||||
|
||||
// Ball direkt auf dem Boden spawnen (keine Y-Offset Erhöhung)
|
||||
Location spawnLoc = spawnLocation.clone();
|
||||
ball = (ArmorStand) spawnLoc.getWorld().spawnEntity(spawnLoc, EntityType.ARMOR_STAND);
|
||||
|
||||
ball.setInvisible(true);
|
||||
ball.setGravity(true);
|
||||
ball.setBasePlate(false);
|
||||
ball.setSmall(true);
|
||||
ball.setInvulnerable(false);
|
||||
ball.setArms(false);
|
||||
ball.setCustomNameVisible(false);
|
||||
|
||||
// Setze Custom Name für zusätzliche Erkennung (unsichtbar für Spieler)
|
||||
ball.setCustomName(BALL_NAME);
|
||||
|
||||
// Deaktiviert das Speichern in der Welt-Datei
|
||||
ball.setPersistent(false);
|
||||
// Markiert den Ball für das Cleanup-System
|
||||
ball.addScoreboardTag(BALL_TAG);
|
||||
|
||||
ItemStack ballHead = getSoccerHead();
|
||||
if (ball.getEquipment() != null) ball.getEquipment().setHelmet(ballHead);
|
||||
lastMoveTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private ItemStack getSoccerHead() {
|
||||
ItemStack head = new ItemStack(Material.PLAYER_HEAD);
|
||||
SkullMeta meta = (SkullMeta) head.getItemMeta();
|
||||
if (meta == null) return head;
|
||||
|
||||
PlayerProfile profile = Bukkit.createPlayerProfile(UUID.randomUUID(), "SoccerBall");
|
||||
try {
|
||||
profile.getTextures().setSkin(new URL(TEXTURE_URL));
|
||||
} catch (MalformedURLException ignored) {}
|
||||
meta.setOwnerProfile(profile);
|
||||
|
||||
head.setItemMeta(meta);
|
||||
return head;
|
||||
}
|
||||
|
||||
public void respawnBall() {
|
||||
if (ball != null) {
|
||||
ball.remove();
|
||||
ball = null;
|
||||
}
|
||||
removeAllOldBallsGlobal();
|
||||
spawnBall();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBallPunch(EntityDamageByEntityEvent event) {
|
||||
if (event.getEntity().equals(ball)) {
|
||||
event.setCancelled(true);
|
||||
if (event.getDamager() instanceof Player p) {
|
||||
Vector shootDir = p.getLocation().getDirection();
|
||||
if (shootDir.lengthSquared() > 0) {
|
||||
shootDir.normalize().multiply(1.35).setY(0.38);
|
||||
ball.setVelocity(shootDir);
|
||||
}
|
||||
ball.getWorld().playSound(ball.getLocation(), Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, 0.6f, 1.5f);
|
||||
lastMoveTime = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBallInteract(PlayerInteractAtEntityEvent event) {
|
||||
if (event.getRightClicked().equals(ball)) event.setCancelled(true);
|
||||
}
|
||||
|
||||
private void loadConfigLocation() {
|
||||
FileConfiguration config = NexusLobby.getInstance().getConfig();
|
||||
if (config.contains("ball.spawn")) spawnLocation = config.getLocation("ball.spawn");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!(sender instanceof Player p) || !p.hasPermission("nexuslobby.admin")) return true;
|
||||
|
||||
if (args.length >= 2 && args[0].equalsIgnoreCase("ball")) {
|
||||
if (args[1].equalsIgnoreCase("setspawn")) {
|
||||
spawnLocation = p.getLocation();
|
||||
NexusLobby.getInstance().getConfig().set("ball.spawn", spawnLocation);
|
||||
NexusLobby.getInstance().saveConfig();
|
||||
respawnBall();
|
||||
p.sendMessage("§8[§6Nexus§8] §aBall-Spawn gesetzt. Cleanup ist aktiv!");
|
||||
return true;
|
||||
} else if (args[1].equalsIgnoreCase("respawn")) {
|
||||
respawnBall();
|
||||
p.sendMessage("§8[§6Nexus§8] §eBall manuell respawnt.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (ball != null) ball.remove();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user