This commit is contained in:
Minecon724 2025-01-05 11:28:18 +01:00
parent f2006797e0
commit 687afb9d5d
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
7 changed files with 151 additions and 165 deletions

View file

@ -8,7 +8,7 @@
</list> </list>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="temurin-11" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="temurin-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/classes" /> <output url="file://$PROJECT_DIR$/classes" />
</component> </component>
</project> </project>

View file

@ -5,7 +5,7 @@
<version>2.0.11-SNAPSHOT</version> <version>2.0.11-SNAPSHOT</version>
<properties> <properties>
<maven.compiler.release>11</maven.compiler.release> <maven.compiler.release>17</maven.compiler.release>
<jarsigner.keystore>${project.basedir}/keystore.jks</jarsigner.keystore> <jarsigner.keystore>${project.basedir}/keystore.jks</jarsigner.keystore>
<jarsigner.alias>mykey</jarsigner.alias> <jarsigner.alias>mykey</jarsigner.alias>
<jarsigner.storepass>123456</jarsigner.storepass> <jarsigner.storepass>123456</jarsigner.storepass>

View file

@ -13,11 +13,10 @@ import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;
import java.util.*; import java.util.*;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
// TODO move ai stuff to another class // TODO move ai stuff to another class
@ -28,13 +27,12 @@ public class GiantProcessor implements Listener {
private static final int VERSION = 1; private static final int VERSION = 1;
private final JavaPlugin plugin; private final JavaPlugin plugin;
private final Configuration configuration; final Configuration configuration;
private final Logger logger; private final Logger logger;
// private final Set<Giant> trackedGiants = new HashSet<>(); // private final Set<Giant> trackedGiants = new HashSet<>();
private final Set<Husk> trackedHusks = new HashSet<>(); final Set<Husk> trackedHusks = new HashSet<>();
private final Map<Entity, Location> giantLocationMap = new HashMap<>(); final Map<Entity, Location> giantLocationMap = new HashMap<>();
private final Map<Entity, Long> giantLastJump = new HashMap<>();
private final ThreadLocalRandom random = ThreadLocalRandom.current(); private final ThreadLocalRandom random = ThreadLocalRandom.current();
private final NamespacedKey huskKey; private final NamespacedKey huskKey;
@ -43,120 +41,18 @@ public class GiantProcessor implements Listener {
this.plugin = plugin; this.plugin = plugin;
this.configuration = configuration; this.configuration = configuration;
this.logger = Logger.getLogger(plugin.getLogger().getName() + ".GiantProcessor"); this.logger = Logger.getLogger(plugin.getLogger().getName() + ".GiantProcessor");
logger.setLevel(Level.ALL);
this.huskKey = new NamespacedKey(plugin, "husk"); this.huskKey = new NamespacedKey(plugin, "husk");
} }
void start() { void start() {
if (configuration.ai) { if (configuration.ai) {
new BukkitRunnable() { new GiantRunnable(this).runTaskTimer(plugin, 0, configuration.attackDelay);
@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);
} }
plugin.getServer().getPluginManager().registerEvents(this, plugin); 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 * The check is very approximate
* *
@ -172,14 +68,6 @@ public class GiantProcessor implements Listener {
return true; return true;
} }
public LivingEntity spawnGiantIfPossible(Location location) {
if (isSpawnableAt(location)) {
return spawnGiant(location);
} else {
return null;
}
}
@EventHandler @EventHandler
public void onChunkLoad(ChunkLoadEvent event) { public void onChunkLoad(ChunkLoadEvent event) {
Entity[] entities = event.getChunk().getEntities(); 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 @EventHandler
public void entitySpawn(EntitySpawnEvent e) { 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())) if (configuration.worldBlacklist.contains(e.getLocation().getWorld().getName()))
return; return;
if (e.getEntityType() == EntityType.ZOMBIE) { if (e.getEntityType() == EntityType.ZOMBIE) {
if (configuration.chance > random.nextDouble()) { if (configuration.chance > random.nextDouble()) {
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()); logger.fine("Spawned a Giant by chance at " + e.getLocation());
if (spawnGiantIfPossible(e.getLocation()) != null) { e.getLocation().getWorld().spawnEntity(e.getLocation(), EntityType.GIANT);
e.setCancelled(true); e.setCancelled(true);
} }
} }

View file

@ -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<Entity, Long> 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));
}
}
}

View file

@ -5,6 +5,7 @@ import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@ -46,11 +47,13 @@ public class GiantsCommand implements CommandExecutor {
Player player = sender instanceof Player ? (Player) sender : null; Player player = sender instanceof Player ? (Player) sender : null;
if (action.equals("spawn")) { if (action.equals("spawn")) {
sender.sendMessage("This command is deprecated. Use /summon giant instead");
if (player != null) { if (player != null) {
if (plugin.spawnGiantIfPossible(player.getLocation()) == null) { if (plugin.isSpawnableAt(player.getLocation())) {
sender.sendMessage("No space here for a Giant");
} else {
sender.sendMessage("Spawned a Giant"); sender.sendMessage("Spawned a Giant");
player.getWorld().spawnEntity(player.getLocation(), EntityType.GIANT);
} else {
sender.sendMessage("No space here for a Giant");
} }
} else { } else {
sender.sendMessage("Only players can use this command."); sender.sendMessage("Only players can use this command.");

View file

@ -6,8 +6,6 @@ import eu.m724.jarupdater.verify.VerificationException;
import org.bstats.bukkit.Metrics; import org.bstats.bukkit.Metrics;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.entity.Giant;
import org.bukkit.entity.LivingEntity;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import java.io.File; import java.io.File;
@ -30,9 +28,6 @@ public class GiantsPlugin extends JavaPlugin implements CommandExecutor {
giantProcessor.start(); giantProcessor.start();
// bStats
new Metrics(this, 14131);
// updater // updater
PluginUpdater updater = null; PluginUpdater updater = null;
@ -52,7 +47,7 @@ public class GiantsPlugin extends JavaPlugin implements CommandExecutor {
} catch (VerificationException e) { } catch (VerificationException e) {
getLogger().warning(e.getMessage()); getLogger().warning(e.getMessage());
getLogger().warning("Plugin JAR is of invalid signature. If this persists, re-download the JAR from SpigotMC."); 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(); updater.initNotifier();
@ -63,32 +58,11 @@ public class GiantsPlugin extends JavaPlugin implements CommandExecutor {
getCommand("giants").setExecutor(new GiantsCommand(this, configuration, updateCommand)); getCommand("giants").setExecutor(new GiantsCommand(this, configuration, updateCommand));
/* bStats is optional. not anymore new Metrics(this, 14131);
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() + ")");
}*/
} }
// TODO api, untested // 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<br> * Checks if a giant can be spawned at a location<br>
* The check is very approximate, but works for most scenarios * 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) { public boolean isSpawnableAt(Location location) {
return giantProcessor.isSpawnableAt(location); return giantProcessor.isSpawnableAt(location);
} }
/**
* Checks if a giant can be spawned at a location and spawns it<br>
* * 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);
}
} }

View file

@ -1,5 +1,5 @@
# Notes: # 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 # Enable updater
# It still requires an admin to confirm update # It still requires an admin to confirm update