diff --git a/.idea/misc.xml b/.idea/misc.xml index 9b2882f..5d55df2 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,7 +8,7 @@ - + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 0b7faca..5bd5c67 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 2.0.11-SNAPSHOT - 11 + 17 ${project.basedir}/keystore.jks mykey 123456 diff --git a/src/main/java/eu/m724/giants/GiantProcessor.java b/src/main/java/eu/m724/giants/GiantProcessor.java index 8dace27..017d336 100644 --- a/src/main/java/eu/m724/giants/GiantProcessor.java +++ b/src/main/java/eu/m724/giants/GiantProcessor.java @@ -13,11 +13,10 @@ import org.bukkit.persistence.PersistentDataType; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; -import org.bukkit.scheduler.BukkitRunnable; -import org.bukkit.util.Vector; import java.util.*; import java.util.concurrent.ThreadLocalRandom; +import java.util.logging.Level; import java.util.logging.Logger; // TODO move ai stuff to another class @@ -28,13 +27,12 @@ public class GiantProcessor implements Listener { private static final int VERSION = 1; private final JavaPlugin plugin; - private final Configuration configuration; + final Configuration configuration; private final Logger logger; // private final Set trackedGiants = new HashSet<>(); - private final Set trackedHusks = new HashSet<>(); - private final Map giantLocationMap = new HashMap<>(); - private final Map giantLastJump = new HashMap<>(); + final Set trackedHusks = new HashSet<>(); + final Map giantLocationMap = new HashMap<>(); private final ThreadLocalRandom random = ThreadLocalRandom.current(); private final NamespacedKey huskKey; @@ -43,120 +41,18 @@ public class GiantProcessor implements Listener { this.plugin = plugin; this.configuration = configuration; this.logger = Logger.getLogger(plugin.getLogger().getName() + ".GiantProcessor"); + logger.setLevel(Level.ALL); this.huskKey = new NamespacedKey(plugin, "husk"); } void start() { if (configuration.ai) { - new BukkitRunnable() { - @Override - public void run() { - for (Husk husk : Set.copyOf(trackedHusks)) { - if (husk.isValid()) { - Location huskLocation = husk.getLocation(); - Entity giant = husk.getVehicle(); - - if (giant instanceof Giant) { - if (giant.isValid()) { // TODO reconsider - giant.getWorld().getNearbyEntities( - giant.getBoundingBox().expand(configuration.attackReach), - e -> (e instanceof Player && !e.isInvulnerable()) - ).forEach(p -> ((Player) p).damage(configuration.attackDamage, giant)); - giant.setRotation(huskLocation.getYaw(), huskLocation.getPitch()); - - // jumping - if (configuration.jumpMode != 0) { - // tracking location is only required for jumping - Location prevLocation = giantLocationMap.get(giant); - Location location = giant.getLocation(); - if (prevLocation == null) { - prevLocation = location; - } - giantLocationMap.put(giant, location); - - LivingEntity target = husk.getTarget(); - if (target != null) { - processJump(giant, prevLocation, location, target.getLocation()); - } - } - } - } else { - // no vehicle means the giant doesn't exist anymore and the husk should also not exist - husk.setHealth(0); - - trackedHusks.remove(husk); - logger.fine("Husk killed because Giant died at " + husk.getLocation()); - } - } else { - trackedHusks.remove(husk); - logger.fine("Husk unloaded at " + husk.getLocation()); - } - } - } - }.runTaskTimer(plugin, configuration.attackDelay, 0L); + new GiantRunnable(this).runTaskTimer(plugin, 0, configuration.attackDelay); } plugin.getServer().getPluginManager().registerEvents(this, plugin); } - private void processJump(Entity giant, Location prevLocation, Location location, Location targetLocation) { - long now = System.currentTimeMillis(); - if (now - giantLastJump.getOrDefault(giant, 0L) < configuration.jumpDelay) { - return; - } - - if (giant.isOnGround()) { - giantLastJump.put(giant, now); - if (configuration.jumpCondition == 0) { - if (targetLocation.subtract(location).getY() > 0) { - jump(giant); - } - } else if (configuration.jumpCondition == 1) { - Location delta = prevLocation.subtract(location); - if (targetLocation.subtract(location).getY() > 0 && (delta.getX() == 0 || delta.getZ() == 0)) { - jump(giant); - } - } else if (configuration.jumpCondition == 2) { - Location delta = prevLocation.subtract(location); - if (delta.getX() == 0 || delta.getZ() == 0) { - jump(giant); - } - - } // I could probably simplify that code - } - } - - private void jump(Entity giant) { - if (configuration.jumpMode == 1) { - giant.setVelocity(new Vector(0, configuration.jumpHeight, 0)); - } else if (configuration.jumpMode == 2) { - giant.teleport(giant.getLocation().add(0, configuration.jumpHeight, 0)); - } - } // TODO those should be moved - - public LivingEntity spawnGiant(Location pos) { - LivingEntity entity = (LivingEntity) pos.getWorld().spawnEntity(pos, EntityType.GIANT); - - if (configuration.ai) { - // the husk basically moves the giant - LivingEntity passenger = (LivingEntity) pos.getWorld().spawnEntity(pos, EntityType.HUSK); - new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 1).apply(passenger); - passenger.setInvulnerable(true); - passenger.setPersistent(true); - passenger.getPersistentDataContainer().set(huskKey, PersistentDataType.INTEGER, VERSION); - - entity.addPassenger(passenger); - - trackedHusks.add((Husk) passenger); - } - - configuration.effects.forEach(entity::addPotionEffect); - - logger.fine("Spawned a Giant at " + pos); - - return entity; - } - /** * The check is very approximate * @@ -172,14 +68,6 @@ public class GiantProcessor implements Listener { return true; } - public LivingEntity spawnGiantIfPossible(Location location) { - if (isSpawnableAt(location)) { - return spawnGiant(location); - } else { - return null; - } - } - @EventHandler public void onChunkLoad(ChunkLoadEvent event) { Entity[] entities = event.getChunk().getEntities(); @@ -209,15 +97,42 @@ public class GiantProcessor implements Listener { } } + public void applyGiantsLogic(Giant giant) { + if (configuration.ai) { + // the husk basically moves the giant + LivingEntity passenger = (LivingEntity) giant.getWorld().spawnEntity(giant.getLocation(), EntityType.HUSK); + new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 1).apply(passenger); + passenger.setInvulnerable(true); + passenger.setPersistent(true); + passenger.getPersistentDataContainer().set(huskKey, PersistentDataType.INTEGER, VERSION); + + giant.addPassenger(passenger); + + trackedHusks.add((Husk) passenger); + } + + configuration.effects.forEach(giant::addPotionEffect); + } + @EventHandler public void entitySpawn(EntitySpawnEvent e) { + if (e.getEntityType() == EntityType.GIANT) { + logger.fine("Handling spawned Giant at " + e.getLocation()); + + var giant = (Giant) e.getEntity(); + if (giant.hasAI()) // NoAI flag + applyGiantsLogic(giant); + } + if (configuration.worldBlacklist.contains(e.getLocation().getWorld().getName())) return; if (e.getEntityType() == EntityType.ZOMBIE) { if (configuration.chance > random.nextDouble()) { - logger.fine("Spawned a Giant by chance at " + e.getLocation()); - if (spawnGiantIfPossible(e.getLocation()) != null) { + logger.fine("Trying to spawn a Giant by chance at " + e.getLocation()); + if (isSpawnableAt(e.getLocation())) { + logger.fine("Spawned a Giant by chance at " + e.getLocation()); + e.getLocation().getWorld().spawnEntity(e.getLocation(), EntityType.GIANT); e.setCancelled(true); } } diff --git a/src/main/java/eu/m724/giants/GiantRunnable.java b/src/main/java/eu/m724/giants/GiantRunnable.java new file mode 100644 index 0000000..546820c --- /dev/null +++ b/src/main/java/eu/m724/giants/GiantRunnable.java @@ -0,0 +1,105 @@ +package eu.m724.giants; + +import org.bukkit.Location; +import org.bukkit.entity.*; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.util.Vector; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * Ticks giants + */ +public class GiantRunnable extends BukkitRunnable { + private final GiantProcessor giantProcessor; + private final Configuration configuration; + + private final Map giantLastJump = new HashMap<>(); + + public GiantRunnable(GiantProcessor giantProcessor) { + this.giantProcessor = giantProcessor; + this.configuration = giantProcessor.configuration; + } + + @Override + public void run() { + for (Husk husk : Set.copyOf(giantProcessor.trackedHusks)) { + if (husk.isValid()) { + Location huskLocation = husk.getLocation(); + Entity giant = husk.getVehicle(); + + if (giant instanceof Giant) { + if (giant.isValid()) { // TODO reconsider + giant.getWorld().getNearbyEntities( + giant.getBoundingBox().expand(configuration.attackReach), + e -> (e instanceof Player && !e.isInvulnerable()) + ).forEach(p -> ((Player) p).damage(configuration.attackDamage, giant)); + giant.setRotation(huskLocation.getYaw(), huskLocation.getPitch()); + + // jumping + if (configuration.jumpMode != 0) { + // tracking location is only required for jumping + Location prevLocation = giantProcessor.giantLocationMap.get(giant); + Location location = giant.getLocation(); + if (prevLocation == null) { + prevLocation = location; + } + giantProcessor.giantLocationMap.put(giant, location); + + LivingEntity target = husk.getTarget(); + if (target != null) { + processJump(giant, prevLocation, location, target.getLocation()); + } + } + } + } else { + // no vehicle means the giant doesn't exist anymore and the husk should also not exist + husk.setHealth(0); + + giantProcessor.trackedHusks.remove(husk); + //logger.fine("Husk killed because Giant died at " + husk.getLocation()); + } + } else { + giantProcessor.trackedHusks.remove(husk); + //logger.fine("Husk unloaded at " + husk.getLocation()); + } + } + } + + private void processJump(Entity giant, Location prevLocation, Location location, Location targetLocation) { + long now = System.currentTimeMillis(); + if (now - giantLastJump.getOrDefault(giant, 0L) < configuration.jumpDelay) { + return; + } + + if (giant.isOnGround()) { + giantLastJump.put(giant, now); + if (configuration.jumpCondition == 0) { + if (targetLocation.subtract(location).getY() > 0) { + jump(giant); + } + } else if (configuration.jumpCondition == 1) { + Location delta = prevLocation.subtract(location); + if (targetLocation.subtract(location).getY() > 0 && (delta.getX() == 0 || delta.getZ() == 0)) { + jump(giant); + } + } else if (configuration.jumpCondition == 2) { + Location delta = prevLocation.subtract(location); + if (delta.getX() == 0 || delta.getZ() == 0) { + jump(giant); + } + + } // I could probably simplify that code + } + } + + private void jump(Entity giant) { + if (configuration.jumpMode == 1) { + giant.setVelocity(new Vector(0, configuration.jumpHeight, 0)); + } else if (configuration.jumpMode == 2) { + giant.teleport(giant.getLocation().add(0, configuration.jumpHeight, 0)); + } + } +} diff --git a/src/main/java/eu/m724/giants/GiantsCommand.java b/src/main/java/eu/m724/giants/GiantsCommand.java index 32935e5..47bab16 100644 --- a/src/main/java/eu/m724/giants/GiantsCommand.java +++ b/src/main/java/eu/m724/giants/GiantsCommand.java @@ -5,6 +5,7 @@ import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; @@ -46,11 +47,13 @@ public class GiantsCommand implements CommandExecutor { Player player = sender instanceof Player ? (Player) sender : null; if (action.equals("spawn")) { + sender.sendMessage("This command is deprecated. Use /summon giant instead"); if (player != null) { - if (plugin.spawnGiantIfPossible(player.getLocation()) == null) { - sender.sendMessage("No space here for a Giant"); - } else { + if (plugin.isSpawnableAt(player.getLocation())) { sender.sendMessage("Spawned a Giant"); + player.getWorld().spawnEntity(player.getLocation(), EntityType.GIANT); + } else { + sender.sendMessage("No space here for a Giant"); } } else { sender.sendMessage("Only players can use this command."); diff --git a/src/main/java/eu/m724/giants/GiantsPlugin.java b/src/main/java/eu/m724/giants/GiantsPlugin.java index aed8ad7..1157f60 100644 --- a/src/main/java/eu/m724/giants/GiantsPlugin.java +++ b/src/main/java/eu/m724/giants/GiantsPlugin.java @@ -6,8 +6,6 @@ import eu.m724.jarupdater.verify.VerificationException; import org.bstats.bukkit.Metrics; import org.bukkit.Location; import org.bukkit.command.CommandExecutor; -import org.bukkit.entity.Giant; -import org.bukkit.entity.LivingEntity; import org.bukkit.plugin.java.JavaPlugin; import java.io.File; @@ -30,9 +28,6 @@ public class GiantsPlugin extends JavaPlugin implements CommandExecutor { giantProcessor.start(); - // bStats - new Metrics(this, 14131); - // updater PluginUpdater updater = null; @@ -52,7 +47,7 @@ public class GiantsPlugin extends JavaPlugin implements CommandExecutor { } catch (VerificationException e) { getLogger().warning(e.getMessage()); getLogger().warning("Plugin JAR is of invalid signature. If this persists, re-download the JAR from SpigotMC."); - getLogger().warning("Did you update from 2.0.7? If yes, you must re-download 2.0.9+ from SpigotMC, then delete plugins/.paper-remapped"); + getLogger().warning("If on Paper, try removing plugins/.paper-remapped"); } updater.initNotifier(); @@ -63,32 +58,11 @@ public class GiantsPlugin extends JavaPlugin implements CommandExecutor { getCommand("giants").setExecutor(new GiantsCommand(this, configuration, updateCommand)); - /* bStats is optional. not anymore - try { - Class clazz = Class.forName("eu.m724.giants.bukkit.Metrics"); - Constructor constructor = clazz.getDeclaredConstructor(JavaPlugin.class, int.class); - constructor.newInstance(this, 14131); - getLogger().info("Enabled bStats"); - } catch (Exception e) { - getLogger().info("Not using bStats (" + e.getClass().getName() + ")"); - }*/ - + new Metrics(this, 14131); } - - // TODO api, untested - /** - * Spawns a {@link Giant} at the specified {@link Location} - * - * @param location The location - * @return The spawned {@link Giant} (as a {@link LivingEntity}, but you can cast) - */ - public LivingEntity spawnGiant(Location location) { - return giantProcessor.spawnGiant(location); - } - /** * Checks if a giant can be spawned at a location
* The check is very approximate, but works for most scenarios @@ -99,15 +73,4 @@ public class GiantsPlugin extends JavaPlugin implements CommandExecutor { public boolean isSpawnableAt(Location location) { return giantProcessor.isSpawnableAt(location); } - - /** - * Checks if a giant can be spawned at a location and spawns it
- * * The check is very approximate, but works for most scenarios - * - * @param location The location - * @return The spawned {@link Giant} or null if no room for it - */ - public LivingEntity spawnGiantIfPossible(Location location) { - return giantProcessor.spawnGiantIfPossible(location); - } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index baa67bd..82a03f7 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,5 +1,5 @@ # Notes: -# To have no values in a list, remove every value below it and change to [] like `effects: []` +# To clear a list, remove every value below it and change to [] like `effects: []` # Enable updater # It still requires an admin to confirm update