diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..aa00ffa
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
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/README.md b/README.md
index f4feca1..3267468 100644
--- a/README.md
+++ b/README.md
@@ -2,10 +2,28 @@
This plugin adds naturally spawning Giants with AI to your Minecraft server.
-### Requested features
-- Texture variation \
- I think this can be done, but would require a texture pack
-- Boss bar \
- I don't want to get buried in that, so this would require an API, which, for performance reasons, should not be mandatory
-- Hitboxes \
- I don't know if this is still a major issue, but a person reported that the hitbox doesn't cover the whole body
\ No newline at end of file
+### Signing
+Public key goes into `resources/verifies_downloaded_jars.pem`
+
+A default keystore is not provided.
+
+To create a keystore and export public key:
+```
+keytool -keystore testkeystore2.jks -genkeypair -keyalg RSA -alias testkey -validity 999999
+keytool -exportcert -alias testkey -keystore testkeystore2.jks -file cert.cer -rfc
+openssl x509 -inform pem -in cert.cer -pubkey -noout > public_key.pem
+```
+
+When using `mvn`, override with `-Djarsigner.`
+```
+mvn clean package -Djarsigner.keystore=/home/user/mykeystore.jks -Djarsigner.alias=mykey
+```
+
+### Color scheme
+The following color scheme is used for chat messages:
+- Errors: RED
+- "Soft errors" (like no permission): GRAY
+- Status messages: GRAY
+- Notice / call for action: YELLOW (optionally also BOLD)
+- Information: GOLD
+- Highlight: AQUA (TODO do that)
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 2f8465c..b8c0218 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,11 +2,15 @@
4.0.0
eu.m724
giants
- 2.0.7
+ 2.0.12-SNAPSHOT
- 11
- 11
+ 17
+ ${project.basedir}/keystore.jks
+ mykey
+ 123456
+ UTF-8
+ UTF-8
@@ -53,7 +57,7 @@
eu.m724
jarupdater
- 0.1.3
+ 0.1.10
@@ -79,6 +83,7 @@
3.6.0
false
+ true
@@ -93,6 +98,15 @@
eu.m724:jarupdater
+
+
+ *
+
+ META-INF/MANIFEST.MF
+ META-INF/maven/**
+
+
+
@@ -103,13 +117,37 @@
+
+ org.apache.maven.plugins
+ maven-jarsigner-plugin
+ 3.1.0
+
+
+ sign
+
+ sign
+
+
+
+ verify
+
+ verify
+
+
+
+
+ ${jarsigner.keystore}
+ ${jarsigner.alias}
+ ${jarsigner.storepass}
+
+
scm:git:git@git.m724.eu:Minecon724/giants.git
- giants-2.0.7
+ HEAD
diff --git a/src/main/java/eu/m724/giants/Configuration.java b/src/main/java/eu/m724/giants/Configuration.java
deleted file mode 100644
index a754743..0000000
--- a/src/main/java/eu/m724/giants/Configuration.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package eu.m724.giants;
-
-import org.bukkit.Material;
-import org.bukkit.configuration.file.YamlConfiguration;
-import org.bukkit.inventory.ItemStack;
-import org.bukkit.plugin.Plugin;
-import org.bukkit.potion.PotionEffect;
-import org.bukkit.potion.PotionEffectType;
-import org.bukkit.util.Vector;
-
-import java.io.File;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.logging.Logger;
-
-public class Configuration {
- private final File file;
- private final Logger logger;
-
- String updater;
-
- boolean ai;
-
- double attackDamage;
- int attackDelay;
- Vector attackReach;
- int jumpMode;
- int jumpCondition;
- int jumpDelay;
- double jumpHeight = -1;
-
- double chance;
- List worldBlacklist;
- Set effects = new HashSet<>();
- Set drops = new HashSet<>();
-
- public Configuration(Plugin plugin, File file) {
- this.file = file;
- this.logger = Logger.getLogger(plugin.getLogger().getName() + ".Configuration");
- }
-
- public void load() {
- YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
-
- updater = config.getString("updater", "true");
- if (updater.equalsIgnoreCase("true")) {
- updater = "release";
- } else if (updater.equalsIgnoreCase("false")) {
- updater = null;
- }
-
- ai = config.getBoolean("ai");
-
- chance = config.getDouble("chance");
- attackDamage = config.getDouble("attackDamage");
- attackDelay = config.getInt("attackDelay");
- jumpMode = config.getInt("jumpMode");
- jumpCondition = config.getInt("jumpCondition");
- jumpDelay = config.getInt("jumpDelay");
-
- if (jumpMode != 0) {
- jumpHeight = config.getDouble("jumpHeight", -1);
- if (jumpHeight == -1) {
- jumpHeight = defaultJumpHeight();
- }
- logger.info("Jumping is experimental.");
- logger.info("Jump mode: " + jumpMode);
- logger.info("Jump condition: " + jumpCondition);
- logger.info("Jump height: " + jumpHeight);
- }
-
- double _attackReach = config.getDouble("attackReach");
- double _attackVerticalReach = config.getDouble("attackVerticalReach");
- attackReach = new Vector(_attackReach, _attackVerticalReach, _attackReach);
-
- worldBlacklist = config.getStringList("blacklist");
-
- for (String line : config.getStringList("effects")) {
- String[] parts = line.split(":");
-
- try {
- PotionEffectType effectType = PotionEffectType.getByName(parts[0]);
- if (effectType == null) {
- throw new IllegalArgumentException("Invalid PotionEffectType");
- }
- int amplifier = Integer.parseInt(parts[1]);
- effects.add(new PotionEffect(effectType, Integer.MAX_VALUE, amplifier));
- } catch (IllegalArgumentException e) {
- logger.warning("Parsing a potion effect failed:");
- logger.warning(line);
- logger.warning(e.getMessage());
- }
- }
-
- for (Map, ?> dropMap : config.getMapList("drops")) {
- try {
- ItemStack itemStack;
- if (dropMap.containsKey("itemStack")) {
- itemStack = (ItemStack) dropMap.get("itemStack");
- } else {
- Material material = Material.getMaterial((String) dropMap.get("material"));
- if (material == null) {
- throw new IllegalArgumentException("Invalid Material");
- }
- itemStack = new ItemStack(material, 1);
- }
-
- int min = (int) dropMap.get("quantityMin");
- int max = (int) dropMap.get("quantityMax");
- double chance;
- try {
- chance = (double) dropMap.get("chance");
- } catch (ClassCastException e) { // pointlessest error ever
- chance = ((Integer) dropMap.get("chance")).doubleValue();
- }
-
- drops.add(new Drop(itemStack, min, max, chance));
- } catch (IllegalArgumentException e) {
- logger.warning("Parsing a drop failed:");
- logger.warning(e.getMessage());
- }
- }
- }
-
- public double defaultJumpHeight() {
- switch (jumpMode) {
- case 1:
- case 2:
- return 0.42;
- case 3:
- case 4:
- return 1.2;
- }
-
- return -1;
- }
-}
diff --git a/src/main/java/eu/m724/giants/Drop.java b/src/main/java/eu/m724/giants/Drop.java
index d5884ca..ad51803 100644
--- a/src/main/java/eu/m724/giants/Drop.java
+++ b/src/main/java/eu/m724/giants/Drop.java
@@ -1,25 +1,34 @@
package eu.m724.giants;
+import org.bukkit.Location;
import org.bukkit.inventory.ItemStack;
import java.util.concurrent.ThreadLocalRandom;
-public class Drop {
- public final ItemStack itemStack;
- public final int min, max;
- public final double chance;
-
- public Drop(ItemStack itemStack, int min, int max, double chance) {
- this.itemStack = itemStack;
- this.min = min;
- this.max = max;
- this.chance = chance;
- }
-
- public ItemStack generateItemStack() {
+public record Drop(ItemStack itemStack, int min, int max, double chance) {
+ /**
+ * Randomizes quantity and returns {@link ItemStack}.
+ * This should be called every drop.
+ *
+ * @return A {@link ItemStack} with randomized quantity
+ */
+ private ItemStack generate() {
int amount = ThreadLocalRandom.current().nextInt(min, max + 1);
+
ItemStack itemStack = this.itemStack.clone();
itemStack.setAmount(amount);
return itemStack;
}
+
+ /**
+ * Drops the item at {@code location} taking into account quantity and chance.
+ *
+ * @param location The location to drop the drop at
+ */
+ public void dropAt(Location location) {
+ if (chance > ThreadLocalRandom.current().nextDouble()) {
+ ItemStack itemStack = generate();
+ location.getWorld().dropItemNaturally(location, itemStack);
+ }
+ }
}
diff --git a/src/main/java/eu/m724/giants/GiantProcessor.java b/src/main/java/eu/m724/giants/GiantProcessor.java
deleted file mode 100644
index 7aff656..0000000
--- a/src/main/java/eu/m724/giants/GiantProcessor.java
+++ /dev/null
@@ -1,234 +0,0 @@
-package eu.m724.giants;
-
-import org.bukkit.Location;
-import org.bukkit.Material;
-import org.bukkit.NamespacedKey;
-import org.bukkit.entity.*;
-import org.bukkit.event.EventHandler;
-import org.bukkit.event.Listener;
-import org.bukkit.event.entity.EntityDeathEvent;
-import org.bukkit.event.entity.EntitySpawnEvent;
-import org.bukkit.event.entity.EntityTargetLivingEntityEvent;
-import org.bukkit.event.world.ChunkLoadEvent;
-import org.bukkit.inventory.ItemStack;
-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.Logger;
-
-// TODO move ai stuff to another class
-/**
- * A processor class that processes giants
- */
-public class GiantProcessor implements Listener {
- private static final int VERSION = 1;
-
- private final JavaPlugin plugin;
- private 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<>();
-
- private final ThreadLocalRandom random = ThreadLocalRandom.current();
- private final NamespacedKey huskKey;
-
- public GiantProcessor(JavaPlugin plugin, Configuration configuration) {
- this.plugin = plugin;
- this.configuration = configuration;
- this.logger = Logger.getLogger(plugin.getLogger().getName() + ".GiantProcessor");
- 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);
- }
-
- 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);
- //trackedGiants.add((Giant) entity);
-
- logger.fine("Spawned a Giant at " + pos);
-
- return entity;
- }
-
- @EventHandler
- public void onChunkLoad(ChunkLoadEvent event) {
- Entity[] entities = event.getChunk().getEntities();
- logger.fine("Chunk loaded: " + event.getChunk().getX() + " " + event.getChunk().getZ());
-
- Husk[] husks = Arrays.stream(entities)
- .filter(entity -> entity instanceof Husk && entity.getPersistentDataContainer().has(huskKey, PersistentDataType.INTEGER))
- .map(Husk.class::cast)
- .toArray(Husk[]::new);
-
- for (Husk husk : husks) {
- logger.fine("Husk found at " + husk.getLocation());
-
- Entity giant = husk.getVehicle();
-
- if (giant instanceof Giant) {
- trackedHusks.add(husk);
- //trackedGiants.add((Giant) giant);
-
- logger.fine("Tracking a loaded Giant at " + giant.getLocation());
- } else {
- // kill stray husks, that is those without a giant
- husk.setHealth(0);
-
- logger.fine("Stray Husk killed at " + husk.getLocation());
- }
- }
- }
-
- @EventHandler
- public void entitySpawn(EntitySpawnEvent e) {
- if (configuration.worldBlacklist.contains(e.getLocation().getWorld().getName()))
- return;
-
- if (e.getEntityType() == EntityType.ZOMBIE) {
- if (configuration.chance > random.nextDouble()) {
- e.setCancelled(true);
- spawnGiant(e.getLocation());
-
- logger.fine("Spawned a Giant by chance at " + e.getLocation());
- }
- }
- }
-
- @EventHandler
- public void entityDeath(EntityDeathEvent e) {
- LivingEntity entity = e.getEntity();
-
- if (entity.getType() == EntityType.GIANT) {
- Location location = entity.getLocation();
- logger.fine("A Giant died at " + location);
-
- for (Drop drop : configuration.drops) {
- logger.fine("Rolling a drop");
-
- if (drop.chance > random.nextDouble()) {
- ItemStack is = drop.generateItemStack();
- entity.getWorld().dropItemNaturally(location, is);
-
- logger.fine("Dropped " + is);
- }
- }
-
- for (Entity passenger : entity.getPassengers()) {
- if (passenger.getPersistentDataContainer().has(huskKey, PersistentDataType.INTEGER)) {
- ((LivingEntity) passenger).setHealth(0);
- logger.fine("Killed a Husk");
- }
- }
- }
- }
-}
diff --git a/src/main/java/eu/m724/giants/GiantsCommand.java b/src/main/java/eu/m724/giants/GiantsCommand.java
index 14366c0..d7bfa35 100644
--- a/src/main/java/eu/m724/giants/GiantsCommand.java
+++ b/src/main/java/eu/m724/giants/GiantsCommand.java
@@ -1,6 +1,8 @@
package eu.m724.giants;
import eu.m724.giants.updater.UpdateCommand;
+import net.md_5.bungee.api.ChatColor;
+import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
@@ -19,126 +21,89 @@ import java.util.Map;
public class GiantsCommand implements CommandExecutor {
private final GiantsPlugin plugin;
- private final Configuration configuration;
private final UpdateCommand updateCommand;
- public GiantsCommand(GiantsPlugin plugin, Configuration configuration, UpdateCommand updateCommand) {
+ public GiantsCommand(GiantsPlugin plugin, UpdateCommand updateCommand) {
this.plugin = plugin;
- this.configuration = configuration;
this.updateCommand = updateCommand;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length == 0) {
- sender.sendMessage("Giants " + plugin.getDescription().getVersion());
+ sender.sendMessage(ChatColor.GRAY + "Giants " + plugin.getDescription().getVersion());
return true;
}
String action = args[0];
if (!sender.hasPermission("giants.command." + action)) {
- sender.sendMessage("You don't have permission to use this command, or it doesn't exist.");
+ sender.sendMessage(ChatColor.GRAY + "You don't have permission to use this command, or it doesn't exist.");
return true;
}
- Player player = sender instanceof Player ? (Player) sender : null;
-
- if (action.equals("spawn")) {
- if (player != null) {
- plugin.spawnGiant(player.getLocation());
- sender.sendMessage("Spawned a Giant");
- } else {
- sender.sendMessage("Only players can use this command.");
- }
- } else if (action.equals("serialize")) {
- if (player != null) {
- ItemStack itemStack = player.getInventory().getItemInMainHand();
-
- List