From 5dd1305283baadc2a116bb17d1cfecf50863f237 Mon Sep 17 00:00:00 2001 From: pxav Date: Sun, 28 Feb 2021 12:00:42 +0100 Subject: [PATCH 01/81] Add methods to get blocks and chunks from axis value directly without having to create a location object --- .../de/pxav/kelp/core/world/KelpWorld.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/KelpWorld.java b/core/src/main/java/de/pxav/kelp/core/world/KelpWorld.java index 5d9b4870..2cc99284 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/KelpWorld.java +++ b/core/src/main/java/de/pxav/kelp/core/world/KelpWorld.java @@ -87,6 +87,20 @@ public KelpBlock getBlockAt(KelpLocation location) { return versionTemplate.getBlockAt(this, location); } + /** + * Gets the {@link KelpBlock} at the given location of the world. + * + * @param x The x-coordinate of the location you want to get the block at. + * @param y The y-coordinate of the location you want to get the block at. + * @param z The z-coordinate of the location you want to get the block at. + * @return The {@link KelpBlock} object at the given location. + * If the block's material is {@link de.pxav.kelp.core.inventory.material.KelpMaterial#AIR}, + * the block won't be {@code null} but of type {@code AIR} + */ + public KelpBlock getBlockAt(double x, double y, double z) { + return versionTemplate.getBlockAt(this, KelpLocation.from(getName(), x, y, z)); + } + /** * Gets the chunk at the given location in this world. * A chunk is a 16x16x256 (320 if you are on 1.17+) @@ -100,6 +114,19 @@ public KelpChunk getChunkAt(KelpLocation location) { return versionTemplate.getChunkAt(this, location); } + /** + * Gets the chunk at the given location in this world. + * More information can be found in {@link KelpChunk}. + * + * @param x The x-coordinate of the location to get the chunk at. + * @param y The y-coordinate of the location to get the chunk at. + * @param z The z-coordinate of the location to get the chunk at. + * @return The chunk at the given location. + */ + public KelpChunk getChunkAt(double x, double y, double z) { + return versionTemplate.getChunkAt(this, KelpLocation.from(getName(), x, y, z)); + } + /** * Gets the name of this world in the bukkit world registration. * From a375fe1ee6aea9feaa7aa563e2fcfa3d165becd1 Mon Sep 17 00:00:00 2001 From: pxav Date: Tue, 2 Mar 2021 20:05:26 +0100 Subject: [PATCH 02/81] Add custom implementation of BlockFace --- .../kelp/core/world/util/KelpBlockFace.java | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 core/src/main/java/de/pxav/kelp/core/world/util/KelpBlockFace.java diff --git a/core/src/main/java/de/pxav/kelp/core/world/util/KelpBlockFace.java b/core/src/main/java/de/pxav/kelp/core/world/util/KelpBlockFace.java new file mode 100644 index 00000000..eedabdc9 --- /dev/null +++ b/core/src/main/java/de/pxav/kelp/core/world/util/KelpBlockFace.java @@ -0,0 +1,116 @@ +package de.pxav.kelp.core.world.util; + +import org.bukkit.block.BlockFace; +import org.bukkit.util.Vector; + +public enum KelpBlockFace { + + NORTH(0, 0, -1), + EAST(1, 0, 0), + SOUTH(0, 0, 1), + WEST(-1, 0, 0), + UP(0, 1, 0), + DOWN(0, -1, 0), + NORTH_EAST(NORTH, EAST), + NORTH_WEST(NORTH, WEST), + SOUTH_EAST(SOUTH, EAST), + SOUTH_WEST(SOUTH, WEST), + WEST_NORTH_WEST(WEST, NORTH_WEST), + NORTH_NORTH_WEST(NORTH, NORTH_WEST), + NORTH_NORTH_EAST(NORTH, NORTH_EAST), + EAST_NORTH_EAST(EAST, NORTH_EAST), + EAST_SOUTH_EAST(EAST, SOUTH_EAST), + SOUTH_SOUTH_EAST(SOUTH, SOUTH_EAST), + SOUTH_SOUTH_WEST(SOUTH, SOUTH_WEST), + WEST_SOUTH_WEST(WEST, SOUTH_WEST), + SELF(0, 0, 0); + + private final int deltaX; + private final int deltaY; + private final int deltaZ; + + public static KelpBlockFace from(BlockFace blockFace) { + return KelpBlockFace.valueOf(blockFace.name()); + } + + KelpBlockFace(int deltaX, int deltaY, int deltaZ) { + this.deltaX = deltaX; + this.deltaY = deltaY; + this.deltaZ = deltaZ; + } + + KelpBlockFace(KelpBlockFace face1, KelpBlockFace face2) { + this.deltaX = face1.getDeltaX() + face2.getDeltaX(); + this.deltaY = face1.getDeltaY() + face2.getDeltaY(); + this.deltaZ = face1.getDeltaZ() + face2.getDeltaZ(); + } + + public int getDeltaX() { + return this.deltaX; + } + + public int getDeltaY() { + return this.deltaY; + } + + public int getDeltaZ() { + return this.deltaZ; + } + + public Vector getDirection() { + Vector direction = new Vector(this.deltaX, this.deltaY, this.deltaZ); + if (this.deltaX != 0 || this.deltaY != 0 || this.deltaZ != 0) { + direction.normalize(); + } + + return direction; + } + + public KelpBlockFace getOppositeFace() { + switch(this) { + case NORTH: + return SOUTH; + case EAST: + return WEST; + case SOUTH: + return NORTH; + case WEST: + return EAST; + case UP: + return DOWN; + case DOWN: + return UP; + case NORTH_EAST: + return SOUTH_WEST; + case NORTH_WEST: + return SOUTH_EAST; + case SOUTH_EAST: + return NORTH_WEST; + case SOUTH_WEST: + return NORTH_EAST; + case WEST_NORTH_WEST: + return EAST_SOUTH_EAST; + case NORTH_NORTH_WEST: + return SOUTH_SOUTH_EAST; + case NORTH_NORTH_EAST: + return SOUTH_SOUTH_WEST; + case EAST_NORTH_EAST: + return WEST_SOUTH_WEST; + case EAST_SOUTH_EAST: + return WEST_NORTH_WEST; + case SOUTH_SOUTH_EAST: + return NORTH_NORTH_WEST; + case SOUTH_SOUTH_WEST: + return NORTH_NORTH_EAST; + case WEST_SOUTH_WEST: + return EAST_NORTH_EAST; + default: + return SELF; + } + } + + public BlockFace getBukkitFace() { + return BlockFace.valueOf(this.toString()); + } + +} From 5dc7924ba3834c0dae6ae89a7523a5597ec83f08 Mon Sep 17 00:00:00 2001 From: pxav Date: Tue, 2 Mar 2021 20:06:01 +0100 Subject: [PATCH 03/81] Implement new BlockFace class in KelpBlock --- .../src/main/java/de/pxav/kelp/core/world/KelpBlock.java | 9 +++++---- .../kelp/core/world/version/BlockVersionTemplate.java | 3 ++- .../kelp/implementation1_8/world/VersionedBlock.java | 3 ++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/KelpBlock.java b/core/src/main/java/de/pxav/kelp/core/world/KelpBlock.java index 2ec689d1..70140202 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/KelpBlock.java +++ b/core/src/main/java/de/pxav/kelp/core/world/KelpBlock.java @@ -3,9 +3,9 @@ import de.pxav.kelp.core.KelpPlugin; import de.pxav.kelp.core.inventory.material.KelpMaterial; import de.pxav.kelp.core.world.util.CardinalDirection; +import de.pxav.kelp.core.world.util.KelpBlockFace; import de.pxav.kelp.core.world.version.BlockVersionTemplate; import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; import org.bukkit.util.Vector; /** @@ -275,9 +275,10 @@ public void setMaterial(KelpMaterial material) { * might grow to a tree or grass might spawn random flowers * and so on. * - * This method by default applies the bone meal on the upper side of a block. */ + * This method by default applies the bone meal on the upper side of a block. + */ public void applyBoneMeal() { - versionTemplate.applyBoneMeal(this, BlockFace.UP); + versionTemplate.applyBoneMeal(this, KelpBlockFace.UP); } /** @@ -288,7 +289,7 @@ public void applyBoneMeal() { * * @param blockFace The face of the block to apply the bone meal on. */ - public void applyBoneMeal(BlockFace blockFace) { + public void applyBoneMeal(KelpBlockFace blockFace) { versionTemplate.applyBoneMeal(this, blockFace); } diff --git a/core/src/main/java/de/pxav/kelp/core/world/version/BlockVersionTemplate.java b/core/src/main/java/de/pxav/kelp/core/world/version/BlockVersionTemplate.java index acf50caf..a8b6c03c 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/version/BlockVersionTemplate.java +++ b/core/src/main/java/de/pxav/kelp/core/world/version/BlockVersionTemplate.java @@ -5,6 +5,7 @@ import de.pxav.kelp.core.world.KelpBlock; import de.pxav.kelp.core.world.KelpChunk; import de.pxav.kelp.core.world.KelpLocation; +import de.pxav.kelp.core.world.util.KelpBlockFace; import org.bukkit.block.BlockFace; /** @@ -72,6 +73,6 @@ public abstract class BlockVersionTemplate { * @param block The block you want to simulate the application of. * @param blockFace The face of the block to apply the bone meal to. */ - public abstract void applyBoneMeal(KelpBlock block, BlockFace blockFace); + public abstract void applyBoneMeal(KelpBlock block, KelpBlockFace blockFace); } diff --git a/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/world/VersionedBlock.java b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/world/VersionedBlock.java index bce35e79..13acd87d 100644 --- a/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/world/VersionedBlock.java +++ b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/world/VersionedBlock.java @@ -6,6 +6,7 @@ import de.pxav.kelp.core.inventory.material.MaterialContainer; import de.pxav.kelp.core.version.Versioned; import de.pxav.kelp.core.world.KelpLocation; +import de.pxav.kelp.core.world.util.KelpBlockFace; import de.pxav.kelp.core.world.version.BlockVersionTemplate; import de.pxav.kelp.core.world.KelpBlock; import de.pxav.kelp.core.world.KelpChunk; @@ -118,7 +119,7 @@ public boolean canApplyBoneMeal(KelpBlock block) { * @param blockFace The face of the block to apply the bone meal to. */ @Override - public void applyBoneMeal(KelpBlock kBlock, BlockFace blockFace) { + public void applyBoneMeal(KelpBlock kBlock, KelpBlockFace blockFace) { Block block = kBlock.getBukkitBlock(); // cause those plant types to grow by 2 to 5 stages. From b7fbef72f8d68bc9ac03606e2c7fb555a46b3db1 Mon Sep 17 00:00:00 2001 From: pxav Date: Tue, 2 Mar 2021 20:06:43 +0100 Subject: [PATCH 04/81] Add abstract class representing any region of blocks in a world --- .../kelp/core/world/region/KelpRegion.java | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java new file mode 100644 index 00000000..7447d97b --- /dev/null +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java @@ -0,0 +1,124 @@ +package de.pxav.kelp.core.world.region; + +import de.pxav.kelp.core.player.KelpPlayer; +import de.pxav.kelp.core.world.KelpBlock; +import de.pxav.kelp.core.world.KelpChunk; +import de.pxav.kelp.core.world.KelpLocation; +import de.pxav.kelp.core.world.KelpWorld; +import de.pxav.kelp.core.world.util.KelpBlockFace; +import org.bukkit.util.Vector; + +import java.util.Set; + +public abstract class KelpRegion implements Cloneable { + + protected String worldName; + protected KelpLocation minPos; + protected KelpLocation maxPos; + + public abstract void move(Vector vector); + + public abstract void move(double dx, double dy, double dz); + + public abstract double getVolume(); + + public abstract int getBlockVolume(); + + public abstract KelpLocation getCenter(); + + public abstract boolean intersectsWith(KelpRegion region); + + public abstract Set getIntersectionWith(KelpRegion region); + + public abstract Set getSurfaceBlocks(); + + public abstract Set getBlocks(); + + public abstract Set getChunks(); + + public abstract Set getLoadedChunks(); + + public abstract void expand(double amount); + + public abstract void expand(KelpBlockFace direction, double amount); + + public abstract void expand(double negativeX, + double positiveX, + double negativeY, + double positiveY, + double negativeZ, + double positiveZ); + + public abstract boolean contains(KelpLocation location); + + public boolean contains(KelpPlayer player) { + return contains(player.getLocation()); + } + + public boolean contains(KelpBlock block) { + return contains(block.getLocation()); + } + + public boolean contains(double x, double y, double z) { + if (worldName == null) { + return false; + } + return contains(KelpLocation.from(worldName, x, y, z)); + } + + public int[] getBlockDimensions() { + return new int[] { + maxPos.getBlockX() - minPos.getBlockX(), + maxPos.getBlockY() - minPos.getBlockY(), + maxPos.getBlockZ() - minPos.getBlockZ() + }; + } + + public double[] getDimensions() { + return new double[] { + maxPos.getX() - minPos.getX(), + maxPos.getY() - minPos.getY(), + maxPos.getZ() - minPos.getZ() + }; + } + + public KelpLocation[] getOuterCorners() { + return new KelpLocation[] { + minPos, + maxPos, + KelpLocation.from(getWorld().getName(), minPos.getX(), minPos.getY(), maxPos.getZ()), + KelpLocation.from(getWorld().getName(), minPos.getX(), maxPos.getY(), minPos.getZ()), + KelpLocation.from(getWorld().getName(), maxPos.getX(), minPos.getY(), minPos.getZ()), + KelpLocation.from(getWorld().getName(), minPos.getX(), maxPos.getY(), maxPos.getZ()), + KelpLocation.from(getWorld().getName(), maxPos.getX(), maxPos.getY(), minPos.getZ()), + KelpLocation.from(getWorld().getName(), maxPos.getX(), minPos.getY(), maxPos.getZ()) + }; + } + + public String getWorldName() { + return worldName; + } + + public void setWorldName(String worldName) { + this.worldName = worldName; + } + + public KelpWorld getWorld() { + if (this.worldName == null) { + return null; + } + return KelpWorld.from(worldName); + } + + public KelpLocation getMinPos() { + return minPos; + } + + public KelpLocation getMaxPos() { + return maxPos; + } + + @Override + public abstract KelpRegion clone(); + +} From bffc677afa90a344026bb86a79953e0028a562e3 Mon Sep 17 00:00:00 2001 From: pxav Date: Tue, 2 Mar 2021 20:07:00 +0100 Subject: [PATCH 05/81] Add cuboid implementation of KelpRegion --- .../kelp/core/world/region/CuboidRegion.java | 236 ++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java new file mode 100644 index 00000000..e73fe477 --- /dev/null +++ b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java @@ -0,0 +1,236 @@ +package de.pxav.kelp.core.world.region; + +import com.google.common.collect.Sets; +import de.pxav.kelp.core.world.KelpBlock; +import de.pxav.kelp.core.world.KelpChunk; +import de.pxav.kelp.core.world.KelpLocation; +import de.pxav.kelp.core.world.util.KelpBlockFace; +import org.bukkit.util.Vector; + +import java.util.Set; + +public class CuboidRegion extends KelpRegion { + + public static CuboidRegion create(KelpLocation pos1, KelpLocation pos2) { + CuboidRegion region = new CuboidRegion(); + region.setBoundingPositions(pos1, pos2); + region.setWorldName(pos1.getWorldName()); + return region; + } + + public static CuboidRegion create() { + return new CuboidRegion(); + } + + @Override + public void move(Vector vector) { + this.minPos.add(vector); + this.maxPos.add(vector); + } + + @Override + public void move(double dx, double dy, double dz) { + move(new Vector(dx, dy, dz)); + } + + @Override + public double getVolume() { + double[] dimensions = getDimensions(); + return dimensions[0] * dimensions[1] * dimensions[2]; + } + + @Override + public int getBlockVolume() { + int[] blockDimensions = getBlockDimensions(); + return blockDimensions[0] * blockDimensions[1] * blockDimensions[2]; + } + + @Override + public boolean contains(KelpLocation location) { + // X + double maxX = Math.max(this.minPos.getX(), this.maxPos.getX()); + double minX = Math.min(this.minPos.getX(), this.maxPos.getX()); + + // Y + double maxY = Math.max(this.minPos.getY(), this.maxPos.getY()); + double minY = Math.min(this.minPos.getY(), this.maxPos.getY()); + + // Z + double maxZ = Math.max(this.minPos.getZ(), this.maxPos.getZ()); + double minZ = Math.min(this.minPos.getZ(), this.maxPos.getZ()); + + if(location.getX() <= maxX && location.getX() >= minX) { + if(location.getY() <= maxY && location.getY() >= minY) { + return location.getZ() <= maxZ && location.getZ() >= minZ; + } + } + return false; + } + + @Override + public KelpLocation getCenter() { + return this.minPos.clone().add(this.maxPos).multiply(0.5); + } + + @Override + public boolean intersectsWith(KelpRegion region) { + return false; + } + + @Override + public Set getIntersectionWith(KelpRegion region) { + return null; + } + + @Override + public Set getSurfaceBlocks() { + Set output = Sets.newHashSet(); + output.addAll(getFaceBlocks(KelpBlockFace.UP)); + output.addAll(getFaceBlocks(KelpBlockFace.DOWN)); + output.addAll(getFaceBlocks(KelpBlockFace.EAST)); + output.addAll(getFaceBlocks(KelpBlockFace.WEST)); + output.addAll(getFaceBlocks(KelpBlockFace.NORTH)); + output.addAll(getFaceBlocks(KelpBlockFace.SOUTH)); + return output; + } + + @Override + public Set getBlocks() { + Set output = Sets.newHashSet(); + for (int y = minPos.getBlockY(); y <= maxPos.getBlockY(); y++) { + for (int x = minPos.getBlockX(); x <= maxPos.getBlockX(); x++) { + for (int z = minPos.getBlockZ(); z <= maxPos.getBlockZ(); z++) { + output.add(KelpLocation.from(getWorldName(), x, y, z).getBlock()); + } + } + } + return output; + } + + public Set getFaceBlocks(KelpBlockFace direction) { + return getFace(direction).getBlocks(); + } + + public double measure(KelpBlockFace direction) { + switch (direction) { + case UP: + case DOWN: + return getDimensions()[1]; + case EAST: + case WEST: + return getDimensions()[0]; + case NORTH: + case SOUTH: + return getDimensions()[2]; + } + throw new IllegalArgumentException("Cannot measure region axis '" + direction + "'. Only use UP, DOWN, EAST, WEST, NORTH, SOUTH"); + } + + public int measureBlocks(KelpBlockFace direction) { + switch (direction) { + case UP: + case DOWN: + return getBlockDimensions()[1]; + case EAST: + case WEST: + return getBlockDimensions()[0]; + case NORTH: + case SOUTH: + return getBlockDimensions()[2]; + } + throw new IllegalArgumentException("Cannot measure region axis '" + direction + "'. Only use UP, DOWN, EAST, WEST, NORTH, SOUTH"); + } + + @Override + public Set getChunks() { + Set output = Sets.newHashSet(); + KelpChunk minChunk = getMinPos().getChunk(); + KelpChunk maxChunk = getMaxPos().getChunk(); + + for (int cx = minChunk.getX(); cx <= maxChunk.getX(); cx++) { + for (int cz = minChunk.getZ(); cz <= maxChunk.getZ(); cz++) { + output.add(getWorld().getChunkAt(cx, 0, cz)); + } + } + return output; + } + + @Override + public Set getLoadedChunks() { + Set output = Sets.newHashSet(); + KelpChunk minChunk = getMinPos().getChunk(); + KelpChunk maxChunk = getMaxPos().getChunk(); + + for (int cx = minChunk.getX(); cx <= maxChunk.getX(); cx++) { + for (int cz = minChunk.getZ(); cz <= maxChunk.getZ(); cz++) { + KelpChunk toAdd = getWorld().getChunkAt(cx, 0, cz); + if (toAdd.isLoaded()) { + output.add(toAdd); + } + } + } + return output; + } + + public CuboidRegion getFace(KelpBlockFace direction) { + System.out.println("opposite face: " + direction.getOppositeFace()); + System.out.println("expand amount: " + -measureBlocks(direction) + 1); + System.out.println("expand amount -1: " + -measureBlocks(direction)); + System.out.println("measure down: " + measureBlocks(KelpBlockFace.DOWN)); + System.out.println("measure east: " + measureBlocks(KelpBlockFace.EAST)); + System.out.println("measure north: " + measureBlocks(KelpBlockFace.NORTH)); + System.out.println("measure up: " + measureBlocks(KelpBlockFace.UP)); + System.out.println("measure south: " + measureBlocks(KelpBlockFace.SOUTH)); + CuboidRegion output = this.clone(); + output.expand(direction.getOppositeFace(), -output.measure(direction) + 1); + return output; + } + + @Override + public void expand(double amount) { + expand(amount, amount, amount, amount, amount, amount); + } + + @Override + public void expand(KelpBlockFace direction, double amount) { + Vector vector = direction.getDirection(); + if (vector.getX() + vector.getY() + vector.getZ() > 0) { + vector = vector.multiply(amount); + maxPos.add(vector); + } else { + vector = vector.multiply(amount); + minPos.add(vector); + } + setBoundingPositions(minPos, maxPos); + } + + @Override + public void expand(double negativeX, double positiveX, double negativeY, double positiveY, double negativeZ, double positiveZ) { + this.minPos.subtract(negativeX, negativeY, negativeZ); + this.maxPos.add(positiveX, positiveY, positiveZ); + setBoundingPositions(minPos, maxPos); + } + + @Override + public CuboidRegion clone() { + return CuboidRegion.create(this.minPos, this.maxPos); + } + + public void setBoundingPositions(KelpLocation pos1, KelpLocation pos2) { + if (!pos1.getWorldName().equals(pos2.getWorldName())) { + throw new IllegalArgumentException("Cannot build CuboidRegion from locations of differing worlds!"); + } + + double minX = Math.min(pos1.getX(), pos2.getX()); + double minY = Math.min(pos1.getY(), pos2.getY()); + double minZ = Math.min(pos1.getZ(), pos2.getZ()); + + double maxX = Math.max(pos1.getX(), pos2.getX()); + double maxY = Math.max(pos1.getY(), pos2.getY()); + double maxZ = Math.max(pos1.getZ(), pos2.getZ()); + + this.minPos = KelpLocation.from(pos1.getWorldName(), minX, minY, minZ); + this.maxPos = KelpLocation.from(pos1.getWorldName(), maxX, maxY, maxZ); + } + +} From aaddeefbd9327500b6210648d47600d9099c8793 Mon Sep 17 00:00:00 2001 From: pxav Date: Tue, 2 Mar 2021 20:20:33 +0100 Subject: [PATCH 06/81] Fix dimension calculations in KelpRegion --- .../de/pxav/kelp/core/world/region/KelpRegion.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java index 7447d97b..c7bd2a56 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java @@ -68,17 +68,17 @@ public boolean contains(double x, double y, double z) { public int[] getBlockDimensions() { return new int[] { - maxPos.getBlockX() - minPos.getBlockX(), - maxPos.getBlockY() - minPos.getBlockY(), - maxPos.getBlockZ() - minPos.getBlockZ() + maxPos.getBlockX() - minPos.getBlockX() + 1, + maxPos.getBlockY() - minPos.getBlockY() + 1, + maxPos.getBlockZ() - minPos.getBlockZ() + 1 }; } public double[] getDimensions() { return new double[] { - maxPos.getX() - minPos.getX(), - maxPos.getY() - minPos.getY(), - maxPos.getZ() - minPos.getZ() + maxPos.getX() - minPos.getX() + 1, + maxPos.getY() - minPos.getY() + 1, + maxPos.getZ() - minPos.getZ() + 1 }; } From e3f5dd6e3e40f99aa5f052eaadaa761cc71fb329 Mon Sep 17 00:00:00 2001 From: pxav Date: Tue, 2 Mar 2021 20:20:51 +0100 Subject: [PATCH 07/81] Fix face calculation in CuboidRegion --- .../de/pxav/kelp/core/world/region/CuboidRegion.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java index e73fe477..98035487 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java @@ -173,16 +173,8 @@ public Set getLoadedChunks() { } public CuboidRegion getFace(KelpBlockFace direction) { - System.out.println("opposite face: " + direction.getOppositeFace()); - System.out.println("expand amount: " + -measureBlocks(direction) + 1); - System.out.println("expand amount -1: " + -measureBlocks(direction)); - System.out.println("measure down: " + measureBlocks(KelpBlockFace.DOWN)); - System.out.println("measure east: " + measureBlocks(KelpBlockFace.EAST)); - System.out.println("measure north: " + measureBlocks(KelpBlockFace.NORTH)); - System.out.println("measure up: " + measureBlocks(KelpBlockFace.UP)); - System.out.println("measure south: " + measureBlocks(KelpBlockFace.SOUTH)); CuboidRegion output = this.clone(); - output.expand(direction.getOppositeFace(), -output.measure(direction) + 1); + output.expand(direction.getOppositeFace(), -output.measureBlocks(direction) + 1); return output; } From c1d5a0860c83b3e543098eb5794f1147299c032e Mon Sep 17 00:00:00 2001 From: pxav Date: Thu, 4 Mar 2021 20:00:34 +0100 Subject: [PATCH 08/81] You can now create a CuboidRegion out of a KelpRegion --- .../main/java/de/pxav/kelp/core/world/region/KelpRegion.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java index c7bd2a56..b243529d 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java @@ -118,6 +118,10 @@ public KelpLocation getMaxPos() { return maxPos; } + public CuboidRegion toCuboid() { + return CuboidRegion.create(minPos, maxPos); + } + @Override public abstract KelpRegion clone(); From 36913d59e70b44a399f17526508af5a2f1b4d866 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 5 Mar 2021 11:51:31 +0100 Subject: [PATCH 09/81] Apply fluent builder design to KelpLocation --- .../de/pxav/kelp/core/world/KelpLocation.java | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java b/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java index 47d0f749..538b7096 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java +++ b/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java @@ -111,9 +111,11 @@ public String getWorldName() { * when working with the location. This might cause lag to the server. * * @param worldName The name of the new world for this location. + * @return Instance of the current location */ - public void setWorldName(String worldName) { + public KelpLocation setWorldName(String worldName) { this.worldName = worldName; + return this; } /** @@ -129,9 +131,11 @@ public double getX() { * Sets the location's exact value on the world's X-axis. * * @param x The new value on the world's X-axis for the location. + * @return Instance of the current location */ - public void setX(double x) { + public KelpLocation setX(double x) { this.x = x; + return this; } /** @@ -147,9 +151,11 @@ public double getY() { * Sets the location's exact value on the world's Y-axis (height). * * @param y The new value on the world's Y-axis for the location. + * @return Instance of the current location */ - public void setY(double y) { + public KelpLocation setY(double y) { this.y = y; + return this; } /** @@ -165,9 +171,11 @@ public double getZ() { * Sets the location's exact value on the world's Y-axis. * * @param z The new value on the world's Y-axis for the location. + * @return Instance of the current location */ - public void setZ(double z) { + public KelpLocation setZ(double z) { this.z = z; + return this; } /** @@ -194,9 +202,11 @@ public float getYaw() { * Sets the location's yaw (rotation around the y-Axis). * * @param yaw The new yaw to set for this location. + * @return Instance of the current location */ - public void setYaw(float yaw) { + public KelpLocation setYaw(float yaw) { this.yaw = yaw; + return this; } /** @@ -218,9 +228,11 @@ public float getPitch() { * Sets the locations pitch. This is the angle in which the entity is facing up or down. * * @param pitch The new pitch for the location. + * @return Instance of the current location */ - public void setPitch(float pitch) { + public KelpLocation setPitch(float pitch) { this.pitch = pitch; + return this; } /** @@ -263,9 +275,11 @@ public int getBlockZ() { * so be careful if you still need the exact value. * * @param x The new block x-value of this location. + * @return Instance of the current location */ - public void setBlockX(double x) { + public KelpLocation setBlockX(double x) { this.x = Location.locToBlock(this.x); + return this; } /** @@ -275,9 +289,11 @@ public void setBlockX(double x) { * so be careful if you still need the exact value. * * @param y The new block y-value of this location. + * @return Instance of the current location */ - public void setBlockY(double y) { + public KelpLocation setBlockY(double y) { this.y = Location.locToBlock(this.y); + return this; } /** @@ -287,9 +303,11 @@ public void setBlockY(double y) { * so be careful if you still need the exact value. * * @param z The new block z-value of this location. + * @return Instance of the current location */ - public void setBlockZ(double z) { + public KelpLocation setBlockZ(double z) { this.z = Location.locToBlock(this.z); + return this; } /** @@ -834,6 +852,13 @@ public KelpLocation multiply(KelpLocation multiplier) { this.z *= multiplier.getZ(); return this; } +// +// public KelpLocation getMinimalLocation(KelpLocation compareTo) { +// double minX = Math.max(compareTo.getX(), getX()); +// double minY = Math.max(compareTo.getY(), getY()); +// double minZ = Math.max(compareTo.getZ(), getZ()); +// return this.clone().setX(minX); +// } /** * Zeros all axis of the location. This sets the x, y, and z From 169a61291ac2ac550172009da5f8a6fe9ea0ab7c Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 5 Mar 2021 12:29:17 +0100 Subject: [PATCH 10/81] Add methods to get the minimal/maximal location of two KelpLocations --- .../de/pxav/kelp/core/world/KelpLocation.java | 50 ++++++++++++++++--- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java b/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java index 538b7096..175de7b9 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java +++ b/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java @@ -852,13 +852,49 @@ public KelpLocation multiply(KelpLocation multiplier) { this.z *= multiplier.getZ(); return this; } -// -// public KelpLocation getMinimalLocation(KelpLocation compareTo) { -// double minX = Math.max(compareTo.getX(), getX()); -// double minY = Math.max(compareTo.getY(), getY()); -// double minZ = Math.max(compareTo.getZ(), getZ()); -// return this.clone().setX(minX); -// } + + /** + * Compares the current location with the given location + * and returns the location that is lower in the world's grid. + * When a location is 'lower' than another it means that its + * coordinate values (x, y, z) are smaller. The yaw and pitch + * value is ignored in this calculation. + * + * @param compareTo The location to compare the current location to. + * @return The location that is lower in the world grid. + */ + public KelpLocation getMinimalLocation(KelpLocation compareTo) { + double minX = Math.min(compareTo.getX(), getX()); + double minY = Math.min(compareTo.getY(), getY()); + double minZ = Math.min(compareTo.getZ(), getZ()); + + return this.clone() + .setX(minX) + .setY(minY) + .setZ(minZ); + } + + + /** + * Compares the current location with the given location + * and returns the location that is higher in the world's grid. + * When a location is 'higher' than another it means that its + * coordinate values (x, y, z) are bigger. The yaw and pitch + * value is ignored in this calculation. + * + * @param compareTo The location to compare the current location to. + * @return The location that is higher in the world grid. + */ + public KelpLocation getMaximalLocation(KelpLocation compareTo) { + double maxX = Math.max(compareTo.getX(), getX()); + double maxY = Math.max(compareTo.getY(), getY()); + double maxZ = Math.max(compareTo.getZ(), getZ()); + + return this.clone() + .setX(maxX) + .setY(maxY) + .setZ(maxZ); + } /** * Zeros all axis of the location. This sets the x, y, and z From fb5e40c0c8b9bfe4092c405173dd3228aec1af39 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 5 Mar 2021 12:29:42 +0100 Subject: [PATCH 11/81] You can now get the intersections of two Cuboid regions --- .../kelp/core/world/region/CuboidRegion.java | 24 +++++++++++++------ .../kelp/core/world/region/KelpRegion.java | 4 ---- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java index 98035487..6c7737e5 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java @@ -46,7 +46,7 @@ public int getBlockVolume() { } @Override - public boolean contains(KelpLocation location) { + public boolean contains(KelpLocation location) {//todo world check // X double maxX = Math.max(this.minPos.getX(), this.maxPos.getX()); double minX = Math.min(this.minPos.getX(), this.maxPos.getX()); @@ -72,14 +72,24 @@ public KelpLocation getCenter() { return this.minPos.clone().add(this.maxPos).multiply(0.5); } - @Override - public boolean intersectsWith(KelpRegion region) { - return false; + public boolean hasCuboidIntersection(CuboidRegion region) { + if (!region.getWorldName().equalsIgnoreCase(worldName)) { + return false; + } + + return (!(minPos.getX() > region.getMaxPos().getX() || region.getMinPos().getX() > maxPos.getX() + || minPos.getY() > region.getMaxPos().getY() || region.getMinPos().getY() > maxPos.getY() + || minPos.getZ() > region.getMaxPos().getZ() || region.getMinPos().getZ() > maxPos.getZ())); } - @Override - public Set getIntersectionWith(KelpRegion region) { - return null; + public CuboidRegion cuboidIntersection(CuboidRegion region) { + if (!hasCuboidIntersection(region)) { + return null; + } + + return CuboidRegion.create( + getMinPos().getMaximalLocation(region.getMinPos()), + getMaxPos().getMinimalLocation(region.getMaxPos())); } @Override diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java index b243529d..374baed1 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java @@ -26,10 +26,6 @@ public abstract class KelpRegion implements Cloneable { public abstract KelpLocation getCenter(); - public abstract boolean intersectsWith(KelpRegion region); - - public abstract Set getIntersectionWith(KelpRegion region); - public abstract Set getSurfaceBlocks(); public abstract Set getBlocks(); From 13e2a3a9af09066b1765b775141f5c85e675b45f Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 5 Mar 2021 12:32:11 +0100 Subject: [PATCH 12/81] Fix bug that contains check of a region returned true although the worlds differed --- .../java/de/pxav/kelp/core/world/region/CuboidRegion.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java index 6c7737e5..a97a8e99 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java @@ -46,7 +46,11 @@ public int getBlockVolume() { } @Override - public boolean contains(KelpLocation location) {//todo world check + public boolean contains(KelpLocation location) { + if (!worldName.equalsIgnoreCase(location.getWorldName())) { + return false; + } + // X double maxX = Math.max(this.minPos.getX(), this.maxPos.getX()); double minX = Math.min(this.minPos.getX(), this.maxPos.getX()); From a97b4ca3b4b83f92b0c413a1133bdc1f36939ed9 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 5 Mar 2021 16:54:44 +0100 Subject: [PATCH 13/81] Add method adding/subtracting the same value from x, y and z in KelpLocation --- .../de/pxav/kelp/core/world/KelpLocation.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java b/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java index 175de7b9..9ae2b6b1 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java +++ b/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java @@ -347,6 +347,21 @@ public KelpLocation add(double x, double y, double z) { return this; } + /** + * Adds the given value to all coordinate values of this location. + * This means that all axis {@code x, y and z} will grow + * by {@code value}. + * + * @param value The value to add to all location coordinates. + * @return The current location object with the added values. + */ + public KelpLocation add(double value) { + this.x += value; + this.y += value; + this.z += value; + return this; + } + /** * Only adds the x-coordinate of this location. * Other coordinates are not affected by this method. @@ -414,6 +429,20 @@ public KelpLocation subtract(double x, double y, double z) { return this; } + /** + * Subtracts the given value from all the locations coordinates. + * So all axis {@code x, y and z} will be smaller by {@code value}. + * + * @param value The value to subtract from all coordinate values. + * @return The current location object with the subtracted values. + */ + public KelpLocation subtract(double value) { + this.x -= value; + this.y -= value; + this.z -= value; + return this; + } + /** * Only subtracts the x-coordinate of this location. * Other values are not affected by this method. From 91d3f2afe4d11ed2af21eca5a890677c40a0dcd9 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 5 Mar 2021 16:55:17 +0100 Subject: [PATCH 14/81] Add new implementation of KelpRegion: EllipsoidRegion --- .../core/world/region/EllipsoidRegion.java | 168 ++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java new file mode 100644 index 00000000..028866e2 --- /dev/null +++ b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java @@ -0,0 +1,168 @@ +package de.pxav.kelp.core.world.region; + +import com.google.common.collect.Sets; +import de.pxav.kelp.core.world.KelpBlock; +import de.pxav.kelp.core.world.KelpChunk; +import de.pxav.kelp.core.world.KelpLocation; +import de.pxav.kelp.core.world.util.KelpBlockFace; +import org.bukkit.util.Vector; + +import java.util.Set; + +public class EllipsoidRegion extends KelpRegion { + + private KelpLocation center; + private double xRadius; + private double yRadius; + private double zRadius; + + public static EllipsoidRegion create(KelpLocation center, double radius) { + EllipsoidRegion region = new EllipsoidRegion(); + region.setCenter(center); + region.setRadius(radius); + region.minPos = center.clone().subtract(radius); + region.maxPos = center.clone().add(radius); + return region; + } + + public static EllipsoidRegion create(KelpLocation center, double xRadius, double yRadius, double zRadius) { + EllipsoidRegion region = new EllipsoidRegion(); + region.setCenter(center); + region.setXRadius(xRadius); + region.setYRadius(yRadius); + region.setZRadius(zRadius); + region.minPos = center.clone().subtract(xRadius, yRadius, zRadius); + region.maxPos = center.clone().add(xRadius, yRadius, zRadius); + return region; + } + + @Override + public void move(Vector vector) { + + } + + @Override + public void move(double dx, double dy, double dz) { + + } + + @Override + public double getVolume() { + return 0; + } + + @Override + public int getBlockVolume() { + return 0; + } + + public EllipsoidRegion setCenter(KelpLocation center) { + this.center = center; + return this; + } + + @Override + public KelpLocation getCenter() { + return this.center; + } + + @Override + public Set getSurfaceBlocks() { + return null; + } + + @Override + public Set getBlocks() { + Set output = Sets.newConcurrentHashSet(); + this.clone().toCuboid() + .getBlocks() + .parallelStream() + .filter(this::contains) + .forEach(output::add); + return output; + } + + @Override + public Set getChunks() { + return null; + } + + @Override + public Set getLoadedChunks() { + return null; + } + + @Override + public void expand(double amount) { + + } + + @Override + public void expand(KelpBlockFace direction, double amount) { + + } + + @Override + public void expand(double negativeX, double positiveX, double negativeY, double positiveY, double negativeZ, double positiveZ) { + + } + + @Override + public boolean contains(KelpLocation location) { + double length = ((location.getX() - center.getX()) / xRadius) * ((location.getX() - center.getX()) / xRadius) + + ((location.getY() - center.getY()) / yRadius) * ((location.getY() - center.getY()) / yRadius) + + ((location.getZ() - center.getZ()) / zRadius) * ((location.getZ() - center.getZ()) / zRadius); + return length <= 1; + } + + public boolean isSphere() { + return xRadius == yRadius && xRadius == zRadius; + } + + public boolean isSpheroid() { + return xRadius == yRadius || xRadius == zRadius || yRadius == zRadius; + } + + public EllipsoidRegion setRadius(double radius) { + this.xRadius = radius; + this.yRadius = radius; + this.zRadius = radius; + return this; + } + + public EllipsoidRegion setXRadius(double xRadius) { + this.xRadius = xRadius; + return this; + } + + public EllipsoidRegion setYRadius(double yRadius) { + this.yRadius = yRadius; + return this; + } + + public EllipsoidRegion setZRadius(double zRadius) { + this.zRadius = zRadius; + return this; + } + + public double getXRadius() { + return xRadius; + } + + public double getYRadius() { + return yRadius; + } + + public double getZRadius() { + return zRadius; + } + + public String getWorldName() { + return center.getWorldName(); + } + + @Override + public KelpRegion clone() { + return EllipsoidRegion.create(this.center, xRadius, yRadius, zRadius); + } +} From 90553d25404ddf47c21b09d0e2013a91dd192790 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 5 Mar 2021 16:55:38 +0100 Subject: [PATCH 15/81] CuboidRegion is now using concurrent sets --- .../java/de/pxav/kelp/core/world/region/CuboidRegion.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java index a97a8e99..cd7a9ec9 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java @@ -98,7 +98,7 @@ public CuboidRegion cuboidIntersection(CuboidRegion region) { @Override public Set getSurfaceBlocks() { - Set output = Sets.newHashSet(); + Set output = Sets.newConcurrentHashSet(); output.addAll(getFaceBlocks(KelpBlockFace.UP)); output.addAll(getFaceBlocks(KelpBlockFace.DOWN)); output.addAll(getFaceBlocks(KelpBlockFace.EAST)); @@ -110,7 +110,7 @@ public Set getSurfaceBlocks() { @Override public Set getBlocks() { - Set output = Sets.newHashSet(); + Set output = Sets.newConcurrentHashSet(); for (int y = minPos.getBlockY(); y <= maxPos.getBlockY(); y++) { for (int x = minPos.getBlockX(); x <= maxPos.getBlockX(); x++) { for (int z = minPos.getBlockZ(); z <= maxPos.getBlockZ(); z++) { From c30cdc39dbf349dffb4ffed73b9fcc764a9a3602 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 5 Mar 2021 17:07:34 +0100 Subject: [PATCH 16/81] You can now get the midpoint of two KelpLocations --- .../main/java/de/pxav/kelp/core/world/KelpLocation.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java b/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java index 9ae2b6b1..ead5f8a9 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java +++ b/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java @@ -651,6 +651,10 @@ public CardinalDirection getCardinalDirection() { } } + public KelpLocation findMidpoint(KelpLocation to) { + return getMinimalLocation(to).add(getMaximalLocation(to)).multiply(0.5); + } + /** * Sets the yaw and pitch value of this location based on any * vector. The length of the vector is ignored for this operation. @@ -889,6 +893,8 @@ public KelpLocation multiply(KelpLocation multiplier) { * coordinate values (x, y, z) are smaller. The yaw and pitch * value is ignored in this calculation. * + * This method will automatically clone the source location. + * * @param compareTo The location to compare the current location to. * @return The location that is lower in the world grid. */ @@ -911,6 +917,8 @@ public KelpLocation getMinimalLocation(KelpLocation compareTo) { * coordinate values (x, y, z) are bigger. The yaw and pitch * value is ignored in this calculation. * + * This method will automatically clone the source location. + * * @param compareTo The location to compare the current location to. * @return The location that is higher in the world grid. */ From bae4e6987e14ca5a794491784cce048b78dd97de Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 5 Mar 2021 17:08:05 +0100 Subject: [PATCH 17/81] Add more factory methods to EllipsoidRegion --- .../kelp/core/world/region/EllipsoidRegion.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java index 028866e2..ceefaffe 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java @@ -36,6 +36,18 @@ public static EllipsoidRegion create(KelpLocation center, double xRadius, double return region; } + public static EllipsoidRegion create(KelpLocation pos1, KelpLocation pos2) { + EllipsoidRegion region = new EllipsoidRegion(); + region.maxPos = pos1.getMaximalLocation(pos2); + region.minPos = pos1.getMinimalLocation(pos2); + + region.setCenter(pos1.findMidpoint(pos2)); + region.setXRadius(Math.abs(region.minPos.getX() - region.maxPos.getX()) * 0.5); + region.setYRadius(Math.abs(region.minPos.getY() - region.maxPos.getY()) * 0.5); + region.setZRadius(Math.abs(region.minPos.getZ() - region.maxPos.getZ()) * 0.5); + return region; + } + @Override public void move(Vector vector) { @@ -84,12 +96,12 @@ public Set getBlocks() { @Override public Set getChunks() { - return null; + return toCuboid().getChunks(); } @Override public Set getLoadedChunks() { - return null; + return toCuboid().getLoadedChunks(); } @Override From e5fafd29696567968553d9ceb3cafa6ea7c14d18 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 5 Mar 2021 17:08:25 +0100 Subject: [PATCH 18/81] You can now convert any KelpRegion to an EllipsoidRegion --- .../main/java/de/pxav/kelp/core/world/region/KelpRegion.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java index 374baed1..8c47a394 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java @@ -118,6 +118,10 @@ public CuboidRegion toCuboid() { return CuboidRegion.create(minPos, maxPos); } + public EllipsoidRegion toEllipsoid() { + return EllipsoidRegion.create(minPos, maxPos); + } + @Override public abstract KelpRegion clone(); From 7189ff4bb22341540c3d39d7a262652a1466ae2f Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 5 Mar 2021 18:07:45 +0100 Subject: [PATCH 19/81] You can now get the surface of an EllipsoidRegion --- .../core/world/region/EllipsoidRegion.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java index ceefaffe..b7c4884b 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java @@ -5,6 +5,7 @@ import de.pxav.kelp.core.world.KelpChunk; import de.pxav.kelp.core.world.KelpLocation; import de.pxav.kelp.core.world.util.KelpBlockFace; +import org.bukkit.util.NumberConversions; import org.bukkit.util.Vector; import java.util.Set; @@ -60,12 +61,12 @@ public void move(double dx, double dy, double dz) { @Override public double getVolume() { - return 0; + return (4d / 3d) * Math.PI * xRadius * yRadius * zRadius; } @Override public int getBlockVolume() { - return 0; + return NumberConversions.floor(getVolume()); } public EllipsoidRegion setCenter(KelpLocation center) { @@ -80,7 +81,14 @@ public KelpLocation getCenter() { @Override public Set getSurfaceBlocks() { - return null; + Set output = Sets.newConcurrentHashSet(); + this.clone().toCuboid() + .getBlocks() + .parallelStream() + .filter(this::contains) + .filter(b -> getCostAt(b.getLocation()) > 0.55) + .forEach(output::add); + return output; } @Override @@ -121,10 +129,13 @@ public void expand(double negativeX, double positiveX, double negativeY, double @Override public boolean contains(KelpLocation location) { - double length = ((location.getX() - center.getX()) / xRadius) * ((location.getX() - center.getX()) / xRadius) + return getCostAt(location) <= 1; + } + + public double getCostAt(KelpLocation location) { + return ((location.getX() - center.getX()) / xRadius) * ((location.getX() - center.getX()) / xRadius) + ((location.getY() - center.getY()) / yRadius) * ((location.getY() - center.getY()) / yRadius) + ((location.getZ() - center.getZ()) / zRadius) * ((location.getZ() - center.getZ()) / zRadius); - return length <= 1; } public boolean isSphere() { From 7adca78fb3bb738169f88bdc13c8eb7f03055f3e Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 5 Mar 2021 18:22:39 +0100 Subject: [PATCH 20/81] You can now expand an EllipsoidRegion --- .../core/world/region/EllipsoidRegion.java | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java index b7c4884b..6ec211d0 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java @@ -51,12 +51,12 @@ public static EllipsoidRegion create(KelpLocation pos1, KelpLocation pos2) { @Override public void move(Vector vector) { - + center.add(vector); } @Override public void move(double dx, double dy, double dz) { - + center.add(new Vector(dx, dy, dz)); } @Override @@ -114,17 +114,36 @@ public Set getLoadedChunks() { @Override public void expand(double amount) { - + xRadius += amount; + yRadius += amount; + zRadius += amount; } @Override public void expand(KelpBlockFace direction, double amount) { - + switch (direction) { + case UP: + case DOWN: + yRadius += amount / 2; + break; + case EAST: + case WEST: + xRadius += amount / 2; + break; + case NORTH: + case SOUTH: + zRadius += amount / 2; + break; + default: + throw new IllegalArgumentException("Error when expanding EllipsoidRegion: BlockFace must be one of UP, DOWN, NORTH, SOUTH, EAST, WEST"); + } } @Override public void expand(double negativeX, double positiveX, double negativeY, double positiveY, double negativeZ, double positiveZ) { - + this.xRadius = positiveX + negativeX; + this.yRadius = positiveY + negativeY; + this.zRadius = positiveZ + negativeZ; } @Override From 62c3f377dc2636bc6cf0c8d4ef9357b4c741690a Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 5 Mar 2021 19:01:46 +0100 Subject: [PATCH 21/81] A location's magnitude can now be queried from simply the coordinates x, y, z --- core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java b/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java index ead5f8a9..16c7e608 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java +++ b/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java @@ -95,6 +95,10 @@ public static KelpLocation create() { return new KelpLocation(); } + public static double magnitude(double x, double y, double z) { + return (x * x) + (y * y) + (z * z); + } + /** * Gets the name of the world this location is valid for. * From 96f1b29206d9ffc4b27650a7d400c92502366088 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 5 Mar 2021 19:02:28 +0100 Subject: [PATCH 22/81] Fix bug that surface blocks of an EllipsoidRegion contained multiple layers when the ellipsoid was bigger --- .../core/world/region/EllipsoidRegion.java | 73 +++++++++++++++---- 1 file changed, 60 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java index 6ec211d0..74fb688a 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java @@ -81,24 +81,71 @@ public KelpLocation getCenter() { @Override public Set getSurfaceBlocks() { - Set output = Sets.newConcurrentHashSet(); - this.clone().toCuboid() - .getBlocks() - .parallelStream() - .filter(this::contains) - .filter(b -> getCostAt(b.getLocation()) > 0.55) - .forEach(output::add); - return output; + return getBlocks(true); } @Override public Set getBlocks() { + return getBlocks(false); + } + + private Set getBlocks(boolean surface) { Set output = Sets.newConcurrentHashSet(); - this.clone().toCuboid() - .getBlocks() - .parallelStream() - .filter(this::contains) - .forEach(output::add); + double rX = xRadius + 0.5; + double rY = yRadius + 0.5; + double rZ = zRadius + 0.5; + + final double invRadiusX = 1 / rX; + final double invRadiusY = 1 / rY; + final double invRadiusZ = 1 / rZ; + + final int ceilRadiusX = (int) Math.ceil(rX); + final int ceilRadiusY = (int) Math.ceil(rY); + final int ceilRadiusZ = (int) Math.ceil(rZ); + + double nextXn = 0; + forX: for (int x = 0; x <= ceilRadiusX; ++x) { + final double xn = nextXn; + nextXn = (x + 1) * invRadiusX; + double nextYn = 0; + forY: for (int y = 0; y <= ceilRadiusY; ++y) { + final double yn = nextYn; + nextYn = (y + 1) * invRadiusY; + double nextZn = 0; + forZ: for (int z = 0; z <= ceilRadiusZ; ++z) { + final double zn = nextZn; + nextZn = (z + 1) * invRadiusZ; + + double magnitude = KelpLocation.magnitude(xn, yn, zn); + if (magnitude > 1) { + if (z == 0) { + if (y == 0) { + break forX; + } + break forY; + } + break forZ; + } + + if (surface) { + if (KelpLocation.magnitude(nextXn, yn, zn) <= 1 + && KelpLocation.magnitude(xn, nextYn, zn) <= 1 + && KelpLocation.magnitude(xn, yn, nextZn) <= 1) { + continue; + } + } + + output.add(center.clone().add(x, y, z).getBlock()); + output.add(center.clone().add(-x, y, z).getBlock()); + output.add(center.clone().add(x, -y, z).getBlock()); + output.add(center.clone().add(x, y, -z).getBlock()); + output.add(center.clone().add(-x, -y, z).getBlock()); + output.add(center.clone().add(x, -y, -z).getBlock()); + output.add(center.clone().add(-x, y, -z).getBlock()); + output.add(center.clone().add(-x, -y, -z).getBlock()); + } + } + } return output; } From aca8ddfe3a651670bb3d901ee0f190d5bd6330b1 Mon Sep 17 00:00:00 2001 From: pxav Date: Sun, 7 Mar 2021 11:45:23 +0100 Subject: [PATCH 23/81] You can now move an EllipsoidRegion after you expanded it so that only one side of the sphere is expanded --- .../pxav/kelp/core/world/region/EllipsoidRegion.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java index 74fb688a..9c63a2ec 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java @@ -186,6 +186,11 @@ public void expand(KelpBlockFace direction, double amount) { } } + public void expandAndMove(KelpBlockFace direction, double amount) { + expand(direction, amount); + move(direction.getDirection().multiply(amount / 2)); + } + @Override public void expand(double negativeX, double positiveX, double negativeY, double positiveY, double negativeZ, double positiveZ) { this.xRadius = positiveX + negativeX; @@ -193,6 +198,11 @@ public void expand(double negativeX, double positiveX, double negativeY, double this.zRadius = positiveZ + negativeZ; } + public void expandAndMove(double negativeX, double positiveX, double negativeY, double positiveY, double negativeZ, double positiveZ) { + expand(negativeX, positiveX, negativeY, positiveY, negativeZ, positiveZ); + move(positiveX - negativeX, positiveY - negativeY, positiveZ - negativeZ); + } + @Override public boolean contains(KelpLocation location) { return getCostAt(location) <= 1; From 6e46894d61fe9cf9bb3c5b50241c5acacc015a06 Mon Sep 17 00:00:00 2001 From: pxav Date: Sun, 7 Mar 2021 11:56:19 +0100 Subject: [PATCH 24/81] Predefine value of 4/3 in EllipsoidRegion --- .../java/de/pxav/kelp/core/world/region/EllipsoidRegion.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java index 9c63a2ec..cf438fdd 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java @@ -12,6 +12,8 @@ public class EllipsoidRegion extends KelpRegion { + private static final double FOUR_THIRDS = 1.33333333333333333333d; + private KelpLocation center; private double xRadius; private double yRadius; @@ -61,7 +63,7 @@ public void move(double dx, double dy, double dz) { @Override public double getVolume() { - return (4d / 3d) * Math.PI * xRadius * yRadius * zRadius; + return FOUR_THIRDS * Math.PI * xRadius * yRadius * zRadius; } @Override From 28860f9e221cd333cf781eb272e056ced05ef000 Mon Sep 17 00:00:00 2001 From: pxav Date: Sun, 7 Mar 2021 13:06:38 +0100 Subject: [PATCH 25/81] Add equals and hashCode to KelpWorld --- .../de/pxav/kelp/core/world/KelpWorld.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/KelpWorld.java b/core/src/main/java/de/pxav/kelp/core/world/KelpWorld.java index 2cc99284..8fe95886 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/KelpWorld.java +++ b/core/src/main/java/de/pxav/kelp/core/world/KelpWorld.java @@ -11,6 +11,7 @@ import org.bukkit.Bukkit; import org.bukkit.Difficulty; import org.bukkit.World; +import org.bukkit.craftbukkit.v1_16_R3.CraftWorld; import java.util.Collection; import java.util.UUID; @@ -640,4 +641,23 @@ public KelpWorld strikeLightningEffect(KelpLocation location) { return this; } + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (!(object instanceof KelpWorld)) { + return false; + } + + KelpWorld world = (KelpWorld) object; + return world.hashCode() == this.hashCode(); + } + + @Override + public int hashCode() { + return getUUID().hashCode(); + } + } From f6a3c0aa0145cb885e53d5a2cff889702edd9da1 Mon Sep 17 00:00:00 2001 From: pxav Date: Sun, 7 Mar 2021 13:06:48 +0100 Subject: [PATCH 26/81] Add equals and hashCode to KelpLocation --- .../de/pxav/kelp/core/world/KelpLocation.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java b/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java index 16c7e608..781193f0 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java +++ b/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java @@ -998,4 +998,30 @@ public Location getBukkitLocation() { return new Location(Bukkit.getWorld(worldName), x, y, z, yaw, pitch); } + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (!(object instanceof KelpLocation)) { + return false; + } + + KelpLocation location = (KelpLocation) object; + return location.hashCode() == this.hashCode(); + } + + @Override + public int hashCode() { + int hash = 19 * 3 + (worldName != null ? worldName.hashCode() : 0); + hash = 19 * hash + (int)(Double.doubleToLongBits(this.x) ^ Double.doubleToLongBits(this.x) >>> 32); + hash = 19 * hash + (int)(Double.doubleToLongBits(this.y) ^ Double.doubleToLongBits(this.y) >>> 32); + hash = 19 * hash + (int)(Double.doubleToLongBits(this.z) ^ Double.doubleToLongBits(this.z) >>> 32); + hash = 19 * hash + Float.floatToIntBits(this.pitch); + hash = 19 * hash + Float.floatToIntBits(this.yaw); + + return hash; + } + } From 9e9985bbb5e4fefe86cd3a0519dd6eb0c8a7c46e Mon Sep 17 00:00:00 2001 From: pxav Date: Sun, 7 Mar 2021 13:06:59 +0100 Subject: [PATCH 27/81] Add equals and hashCode to KelpChunk --- .../de/pxav/kelp/core/world/KelpChunk.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/KelpChunk.java b/core/src/main/java/de/pxav/kelp/core/world/KelpChunk.java index 9a35c3db..352a86d4 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/KelpChunk.java +++ b/core/src/main/java/de/pxav/kelp/core/world/KelpChunk.java @@ -169,4 +169,27 @@ public boolean equals(Chunk compareTo) { public Chunk getBukkitChunk() { return bukkitChunk; } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (!(object instanceof KelpChunk)) { + return false; + } + + KelpChunk chunk = (KelpChunk) object; + return chunk.hashCode() == this.hashCode(); + } + + @Override + public int hashCode() { + int hash = 19 * 3 + (getWorld() != null ? getWorld().hashCode() : 0); + hash *= getX() * getX(); + hash *= getZ() * getZ(); + + return hash; + } } From 2f0dd627bab7c6616f047d91909bd8d2034f507b Mon Sep 17 00:00:00 2001 From: pxav Date: Sun, 7 Mar 2021 13:07:14 +0100 Subject: [PATCH 28/81] KelpBlock is now comparable --- .../de/pxav/kelp/core/world/KelpBlock.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/KelpBlock.java b/core/src/main/java/de/pxav/kelp/core/world/KelpBlock.java index 70140202..2dc907ef 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/KelpBlock.java +++ b/core/src/main/java/de/pxav/kelp/core/world/KelpBlock.java @@ -1,5 +1,6 @@ package de.pxav.kelp.core.world; +import com.google.common.base.Objects; import de.pxav.kelp.core.KelpPlugin; import de.pxav.kelp.core.inventory.material.KelpMaterial; import de.pxav.kelp.core.world.util.CardinalDirection; @@ -316,4 +317,23 @@ public Block getBukkitBlock() { return bukkitBlock; } + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (!(object instanceof KelpBlock)) { + return false; + } + + KelpBlock kelpBlock = (KelpBlock) object; + return kelpBlock.hashCode() == this.hashCode(); + } + + @Override + public int hashCode() { + return getX() * getX() + getY() * getY() + getZ() * getZ() + getWorldName().hashCode(); + } + } From 4232dc314c2073277bced95f16c0226e81be70ac Mon Sep 17 00:00:00 2001 From: pxav Date: Sun, 7 Mar 2021 13:07:33 +0100 Subject: [PATCH 29/81] Add equals and hashCode to EllipsoidRegion --- .../core/world/region/EllipsoidRegion.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java index cf438fdd..f90a952e 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java @@ -266,4 +266,23 @@ public String getWorldName() { public KelpRegion clone() { return EllipsoidRegion.create(this.center, xRadius, yRadius, zRadius); } + + @Override + public boolean equals(Object object) { + if (!(object instanceof EllipsoidRegion)) { + return false; + } + + EllipsoidRegion region = (EllipsoidRegion) object; + return region.hashCode() == this.hashCode(); + } + + @Override + public int hashCode() { + return (int) ("ELLIPSOID".hashCode() + + center.hashCode() + + ((int) xRadius * xRadius) + + ((int) yRadius * yRadius) + + ((int) zRadius * zRadius)); + } } From 8cc53f1eee7c44ca8484a02a103be46b1c776708 Mon Sep 17 00:00:00 2001 From: pxav Date: Sun, 7 Mar 2021 13:31:31 +0100 Subject: [PATCH 30/81] The getBlocks() method of an EllipsoidRegion can now be limited --- .../core/world/region/EllipsoidRegion.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java index f90a952e..6c68b5f7 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java @@ -18,6 +18,9 @@ public class EllipsoidRegion extends KelpRegion { private double xRadius; private double yRadius; private double zRadius; + private double limitX = 0; + private double limitY = 0; + private double limitZ = 0; public static EllipsoidRegion create(KelpLocation center, double radius) { EllipsoidRegion region = new EllipsoidRegion(); @@ -51,6 +54,24 @@ public static EllipsoidRegion create(KelpLocation pos1, KelpLocation pos2) { return region; } + public void limitRadius(double limiter) { + this.limitX = limiter; + this.limitY = limiter; + this.limitZ = limiter; + } + + public void limitXRadius(double limiter) { + this.limitX = limiter; + } + + public void limitYRadius(double limiter) { + this.limitY = limiter; + } + + public void limitZRadius(double limiter) { + this.limitZ = limiter; + } + @Override public void move(Vector vector) { center.add(vector); @@ -148,6 +169,25 @@ private Set getBlocks(boolean surface) { } } } + + if (limitX > 0 || limitY > 0 || limitZ > 0) { + output.parallelStream() + .filter(block -> { + if (limitX > 0) { + return block.getX() > center.addX(limitX).getX() || block.getX() < center.subtractX(limitX).getX(); + } + if (limitY > 0) { + return block.getY() > center.addY(limitY).getY() || block.getY() < center.subtractY(limitY).getY(); + } + if (limitZ > 0) { + return block.getZ() > center.addZ(limitZ).getZ() || block.getZ() < center.subtractZ(limitZ).getZ(); + } + + return false; + }) + .forEach(output::remove); + } + return output; } From 7f3175c887a7fa41abd1cce25d47be8dc3d59183 Mon Sep 17 00:00:00 2001 From: pxav Date: Sun, 7 Mar 2021 19:12:13 +0100 Subject: [PATCH 31/81] Add hashCode and equals to CuboidRegion --- .../pxav/kelp/core/world/region/CuboidRegion.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java index cd7a9ec9..859f4f38 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java @@ -222,6 +222,21 @@ public CuboidRegion clone() { return CuboidRegion.create(this.minPos, this.maxPos); } + @Override + public boolean equals(Object object) { + if (!(object instanceof CuboidRegion)) { + return false; + } + + CuboidRegion region = (CuboidRegion) object; + return region.hashCode() == this.hashCode(); + } + + @Override + public int hashCode() { + return "CUBOID".hashCode() + minPos.hashCode() + maxPos.hashCode(); + } + public void setBoundingPositions(KelpLocation pos1, KelpLocation pos2) { if (!pos1.getWorldName().equals(pos2.getWorldName())) { throw new IllegalArgumentException("Cannot build CuboidRegion from locations of differing worlds!"); From e525cf2019d9e0d5feb4450a7fa70fa87f9082fd Mon Sep 17 00:00:00 2001 From: pxav Date: Sun, 7 Mar 2021 19:12:39 +0100 Subject: [PATCH 32/81] Add abstract methods for equals and hashCode in KelpRegion so every region type has to define those --- .../main/java/de/pxav/kelp/core/world/region/KelpRegion.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java index 8c47a394..48b1c10a 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java @@ -125,4 +125,8 @@ public EllipsoidRegion toEllipsoid() { @Override public abstract KelpRegion clone(); + public abstract boolean equals(Object object); + + public abstract int hashCode(); + } From 6c78870afe1e9d38ebd98193e9ce2be765018739 Mon Sep 17 00:00:00 2001 From: pxav Date: Tue, 9 Mar 2021 16:08:18 +0100 Subject: [PATCH 33/81] Change hashCode algorithm to reduce hash collisions --- .../de/pxav/kelp/core/world/KelpBlock.java | 14 +++++++++-- .../de/pxav/kelp/core/world/KelpChunk.java | 15 +++++++----- .../de/pxav/kelp/core/world/KelpLocation.java | 24 ++++++++++++------- .../de/pxav/kelp/core/world/KelpWorld.java | 2 +- 4 files changed, 37 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/KelpBlock.java b/core/src/main/java/de/pxav/kelp/core/world/KelpBlock.java index 2dc907ef..9f72e83a 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/KelpBlock.java +++ b/core/src/main/java/de/pxav/kelp/core/world/KelpBlock.java @@ -6,7 +6,9 @@ import de.pxav.kelp.core.world.util.CardinalDirection; import de.pxav.kelp.core.world.util.KelpBlockFace; import de.pxav.kelp.core.world.version.BlockVersionTemplate; +import org.apache.commons.lang.builder.HashCodeBuilder; import org.bukkit.block.Block; +import org.bukkit.craftbukkit.v1_16_R3.block.CraftBlock; import org.bukkit.util.Vector; /** @@ -328,12 +330,20 @@ public boolean equals(Object object) { } KelpBlock kelpBlock = (KelpBlock) object; - return kelpBlock.hashCode() == this.hashCode(); + return kelpBlock.getX() == this.getX() + && kelpBlock.getY() == this.getY() + && kelpBlock.getZ() == this.getZ() + && kelpBlock.getWorldName().equalsIgnoreCase(this.getWorldName()); } @Override public int hashCode() { - return getX() * getX() + getY() * getY() + getZ() * getZ() + getWorldName().hashCode(); + return new HashCodeBuilder(17, 37) + .append(this.getWorldName()) + .append(getX()) + .append(getY()) + .append(getZ()) + .toHashCode(); } } diff --git a/core/src/main/java/de/pxav/kelp/core/world/KelpChunk.java b/core/src/main/java/de/pxav/kelp/core/world/KelpChunk.java index 352a86d4..174c7af6 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/KelpChunk.java +++ b/core/src/main/java/de/pxav/kelp/core/world/KelpChunk.java @@ -4,6 +4,7 @@ import de.pxav.kelp.core.application.KelpApplication; import de.pxav.kelp.core.player.KelpPlayer; import de.pxav.kelp.core.world.version.ChunkVersionTemplate; +import org.apache.commons.lang.builder.HashCodeBuilder; import org.bukkit.Chunk; import java.util.Collection; @@ -181,15 +182,17 @@ public boolean equals(Object object) { } KelpChunk chunk = (KelpChunk) object; - return chunk.hashCode() == this.hashCode(); + return chunk.getZ() == this.getZ() + && chunk.getX() == this.getX() + && chunk.getWorld().getName().equalsIgnoreCase(this.getWorld().getName()); } @Override public int hashCode() { - int hash = 19 * 3 + (getWorld() != null ? getWorld().hashCode() : 0); - hash *= getX() * getX(); - hash *= getZ() * getZ(); - - return hash; + return new HashCodeBuilder(17, 37) + .append(this.getWorld().getName()) + .append(this.getX()) + .append(this.getZ()) + .toHashCode(); } } diff --git a/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java b/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java index 781193f0..fb7879f4 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java +++ b/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java @@ -2,6 +2,7 @@ import com.google.common.base.Preconditions; import de.pxav.kelp.core.world.util.CardinalDirection; +import org.apache.commons.lang.builder.HashCodeBuilder; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; @@ -1009,19 +1010,24 @@ public boolean equals(Object object) { } KelpLocation location = (KelpLocation) object; - return location.hashCode() == this.hashCode(); + return this.getWorldName().equalsIgnoreCase(location.getWorldName()) + && this.getX() == location.getX() + && this.getY() == location.getY() + && this.getZ() == location.getZ() + && this.getYaw() == location.getYaw() + && this.getPitch() == location.getPitch(); } @Override public int hashCode() { - int hash = 19 * 3 + (worldName != null ? worldName.hashCode() : 0); - hash = 19 * hash + (int)(Double.doubleToLongBits(this.x) ^ Double.doubleToLongBits(this.x) >>> 32); - hash = 19 * hash + (int)(Double.doubleToLongBits(this.y) ^ Double.doubleToLongBits(this.y) >>> 32); - hash = 19 * hash + (int)(Double.doubleToLongBits(this.z) ^ Double.doubleToLongBits(this.z) >>> 32); - hash = 19 * hash + Float.floatToIntBits(this.pitch); - hash = 19 * hash + Float.floatToIntBits(this.yaw); - - return hash; + return new HashCodeBuilder(17, 37) + .append(this.worldName) + .append(this.x) + .append(this.y) + .append(this.z) + .append(this.yaw) + .append(this.pitch) + .toHashCode(); } } diff --git a/core/src/main/java/de/pxav/kelp/core/world/KelpWorld.java b/core/src/main/java/de/pxav/kelp/core/world/KelpWorld.java index 8fe95886..2925f564 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/KelpWorld.java +++ b/core/src/main/java/de/pxav/kelp/core/world/KelpWorld.java @@ -652,7 +652,7 @@ public boolean equals(Object object) { } KelpWorld world = (KelpWorld) object; - return world.hashCode() == this.hashCode(); + return world.getName().equalsIgnoreCase(this.getName()); } @Override From fc9dd9a291053d4b66c5a19fc7c588eb1094f4e2 Mon Sep 17 00:00:00 2001 From: pxav Date: Tue, 9 Mar 2021 16:22:45 +0100 Subject: [PATCH 34/81] You can now limit the radius of an Ellipsoid --- .../core/world/region/EllipsoidRegion.java | 68 +++++++++++++++++-- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java index 6c68b5f7..19d8f965 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java @@ -5,6 +5,7 @@ import de.pxav.kelp.core.world.KelpChunk; import de.pxav.kelp.core.world.KelpLocation; import de.pxav.kelp.core.world.util.KelpBlockFace; +import org.bukkit.Location; import org.bukkit.util.NumberConversions; import org.bukkit.util.Vector; @@ -112,7 +113,7 @@ public Set getBlocks() { return getBlocks(false); } - private Set getBlocks(boolean surface) { + private Set getBlocks(boolean surfaceOnly) { Set output = Sets.newConcurrentHashSet(); double rX = xRadius + 0.5; double rY = yRadius + 0.5; @@ -150,7 +151,7 @@ private Set getBlocks(boolean surface) { break forZ; } - if (surface) { + if (surfaceOnly) { if (KelpLocation.magnitude(nextXn, yn, zn) <= 1 && KelpLocation.magnitude(xn, nextYn, zn) <= 1 && KelpLocation.magnitude(xn, yn, nextZn) <= 1) { @@ -170,27 +171,68 @@ private Set getBlocks(boolean surface) { } } + if (limitX > 0 || limitY > 0 || limitZ > 0) { output.parallelStream() .filter(block -> { if (limitX > 0) { - return block.getX() > center.addX(limitX).getX() || block.getX() < center.subtractX(limitX).getX(); + return block.getX() > center.clone().addX(limitX).getX() || block.getX() < center.clone().subtractX(limitX).getX(); } if (limitY > 0) { - return block.getY() > center.addY(limitY).getY() || block.getY() < center.subtractY(limitY).getY(); + return block.getY() > center.clone().addY(limitY).getY() || block.getY() < center.clone().subtractY(limitY).getY(); } if (limitZ > 0) { - return block.getZ() > center.addZ(limitZ).getZ() || block.getZ() < center.subtractZ(limitZ).getZ(); + return block.getZ() > center.clone().addZ(limitZ).getZ() || block.getZ() < center.clone().subtractZ(limitZ).getZ(); } return false; }) .forEach(output::remove); + + if (surfaceOnly) { + if (limitX > 0) { + output.addAll(getZSliceAt(center.getBlockX() + limitX)); + output.addAll(getZSliceAt(center.getBlockX() - limitX)); + } + if (limitY > 0) { + output.addAll(getYSliceAt(center.getBlockY() + limitY)); + output.addAll(getYSliceAt(center.getBlockY() - limitY)); + } + if (limitZ > 0) { + output.addAll(getXSliceAt(center.getBlockZ() + limitZ)); + output.addAll(getXSliceAt(center.getBlockZ() - limitZ)); + } + } + } return output; } + public Set getYSliceAt(double y) { + Set blocks = Sets.newConcurrentHashSet(); + getBlocks().parallelStream() + .filter(block -> block.getY() == Location.locToBlock(y)) + .forEach(blocks::add); + return blocks; + } + + public Set getXSliceAt(double x) { + Set blocks = Sets.newConcurrentHashSet(); + getBlocks().parallelStream() + .filter(block -> block.getX() == Location.locToBlock(x)) + .forEach(blocks::add); + return blocks; + } + + public Set getZSliceAt(double z) { + Set blocks = Sets.newConcurrentHashSet(); + getBlocks().parallelStream() + .filter(block -> block.getZ() == Location.locToBlock(z)) + .forEach(blocks::add); + return blocks; + } + @Override public Set getChunks() { return toCuboid().getChunks(); @@ -247,7 +289,21 @@ public void expandAndMove(double negativeX, double positiveX, double negativeY, @Override public boolean contains(KelpLocation location) { - return getCostAt(location) <= 1; + + // if the location is excluded due to a limited radius + boolean limitCriteria = false; + + if (limitX > 0) { + limitCriteria = location.getX() > center.clone().addX(limitX).getX() || location.getX() < center.clone().subtractX(limitX).getX(); + } + if (limitY > 0) { + limitCriteria = location.getY() > center.clone().addY(limitY).getY() || location.getY() < center.clone().subtractY(limitY).getY(); + } + if (limitZ > 0) { + limitCriteria = location.getZ() > center.clone().addZ(limitZ).getZ() || location.getZ() < center.clone().subtractZ(limitZ).getZ(); + } + + return getCostAt(location) <= 1 && !limitCriteria; } public double getCostAt(KelpLocation location) { From 2f33a6c2440cfdcdabfb1225d113075f1fa1fad5 Mon Sep 17 00:00:00 2001 From: pxav Date: Tue, 9 Mar 2021 18:05:23 +0100 Subject: [PATCH 35/81] Update equals and hash code algorithm for CuboidRegion --- .../de/pxav/kelp/core/world/region/CuboidRegion.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java index 859f4f38..c943d2af 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java @@ -5,6 +5,7 @@ import de.pxav.kelp.core.world.KelpChunk; import de.pxav.kelp.core.world.KelpLocation; import de.pxav.kelp.core.world.util.KelpBlockFace; +import org.apache.commons.lang.builder.HashCodeBuilder; import org.bukkit.util.Vector; import java.util.Set; @@ -229,12 +230,19 @@ public boolean equals(Object object) { } CuboidRegion region = (CuboidRegion) object; - return region.hashCode() == this.hashCode(); + return region.getWorldName().equalsIgnoreCase(worldName) + && minPos.equals(region.getMinPos()) + && maxPos.equals(region.getMaxPos()); } @Override public int hashCode() { - return "CUBOID".hashCode() + minPos.hashCode() + maxPos.hashCode(); + return new HashCodeBuilder(17, 37) + .append(this.worldName) + .append("CUBOID") + .append(maxPos.hashCode()) + .append(minPos.hashCode()) + .toHashCode(); } public void setBoundingPositions(KelpLocation pos1, KelpLocation pos2) { From 01cddc7d335c821fde50b2c95e62d3824ba81656 Mon Sep 17 00:00:00 2001 From: pxav Date: Tue, 9 Mar 2021 18:05:36 +0100 Subject: [PATCH 36/81] Update equals and hash code algorithm for EllipsoidRegion --- .../core/world/region/EllipsoidRegion.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java index 19d8f965..cb04f3e6 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java @@ -5,6 +5,7 @@ import de.pxav.kelp.core.world.KelpChunk; import de.pxav.kelp.core.world.KelpLocation; import de.pxav.kelp.core.world.util.KelpBlockFace; +import org.apache.commons.lang.builder.HashCodeBuilder; import org.bukkit.Location; import org.bukkit.util.NumberConversions; import org.bukkit.util.Vector; @@ -370,15 +371,22 @@ public boolean equals(Object object) { } EllipsoidRegion region = (EllipsoidRegion) object; - return region.hashCode() == this.hashCode(); + return region.getWorldName().equalsIgnoreCase(worldName) + && region.getXRadius() == this.getXRadius() + && region.getYRadius() == this.getYRadius() + && region.getZRadius() == this.getZRadius() + && region.getCenter().equals(this.center); } @Override public int hashCode() { - return (int) ("ELLIPSOID".hashCode() - + center.hashCode() - + ((int) xRadius * xRadius) - + ((int) yRadius * yRadius) - + ((int) zRadius * zRadius)); + return new HashCodeBuilder(17, 37) + .append(getWorldName()) + .append("ELLIPSOID") + .append(xRadius) + .append(xRadius) + .append(yRadius) + .append(center.hashCode()) + .toHashCode(); } } From cdc4fad9ee85ed5aaed310e6140044cff3d416cd Mon Sep 17 00:00:00 2001 From: pxav Date: Tue, 16 Mar 2021 20:12:21 +0100 Subject: [PATCH 37/81] Add KelpRegionRepository managing region listeners --- .../world/region/KelpRegionRepository.java | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java new file mode 100644 index 00000000..e5181363 --- /dev/null +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java @@ -0,0 +1,80 @@ +package de.pxav.kelp.core.world.region; + +import com.google.common.collect.*; +import de.pxav.kelp.core.scheduler.KelpSchedulerRepository; +import de.pxav.kelp.core.scheduler.type.RepeatingScheduler; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.Map; +import java.util.UUID; + +@Singleton +public class KelpRegionRepository { + + // region id -> region object + private Map listenedRegions; + + // player -> region id + private Multimap containedBy; + + private boolean listenerRunning = false; + + private KelpSchedulerRepository schedulerRepository; + + @Inject + public KelpRegionRepository(KelpSchedulerRepository schedulerRepository) { + this.listenedRegions = Maps.newConcurrentMap(); + this.containedBy = ArrayListMultimap.create(); + + this.schedulerRepository = schedulerRepository; + } + + public void listenTo(KelpRegion region) { + this.listenedRegions.put(region.getRegionId(), region); + if (!listenerRunning) { + startListenerTasks(); + } + } + + public void stopListeningTo(KelpRegion region) { + this.listenedRegions.remove(region.getRegionId()); + + } + + public boolean isListeningTo(KelpRegion region) { + return this.listenedRegions.containsKey(region.getRegionId()); + } + + public void startListenerTasks() { + RepeatingScheduler.create() + .async() + .every(100) + .milliseconds() + .waitForTaskCompletion(true) + .run(taskId -> { + + // if there are no regions to listen to anymore, cancel the scheduler + if (listenedRegions.isEmpty()) { + listenerRunning = false; + schedulerRepository.interruptScheduler(taskId); + } + + listenerRunning = true; + + for (Player player : Bukkit.getOnlinePlayers()) { + listenedRegions.forEach((regionId, region) -> { + if (!region.getWorldName().equalsIgnoreCase(player.getWorld().getName())) { + return; + } + + + }); + } + }); + } + + +} From bf60a9d4ead7f4460ae7d6fe7a7d126df1ed938f Mon Sep 17 00:00:00 2001 From: pxav Date: Tue, 16 Mar 2021 20:12:41 +0100 Subject: [PATCH 38/81] Regions now have an id and repository as a dependency --- .../kelp/core/world/region/KelpRegion.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java index 48b1c10a..ff6ef69f 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java @@ -1,5 +1,6 @@ package de.pxav.kelp.core.world.region; +import de.pxav.kelp.core.KelpPlugin; import de.pxav.kelp.core.player.KelpPlayer; import de.pxav.kelp.core.world.KelpBlock; import de.pxav.kelp.core.world.KelpChunk; @@ -8,14 +9,23 @@ import de.pxav.kelp.core.world.util.KelpBlockFace; import org.bukkit.util.Vector; +import javax.inject.Singleton; import java.util.Set; +import java.util.UUID; +@Singleton public abstract class KelpRegion implements Cloneable { + protected KelpRegionRepository regionRepository; + protected UUID regionId = UUID.randomUUID(); protected String worldName; protected KelpLocation minPos; protected KelpLocation maxPos; + public KelpRegion(KelpRegionRepository regionRepository) { + this.regionRepository = regionRepository; + } + public abstract void move(Vector vector); public abstract void move(double dx, double dy, double dz); @@ -122,6 +132,10 @@ public EllipsoidRegion toEllipsoid() { return EllipsoidRegion.create(minPos, maxPos); } + public UUID getRegionId() { + return regionId; + } + @Override public abstract KelpRegion clone(); @@ -129,4 +143,8 @@ public EllipsoidRegion toEllipsoid() { public abstract int hashCode(); + protected static KelpRegionRepository getRegionRepository() { + return KelpPlugin.getInjector().getInstance(KelpRegionRepository.class); + } + } From eb0ac6fdf085f07c9a2a61c64006cd3794ea3249 Mon Sep 17 00:00:00 2001 From: pxav Date: Tue, 16 Mar 2021 20:13:12 +0100 Subject: [PATCH 39/81] Adjust cuboid and ellipsoid constructor to new dependencies --- .../de/pxav/kelp/core/world/region/CuboidRegion.java | 8 ++++++-- .../pxav/kelp/core/world/region/EllipsoidRegion.java | 12 +++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java index c943d2af..264c2b96 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java @@ -13,14 +13,18 @@ public class CuboidRegion extends KelpRegion { public static CuboidRegion create(KelpLocation pos1, KelpLocation pos2) { - CuboidRegion region = new CuboidRegion(); + CuboidRegion region = new CuboidRegion(getRegionRepository()); region.setBoundingPositions(pos1, pos2); region.setWorldName(pos1.getWorldName()); return region; } public static CuboidRegion create() { - return new CuboidRegion(); + return new CuboidRegion(getRegionRepository()); + } + + public CuboidRegion(KelpRegionRepository regionRepository) { + super(regionRepository); } @Override diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java index cb04f3e6..5f204ee6 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java @@ -1,6 +1,8 @@ package de.pxav.kelp.core.world.region; import com.google.common.collect.Sets; +import de.pxav.kelp.core.KelpPlugin; +import de.pxav.kelp.core.application.KelpApplicationRepository; import de.pxav.kelp.core.world.KelpBlock; import de.pxav.kelp.core.world.KelpChunk; import de.pxav.kelp.core.world.KelpLocation; @@ -25,7 +27,7 @@ public class EllipsoidRegion extends KelpRegion { private double limitZ = 0; public static EllipsoidRegion create(KelpLocation center, double radius) { - EllipsoidRegion region = new EllipsoidRegion(); + EllipsoidRegion region = new EllipsoidRegion(getRegionRepository()); region.setCenter(center); region.setRadius(radius); region.minPos = center.clone().subtract(radius); @@ -34,7 +36,7 @@ public static EllipsoidRegion create(KelpLocation center, double radius) { } public static EllipsoidRegion create(KelpLocation center, double xRadius, double yRadius, double zRadius) { - EllipsoidRegion region = new EllipsoidRegion(); + EllipsoidRegion region = new EllipsoidRegion(getRegionRepository()); region.setCenter(center); region.setXRadius(xRadius); region.setYRadius(yRadius); @@ -45,7 +47,7 @@ public static EllipsoidRegion create(KelpLocation center, double xRadius, double } public static EllipsoidRegion create(KelpLocation pos1, KelpLocation pos2) { - EllipsoidRegion region = new EllipsoidRegion(); + EllipsoidRegion region = new EllipsoidRegion(getRegionRepository()); region.maxPos = pos1.getMaximalLocation(pos2); region.minPos = pos1.getMinimalLocation(pos2); @@ -56,6 +58,10 @@ public static EllipsoidRegion create(KelpLocation pos1, KelpLocation pos2) { return region; } + public EllipsoidRegion(KelpRegionRepository regionRepository) { + super(regionRepository); + } + public void limitRadius(double limiter) { this.limitX = limiter; this.limitY = limiter; From 82ec18d6b1cdc111d56f41c0f212db845e4819fa Mon Sep 17 00:00:00 2001 From: pxav Date: Tue, 16 Mar 2021 20:24:22 +0100 Subject: [PATCH 40/81] contains() method of regions now takes x, y, z by default --- .../kelp/core/world/region/CuboidRegion.java | 20 +++++++++++------- .../core/world/region/EllipsoidRegion.java | 21 +++++++++++-------- .../kelp/core/world/region/KelpRegion.java | 16 +++++++------- 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java index 264c2b96..06b40996 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java @@ -51,11 +51,7 @@ public int getBlockVolume() { } @Override - public boolean contains(KelpLocation location) { - if (!worldName.equalsIgnoreCase(location.getWorldName())) { - return false; - } - + public boolean contains(double x, double y, double z) { // X double maxX = Math.max(this.minPos.getX(), this.maxPos.getX()); double minX = Math.min(this.minPos.getX(), this.maxPos.getX()); @@ -68,14 +64,22 @@ public boolean contains(KelpLocation location) { double maxZ = Math.max(this.minPos.getZ(), this.maxPos.getZ()); double minZ = Math.min(this.minPos.getZ(), this.maxPos.getZ()); - if(location.getX() <= maxX && location.getX() >= minX) { - if(location.getY() <= maxY && location.getY() >= minY) { - return location.getZ() <= maxZ && location.getZ() >= minZ; + if(x <= maxX && x >= minX) { + if(y <= maxY && y >= minY) { + return z <= maxZ && z >= minZ; } } return false; } + @Override + public boolean contains(KelpLocation location) { + if (!worldName.equalsIgnoreCase(location.getWorldName())) { + return false; + } + return contains(location.getX(), location.getY(), location.getZ()); + } + @Override public KelpLocation getCenter() { return this.minPos.clone().add(this.maxPos).multiply(0.5); diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java index 5f204ee6..79673e3b 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java @@ -295,28 +295,31 @@ public void expandAndMove(double negativeX, double positiveX, double negativeY, } @Override - public boolean contains(KelpLocation location) { - + public boolean contains(double x, double y, double z) { // if the location is excluded due to a limited radius boolean limitCriteria = false; if (limitX > 0) { - limitCriteria = location.getX() > center.clone().addX(limitX).getX() || location.getX() < center.clone().subtractX(limitX).getX(); + limitCriteria = x > center.clone().addX(limitX).getX() || x < center.clone().subtractX(limitX).getX(); } if (limitY > 0) { - limitCriteria = location.getY() > center.clone().addY(limitY).getY() || location.getY() < center.clone().subtractY(limitY).getY(); + limitCriteria = y > center.clone().addY(limitY).getY() || y < center.clone().subtractY(limitY).getY(); } if (limitZ > 0) { - limitCriteria = location.getZ() > center.clone().addZ(limitZ).getZ() || location.getZ() < center.clone().subtractZ(limitZ).getZ(); + limitCriteria = z > center.clone().addZ(limitZ).getZ() || z < center.clone().subtractZ(limitZ).getZ(); } - return getCostAt(location) <= 1 && !limitCriteria; + return getCostAt(x, y, z) <= 1 && !limitCriteria; } public double getCostAt(KelpLocation location) { - return ((location.getX() - center.getX()) / xRadius) * ((location.getX() - center.getX()) / xRadius) - + ((location.getY() - center.getY()) / yRadius) * ((location.getY() - center.getY()) / yRadius) - + ((location.getZ() - center.getZ()) / zRadius) * ((location.getZ() - center.getZ()) / zRadius); + return getCostAt(location.getX(), location.getY(), location.getZ()); + } + + public double getCostAt(double x, double y, double z) { + return ((x - center.getX()) / xRadius) * ((x - center.getX()) / xRadius) + + ((y - center.getY()) / yRadius) * ((y - center.getY()) / yRadius) + + ((z - center.getZ()) / zRadius) * ((z - center.getZ()) / zRadius); } public boolean isSphere() { diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java index ff6ef69f..f3bebe91 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java @@ -55,7 +55,14 @@ public abstract void expand(double negativeX, double negativeZ, double positiveZ); - public abstract boolean contains(KelpLocation location); + public abstract boolean contains(double x, double y, double z); + + public boolean contains(KelpLocation location) { + if (!worldName.equalsIgnoreCase(location.getWorldName())) { + return false; + } + return contains(location.getX(), location.getZ(), location.getZ()); + } public boolean contains(KelpPlayer player) { return contains(player.getLocation()); @@ -65,13 +72,6 @@ public boolean contains(KelpBlock block) { return contains(block.getLocation()); } - public boolean contains(double x, double y, double z) { - if (worldName == null) { - return false; - } - return contains(KelpLocation.from(worldName, x, y, z)); - } - public int[] getBlockDimensions() { return new int[] { maxPos.getBlockX() - minPos.getBlockX() + 1, From 9663ce9877e461f6696be6d5ea3971a4b149831d Mon Sep 17 00:00:00 2001 From: pxav Date: Thu, 18 Mar 2021 17:56:47 +0100 Subject: [PATCH 41/81] Add custom interface for concurrent multimaps --- .../kelp/core/common/ConcurrentMultimap.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 core/src/main/java/de/pxav/kelp/core/common/ConcurrentMultimap.java diff --git a/core/src/main/java/de/pxav/kelp/core/common/ConcurrentMultimap.java b/core/src/main/java/de/pxav/kelp/core/common/ConcurrentMultimap.java new file mode 100644 index 00000000..8b8a1772 --- /dev/null +++ b/core/src/main/java/de/pxav/kelp/core/common/ConcurrentMultimap.java @@ -0,0 +1,18 @@ +package de.pxav.kelp.core.common; + +import com.google.common.collect.Multimap; + +import java.util.Collection; +import java.util.Map; + +public interface ConcurrentMultimap extends Multimap { + + boolean containsValue(Collection iterable); + + Collection getOrDefault(K key, Collection defaultCollection); + + Collection getOrEmpty(K key); + + void putAll(Map newMap); + +} From 093a9820e8572f96f095a5d3cff674bc256f1bb0 Mon Sep 17 00:00:00 2001 From: pxav Date: Thu, 18 Mar 2021 17:57:09 +0100 Subject: [PATCH 42/81] Add concurrent multimap using lists to save value data --- .../core/common/ConcurrentListMultimap.java | 214 ++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 core/src/main/java/de/pxav/kelp/core/common/ConcurrentListMultimap.java diff --git a/core/src/main/java/de/pxav/kelp/core/common/ConcurrentListMultimap.java b/core/src/main/java/de/pxav/kelp/core/common/ConcurrentListMultimap.java new file mode 100644 index 00000000..71a31bab --- /dev/null +++ b/core/src/main/java/de/pxav/kelp/core/common/ConcurrentListMultimap.java @@ -0,0 +1,214 @@ +package de.pxav.kelp.core.common; + +import com.google.common.collect.*; +import com.sun.istack.internal.NotNull; + +import javax.annotation.Nullable; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +public class ConcurrentListMultimap implements ConcurrentMultimap { + + private final ConcurrentMap> map; + + public static ConcurrentListMultimap create() { + return new ConcurrentListMultimap<>(); + } + + public static ConcurrentListMultimap create(Multimap source) { + ConcurrentListMultimap multimap = new ConcurrentListMultimap<>(); + multimap.putAll(source); + return multimap; + } + + public static ConcurrentListMultimap create(Map source) { + ConcurrentListMultimap multimap = new ConcurrentListMultimap<>(); + source.forEach(multimap::put); + multimap.putAll(source); + return multimap; + } + + public ConcurrentListMultimap() { + this.map = new ConcurrentHashMap<>(); + } + + @Override + public int size() { + int size = 0; + for (Collection value : this.map.values()) { + size += value.size(); + } + return size; + } + + @Override + public boolean isEmpty() { + return this.map.isEmpty(); + } + + @Override + public boolean containsKey(@NotNull Object o) { + return this.map.containsKey(o); + } + + @Override + public boolean containsValue(@NotNull Object o) { + Iterator> iterator = this.asMap().values().iterator(); + + Collection collection; + + do { + if (!iterator.hasNext()) { + return false; + } + + collection = iterator.next(); + } while(!collection.contains(o)); + + return true; + } + + @Override + public boolean containsEntry(@NotNull Object o, @NotNull Object o1) { + Collection collection = this.map.get(o); + return collection != null && collection.contains(o1); + } + + @Override + public boolean put(@NotNull K k, @NotNull V v) { + if (this.get(k) == null) { + this.map.put(k, Lists.newArrayList()); + } + return this.get(k).add(v); + } + + @Override + public boolean remove(@NotNull Object key, @NotNull Object value) { + if (this.map.get(key) == null) { + return false; + } + return this.map.get(key).remove(value); + } + + @Override + public boolean putAll(@NotNull K k, Iterable iterable) { + if (iterable instanceof Collection) { + Collection collection = (Collection) iterable; + if (collection.isEmpty()) { + return false; + } + + if (this.get(k) == null) { + this.map.put(k, Lists.newArrayList()); + } + return this.get(k).addAll(collection); + } else { + Iterator valueItr = iterable.iterator(); + if (!valueItr.hasNext()) { + return false; + } + + if (this.get(k) == null) { + this.map.put(k, Lists.newArrayList()); + } + return Iterators.addAll(this.get(k), valueItr); + } + } + + @Override + public boolean putAll(Multimap multimap) { + for (Map.Entry entry : multimap.entries()) { + this.put(entry.getKey(), entry.getValue()); + } + return false; + } + + @Override + public void putAll(Map newMap) { + for (Map.Entry entry : newMap.entrySet()) { + this.put(entry.getKey(), entry.getValue()); + } + } + + @Override + public Collection replaceValues(@NotNull K k, Iterable iterable) { + Collection result = this.removeAll(k); + this.putAll(k, iterable); + return result; + } + + @Override + public Collection removeAll(@NotNull Object key) { + if (this.map.get(key) == null) { + return Lists.newArrayList(); + } + return this.map.remove(key); + } + + @Override + public void clear() { + this.map.clear(); + } + + @Override + public Collection get(@Nullable K k) { + return this.map.get(k); + } + + @Override + public Set keySet() { + return this.map.keySet(); + } + + @Override + public Multiset keys() { + return HashMultiset.create(this.map.keySet()); + } + + @Override + public Collection values() { + Iterator> iterator = this.asMap().values().iterator(); + Collection result = Lists.newArrayList(); + + while (iterator.hasNext()) { + result.addAll(iterator.next()); + } + + return result; + } + + @Override + public Collection> entries() { + Collection> entries = Lists.newArrayList(); + this.map.forEach((key, valueSet) -> valueSet.forEach(element -> { + AbstractMap.SimpleEntry entry = new AbstractMap.SimpleEntry<>(key, element); + entries.add(entry); + })); + return entries; + } + + + @Override + public ConcurrentMap> asMap() { + ConcurrentMap> output = Maps.newConcurrentMap(); + output.putAll(this.map); + return output; + } + + @Override + public boolean containsValue(Collection iterable) { + return this.map.containsValue(iterable); + } + + @Override + public Collection getOrDefault(K key, Collection defaultCollection) { + return this.map.getOrDefault(key, Lists.newArrayList(defaultCollection)); + } + + @Override + public Collection getOrEmpty(K key) { + return this.map.getOrDefault(key, Lists.newArrayList()); + } + +} From bf109f630f8d80e636e96d78aadd46cc18a5d61e Mon Sep 17 00:00:00 2001 From: pxav Date: Thu, 18 Mar 2021 17:57:25 +0100 Subject: [PATCH 43/81] Add concurrent multimap using sets to save value data (no dupes) --- .../core/common/ConcurrentSetMultimap.java | 213 ++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 core/src/main/java/de/pxav/kelp/core/common/ConcurrentSetMultimap.java diff --git a/core/src/main/java/de/pxav/kelp/core/common/ConcurrentSetMultimap.java b/core/src/main/java/de/pxav/kelp/core/common/ConcurrentSetMultimap.java new file mode 100644 index 00000000..35a209d9 --- /dev/null +++ b/core/src/main/java/de/pxav/kelp/core/common/ConcurrentSetMultimap.java @@ -0,0 +1,213 @@ +package de.pxav.kelp.core.common; + +import com.google.common.collect.*; +import com.sun.istack.internal.NotNull; + +import javax.annotation.Nullable; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +public class ConcurrentSetMultimap implements ConcurrentMultimap { + + private final ConcurrentMap> map; + + public static ConcurrentSetMultimap create() { + return new ConcurrentSetMultimap<>(); + } + + public static ConcurrentSetMultimap create(Multimap source) { + ConcurrentSetMultimap multimap = new ConcurrentSetMultimap<>(); + multimap.putAll(source); + return multimap; + } + + public static ConcurrentSetMultimap create(Map source) { + ConcurrentSetMultimap multimap = new ConcurrentSetMultimap<>(); + source.forEach(multimap::put); + multimap.putAll(source); + return multimap; + } + + public ConcurrentSetMultimap() { + this.map = new ConcurrentHashMap<>(); + } + + @Override + public int size() { + int size = 0; + for (Collection value : this.map.values()) { + size += value.size(); + } + return size; + } + + @Override + public boolean isEmpty() { + return this.map.isEmpty(); + } + + @Override + public boolean containsKey(@NotNull Object o) { + return this.map.containsKey(o); + } + + @Override + public boolean containsValue(@NotNull Object o) { + Iterator> iterator = this.asMap().values().iterator(); + + Collection collection; + + do { + if (!iterator.hasNext()) { + return false; + } + + collection = iterator.next(); + } while(!collection.contains(o)); + + return true; + } + + @Override + public boolean containsEntry(@NotNull Object o, @NotNull Object o1) { + Collection collection = this.map.get(o); + return collection != null && collection.contains(o1); + } + + @Override + public boolean put(@NotNull K k, @NotNull V v) { + if (this.get(k) == null) { + this.map.put(k, Sets.newHashSet()); + } + return this.get(k).add(v); + } + + @Override + public boolean remove(@NotNull Object key, @NotNull Object value) { + if (this.map.get(key) == null) { + return false; + } + return this.map.get(key).remove(value); + } + + @Override + public boolean putAll(@NotNull K k, Iterable iterable) { + if (iterable instanceof Collection) { + Collection collection = (Collection) iterable; + if (collection.isEmpty()) { + return false; + } + + if (this.get(k) == null) { + this.map.put(k, Sets.newHashSet()); + } + return this.get(k).addAll(collection); + } else { + Iterator valueItr = iterable.iterator(); + if (!valueItr.hasNext()) { + return false; + } + + if (this.get(k) == null) { + this.map.put(k, Sets.newHashSet()); + } + return Iterators.addAll(this.get(k), valueItr); + } + } + + @Override + public boolean putAll(Multimap multimap) { + for (Map.Entry entry : multimap.entries()) { + this.put(entry.getKey(), entry.getValue()); + } + return false; + } + + @Override + public void putAll(Map newMap) { + for (Map.Entry entry : newMap.entrySet()) { + this.put(entry.getKey(), entry.getValue()); + } + } + + @Override + public Collection replaceValues(@NotNull K k, Iterable iterable) { + Collection result = this.removeAll(k); + this.putAll(k, iterable); + return result; + } + + @Override + public Collection removeAll(@NotNull Object key) { + if (this.map.get(key) == null) { + return Lists.newArrayList(); + } + return this.map.remove(key); + } + + @Override + public void clear() { + this.map.clear(); + } + + @Override + public Collection get(@Nullable K k) { + return this.map.get(k); + } + + @Override + public Set keySet() { + return this.map.keySet(); + } + + @Override + public Multiset keys() { + return HashMultiset.create(this.map.keySet()); + } + + @Override + public Collection values() { + Iterator> iterator = this.asMap().values().iterator(); + Collection result = Lists.newArrayList(); + + while (iterator.hasNext()) { + result.addAll(iterator.next()); + } + + return result; + } + + @Override + public Collection> entries() { + Collection> entries = Sets.newHashSet(); + this.map.forEach((key, valueSet) -> valueSet.forEach(element -> { + AbstractMap.SimpleEntry entry = new AbstractMap.SimpleEntry<>(key, element); + entries.add(entry); + })); + return entries; + } + + + @Override + public ConcurrentMap> asMap() { + ConcurrentMap> output = Maps.newConcurrentMap(); + output.putAll(this.map); + return output; + } + + @Override + public boolean containsValue(Collection iterable) { + return this.map.containsValue(iterable); + } + + @Override + public Collection getOrDefault(K key, Collection defaultCollection) { + return this.map.getOrDefault(key, Sets.newHashSet(defaultCollection)); + } + + @Override + public Collection getOrEmpty(K key) { + return this.map.getOrDefault(key, Sets.newHashSet()); + } +} From e9bdc962da774c87e378d76c1ed662a3487f45be Mon Sep 17 00:00:00 2001 From: pxav Date: Thu, 18 Mar 2021 18:00:42 +0100 Subject: [PATCH 44/81] Set multimap is now actually returning sets in default methods --- .../java/de/pxav/kelp/core/common/ConcurrentSetMultimap.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/common/ConcurrentSetMultimap.java b/core/src/main/java/de/pxav/kelp/core/common/ConcurrentSetMultimap.java index 35a209d9..3a436b3a 100644 --- a/core/src/main/java/de/pxav/kelp/core/common/ConcurrentSetMultimap.java +++ b/core/src/main/java/de/pxav/kelp/core/common/ConcurrentSetMultimap.java @@ -202,12 +202,12 @@ public boolean containsValue(Collection iterable) { } @Override - public Collection getOrDefault(K key, Collection defaultCollection) { + public Set getOrDefault(K key, Collection defaultCollection) { return this.map.getOrDefault(key, Sets.newHashSet(defaultCollection)); } @Override - public Collection getOrEmpty(K key) { + public Set getOrEmpty(K key) { return this.map.getOrDefault(key, Sets.newHashSet()); } } From af4d1a4a17490c2124c42641da62a6e474611a01 Mon Sep 17 00:00:00 2001 From: pxav Date: Thu, 18 Mar 2021 18:20:57 +0100 Subject: [PATCH 45/81] Add method to multimap removing all entries with a specific value --- .../de/pxav/kelp/core/common/ConcurrentListMultimap.java | 9 +++++++++ .../de/pxav/kelp/core/common/ConcurrentMultimap.java | 2 ++ .../de/pxav/kelp/core/common/ConcurrentSetMultimap.java | 9 +++++++++ 3 files changed, 20 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/common/ConcurrentListMultimap.java b/core/src/main/java/de/pxav/kelp/core/common/ConcurrentListMultimap.java index 71a31bab..ceaebe43 100644 --- a/core/src/main/java/de/pxav/kelp/core/common/ConcurrentListMultimap.java +++ b/core/src/main/java/de/pxav/kelp/core/common/ConcurrentListMultimap.java @@ -211,4 +211,13 @@ public Collection getOrEmpty(K key) { return this.map.getOrDefault(key, Lists.newArrayList()); } + @Override + public void removeWithValue(V value) { + this.map.forEach((key, valueSet) -> valueSet.forEach(current -> { + if (current.equals(value)) { + this.remove(key, current); + } + })); + } + } diff --git a/core/src/main/java/de/pxav/kelp/core/common/ConcurrentMultimap.java b/core/src/main/java/de/pxav/kelp/core/common/ConcurrentMultimap.java index 8b8a1772..bb6781c5 100644 --- a/core/src/main/java/de/pxav/kelp/core/common/ConcurrentMultimap.java +++ b/core/src/main/java/de/pxav/kelp/core/common/ConcurrentMultimap.java @@ -13,6 +13,8 @@ public interface ConcurrentMultimap extends Multimap { Collection getOrEmpty(K key); + void removeWithValue(V value); + void putAll(Map newMap); } diff --git a/core/src/main/java/de/pxav/kelp/core/common/ConcurrentSetMultimap.java b/core/src/main/java/de/pxav/kelp/core/common/ConcurrentSetMultimap.java index 3a436b3a..31f65be2 100644 --- a/core/src/main/java/de/pxav/kelp/core/common/ConcurrentSetMultimap.java +++ b/core/src/main/java/de/pxav/kelp/core/common/ConcurrentSetMultimap.java @@ -210,4 +210,13 @@ public Set getOrDefault(K key, Collection defaultCollection) { public Set getOrEmpty(K key) { return this.map.getOrDefault(key, Sets.newHashSet()); } + + @Override + public void removeWithValue(V value) { + this.map.forEach((key, valueSet) -> valueSet.forEach(current -> { + if (current.equals(value)) { + this.remove(key, current); + } + })); + } } From b6b424b06c7bc262b77772e16fcfb4821068d8cd Mon Sep 17 00:00:00 2001 From: pxav Date: Thu, 18 Mar 2021 18:44:22 +0100 Subject: [PATCH 46/81] Add class representing an approximate location in a range of 100 blocks --- .../world/region/ApproximateLocation.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 core/src/main/java/de/pxav/kelp/core/world/region/ApproximateLocation.java diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/ApproximateLocation.java b/core/src/main/java/de/pxav/kelp/core/world/region/ApproximateLocation.java new file mode 100644 index 00000000..58c5aee7 --- /dev/null +++ b/core/src/main/java/de/pxav/kelp/core/world/region/ApproximateLocation.java @@ -0,0 +1,83 @@ +package de.pxav.kelp.core.world.region; + +import de.pxav.kelp.core.world.KelpLocation; +import de.pxav.kelp.core.world.KelpWorld; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.bukkit.Location; + +public class ApproximateLocation { + + private int x; + private int z; + private String worldName; + + public static ApproximateLocation from(KelpLocation location) { + return new ApproximateLocation(location.getWorldName(), location.getBlockX(), location.getBlockZ()); + } + + public static ApproximateLocation from(Location location) { + return new ApproximateLocation(location.getWorld().getName(), location.getBlockX(), location.getBlockZ()); + } + + public static ApproximateLocation create(String worldName, int x, int z) { + return new ApproximateLocation(worldName, x, z); + } + + public static ApproximateLocation create(KelpWorld world, int x, int z) { + return create(world.getName(), x, z); + } + + private ApproximateLocation(KelpLocation location) { + this.x = location.getBlockX() / 100; + this.z = location.getBlockZ() / 100; + this.worldName = location.getWorldName(); + } + + private ApproximateLocation(String worldName, int x, int z) { + this.x = x / 100; + this.z = z / 100; + this.worldName = worldName; + } + + public int getX() { + return x; + } + + public int getZ() { + return z; + } + + public String getWorldName() { + return worldName; + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(this.x) + .append(this.z) + .append(this.worldName) + .toHashCode(); + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof ApproximateLocation)) { + return false; + } + ApproximateLocation location = (ApproximateLocation) object; + + return location.getWorldName().equalsIgnoreCase(this.worldName) + && location.getX() == this.getX() + && location.getZ() == this.getZ(); + } + + @Override + public String toString() { + return "ApproximateLocation{" + + "x=" + x + + ", z=" + z + + ", worldName='" + worldName + '\'' + + '}'; + } +} From ac43d902a4c97bd008d0a44489d952e7af3fb8ed Mon Sep 17 00:00:00 2001 From: pxav Date: Thu, 18 Mar 2021 18:44:34 +0100 Subject: [PATCH 47/81] Add new base event for KelpRegions --- .../core/event/kelpevent/KelpRegionEvent.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 core/src/main/java/de/pxav/kelp/core/event/kelpevent/KelpRegionEvent.java diff --git a/core/src/main/java/de/pxav/kelp/core/event/kelpevent/KelpRegionEvent.java b/core/src/main/java/de/pxav/kelp/core/event/kelpevent/KelpRegionEvent.java new file mode 100644 index 00000000..7fa889c1 --- /dev/null +++ b/core/src/main/java/de/pxav/kelp/core/event/kelpevent/KelpRegionEvent.java @@ -0,0 +1,18 @@ +package de.pxav.kelp.core.event.kelpevent; + +import de.pxav.kelp.core.world.region.KelpRegion; +import org.bukkit.event.Event; + +public abstract class KelpRegionEvent extends Event { + + protected KelpRegion region; + + public KelpRegionEvent(KelpRegion region) { + this.region = region; + } + + public KelpRegion getRegion() { + return region; + } + +} From d13b80aaf880f537af59cc51150b5c6362c56fd7 Mon Sep 17 00:00:00 2001 From: pxav Date: Thu, 18 Mar 2021 18:44:56 +0100 Subject: [PATCH 48/81] Add new events which are triggered when a player leaves/enters a region --- .../kelpevent/PlayerEnterRegionEvent.java | 31 +++++++++++++ .../kelpevent/PlayerLeaveRegionEvent.java | 43 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerEnterRegionEvent.java create mode 100644 core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerLeaveRegionEvent.java diff --git a/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerEnterRegionEvent.java b/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerEnterRegionEvent.java new file mode 100644 index 00000000..6bdcbbaf --- /dev/null +++ b/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerEnterRegionEvent.java @@ -0,0 +1,31 @@ +package de.pxav.kelp.core.event.kelpevent; + +import de.pxav.kelp.core.player.KelpPlayer; +import de.pxav.kelp.core.world.region.KelpRegion; +import org.bukkit.event.HandlerList; + +public class PlayerEnterRegionEvent extends KelpRegionEvent { + + private static final HandlerList handlers = new HandlerList(); + + private KelpPlayer player; + + public PlayerEnterRegionEvent(KelpRegion region, KelpPlayer player) { + super(region); + this.player = player; + } + + public KelpPlayer getPlayer() { + return player; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + +} diff --git a/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerLeaveRegionEvent.java b/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerLeaveRegionEvent.java new file mode 100644 index 00000000..1cabc158 --- /dev/null +++ b/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerLeaveRegionEvent.java @@ -0,0 +1,43 @@ +package de.pxav.kelp.core.event.kelpevent; + +import de.pxav.kelp.core.player.KelpPlayer; +import de.pxav.kelp.core.world.region.KelpRegion; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +public class PlayerLeaveRegionEvent extends KelpRegionEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + private KelpPlayer player; + private boolean cancelled; + + public PlayerLeaveRegionEvent(KelpRegion region, KelpPlayer player) { + super(region); + this.player = player; + } + + public KelpPlayer getPlayer() { + return player; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + +} From 705e460098b06d312a9c042c1bc6a9b3e4b5725b Mon Sep 17 00:00:00 2001 From: pxav Date: Thu, 18 Mar 2021 18:45:24 +0100 Subject: [PATCH 49/81] Remove istack dependency from concurrent multimaps --- .../core/common/ConcurrentListMultimap.java | 17 ++++++++--------- .../kelp/core/common/ConcurrentSetMultimap.java | 17 ++++++++--------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/common/ConcurrentListMultimap.java b/core/src/main/java/de/pxav/kelp/core/common/ConcurrentListMultimap.java index ceaebe43..f35e7a91 100644 --- a/core/src/main/java/de/pxav/kelp/core/common/ConcurrentListMultimap.java +++ b/core/src/main/java/de/pxav/kelp/core/common/ConcurrentListMultimap.java @@ -1,7 +1,6 @@ package de.pxav.kelp.core.common; import com.google.common.collect.*; -import com.sun.istack.internal.NotNull; import javax.annotation.Nullable; import java.util.*; @@ -48,12 +47,12 @@ public boolean isEmpty() { } @Override - public boolean containsKey(@NotNull Object o) { + public boolean containsKey(Object o) { return this.map.containsKey(o); } @Override - public boolean containsValue(@NotNull Object o) { + public boolean containsValue(Object o) { Iterator> iterator = this.asMap().values().iterator(); Collection collection; @@ -70,13 +69,13 @@ public boolean containsValue(@NotNull Object o) { } @Override - public boolean containsEntry(@NotNull Object o, @NotNull Object o1) { + public boolean containsEntry(Object o, Object o1) { Collection collection = this.map.get(o); return collection != null && collection.contains(o1); } @Override - public boolean put(@NotNull K k, @NotNull V v) { + public boolean put(K k, V v) { if (this.get(k) == null) { this.map.put(k, Lists.newArrayList()); } @@ -84,7 +83,7 @@ public boolean put(@NotNull K k, @NotNull V v) { } @Override - public boolean remove(@NotNull Object key, @NotNull Object value) { + public boolean remove(Object key, Object value) { if (this.map.get(key) == null) { return false; } @@ -92,7 +91,7 @@ public boolean remove(@NotNull Object key, @NotNull Object value) { } @Override - public boolean putAll(@NotNull K k, Iterable iterable) { + public boolean putAll(K k, Iterable iterable) { if (iterable instanceof Collection) { Collection collection = (Collection) iterable; if (collection.isEmpty()) { @@ -132,14 +131,14 @@ public void putAll(Map newMap) { } @Override - public Collection replaceValues(@NotNull K k, Iterable iterable) { + public Collection replaceValues(K k, Iterable iterable) { Collection result = this.removeAll(k); this.putAll(k, iterable); return result; } @Override - public Collection removeAll(@NotNull Object key) { + public Collection removeAll(Object key) { if (this.map.get(key) == null) { return Lists.newArrayList(); } diff --git a/core/src/main/java/de/pxav/kelp/core/common/ConcurrentSetMultimap.java b/core/src/main/java/de/pxav/kelp/core/common/ConcurrentSetMultimap.java index 31f65be2..55c3b787 100644 --- a/core/src/main/java/de/pxav/kelp/core/common/ConcurrentSetMultimap.java +++ b/core/src/main/java/de/pxav/kelp/core/common/ConcurrentSetMultimap.java @@ -1,7 +1,6 @@ package de.pxav.kelp.core.common; import com.google.common.collect.*; -import com.sun.istack.internal.NotNull; import javax.annotation.Nullable; import java.util.*; @@ -48,12 +47,12 @@ public boolean isEmpty() { } @Override - public boolean containsKey(@NotNull Object o) { + public boolean containsKey(Object o) { return this.map.containsKey(o); } @Override - public boolean containsValue(@NotNull Object o) { + public boolean containsValue(Object o) { Iterator> iterator = this.asMap().values().iterator(); Collection collection; @@ -70,13 +69,13 @@ public boolean containsValue(@NotNull Object o) { } @Override - public boolean containsEntry(@NotNull Object o, @NotNull Object o1) { + public boolean containsEntry(Object o, Object o1) { Collection collection = this.map.get(o); return collection != null && collection.contains(o1); } @Override - public boolean put(@NotNull K k, @NotNull V v) { + public boolean put(K k, V v) { if (this.get(k) == null) { this.map.put(k, Sets.newHashSet()); } @@ -84,7 +83,7 @@ public boolean put(@NotNull K k, @NotNull V v) { } @Override - public boolean remove(@NotNull Object key, @NotNull Object value) { + public boolean remove(Object key, Object value) { if (this.map.get(key) == null) { return false; } @@ -92,7 +91,7 @@ public boolean remove(@NotNull Object key, @NotNull Object value) { } @Override - public boolean putAll(@NotNull K k, Iterable iterable) { + public boolean putAll(K k, Iterable iterable) { if (iterable instanceof Collection) { Collection collection = (Collection) iterable; if (collection.isEmpty()) { @@ -132,14 +131,14 @@ public void putAll(Map newMap) { } @Override - public Collection replaceValues(@NotNull K k, Iterable iterable) { + public Collection replaceValues(K k, Iterable iterable) { Collection result = this.removeAll(k); this.putAll(k, iterable); return result; } @Override - public Collection removeAll(@NotNull Object key) { + public Collection removeAll(Object key) { if (this.map.get(key) == null) { return Lists.newArrayList(); } From 298c4c021666a64153ad0ab3f9a6eef6e4bcb8b9 Mon Sep 17 00:00:00 2001 From: pxav Date: Thu, 18 Mar 2021 18:45:58 +0100 Subject: [PATCH 50/81] Region repository can now trigger region leave and enter events --- .../world/region/KelpRegionRepository.java | 92 ++++++++++++++----- 1 file changed, 70 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java index e5181363..d1d11136 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java @@ -1,55 +1,84 @@ package de.pxav.kelp.core.world.region; -import com.google.common.collect.*; +import de.pxav.kelp.core.common.ConcurrentMultimap; +import de.pxav.kelp.core.common.ConcurrentSetMultimap; +import de.pxav.kelp.core.event.kelpevent.PlayerEnterRegionEvent; +import de.pxav.kelp.core.event.kelpevent.PlayerLeaveRegionEvent; +import de.pxav.kelp.core.player.KelpPlayer; import de.pxav.kelp.core.scheduler.KelpSchedulerRepository; import de.pxav.kelp.core.scheduler.type.RepeatingScheduler; import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerJoinEvent; import javax.inject.Inject; import javax.inject.Singleton; -import java.util.Map; import java.util.UUID; @Singleton public class KelpRegionRepository { - // region id -> region object - private Map listenedRegions; + private ConcurrentSetMultimap nearRegions; // player -> region id - private Multimap containedBy; + private ConcurrentMultimap containedBy; - private boolean listenerRunning = false; + private UUID task; private KelpSchedulerRepository schedulerRepository; @Inject public KelpRegionRepository(KelpSchedulerRepository schedulerRepository) { - this.listenedRegions = Maps.newConcurrentMap(); - this.containedBy = ArrayListMultimap.create(); + this.containedBy = ConcurrentSetMultimap.create(); + this.nearRegions = ConcurrentSetMultimap.create(); this.schedulerRepository = schedulerRepository; } public void listenTo(KelpRegion region) { - this.listenedRegions.put(region.getRegionId(), region); - if (!listenerRunning) { + ApproximateLocation maxPos = ApproximateLocation.from(region.getMaxPos()); + ApproximateLocation minPos = ApproximateLocation.from(region.getMinPos()); + + for (int x = minPos.getX(); x <= maxPos.getX(); x++) { + for (int z = minPos.getZ(); z <= maxPos.getZ(); z++) { + ApproximateLocation current = ApproximateLocation.create(maxPos.getWorldName(), x, z); + nearRegions.put(current, region); + } + } + + if (!isListenerRunning()) { startListenerTasks(); } } public void stopListeningTo(KelpRegion region) { - this.listenedRegions.remove(region.getRegionId()); - + ApproximateLocation maxPos = ApproximateLocation.from(region.getMaxPos()); + ApproximateLocation minPos = ApproximateLocation.from(region.getMinPos()); + + for (int x = minPos.getX(); x <= maxPos.getX(); x++) { + for (int z = minPos.getZ(); z <= maxPos.getZ(); z++) { + ApproximateLocation current = ApproximateLocation.create(maxPos.getWorldName(), x, z); + nearRegions.remove(current, region); + } + } } public boolean isListeningTo(KelpRegion region) { - return this.listenedRegions.containsKey(region.getRegionId()); + return nearRegions.containsValue(region); + } + + @EventHandler + public void handlePlayerJoin(PlayerJoinEvent event) { + if (nearRegions.isEmpty()) { + return; + } + + this.startListenerTasks(); } - public void startListenerTasks() { - RepeatingScheduler.create() + private void startListenerTasks() { + task = RepeatingScheduler.create() .async() .every(100) .milliseconds() @@ -57,24 +86,43 @@ public void startListenerTasks() { .run(taskId -> { // if there are no regions to listen to anymore, cancel the scheduler - if (listenedRegions.isEmpty()) { - listenerRunning = false; - schedulerRepository.interruptScheduler(taskId); + if (nearRegions.isEmpty() || Bukkit.getOnlinePlayers().size() == 0) { + stopListenerTasks(); } - listenerRunning = true; - for (Player player : Bukkit.getOnlinePlayers()) { - listenedRegions.forEach((regionId, region) -> { + nearRegions.getOrEmpty(ApproximateLocation.from(player.getLocation())).forEach(region -> { if (!region.getWorldName().equalsIgnoreCase(player.getWorld().getName())) { return; } - + boolean contains = region.contains( + player.getLocation().getX(), + player.getLocation().getY(), + player.getLocation().getZ() + ); + + if (contains && !containedBy.containsEntry(player.getUniqueId(), region.getRegionId())) { + this.containedBy.put(player.getUniqueId(), region.getRegionId()); + Bukkit.getPluginManager().callEvent(new PlayerEnterRegionEvent(region, KelpPlayer.from(player))); + } else if (!contains && containedBy.containsEntry(player.getUniqueId(), region.getRegionId())) { + this.containedBy.remove(player.getUniqueId(), region.getRegionId()); + Bukkit.getPluginManager().callEvent(new PlayerLeaveRegionEvent(region, KelpPlayer.from(player))); + } }); } }); } + private void stopListenerTasks() { + if (task != null) { + schedulerRepository.interruptScheduler(task); + task = null; + } + } + + private boolean isListenerRunning() { + return task != null; + } } From 64dbce2624199e6e49beaf5d4af71795decb8124 Mon Sep 17 00:00:00 2001 From: pxav Date: Thu, 18 Mar 2021 18:46:19 +0100 Subject: [PATCH 51/81] Listeners can now be enabled/disabled from the KelpRegion class individually --- .../de/pxav/kelp/core/world/region/KelpRegion.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java index f3bebe91..fbbb3fa4 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java @@ -136,6 +136,18 @@ public UUID getRegionId() { return regionId; } + public void enableListeners() { + regionRepository.listenTo(this); + } + + public void disableListeners() { + regionRepository.stopListeningTo(this); + } + + public boolean listenersEnabled() { + return regionRepository.isListeningTo(this); + } + @Override public abstract KelpRegion clone(); From 99a0de5fd4de51a5c9e66123c9757ae279860ebb Mon Sep 17 00:00:00 2001 From: pxav Date: Thu, 18 Mar 2021 18:47:02 +0100 Subject: [PATCH 52/81] Add documentation for KelpBlockFace --- .../kelp/core/world/util/KelpBlockFace.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/util/KelpBlockFace.java b/core/src/main/java/de/pxav/kelp/core/world/util/KelpBlockFace.java index eedabdc9..cbd9e37b 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/util/KelpBlockFace.java +++ b/core/src/main/java/de/pxav/kelp/core/world/util/KelpBlockFace.java @@ -1,8 +1,19 @@ package de.pxav.kelp.core.world.util; +import de.pxav.kelp.core.world.KelpBlock; import org.bukkit.block.BlockFace; import org.bukkit.util.Vector; +/** + * Represents the face of a given block. This face can point + * in a cardinal direction such as {@code NORTH} or {@code SOUTH} + * or around the y-axis with {@link KelpBlockFace#UP} and {@link KelpBlockFace#DOWN}. + * + * This can be used to get neighbouring blocks of another block for example + * with {@link KelpBlock#getBlockBelow()}. + * + * @author pxav + */ public enum KelpBlockFace { NORTH(0, 0, -1), @@ -23,6 +34,10 @@ public enum KelpBlockFace { SOUTH_SOUTH_EAST(SOUTH, SOUTH_EAST), SOUTH_SOUTH_WEST(SOUTH, SOUTH_WEST), WEST_SOUTH_WEST(WEST, SOUTH_WEST), + + /** + * Represents the current block and no specific face of it. + */ SELF(0, 0, 0); private final int deltaX; @@ -45,18 +60,42 @@ public static KelpBlockFace from(BlockFace blockFace) { this.deltaZ = face1.getDeltaZ() + face2.getDeltaZ(); } + /** + * Gets the difference from the original block to the + * current block face in the x direction. + * + * @return The distance from the original block to the current face on the x-axis. + */ public int getDeltaX() { return this.deltaX; } + /** + * Gets the difference from the original block to the + * current block face in the y direction. + * + * @return The distance from the original block to the current face on the y-axis. + */ public int getDeltaY() { return this.deltaY; } + /** + * Gets the difference from the original block to the + * current block face in the z direction. + * + * @return The distance from the original block to the current face on the z-axis. + */ public int getDeltaZ() { return this.deltaZ; } + /** + * Converts the current block face into a {@link Vector}. + * This vector will point in the cardinal direction of the face. + * + * @return A vector pointing in the direction of the current block face. + */ public Vector getDirection() { Vector direction = new Vector(this.deltaX, this.deltaY, this.deltaZ); if (this.deltaX != 0 || this.deltaY != 0 || this.deltaZ != 0) { @@ -66,6 +105,13 @@ public Vector getDirection() { return direction; } + /** + * Gets the block face that is opposite of the current block face. + * If the current block face is {@code NORTH} for example, this will return + * {@code SOUTH}. If it is {@code NORTH_EAST}, it will return {@code SOUTH_WEST} and so on. + * + * @return The block face opposite to the current block face. + */ public KelpBlockFace getOppositeFace() { switch(this) { case NORTH: @@ -109,6 +155,12 @@ public KelpBlockFace getOppositeFace() { } } + /** + * Converts the current block face into a block face of + * the bukkit library. + * + * @return The bukkit block face equivalent to the current block face. + */ public BlockFace getBukkitFace() { return BlockFace.valueOf(this.toString()); } From 9c65a687b51bf3496f48d842b4ec151319dc113b Mon Sep 17 00:00:00 2001 From: pxav Date: Thu, 18 Mar 2021 19:01:54 +0100 Subject: [PATCH 53/81] Fix bug that worldName of EllipsoidRegion was always null --- .../de/pxav/kelp/core/world/region/EllipsoidRegion.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java index 79673e3b..eb457c34 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java @@ -32,6 +32,7 @@ public static EllipsoidRegion create(KelpLocation center, double radius) { region.setRadius(radius); region.minPos = center.clone().subtract(radius); region.maxPos = center.clone().add(radius); + region.worldName = center.getWorldName(); return region; } @@ -43,15 +44,20 @@ public static EllipsoidRegion create(KelpLocation center, double xRadius, double region.setZRadius(zRadius); region.minPos = center.clone().subtract(xRadius, yRadius, zRadius); region.maxPos = center.clone().add(xRadius, yRadius, zRadius); + region.worldName = center.getWorldName(); return region; } public static EllipsoidRegion create(KelpLocation pos1, KelpLocation pos2) { + if (!pos1.getWorldName().equalsIgnoreCase(pos2.getWorldName())) { + throw new IllegalArgumentException("Cannot create region from locations of differing worlds!"); + } EllipsoidRegion region = new EllipsoidRegion(getRegionRepository()); region.maxPos = pos1.getMaximalLocation(pos2); region.minPos = pos1.getMinimalLocation(pos2); region.setCenter(pos1.findMidpoint(pos2)); + region.worldName = pos1.getWorldName(); region.setXRadius(Math.abs(region.minPos.getX() - region.maxPos.getX()) * 0.5); region.setYRadius(Math.abs(region.minPos.getY() - region.maxPos.getY()) * 0.5); region.setZRadius(Math.abs(region.minPos.getZ() - region.maxPos.getZ()) * 0.5); From d98b6c9d6b62f1c8b5a2931f2e9128229817607c Mon Sep 17 00:00:00 2001 From: pxav Date: Thu, 18 Mar 2021 20:07:30 +0100 Subject: [PATCH 54/81] Fix bug that contains() method of EllipsoidRegion did not calculate the limiters correctly --- .../kelp/core/world/region/EllipsoidRegion.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java index eb457c34..584adf2c 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java @@ -303,19 +303,23 @@ public void expandAndMove(double negativeX, double positiveX, double negativeY, @Override public boolean contains(double x, double y, double z) { // if the location is excluded due to a limited radius - boolean limitCriteria = false; + boolean limitCriteriaX = false, limitCriteriaY = false, limitCriteriaZ = false; if (limitX > 0) { - limitCriteria = x > center.clone().addX(limitX).getX() || x < center.clone().subtractX(limitX).getX(); + System.out.println(x + " > " + center.getX() + " + " + limitX + " (" + ((center.getX() + limitX)) + ")"); + System.out.println(x + " < " + center.getX() + " - " + limitX + " (" + ((center.getX() - limitX)) + ")"); + limitCriteriaX = x > (center.getX() + limitX) || x < (center.getX() - limitX); } + if (limitY > 0) { - limitCriteria = y > center.clone().addY(limitY).getY() || y < center.clone().subtractY(limitY).getY(); + limitCriteriaY = y > (center.getY() + limitY) || y < (center.getY() - limitY); } + if (limitZ > 0) { - limitCriteria = z > center.clone().addZ(limitZ).getZ() || z < center.clone().subtractZ(limitZ).getZ(); + limitCriteriaZ = z > (center.getZ() + limitZ) || z < (center.getZ() - limitZ); } - return getCostAt(x, y, z) <= 1 && !limitCriteria; + return getCostAt(x, y, z) <= 1 && !limitCriteriaX && !limitCriteriaY && !limitCriteriaZ; } public double getCostAt(KelpLocation location) { From c3f40da8d801d000ebeab113f22ccaffd13df77c Mon Sep 17 00:00:00 2001 From: pxav Date: Thu, 18 Mar 2021 20:09:22 +0100 Subject: [PATCH 55/81] Remove redundant constructor from ApproximateLocation --- .../de/pxav/kelp/core/world/region/ApproximateLocation.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/ApproximateLocation.java b/core/src/main/java/de/pxav/kelp/core/world/region/ApproximateLocation.java index 58c5aee7..6d3a4e43 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/ApproximateLocation.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/ApproximateLocation.java @@ -27,12 +27,6 @@ public static ApproximateLocation create(KelpWorld world, int x, int z) { return create(world.getName(), x, z); } - private ApproximateLocation(KelpLocation location) { - this.x = location.getBlockX() / 100; - this.z = location.getBlockZ() / 100; - this.worldName = location.getWorldName(); - } - private ApproximateLocation(String worldName, int x, int z) { this.x = x / 100; this.z = z / 100; From daed8e53e7d1ca8e987587cc87c66dd49ef66f91 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 17:26:03 +0100 Subject: [PATCH 56/81] Fix bug that region enter/exit detection did not work if location axis were bigger than 100 --- .../pxav/kelp/core/world/region/KelpRegionRepository.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java index d1d11136..be5313c0 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java @@ -40,8 +40,8 @@ public void listenTo(KelpRegion region) { ApproximateLocation maxPos = ApproximateLocation.from(region.getMaxPos()); ApproximateLocation minPos = ApproximateLocation.from(region.getMinPos()); - for (int x = minPos.getX(); x <= maxPos.getX(); x++) { - for (int z = minPos.getZ(); z <= maxPos.getZ(); z++) { + for (int x = region.getMinPos().getBlockX(); x <= region.getMaxPos().getBlockX(); x++) { + for (int z = region.getMinPos().getBlockZ(); z <= region.getMaxPos().getBlockZ(); z++) { ApproximateLocation current = ApproximateLocation.create(maxPos.getWorldName(), x, z); nearRegions.put(current, region); } @@ -70,11 +70,12 @@ public boolean isListeningTo(KelpRegion region) { @EventHandler public void handlePlayerJoin(PlayerJoinEvent event) { - if (nearRegions.isEmpty()) { + if (nearRegions.isEmpty() || this.isListenerRunning()) { return; } this.startListenerTasks(); + } private void startListenerTasks() { @@ -92,6 +93,7 @@ private void startListenerTasks() { for (Player player : Bukkit.getOnlinePlayers()) { nearRegions.getOrEmpty(ApproximateLocation.from(player.getLocation())).forEach(region -> { + if (!region.getWorldName().equalsIgnoreCase(player.getWorld().getName())) { return; } From c240b84d65c5f10a66d2845147ee30755b8be738 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 17:35:32 +0100 Subject: [PATCH 57/81] Fix bug that block limiter of EllipsoidRegion did not limit y and z axis --- .../kelp/core/world/region/EllipsoidRegion.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java index 584adf2c..0b9059a3 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java @@ -188,17 +188,22 @@ private Set getBlocks(boolean surfaceOnly) { if (limitX > 0 || limitY > 0 || limitZ > 0) { output.parallelStream() .filter(block -> { + // if the location is excluded due to a limited radius + boolean limitCriteriaX = false, limitCriteriaY = false, limitCriteriaZ = false; + if (limitX > 0) { - return block.getX() > center.clone().addX(limitX).getX() || block.getX() < center.clone().subtractX(limitX).getX(); + limitCriteriaX = block.getX() > (center.getX() + limitX) || block.getX() < (center.getX() - limitX); } + if (limitY > 0) { - return block.getY() > center.clone().addY(limitY).getY() || block.getY() < center.clone().subtractY(limitY).getY(); + limitCriteriaY = block.getY() > (center.getY() + limitY) || block.getY() < (center.getY() - limitY); } + if (limitZ > 0) { - return block.getZ() > center.clone().addZ(limitZ).getZ() || block.getZ() < center.clone().subtractZ(limitZ).getZ(); + limitCriteriaZ = block.getZ() > (center.getZ() + limitZ) || block.getZ() < (center.getZ() - limitZ); } - return false; + return limitCriteriaX || limitCriteriaY || limitCriteriaZ; }) .forEach(output::remove); @@ -306,8 +311,6 @@ public boolean contains(double x, double y, double z) { boolean limitCriteriaX = false, limitCriteriaY = false, limitCriteriaZ = false; if (limitX > 0) { - System.out.println(x + " > " + center.getX() + " + " + limitX + " (" + ((center.getX() + limitX)) + ")"); - System.out.println(x + " < " + center.getX() + " - " + limitX + " (" + ((center.getX() - limitX)) + ")"); limitCriteriaX = x > (center.getX() + limitX) || x < (center.getX() - limitX); } From 86f281c1cfe8e030c3db94dd8a4fe06dc807a7a0 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 17:41:29 +0100 Subject: [PATCH 58/81] Add simple unit test for ConcurrentListMultimap --- .../common/ConcurrentListMultimapTest.java | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 core/src/test/java/de/pxav/kelp/core/test/common/ConcurrentListMultimapTest.java diff --git a/core/src/test/java/de/pxav/kelp/core/test/common/ConcurrentListMultimapTest.java b/core/src/test/java/de/pxav/kelp/core/test/common/ConcurrentListMultimapTest.java new file mode 100644 index 00000000..47b5b20b --- /dev/null +++ b/core/src/test/java/de/pxav/kelp/core/test/common/ConcurrentListMultimapTest.java @@ -0,0 +1,91 @@ +package de.pxav.kelp.core.test.common; + +import com.google.common.collect.Maps; +import de.pxav.kelp.core.common.ConcurrentListMultimap; +import de.pxav.kelp.core.common.ConcurrentMultimap; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.Map; + +/** + * This class is a basic test for the features of a list-based + * concurrent multimap provided by Kelp, {@link ConcurrentListMultimap} to be + * specific. + * + * @author pxav + */ +public class ConcurrentListMultimapTest { + + private ConcurrentMultimap map; + + /** + * Refreshes the instance of the map before each test + * so we get neutral and unaffected results in each test. + */ + @Before + public void initMap() { + this.map = ConcurrentListMultimap.create(); + } + + /** + * Tests basic insert operations of the map including: + * - {@link ConcurrentListMultimap#put(Object, Object)} + * - {@link ConcurrentListMultimap#putAll(Map)} + */ + @Test + public void testInsert() { + Map killStreak = Maps.newHashMap(); + killStreak.put("player1", 3); + killStreak.put("player2", 0); + killStreak.put("player3", 5); + + this.map.putAll(killStreak); + this.map.put("player2", 0); + Assert.assertEquals(this.map.size(), 4); + + this.map.put("player4", 6); + Assert.assertEquals(this.map.size(), 5); + } + + /** + * Tests all {@code contains()} methods of the multimap. + * This includes checking for keys, values and whole entries. + */ + @Test + public void testContains() { + this.map.put("player2", 0); + this.map.put("player2", 1); + this.map.put("player3", 4); + + Assert.assertFalse(this.map.containsKey("soos")); + Assert.assertFalse(this.map.containsValue(6)); + Assert.assertFalse(this.map.containsEntry("player3", 1)); + + Assert.assertTrue(this.map.containsValue(0)); + Assert.assertTrue(this.map.containsKey("player2")); + Assert.assertTrue(this.map.containsEntry("player2", 0)); + Assert.assertTrue(this.map.containsEntry("player2", 1)); + } + + /** + * Tests the thread-safety of the multimap. In a normal + * map, a {@link java.util.ConcurrentModificationException} would + * occur if you iterate through a map and modify it at the same time, + * which does not happen if you use a concurrent multimap. + */ + @Test + public void testConcurrentModificationException() { + this.map.put("1", 2); + this.map.put("1", 3); + this.map.put("2", 24); + + // test whether concurrent modification exception can occur + this.map.forEach((player, streak) -> this.map.remove(player, streak)); + + // test whether all items have been removed + Assert.assertEquals(this.map.size(), 0); + } + +} From 2f30213e919cd00e94a73d7e52b473c5eaefd4c3 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 17:57:32 +0100 Subject: [PATCH 59/81] Add documentation to ApproximateLocation --- .../world/region/ApproximateLocation.java | 82 ++++++++++++++++++- .../world/region/KelpRegionRepository.java | 4 +- 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/ApproximateLocation.java b/core/src/main/java/de/pxav/kelp/core/world/region/ApproximateLocation.java index 6d3a4e43..f09529b4 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/ApproximateLocation.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/ApproximateLocation.java @@ -5,26 +5,85 @@ import org.apache.commons.lang.builder.HashCodeBuilder; import org.bukkit.Location; +/** + * An approximate location is an object to save rough locations in a world. + * It is similar to a chunk, which covers a 16x16 space with the main difference + * being that this location type has a scale of 100x100. + * + * So if you get the x and z of an approximate location, {@code x=1} will mean + * that the exact location is {@code 200 > x >= 100}. This is useful if you want to execute + * certain operations for players in a certain area without having to use performance-heavy(er) + * {@code distance()} functions. This is used in the built-in region system for example. + * The region enter and exit event don't check each region on a world to check if a player + * is in there, but only for regions with the same approximate location. This saves lots of + * performance. + * + * @author pxav + */ public class ApproximateLocation { private int x; private int z; private String worldName; + /** + * Converts the given {@link KelpLocation} into an {@link ApproximateLocation}. + * This does not have an effect on the given source location. + * + * @param location The source location to create the approximate location from. + * @return The approximate location equivalent to the given {@link KelpLocation}. + */ public static ApproximateLocation from(KelpLocation location) { return new ApproximateLocation(location.getWorldName(), location.getBlockX(), location.getBlockZ()); } + /** + * Converts the given {@link KelpLocation} into an {@link ApproximateLocation}. + * This does not have an effect on the given source location. + * + * @param location The source location to create the approximate location from. + * @return The approximate location equivalent to the given {@link Location bukkit location}. + */ public static ApproximateLocation from(Location location) { return new ApproximateLocation(location.getWorld().getName(), location.getBlockX(), location.getBlockZ()); } - public static ApproximateLocation create(String worldName, int x, int z) { + /** + * Creates a new approximate location based on the given world and x and z axis. + * + * @param worldName The name of the world for the desired location. + * @param x The exact x axis of the original location. + * @param z The exact z axis of the original location. + * @return The final approximate location equivalent to the given location. + */ + public static ApproximateLocation fromExact(String worldName, int x, int z) { return new ApproximateLocation(worldName, x, z); } - public static ApproximateLocation create(KelpWorld world, int x, int z) { - return create(world.getName(), x, z); + /** + * Creates a new approximate location based on the given world and x and z axis. + * + * @param world The world of the desired location. + * @param x The exact x axis of the original location. + * @param z The exact z axis of the original location. + * @return The final approximate location equivalent to the given location. + */ + public static ApproximateLocation fromExact(KelpWorld world, int x, int z) { + return fromExact(world.getName(), x, z); + } + + /** + * Creates a new approximate location based on a world name and the + * approximate x and z values. If you want to use exact values to calculate + * the approximate ones, use {@link #fromExact(String, int, int)} instead. + * + * @param worldName The name of the world for this location. + * @param x The approximate x value to use (won't be further converted) + * @param z The approximate x value to use (won't be further converted) + * @return The final approximate location based on the given data. + */ + public static ApproximateLocation create(String worldName, int x, int z) { + return new ApproximateLocation(worldName, x, z); } private ApproximateLocation(String worldName, int x, int z) { @@ -33,14 +92,31 @@ private ApproximateLocation(String worldName, int x, int z) { this.worldName = worldName; } + /** + * Gets the x-value in an approximate grid, which is equal to + * the exact location's X divided by 100. + * + * @return The exact x axis divided by 100. + */ public int getX() { return x; } + /** + * Gets the z-value in an approximate grid, which is equal to + * the exact location's Z divided by 100. + * + * @return The exact z axis divided by 100. + */ public int getZ() { return z; } + /** + * Gets the name of the world of this location. + * + * @return This location's world name. + */ public String getWorldName() { return worldName; } diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java index be5313c0..80236dc5 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java @@ -42,7 +42,7 @@ public void listenTo(KelpRegion region) { for (int x = region.getMinPos().getBlockX(); x <= region.getMaxPos().getBlockX(); x++) { for (int z = region.getMinPos().getBlockZ(); z <= region.getMaxPos().getBlockZ(); z++) { - ApproximateLocation current = ApproximateLocation.create(maxPos.getWorldName(), x, z); + ApproximateLocation current = ApproximateLocation.fromExact(maxPos.getWorldName(), x, z); nearRegions.put(current, region); } } @@ -58,7 +58,7 @@ public void stopListeningTo(KelpRegion region) { for (int x = minPos.getX(); x <= maxPos.getX(); x++) { for (int z = minPos.getZ(); z <= maxPos.getZ(); z++) { - ApproximateLocation current = ApproximateLocation.create(maxPos.getWorldName(), x, z); + ApproximateLocation current = ApproximateLocation.fromExact(maxPos.getWorldName(), x, z); nearRegions.remove(current, region); } } From 4db8fa88d92b7b9abd721a8a07ecf6603571046f Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 17:58:05 +0100 Subject: [PATCH 60/81] refactor: move ApproximateLocation from region to util package of kelp world --- .../kelp/core/world/{region => util}/ApproximateLocation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename core/src/main/java/de/pxav/kelp/core/world/{region => util}/ApproximateLocation.java (99%) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/ApproximateLocation.java b/core/src/main/java/de/pxav/kelp/core/world/util/ApproximateLocation.java similarity index 99% rename from core/src/main/java/de/pxav/kelp/core/world/region/ApproximateLocation.java rename to core/src/main/java/de/pxav/kelp/core/world/util/ApproximateLocation.java index f09529b4..121c1910 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/ApproximateLocation.java +++ b/core/src/main/java/de/pxav/kelp/core/world/util/ApproximateLocation.java @@ -1,4 +1,4 @@ -package de.pxav.kelp.core.world.region; +package de.pxav.kelp.core.world.util; import de.pxav.kelp.core.world.KelpLocation; import de.pxav.kelp.core.world.KelpWorld; From 3e94fb86ab2ceb4eb21212176918d48440f7bc0c Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 18:50:48 +0100 Subject: [PATCH 61/81] Apply refactor changes in KelpRegionRepository --- .../de/pxav/kelp/core/world/region/KelpRegionRepository.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java index 80236dc5..ee887686 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java @@ -7,6 +7,7 @@ import de.pxav.kelp.core.player.KelpPlayer; import de.pxav.kelp.core.scheduler.KelpSchedulerRepository; import de.pxav.kelp.core.scheduler.type.RepeatingScheduler; +import de.pxav.kelp.core.world.util.ApproximateLocation; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; From 96ec8e454559d1037bbf7fcebaf168878c660820 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 18:51:06 +0100 Subject: [PATCH 62/81] Add documentation to KelpRegion --- .../kelp/core/world/region/KelpRegion.java | 237 ++++++++++++++++++ 1 file changed, 237 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java index fbbb3fa4..9af4318a 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java @@ -13,41 +13,153 @@ import java.util.Set; import java.util.UUID; +/** + * A KelpRegion can be any collection of blocks in a world that are grouped + * together as one. This can be of different shapes: for example a cube or a sphere. + * + * A region can be expanded and moved in the 3D-space and you can do operations + * with its blocks. + * + * You can also listen for region events, for example the {@link de.pxav.kelp.core.event.kelpevent.PlayerEnterRegionEvent}, + * which is triggered each time a player enters a registered region. For a region to + * be recognized by listeners, you have to enable listeners for it using + * {@link #enableListeners()}, which can be disabled at every time using + * {@link #disableListeners()}. + * + * There are different sub-types of KelpRegions, each of which represent different + * shapes and implement own features such as the simple {@link CuboidRegion} or the + * {@link EllipsoidRegion}. + * + * @author pxav + */ @Singleton public abstract class KelpRegion implements Cloneable { protected KelpRegionRepository regionRepository; + + // the region id used by the listener system for simplified protected UUID regionId = UUID.randomUUID(); + + // the name of the world this region is located in protected String worldName; + + // the lowermost corner of the region with the lowest axis values protected KelpLocation minPos; + + // the uppermost corner of the region with the highest axis values protected KelpLocation maxPos; public KelpRegion(KelpRegionRepository regionRepository) { this.regionRepository = regionRepository; } + /** + * Moves the region into the direction of the given + * {@link Vector}. + * + * @param vector The vector providing the direction and + * power of the movement. + */ public abstract void move(Vector vector); + /** + * Moves the region into a certain direction defined by + * the different axis values. + * + * @param dx How far the region should be moved on the x-axis + * @param dy How far the region should be moved on the y-axis + * @param dz How far the region should be moved on the z-axis + */ public abstract void move(double dx, double dy, double dz); + /** + * Gets the total volume of this region, which depends + * on the region's shape. This method calculates the exact + * value and should be used if your region is not exactly + * based on block locations. Otherwise, use {@link #getBlockVolume()}. + * + * @return The exact volume of this region in blocks. + */ public abstract double getVolume(); + /** + * Gets the total volume of this region, which depends + * on the region's shape. This method uses the integer block + * location values and is therefore an approximation of {@link #getVolume()} + * in most cases. + * + * @return The approximate volume of this location in blocks. + */ public abstract int getBlockVolume(); + /** + * Gets the center of the given region shape. The exact location + * of the center depends on the implementation, but in most cases + * it is the cubic center of a region. + * + * @return The center location of this region. + */ public abstract KelpLocation getCenter(); + /** + * Gets a set of all blocks which are on the regions surface. + * + * @return A set of blocks containing all blocks on the regions surface. + */ public abstract Set getSurfaceBlocks(); + /** + * Gets all blocks contained by this region. + * This can be used to visualize its shape. + * + * @return A set of all blocks contained by this region. + */ public abstract Set getBlocks(); + /** + * Gets all chunks covered by this region no matter if they + * are loaded or not. If you only want loaded chunks, + * use {@link #getLoadedChunks()} instead. + * + * @return A set of all chunks covered by this region. + */ public abstract Set getChunks(); + /** + * Gets all chunks that are loaded and covered by this region. + * If you want to include unloaded chunks as well, use {@link #getChunks()} + * instead. + * + * @return A set of all loaded chunks covered by this region. + */ public abstract Set getLoadedChunks(); + /** + * Expands the current region by a certain multiplier. + * + * @param amount The amount of expansion in blocks. + */ public abstract void expand(double amount); + /** + * Expands the current region into a specific direction by + * a given multiplier. + * + * @param direction The direction to expand the region in. + * @param amount The amount of expansion in blocks. + */ public abstract void expand(KelpBlockFace direction, double amount); + /** + * Expands the current region in the given axis. + * + * @param negativeX The expansion on the negative x-axis (equal to west) + * @param positiveX The expansion on the positive x-axis (equal to east) + * @param negativeY The expansion on the negative y-axis (equal to down) + * @param positiveY The expansion on the positive y-axis (equal to up) + * @param negativeZ The expansion on the negative z-axis (equal to north) + * @param positiveZ The expansion on the positive z-axis (equal to south) + */ public abstract void expand(double negativeX, double positiveX, double negativeY, @@ -55,8 +167,25 @@ public abstract void expand(double negativeX, double negativeZ, double positiveZ); + /** + * Determines whether the given location is contained by + * this region. This method does not check whether the world + * is equal but only the axis values. + * + * @param x The x-coordinate of the location to check. + * @param y The y-coordinate of the location to check. + * @param z The z-coordinate of the location to check. + * @return {@code true} if the location is contained by the region. + */ public abstract boolean contains(double x, double y, double z); + /** + * Checks whether the given location is contained by this region. + * This method also checks if the worlds are equal. + * + * @param location The location to check for. + * @return {@code true} if the location is contained by this region. + */ public boolean contains(KelpLocation location) { if (!worldName.equalsIgnoreCase(location.getWorldName())) { return false; @@ -64,14 +193,37 @@ public boolean contains(KelpLocation location) { return contains(location.getX(), location.getZ(), location.getZ()); } + /** + * Checks whether this region contains the given + * {@link KelpPlayer}. + * + * @param player The player you want to check. + * @return {@code true} if the player is contained by this region. + */ public boolean contains(KelpPlayer player) { return contains(player.getLocation()); } + /** + * Checks whether this region contains the given + * {@link KelpBlock}. + * + * @param block The block you want to check. + * @return {@code true} if the block is contained by this region. + */ public boolean contains(KelpBlock block) { return contains(block.getLocation()); } + /** + * Gets the approximate x, y, and z dimensions of this region. + * So it basically measures how long the region is on a + * specific axis of the world in blocks without fraction digits. + * If you want to get the exact dimensions, use {@link #getDimensions()} + * instead. + * + * @return The dimensions of this region in the format [x, y, z] + */ public int[] getBlockDimensions() { return new int[] { maxPos.getBlockX() - minPos.getBlockX() + 1, @@ -80,6 +232,13 @@ public int[] getBlockDimensions() { }; } + /** + * Gets the exact x, y, and z dimensions of this region. + * So it basically measures how long the region is on a + * specific axis of the world in blocks. + * + * @return The dimensions of this region in the format [x, y, z] + */ public double[] getDimensions() { return new double[] { maxPos.getX() - minPos.getX() + 1, @@ -88,6 +247,13 @@ public double[] getDimensions() { }; } + /** + * Gets the cubic outer corners of this region. It basically + * interpolates the missing corners based on the uppermost + * and lowermost corners ({@link #getMaxPos()} and {@link #getMinPos()}. + * + * @return An array containing all outer cubic corners of this region. + */ public KelpLocation[] getOuterCorners() { return new KelpLocation[] { minPos, @@ -101,14 +267,29 @@ public KelpLocation[] getOuterCorners() { }; } + /** + * Gets the name of the world this region is located in. + * @return + */ public String getWorldName() { return worldName; } + /** + * Sets the name of the world this region is located in. + * + * @param worldName The name of this region's world. + */ public void setWorldName(String worldName) { this.worldName = worldName; } + /** + * Gets the {@link KelpWorld} this region is located in. + * + * @return The world this region is located in. + * {@code null} if the world has not been set. + */ public KelpWorld getWorld() { if (this.worldName == null) { return null; @@ -116,34 +297,90 @@ public KelpWorld getWorld() { return KelpWorld.from(worldName); } + /** + * Gets the lowermost point of this region with + * the lowest axis values. + * + * @return The lowermost point of this region. + */ public KelpLocation getMinPos() { return minPos; } + /** + * Gets the uppermost point of this region with + * the highest axis values. + * + * @return The uppermost point of this region. + */ public KelpLocation getMaxPos() { return maxPos; } + /** + * Converts and clones this region into a cuboid region using its min and max + * pos as outer corners. + * + * @return The cuboid region based on the min and max pos of this region. + */ public CuboidRegion toCuboid() { return CuboidRegion.create(minPos, maxPos); } + /** + * Converts and clones this region into an ellipsoid region, which uses + * the given min and max pos as outer corners, so that the ellipsoid + * fits into the cube with maximum size. + * + * @return The {@link EllipsoidRegion} based on the min and max pos of this region. + */ public EllipsoidRegion toEllipsoid() { return EllipsoidRegion.create(minPos, maxPos); } + /** + * Gets the unique id for this region. This id can be + * used to identify regions without having to compare dynamic values + * such as their corners/etc. this ensures increased consistency. + * + * @return The region to get the unique id of. + */ public UUID getRegionId() { return regionId; } + /** + * Enables all listeners for this region. + * This means that events such as {@link de.pxav.kelp.core.event.kelpevent.PlayerEnterRegionEvent} + * will be triggered. Region listeners might become relatively + * performance intensive if you have many of them, so they are + * disabled by default. + * + * Always call this method first if you plan to use them. + * They can be disabled at any time using {@link #disableListeners()} + */ public void enableListeners() { regionRepository.listenTo(this); } + /** + * Disables all listeners for this region. + * This means that events such as {@link de.pxav.kelp.core.event.kelpevent.PlayerEnterRegionEvent} + * won't be triggered anymore, which saves performance. + * + * Disabled listeners are the default for all regions. + */ public void disableListeners() { regionRepository.stopListeningTo(this); } + /** + * Checks whether listeners are currently enabled for this region. + * Listeners are responsible for triggering events such as + * {@link de.pxav.kelp.core.event.kelpevent.PlayerEnterRegionEvent} + * + * @return {@code true} if listeners are enabled. + */ public boolean listenersEnabled() { return regionRepository.isListeningTo(this); } From 1a2b5976ca2ac5e73b9f64bd9079200784ced8cc Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 19:39:42 +0100 Subject: [PATCH 63/81] Add documentation to KelpRegionRepository --- .../world/region/KelpRegionRepository.java | 80 ++++++++++++++++++- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java index ee887686..55b9a35c 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java @@ -17,16 +17,31 @@ import javax.inject.Singleton; import java.util.UUID; +/** + * This repository class is used to manage {@link KelpRegion}s and their listeners. + * + * The listener system is optimized for maximum performance: Only the regions you need + * the listeners for are really listened by calling {@link KelpRegion#enableListeners()}. + * Furthermore Kelp does not execute the {@code contains} check for every player and every + * region, but only for the regions which are in the range of the player. This massively + * reduces the required computing power for each listener iteration. Look at + * {@link ApproximateLocation} for more detail. + * + * @author pxav + */ @Singleton public class KelpRegionRepository { + // approximate location -> all regions within the area of the approx location private ConcurrentSetMultimap nearRegions; // player -> region id private ConcurrentMultimap containedBy; + // the task id of the listener scheduler private UUID task; + // scheduler repository for interrupting the task private KelpSchedulerRepository schedulerRepository; @Inject @@ -37,10 +52,22 @@ public KelpRegionRepository(KelpSchedulerRepository schedulerRepository) { this.schedulerRepository = schedulerRepository; } - public void listenTo(KelpRegion region) { + /** + * Enables listeners for the given region. This method caches the approximate + * location of the given region, so it should be re-registered every time + * those approximate locations change (on move/expand). + * + * This method automatically activates the listener scheduler if there + * is currently no running. + * + * @param region The region to enable the listeners for. + */ + void listenTo(KelpRegion region) { ApproximateLocation maxPos = ApproximateLocation.from(region.getMaxPos()); ApproximateLocation minPos = ApproximateLocation.from(region.getMinPos()); + // go through all coordinates on the x/z plane and add their approximate + // location to the map. for (int x = region.getMinPos().getBlockX(); x <= region.getMaxPos().getBlockX(); x++) { for (int z = region.getMinPos().getBlockZ(); z <= region.getMaxPos().getBlockZ(); z++) { ApproximateLocation current = ApproximateLocation.fromExact(maxPos.getWorldName(), x, z); @@ -48,16 +75,23 @@ public void listenTo(KelpRegion region) { } } + // enable scheduler if there is no one running if (!isListenerRunning()) { startListenerTasks(); } } - public void stopListeningTo(KelpRegion region) { + /** + * Disables listeners for the given region. This will remove + * all entries in the cache for this region. + * + * @param region The region to remove the listener of. + */ + void stopListeningTo(KelpRegion region) { ApproximateLocation maxPos = ApproximateLocation.from(region.getMaxPos()); ApproximateLocation minPos = ApproximateLocation.from(region.getMinPos()); - for (int x = minPos.getX(); x <= maxPos.getX(); x++) { + for (int x = minPos.getX(); x <= maxPos.getX(); x++) { //todo use exact location here for (int z = minPos.getZ(); z <= maxPos.getZ(); z++) { ApproximateLocation current = ApproximateLocation.fromExact(maxPos.getWorldName(), x, z); nearRegions.remove(current, region); @@ -65,12 +99,29 @@ public void stopListeningTo(KelpRegion region) { } } - public boolean isListeningTo(KelpRegion region) { + /** + * Checks whether the given region has listeners enabled. + * + * @param region The region you want to check for. + * @return {@code true} the region to check for. + */ + boolean isListeningTo(KelpRegion region) { return nearRegions.containsValue(region); } + /** + * This listener checks if there is any region having listeners enabled + * and starts the listener scheduler if needed. + * + * This is needed because the scheduler is shut down automatically if no + * players are online to save performance. Hence, it has to be started when a player + * joins. + * + * @param event The event to listen for. + */ @EventHandler public void handlePlayerJoin(PlayerJoinEvent event) { + // check if there is any region to listen for and if the listeners are really shut down. if (nearRegions.isEmpty() || this.isListenerRunning()) { return; } @@ -79,6 +130,10 @@ public void handlePlayerJoin(PlayerJoinEvent event) { } + /** + * Starts the region listener task. This task automatically + * checks when to shut down the listeners. + */ private void startListenerTasks() { task = RepeatingScheduler.create() .async() @@ -93,8 +148,11 @@ private void startListenerTasks() { } for (Player player : Bukkit.getOnlinePlayers()) { + // iterate each region in range of the player nearRegions.getOrEmpty(ApproximateLocation.from(player.getLocation())).forEach(region -> { + // if the region world differs from the player world, the player cannot + // be contained by the region. if (!region.getWorldName().equalsIgnoreCase(player.getWorld().getName())) { return; } @@ -105,9 +163,12 @@ private void startListenerTasks() { player.getLocation().getZ() ); + // if player is in region but was not in the region before -> Enter if (contains && !containedBy.containsEntry(player.getUniqueId(), region.getRegionId())) { this.containedBy.put(player.getUniqueId(), region.getRegionId()); Bukkit.getPluginManager().callEvent(new PlayerEnterRegionEvent(region, KelpPlayer.from(player))); + + // if the player is not in region but was in there before -> Exit } else if (!contains && containedBy.containsEntry(player.getUniqueId(), region.getRegionId())) { this.containedBy.remove(player.getUniqueId(), region.getRegionId()); Bukkit.getPluginManager().callEvent(new PlayerLeaveRegionEvent(region, KelpPlayer.from(player))); @@ -117,6 +178,12 @@ private void startListenerTasks() { }); } + + /** + * Stops the listener task and sets the listener id back to {@code null}. + * This can be used if there is no region to listen for anymore + * for example and therefore to save performance. + */ private void stopListenerTasks() { if (task != null) { schedulerRepository.interruptScheduler(task); @@ -124,6 +191,11 @@ private void stopListenerTasks() { } } + /** + * Checks whether the listener scheduler is currently running. + * + * @return {@code true} if the listener task is currently running. + */ private boolean isListenerRunning() { return task != null; } From 2ff15862d6387bd1d3a9934a091c383f09ba9184 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 19:40:57 +0100 Subject: [PATCH 64/81] Fix bug that listeners could not be unregistered if region expanded to another approx location --- .../de/pxav/kelp/core/world/region/KelpRegionRepository.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java index 55b9a35c..bb0d8a6f 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java @@ -91,8 +91,8 @@ void stopListeningTo(KelpRegion region) { ApproximateLocation maxPos = ApproximateLocation.from(region.getMaxPos()); ApproximateLocation minPos = ApproximateLocation.from(region.getMinPos()); - for (int x = minPos.getX(); x <= maxPos.getX(); x++) { //todo use exact location here - for (int z = minPos.getZ(); z <= maxPos.getZ(); z++) { + for (int x = region.getMinPos().getBlockX(); x <= region.getMaxPos().getBlockX(); x++) { + for (int z = region.getMinPos().getBlockZ(); z <= region.getMaxPos().getBlockZ(); z++) { ApproximateLocation current = ApproximateLocation.fromExact(maxPos.getWorldName(), x, z); nearRegions.remove(current, region); } From 2f815d10e3e359dc51b5192ee0b68f1c2f7fc885 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 20:17:20 +0100 Subject: [PATCH 65/81] Fix bug that listeners did not adjust to new approx location when expanding/moving a region --- .../kelp/core/world/region/CuboidRegion.java | 16 +-- .../core/world/region/EllipsoidRegion.java | 25 +++-- .../kelp/core/world/region/KelpRegion.java | 97 ++++++++++++++++++- .../world/region/KelpRegionRepository.java | 10 +- 4 files changed, 121 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java index 06b40996..948229c2 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java @@ -28,14 +28,14 @@ public CuboidRegion(KelpRegionRepository regionRepository) { } @Override - public void move(Vector vector) { - this.minPos.add(vector); - this.maxPos.add(vector); + protected void moveIgnoreListeners(double dx, double dy, double dz) { + move(new Vector(dx, dy, dz)); } @Override - public void move(double dx, double dy, double dz) { - move(new Vector(dx, dy, dz)); + protected void moveIgnoreListeners(Vector vector) { + this.minPos.add(vector); + this.maxPos.add(vector); } @Override @@ -202,12 +202,12 @@ public CuboidRegion getFace(KelpBlockFace direction) { } @Override - public void expand(double amount) { + protected void expandIgnoreListeners(double amount) { expand(amount, amount, amount, amount, amount, amount); } @Override - public void expand(KelpBlockFace direction, double amount) { + protected void expandIgnoreListeners(KelpBlockFace direction, double amount) { Vector vector = direction.getDirection(); if (vector.getX() + vector.getY() + vector.getZ() > 0) { vector = vector.multiply(amount); @@ -220,7 +220,7 @@ public void expand(KelpBlockFace direction, double amount) { } @Override - public void expand(double negativeX, double positiveX, double negativeY, double positiveY, double negativeZ, double positiveZ) { + protected void expandIgnoreListener(double negativeX, double positiveX, double negativeY, double positiveY, double negativeZ, double positiveZ) { this.minPos.subtract(negativeX, negativeY, negativeZ); this.maxPos.add(positiveX, positiveY, positiveZ); setBoundingPositions(minPos, maxPos); diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java index 0b9059a3..9bad3625 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java @@ -87,13 +87,18 @@ public void limitZRadius(double limiter) { } @Override - public void move(Vector vector) { + protected void moveIgnoreListeners(Vector vector) { center.add(vector); + minPos.add(vector); + maxPos.add(vector); } @Override - public void move(double dx, double dy, double dz) { - center.add(new Vector(dx, dy, dz)); + protected void moveIgnoreListeners(double dx, double dy, double dz) { + Vector vector = new Vector(dx, dy, dz); + center.add(vector); + minPos.add(vector); + maxPos.add(vector); } @Override @@ -262,14 +267,15 @@ public Set getLoadedChunks() { } @Override - public void expand(double amount) { + protected void expandIgnoreListeners(double amount) { xRadius += amount; yRadius += amount; zRadius += amount; + updateMinMax(); } @Override - public void expand(KelpBlockFace direction, double amount) { + protected void expandIgnoreListeners(KelpBlockFace direction, double amount) { switch (direction) { case UP: case DOWN: @@ -286,6 +292,7 @@ public void expand(KelpBlockFace direction, double amount) { default: throw new IllegalArgumentException("Error when expanding EllipsoidRegion: BlockFace must be one of UP, DOWN, NORTH, SOUTH, EAST, WEST"); } + updateMinMax(); } public void expandAndMove(KelpBlockFace direction, double amount) { @@ -294,10 +301,16 @@ public void expandAndMove(KelpBlockFace direction, double amount) { } @Override - public void expand(double negativeX, double positiveX, double negativeY, double positiveY, double negativeZ, double positiveZ) { + protected void expandIgnoreListener(double negativeX, double positiveX, double negativeY, double positiveY, double negativeZ, double positiveZ) { this.xRadius = positiveX + negativeX; this.yRadius = positiveY + negativeY; this.zRadius = positiveZ + negativeZ; + updateMinMax(); + } + + private void updateMinMax() { + minPos = center.clone().subtract(xRadius, yRadius, zRadius); + maxPos = center.clone().add(xRadius, yRadius, zRadius); } public void expandAndMove(double negativeX, double positiveX, double negativeY, double positiveY, double negativeZ, double positiveZ) { diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java index 9af4318a..6c8deba7 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java @@ -60,7 +60,20 @@ public KelpRegion(KelpRegionRepository regionRepository) { * @param vector The vector providing the direction and * power of the movement. */ - public abstract void move(Vector vector); + public void move(Vector vector) { + this.disableListeners(); + this.moveIgnoreListeners(vector); + this.enableListeners(); + } + + /** + * Moves the region into the direction of the given + * {@link Vector}. + * + * @param vector The vector providing the direction and + * power of the movement. + */ + protected abstract void moveIgnoreListeners(Vector vector); /** * Moves the region into a certain direction defined by @@ -70,7 +83,21 @@ public KelpRegion(KelpRegionRepository regionRepository) { * @param dy How far the region should be moved on the y-axis * @param dz How far the region should be moved on the z-axis */ - public abstract void move(double dx, double dy, double dz); + public void move(double dx, double dy, double dz) { + this.disableListeners(); + this.moveIgnoreListeners(dx, dy, dz); + this.enableListeners(); + } + + /** + * Moves the region into a certain direction defined by + * the different axis values. + * + * @param dx How far the region should be moved on the x-axis + * @param dy How far the region should be moved on the y-axis + * @param dz How far the region should be moved on the z-axis + */ + protected abstract void moveIgnoreListeners(double dx, double dy, double dz); /** * Gets the total volume of this region, which depends @@ -129,6 +156,8 @@ public KelpRegion(KelpRegionRepository regionRepository) { * Gets all chunks that are loaded and covered by this region. * If you want to include unloaded chunks as well, use {@link #getChunks()} * instead. + * This method automatically applies the changes to the listener system + * if it is enabled for this region. * * @return A set of all loaded chunks covered by this region. */ @@ -139,19 +168,71 @@ public KelpRegion(KelpRegionRepository regionRepository) { * * @param amount The amount of expansion in blocks. */ - public abstract void expand(double amount); + public void expand(double amount) { + this.disableListeners(); + this.expandIgnoreListeners(amount); + this.enableListeners(); + } + + /** + * Expands the current region by a certain multiplier. + * This method does not update the listeners for the region. + * + * @param amount The amount of expansion in blocks. + */ + protected abstract void expandIgnoreListeners(double amount); /** * Expands the current region into a specific direction by * a given multiplier. + * This method automatically applies the changes to the listener system + * if it is enabled for this region. * * @param direction The direction to expand the region in. * @param amount The amount of expansion in blocks. */ - public abstract void expand(KelpBlockFace direction, double amount); + public void expand(KelpBlockFace direction, double amount) { + this.disableListeners(); + this.expandIgnoreListeners(direction, amount); + this.enableListeners(); + } + + /** + * Expands the current region into a specific direction by + * a given multiplier. + * This method does not update the listeners for the region. + * + * @param direction The direction to expand the region in. + * @param amount The amount of expansion in blocks. + */ + protected abstract void expandIgnoreListeners(KelpBlockFace direction, double amount); + + /** + * Expands the current region in the given axis. + * This method automatically applies the changes to the listener system + * if it is enabled for this region. + * + * @param negativeX The expansion on the negative x-axis (equal to west) + * @param positiveX The expansion on the positive x-axis (equal to east) + * @param negativeY The expansion on the negative y-axis (equal to down) + * @param positiveY The expansion on the positive y-axis (equal to up) + * @param negativeZ The expansion on the negative z-axis (equal to north) + * @param positiveZ The expansion on the positive z-axis (equal to south) + */ + public void expand(double negativeX, + double positiveX, + double negativeY, + double positiveY, + double negativeZ, + double positiveZ) { + this.disableListeners(); + this.expandIgnoreListener(negativeX, positiveX, negativeY, positiveY, negativeZ, positiveZ); + this.enableListeners(); + } /** * Expands the current region in the given axis. + * This method does not update the listeners for the region. * * @param negativeX The expansion on the negative x-axis (equal to west) * @param positiveX The expansion on the positive x-axis (equal to east) @@ -160,7 +241,7 @@ public KelpRegion(KelpRegionRepository regionRepository) { * @param negativeZ The expansion on the negative z-axis (equal to north) * @param positiveZ The expansion on the positive z-axis (equal to south) */ - public abstract void expand(double negativeX, + protected abstract void expandIgnoreListener(double negativeX, double positiveX, double negativeY, double positiveY, @@ -360,6 +441,9 @@ public UUID getRegionId() { * They can be disabled at any time using {@link #disableListeners()} */ public void enableListeners() { + if (listenersEnabled()) { + return; + } regionRepository.listenTo(this); } @@ -371,6 +455,9 @@ public void enableListeners() { * Disabled listeners are the default for all regions. */ public void disableListeners() { + if (!listenersEnabled()) { + return; + } regionRepository.stopListeningTo(this); } diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java index bb0d8a6f..a96fd3ad 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java @@ -63,14 +63,11 @@ public KelpRegionRepository(KelpSchedulerRepository schedulerRepository) { * @param region The region to enable the listeners for. */ void listenTo(KelpRegion region) { - ApproximateLocation maxPos = ApproximateLocation.from(region.getMaxPos()); - ApproximateLocation minPos = ApproximateLocation.from(region.getMinPos()); - // go through all coordinates on the x/z plane and add their approximate // location to the map. for (int x = region.getMinPos().getBlockX(); x <= region.getMaxPos().getBlockX(); x++) { for (int z = region.getMinPos().getBlockZ(); z <= region.getMaxPos().getBlockZ(); z++) { - ApproximateLocation current = ApproximateLocation.fromExact(maxPos.getWorldName(), x, z); + ApproximateLocation current = ApproximateLocation.fromExact(region.getWorldName(), x, z); nearRegions.put(current, region); } } @@ -88,12 +85,9 @@ void listenTo(KelpRegion region) { * @param region The region to remove the listener of. */ void stopListeningTo(KelpRegion region) { - ApproximateLocation maxPos = ApproximateLocation.from(region.getMaxPos()); - ApproximateLocation minPos = ApproximateLocation.from(region.getMinPos()); - for (int x = region.getMinPos().getBlockX(); x <= region.getMaxPos().getBlockX(); x++) { for (int z = region.getMinPos().getBlockZ(); z <= region.getMaxPos().getBlockZ(); z++) { - ApproximateLocation current = ApproximateLocation.fromExact(maxPos.getWorldName(), x, z); + ApproximateLocation current = ApproximateLocation.fromExact(region.getWorldName(), x, z); nearRegions.remove(current, region); } } From 7842e1956c96a2bf6addf506709ec47274f47415 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 21:48:28 +0100 Subject: [PATCH 66/81] Add documentation to KelpChunk --- .../de/pxav/kelp/core/world/KelpChunk.java | 202 ++++++++++++++++++ 1 file changed, 202 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/world/KelpChunk.java b/core/src/main/java/de/pxav/kelp/core/world/KelpChunk.java index 174c7af6..916f9083 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/KelpChunk.java +++ b/core/src/main/java/de/pxav/kelp/core/world/KelpChunk.java @@ -10,6 +10,15 @@ import java.util.Collection; import java.util.Set; +/** + * A chunk is a 16x16 area of blocks used by Minecraft + * to generate the world and load a world dynamically. + * + * This class is a version-independent alternative to the + * normal {@link Chunk} class provided by bukkit. + * + * @author pxav + */ public class KelpChunk { private Chunk bukkitChunk; @@ -20,6 +29,12 @@ public class KelpChunk { this.versionTemplate = versionTemplate; } + /** + * Converts the bukkit chunk into a {@link KelpChunk}. + * + * @param bukkitChunk The bukkit chunk you want to convert. + * @return The final {@link KelpChunk} + */ public static KelpChunk from(Chunk bukkitChunk) { return new KelpChunk( bukkitChunk, @@ -27,74 +42,186 @@ public static KelpChunk from(Chunk bukkitChunk) { ); } + /** + * Gets the world the given chunk is located in. + * + * @return The world the given chunk is located in. + */ public KelpWorld getWorld() { return versionTemplate.getWorld(this); } + /** + * Gets the X-coordinate in the chunk's world of the given + * chunk. This can be used to compare and identify chunks. + * + * Note that this value does not return the absolute X-block- + * coordinate where the chunk begins, but it returns the X + * block value divided by 16. If you are at x=-35, then this + * would return 2, because it is bigger than 32 (16*2) but smaller + * than 48 (16*3). + * + * @return The X coordinate on the world's chunk grid. + */ public int getX() { return versionTemplate.getX(this); } + /** + * Gets the Z-coordinate in the chunk's world of the given + * chunk. This can be used to compare and identify chunks. + * + * Note that this value does not return the absolute Z-block- + * coordinate where the chunk begins, but it returns the Z + * block value divided by 16. If you are at z=-35, then this + * would return 2, because it is bigger than 32 (16*2) but smaller + * than 48 (16*3). + * + * @return The Z coordinate on the world's chunk grid. + */ public int getZ() { return versionTemplate.getZ(this); } + /** + * Gets a block at the given location inside the chunk. The passed location + * may not be from outside the chunk! + * + * @param location The location where the block is located. + * @return The block at the given location. + */ public KelpBlock getBlockAt(KelpLocation location) { return versionTemplate.getBlockAt(this, location); } + /** + * Determines whether the given location is inside the given chunk. + * + * @param location The location that should be located in the chunk. + * @return {@code true} if the location is inside the chunk. + */ public boolean contains(KelpLocation location) { return this.versionTemplate.contains(this, location); } + /** + * Determines whether the given block is inside the given chunk. + * + * @param block The block that should be located in the chunk. + * @return {@code true} if the block is inside the chunk. + */ public boolean contains(KelpBlock block) { return this.versionTemplate.contains(this, block.getLocation()); } + /** + * Determines whether slime entities can spawn in the given chunk. + * + * @return {@code true} if slime entities can spawn in that chunk. + */ public boolean isSlimeChunk() { return versionTemplate.isSlimeChunk(this); } + /** + * Gets a collection of all players that are currently inside the chunk. + * + * @return A collection of all players that are currently inside the chunk. + */ public Collection getPlayers() { return versionTemplate.getPlayers(this); } + /** + * Gets the approximate center block of this chunk at the given height. + * As a chunk is 16x16 blocks, there is no exact center block, which + * is why this method can only return an approximation. + * + * @param height The height of the desired center block location. + * @return The approximate center of the given chunk. + */ public KelpLocation getCenter(int height) { return KelpLocation.from(getWorld().getName(), getX() << 4, height, getZ() << 4).add(7, 0, 7); } + /** + * Gets the chunk which is west of the current chunk. + * + * @return The chunk west of the current chunk. + */ public KelpChunk getWestEnclosedChunk() { return getNorthWesternBlock(10).getLocation().add(-2, 0, 2).getChunk(); } + /** + * Gets the chunk which is north of the current chunk. + * + * @return The chunk north of the current chunk. + */ public KelpChunk getNorthEnclosedChunk() { return getNorthEasternBlock(10).getLocation().add(-2, 0, -2).getChunk(); } + /** + * Gets the chunk which is north of the current chunk. + * + * @return The chunk north of the current chunk. + */ public KelpChunk getEastEnclosedChunk() { return getNorthEasternBlock(10).getLocation().add(2, 0, 2).getChunk(); } + /** + * Gets the chunk which is south of the current chunk. + * + * @return The chunk south of the current chunk. + */ public KelpChunk getSouthEnclosedChunk() { return getSouthWesternBlock(10).getLocation().add(2, 0, 2).getChunk(); } + /** + * Gets the chunk which is south west of the current chunk. + * + * @return The chunk south west of the current chunk. + */ public KelpChunk getSouthWestEnclosedChunk() { return getSouthWesternBlock(10).getLocation().add(-2, 0, 2).getChunk(); } + /** + * Gets the chunk which is north west of the current chunk. + * + * @return The chunk north west of the current chunk. + */ public KelpChunk getNorthWestEnclosedChunk() { return getNorthWesternBlock(10).getLocation().add(-2, 0, -2).getChunk(); } + /** + * Gets the chunk which is north east of the current chunk. + * + * @return The chunk north east of the current chunk. + */ public KelpChunk getNorthEastEnclosedChunk() { return getNorthEasternBlock(10).getLocation().add(2, 0, -2).getChunk(); } + /** + * Gets the chunk which is south east of the current chunk. + * + * @return The chunk south east of the current chunk. + */ public KelpChunk getSouthEastEnclosedChunk() { return getSouthEasternBlock(10).getLocation().add(2, 0, 2).getChunk(); } + /** + * Gets the block which is most north east in this chunk. + * + * @param height The height of the desired block. + * @return The most north eastern block of this chunk. + */ public KelpBlock getNorthEasternBlock(int height) { KelpLocation location = KelpLocation.from( getWorld().getName(), @@ -105,6 +232,12 @@ public KelpBlock getNorthEasternBlock(int height) { return getWorld().getBlockAt(location); } + /** + * Gets the block which is most north west in this chunk. + * + * @param height The height of the desired block. + * @return The most north western block of this chunk. + */ public KelpBlock getNorthWesternBlock(int height) { KelpLocation location = KelpLocation.from( getWorld().getName(), @@ -115,6 +248,12 @@ public KelpBlock getNorthWesternBlock(int height) { return getWorld().getBlockAt(location); } + /** + * Gets the block which is most south east in this chunk. + * + * @param height The height of the desired block. + * @return The most south eastern block of this chunk. + */ public KelpBlock getSouthEasternBlock(int height) { KelpLocation location = KelpLocation.from( getWorld().getName(), @@ -125,6 +264,12 @@ public KelpBlock getSouthEasternBlock(int height) { return getWorld().getBlockAt(location); } + /** + * Gets the block which is most south west in this chunk. + * + * @param height The height of the desired block. + * @return The most south western block of this chunk. + */ public KelpBlock getSouthWesternBlock(int height) { KelpLocation location = KelpLocation.from( getWorld().getName(), @@ -135,26 +280,78 @@ public KelpBlock getSouthWesternBlock(int height) { return getWorld().getBlockAt(location); } + /** + * Loads the chunk. This will make the chunk passable for players and + * tick operations such as redstone clocks or crop growing will be performed + * again. To save performance, chunks are unloaded by bukkit if they are not used. + * If you need to prevent that you can use {@link #addForceLoadFlag(Class)} + * to keep the chunk loaded until you manually unload it again. But please + * keep in mind that this might have bad performance impact. + */ public void load() { versionTemplate.load(this); } + /** + * Unloads the chunk. That means tick operations in this + * chunk will no longer be performed. Redstone clocks will stop running + * and crops will stop growing. This can be reversed at any time using + * {@link #load()}. + */ public void unload() { versionTemplate.unload(this); } + /** + * Checks whether the given chunk is currently loaded. That + * means it checks whether tick operations are currently ran on + * this chunk. + * + * @return {@code true} if the chunk is currently loaded. + */ public boolean isLoaded() { return versionTemplate.isLoaded(this); } + /** + * Adds a force load flag to the given chunk. A force load flag means that + * the chunk cannot be unloaded by bukkit randomly, but keeps loaded until you + * revert that action by yourself ({@link #removeForceLoadFlag(Class)}). + * + * If you call this method, {@link #unload()} won't have an effect + * as Kelp will immediately load the chunk again if there is a flag to keep + * it loaded. So if you want to unload the chunk, call {@link #removeForceLoadFlag(Class)} + * first. + * + * @param plugin The plugin that should keep the chunk loaded. + * This is important when you remove your flag, + * but another plugin still relies on the chunk to be loaded, + * the chunk will be kept loaded until the other plugin(s) + * unload the chunk as well. + */ public void addForceLoadFlag(Class plugin) { versionTemplate.addForceLoadFlag(this, plugin); } + /** + * Removes a force load flag from the given chunk again. A force load flag means that + * the chunk cannot be unloaded by bukkit randomly, but keeps loaded until you + * revert that action by yourself using this method. Such a flag can be assigned + * to a chunk using {@link #addForceLoadFlag(Class)}. + * + * @param plugin The plugin that removes their flag. If another plugin has still + * loaded this chunk, it won't be unloaded until all plugins have removed + * their flag. + */ public void removeForceLoadFlag(Class plugin) { versionTemplate.removeForceLoadFlag(this, plugin); } + /** + * Gets all {@link KelpApplication}s that are currently forcing this chunk to keep loaded. + * + * @return A set of all plugin main classes that force the chunk to keep loaded. + */ public Set> getForceLoadingPlugins() { return versionTemplate.getForceLoadFlagPlugins(this); } @@ -167,6 +364,11 @@ public boolean equals(Chunk compareTo) { return compareTo.getX() == getX() && compareTo.getZ() == getZ(); } + /** + * Gets the bukkit chunk instance of this KelpChunk. + * + * @return The bukkit instance of this chunk. + */ public Chunk getBukkitChunk() { return bukkitChunk; } From 87943cce098ed5c9e52b57dc07b5f756be718dd0 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 21:49:15 +0100 Subject: [PATCH 67/81] Remove redundant equals methods of KelpChunk --- core/src/main/java/de/pxav/kelp/core/world/KelpChunk.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/KelpChunk.java b/core/src/main/java/de/pxav/kelp/core/world/KelpChunk.java index 916f9083..59596ec6 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/KelpChunk.java +++ b/core/src/main/java/de/pxav/kelp/core/world/KelpChunk.java @@ -356,14 +356,6 @@ public Set> getForceLoadingPlugins() { return versionTemplate.getForceLoadFlagPlugins(this); } - public boolean equals(KelpChunk compareTo) { - return compareTo.getX() == getX() && compareTo.getZ() == getZ(); - } - - public boolean equals(Chunk compareTo) { - return compareTo.getX() == getX() && compareTo.getZ() == getZ(); - } - /** * Gets the bukkit chunk instance of this KelpChunk. * From a85cf745ad052593e0b44f982bb113a652a837ce Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 21:49:26 +0100 Subject: [PATCH 68/81] Add documentation to CuboidRegion --- .../kelp/core/world/region/CuboidRegion.java | 187 +++++++++++++++++- 1 file changed, 178 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java index 948229c2..5c0e41ba 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/CuboidRegion.java @@ -10,8 +10,24 @@ import java.util.Set; +/** + * A CuboidRegion is a region representing a cubic space in a 3d-world. + * This means it has two opposite points and calculates the cuboid in between + * those points. + * + * @author pxav + */ public class CuboidRegion extends KelpRegion { + /** + * Creates a new {@link CuboidRegion} based on two opposite points. + * This method automatically calculates which point is the upper and + * which one the lower point. + * + * @param pos1 The first point + * @param pos2 The second point. + * @return The cuboid region between those points + */ public static CuboidRegion create(KelpLocation pos1, KelpLocation pos2) { CuboidRegion region = new CuboidRegion(getRegionRepository()); region.setBoundingPositions(pos1, pos2); @@ -19,37 +35,85 @@ public static CuboidRegion create(KelpLocation pos1, KelpLocation pos2) { return region; } + /** + * Creates a new {@link CuboidRegion} instance without calculating any points. + * + * @return The fresh and empty cuboid region instance. + */ public static CuboidRegion create() { return new CuboidRegion(getRegionRepository()); } - public CuboidRegion(KelpRegionRepository regionRepository) { + private CuboidRegion(KelpRegionRepository regionRepository) { super(regionRepository); } + /** + * Moves the region into a certain direction defined by + * the different axis values. + * + * @param dx How far the region should be moved on the x-axis + * @param dy How far the region should be moved on the y-axis + * @param dz How far the region should be moved on the z-axis + */ @Override protected void moveIgnoreListeners(double dx, double dy, double dz) { move(new Vector(dx, dy, dz)); } + /** + * Moves the region into the direction of the given + * {@link Vector}. + * + * @param vector The vector providing the direction and + * power of the movement. + */ @Override protected void moveIgnoreListeners(Vector vector) { this.minPos.add(vector); this.maxPos.add(vector); } + /** + * Gets the total volume of this region, which is defined + * by the length on the {@code x * y * z} axis. + * + * This method returns the exact volume of this region. + * If you only want the block count, use {@link #getBlockVolume()} + * instead. + * + * @return The exact volume of this location in blocks. + */ @Override public double getVolume() { double[] dimensions = getDimensions(); return dimensions[0] * dimensions[1] * dimensions[2]; } + /** + * Gets the total volume of this region, which is defined + * by the length on the {@code x * y * z} axis. + * + * This method approximates the volume to whole blocks. + * + * @return The approximate volume of this location in blocks. + */ @Override public int getBlockVolume() { int[] blockDimensions = getBlockDimensions(); return blockDimensions[0] * blockDimensions[1] * blockDimensions[2]; } + /** + * Determines whether the given location is contained by + * this region. This method does not check whether the world + * is equal but only the axis values. + * + * @param x The x-coordinate of the location to check. + * @param y The y-coordinate of the location to check. + * @param z The z-coordinate of the location to check. + * @return {@code true} if the location is contained by the region. + */ @Override public boolean contains(double x, double y, double z) { // X @@ -72,20 +136,25 @@ public boolean contains(double x, double y, double z) { return false; } - @Override - public boolean contains(KelpLocation location) { - if (!worldName.equalsIgnoreCase(location.getWorldName())) { - return false; - } - return contains(location.getX(), location.getY(), location.getZ()); - } - + /** + * Gets the center of the cuboid, which is defined by the center of the + * diagonal line between the two opposite points. + * + * @return The center of this cuboid. + */ @Override public KelpLocation getCenter() { return this.minPos.clone().add(this.maxPos).multiply(0.5); } + /** + * Checks if this region intersects with another {@link CuboidRegion}. + * + * @param region The region to check the intersection with. + * @return {@code true} whether both regions intersect with each other. + */ public boolean hasCuboidIntersection(CuboidRegion region) { + // if the worlds differ, the regions cannot intersect if (!region.getWorldName().equalsIgnoreCase(worldName)) { return false; } @@ -95,7 +164,16 @@ public boolean hasCuboidIntersection(CuboidRegion region) { || minPos.getZ() > region.getMaxPos().getZ() || region.getMinPos().getZ() > maxPos.getZ())); } + /** + * Gets the cuboid intersection between to regions. If two + * {@link CuboidRegion}s intersect with each other, the + * intersecting blocks will be covered by this region. + * + * @param region The region you want to get the intersection with. + * @return The region representing the intersection of the two regions. + */ public CuboidRegion cuboidIntersection(CuboidRegion region) { + // if the regions do not intersect, return null if (!hasCuboidIntersection(region)) { return null; } @@ -105,6 +183,11 @@ public CuboidRegion cuboidIntersection(CuboidRegion region) { getMaxPos().getMinimalLocation(region.getMaxPos())); } + /** + * Gets a set of all blocks which are on the regions surface. + * + * @return A set of blocks containing all blocks on the regions surface. + */ @Override public Set getSurfaceBlocks() { Set output = Sets.newConcurrentHashSet(); @@ -117,6 +200,12 @@ public Set getSurfaceBlocks() { return output; } + /** + * Gets all blocks contained by this region. + * This can be used to visualize its shape. + * + * @return A set of all blocks contained by this region. + */ @Override public Set getBlocks() { Set output = Sets.newConcurrentHashSet(); @@ -130,10 +219,24 @@ public Set getBlocks() { return output; } + /** + * Gets all blocks of a specific face of this cuboid. + * + * @param direction The direction of the face you want to get the blocks of. + * @return A set of blocks of the face in the given direction. + */ public Set getFaceBlocks(KelpBlockFace direction) { return getFace(direction).getBlocks(); } + /** + * Gets the length of the region in the given direction. + * This method gets the exact length, use {@link #measureBlocks(KelpBlockFace)} + * if you only need the block count instead. + * + * @param direction The direction to measure the length of. + * @return The length of this cuboid in a specific direction. + */ public double measure(KelpBlockFace direction) { switch (direction) { case UP: @@ -149,6 +252,14 @@ public double measure(KelpBlockFace direction) { throw new IllegalArgumentException("Cannot measure region axis '" + direction + "'. Only use UP, DOWN, EAST, WEST, NORTH, SOUTH"); } + /** + * Gets the length of the region in the given direction. + * This method approximates the output to an integer + * block count. + * + * @param direction The direction to measure the length of. + * @return The length of this cuboid in a specific direction. + */ public int measureBlocks(KelpBlockFace direction) { switch (direction) { case UP: @@ -164,6 +275,13 @@ public int measureBlocks(KelpBlockFace direction) { throw new IllegalArgumentException("Cannot measure region axis '" + direction + "'. Only use UP, DOWN, EAST, WEST, NORTH, SOUTH"); } + /** + * Gets all chunks covered by this region no matter if they + * are loaded or not. If you only want loaded chunks, + * use {@link #getLoadedChunks()} instead. + * + * @return A set of all chunks covered by this region. + */ @Override public Set getChunks() { Set output = Sets.newHashSet(); @@ -178,6 +296,15 @@ public Set getChunks() { return output; } + /** + * Gets all chunks that are loaded and covered by this region. + * If you want to include unloaded chunks as well, use {@link #getChunks()} + * instead. + * This method automatically applies the changes to the listener system + * if it is enabled for this region. + * + * @return A set of all loaded chunks covered by this region. + */ @Override public Set getLoadedChunks() { Set output = Sets.newHashSet(); @@ -195,17 +322,38 @@ public Set getLoadedChunks() { return output; } + /** + * Gets the cuboid region representing the outermost block-layer + * at a given face. If you choose {@link KelpBlockFace} for example, + * + * @param direction The direction of the face you want to get. + * @return The cuboid region representing the face in the given direction. + */ public CuboidRegion getFace(KelpBlockFace direction) { CuboidRegion output = this.clone(); output.expand(direction.getOppositeFace(), -output.measureBlocks(direction) + 1); return output; } + /** + * Expands the current region by a certain multiplier. + * This method does not update the listeners for the region. + * + * @param amount The amount of expansion in blocks. + */ @Override protected void expandIgnoreListeners(double amount) { expand(amount, amount, amount, amount, amount, amount); } + /** + * Expands the current region into a specific direction by + * a given multiplier. + * This method does not update the listeners for the region. + * + * @param direction The direction to expand the region in. + * @param amount The amount of expansion in blocks. + */ @Override protected void expandIgnoreListeners(KelpBlockFace direction, double amount) { Vector vector = direction.getDirection(); @@ -219,6 +367,17 @@ protected void expandIgnoreListeners(KelpBlockFace direction, double amount) { setBoundingPositions(minPos, maxPos); } + /** + * Expands the current region in the given axis. + * This method does not update the listeners for the region. + * + * @param negativeX The expansion on the negative x-axis (equal to west) + * @param positiveX The expansion on the positive x-axis (equal to east) + * @param negativeY The expansion on the negative y-axis (equal to down) + * @param positiveY The expansion on the positive y-axis (equal to up) + * @param negativeZ The expansion on the negative z-axis (equal to north) + * @param positiveZ The expansion on the positive z-axis (equal to south) + */ @Override protected void expandIgnoreListener(double negativeX, double positiveX, double negativeY, double positiveY, double negativeZ, double positiveZ) { this.minPos.subtract(negativeX, negativeY, negativeZ); @@ -253,6 +412,16 @@ public int hashCode() { .toHashCode(); } + /** + * Takes two opposite points and checks which of those points + * is higher and which one is lower and assigns them to {@code minPos} + * and {@code maxPos} of this location accordingly. + * + * The order you provide the points in does not matter for this function. + * + * @param pos1 The first point + * @param pos2 The second point + */ public void setBoundingPositions(KelpLocation pos1, KelpLocation pos2) { if (!pos1.getWorldName().equals(pos2.getWorldName())) { throw new IllegalArgumentException("Cannot build CuboidRegion from locations of differing worlds!"); From 1d94ad3179c6f4c9d09837df86ff17f295663874 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 21:49:37 +0100 Subject: [PATCH 69/81] Add documentation to EllipsoidRegion --- .../core/world/region/EllipsoidRegion.java | 376 +++++++++++++++++- 1 file changed, 364 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java index 9bad3625..c19b5237 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/EllipsoidRegion.java @@ -1,8 +1,6 @@ package de.pxav.kelp.core.world.region; import com.google.common.collect.Sets; -import de.pxav.kelp.core.KelpPlugin; -import de.pxav.kelp.core.application.KelpApplicationRepository; import de.pxav.kelp.core.world.KelpBlock; import de.pxav.kelp.core.world.KelpChunk; import de.pxav.kelp.core.world.KelpLocation; @@ -14,18 +12,55 @@ import java.util.Set; +/** + * An {@code EllipsoidRegion} represents an ellipsoid shape in a 3d-world. + * An ellipsoid is the 3d-equivalent of an ellipse and has different cases: + * - a sphere: all radius (xRadius, yRadius and zRadius) are equal to each + * other and every outer point of the shape has an equal distance to the center. + * - a spheroid: an ellipse has been rotated around itself. At least two + * radius values have to be equal to each other. + * - a triaxial ellipsoid: all radius values are different from each other + * and there is no symmetry. (most general) + * + * You can check the special cases using {@link #isSphere()} or + * {@link #isSpheroid()}. + * + * An ellipsoid can have a limited radius, which means that it has an + * ellipsoid shape, but is interrupted at some point. Then it has a + * straight border at the given axis as if you would cut it with a 1000° hot + * knife. You can enable those limiters for specific axis using + * {@link #limitXRadius(double)} for example. + * + * @author pxav + */ public class EllipsoidRegion extends KelpRegion { + // four thirds are needed to calculate the volume of an ellipsoid, + // so it is cached here in order to save performance. private static final double FOUR_THIRDS = 1.33333333333333333333d; + // the center of the ellipsoid. If the ellipsoid is a sphere, + // all surface locations will have the same distance to this block. private KelpLocation center; + + // radius in different directions private double xRadius; private double yRadius; private double zRadius; + + // the radius limiters private double limitX = 0; private double limitY = 0; private double limitZ = 0; + /** + * Creates a new {@link EllipsoidRegion} with a given center and + * a radius in all directions. So this ellipsoid will be a sphere. + * + * @param center The center of the sphere. + * @param radius The sphere's radius. + * @return The sphere as an {@link EllipsoidRegion} + */ public static EllipsoidRegion create(KelpLocation center, double radius) { EllipsoidRegion region = new EllipsoidRegion(getRegionRepository()); region.setCenter(center); @@ -36,6 +71,17 @@ public static EllipsoidRegion create(KelpLocation center, double radius) { return region; } + /** + * Creates a new {@link EllipsoidRegion} with a given center and + * different radius values in each direction. So this ellipsoid will + * either be a sphreid or triaxial. + * + * @param center The center of your ellipsoid. + * @param xRadius The radius on the x axis. + * @param yRadius The radius on the x axis. + * @param zRadius The radius on the x axis. + * @return The final {@link EllipsoidRegion} + */ public static EllipsoidRegion create(KelpLocation center, double xRadius, double yRadius, double zRadius) { EllipsoidRegion region = new EllipsoidRegion(getRegionRepository()); region.setCenter(center); @@ -48,6 +94,16 @@ public static EllipsoidRegion create(KelpLocation center, double xRadius, double return region; } + /** + * Creates a new {@link EllipsoidRegion} which fits into the cuboid + * area defined by the two given points. So the outermost points + * will touch the same axis as the limiter blocks, while the sphere + * wont exceed its area. + * + * @param pos1 The first point + * @param pos2 The second point. + * @return The ellipsoid fitting into the cuboid defined by pos1 and pos2. + */ public static EllipsoidRegion create(KelpLocation pos1, KelpLocation pos2) { if (!pos1.getWorldName().equalsIgnoreCase(pos2.getWorldName())) { throw new IllegalArgumentException("Cannot create region from locations of differing worlds!"); @@ -58,34 +114,88 @@ public static EllipsoidRegion create(KelpLocation pos1, KelpLocation pos2) { region.setCenter(pos1.findMidpoint(pos2)); region.worldName = pos1.getWorldName(); - region.setXRadius(Math.abs(region.minPos.getX() - region.maxPos.getX()) * 0.5); - region.setYRadius(Math.abs(region.minPos.getY() - region.maxPos.getY()) * 0.5); - region.setZRadius(Math.abs(region.minPos.getZ() - region.maxPos.getZ()) * 0.5); + + double radius = Math.abs(region.minPos.getX() - region.maxPos.getX()) * 0.5; + region.setXRadius(radius); + region.setYRadius(radius); + region.setZRadius(radius); + return region; } - public EllipsoidRegion(KelpRegionRepository regionRepository) { + private EllipsoidRegion(KelpRegionRepository regionRepository) { super(regionRepository); } + /** + * Limits the ellipsoid radius in the all axis. + * + * Limiting the radius means that if the radius is set to + * 10, while the limiter is 5, the ellipsoid will be calculated + * as a normal 10-radius-ellipsoid, but it will be cut off straight + * as soon as the limiter is exceeded. This will look + * like someone cut with a knife through the ellipsoid. + * + * @param limiter The value to limit all radius values to. + */ public void limitRadius(double limiter) { this.limitX = limiter; this.limitY = limiter; this.limitZ = limiter; } + /** + * Limits the ellipsoid radius in the x axis. + * + * Limiting the radius means that if the radius is set to + * 10, while the limiter is 5, the ellipsoid will be calculated + * as a normal 10-radius-ellipsoid, but it will be cut off straight + * as soon as the limiter is exceeded. This will look + * like someone cut with a knife through the ellipsoid. + * + * @param limiter The value to limit all radius values to. + */ public void limitXRadius(double limiter) { this.limitX = limiter; } + /** + * Limits the ellipsoid radius in the x axis. + * + * Limiting the radius means that if the radius is set to + * 10, while the limiter is 5, the ellipsoid will be calculated + * as a normal 10-radius-ellipsoid, but it will be cut off straight + * as soon as the limiter is exceeded. This will look + * like someone cut with a knife through the ellipsoid. + * + * @param limiter The value to limit all radius values to. + */ public void limitYRadius(double limiter) { this.limitY = limiter; } + /** + * Limits the ellipsoid radius in the x axis. + * + * Limiting the radius means that if the radius is set to + * 10, while the limiter is 5, the ellipsoid will be calculated + * as a normal 10-radius-ellipsoid, but it will be cut off straight + * as soon as the limiter is exceeded. This will look + * like someone cut with a knife through the ellipsoid. + * + * @param limiter The value to limit all radius values to. + */ public void limitZRadius(double limiter) { this.limitZ = limiter; } + /** + * Moves the region into the direction of the given + * {@link Vector}. + * + * @param vector The vector providing the direction and + * power of the movement. + */ @Override protected void moveIgnoreListeners(Vector vector) { center.add(vector); @@ -93,6 +203,14 @@ protected void moveIgnoreListeners(Vector vector) { maxPos.add(vector); } + /** + * Moves the region into a certain direction defined by + * the different axis values. + * + * @param dx How far the region should be moved on the x-axis + * @param dy How far the region should be moved on the y-axis + * @param dz How far the region should be moved on the z-axis + */ @Override protected void moveIgnoreListeners(double dx, double dy, double dz) { Vector vector = new Vector(dx, dy, dz); @@ -101,36 +219,82 @@ protected void moveIgnoreListeners(double dx, double dy, double dz) { maxPos.add(vector); } + /** + * Gets the exact total volume of this region, which is defined + * by {@code (4/3) * PI * xRadius * yRadius * zRadius} + * for all ellipsoid. + * + * @return The exact volume of this region in blocks. + */ @Override public double getVolume() { return FOUR_THIRDS * Math.PI * xRadius * yRadius * zRadius; } + /** + * Gets the approximate total volume of this region, which is defined + * by {@code (4/3) * PI * xRadius * yRadius * zRadius} + * for all ellipsoid. + * + * This method approximates the volume in whole blocks. + * + * @return The approximate volume of this region in blocks. + */ @Override public int getBlockVolume() { return NumberConversions.floor(getVolume()); } + /** + * Sets the center location of this ellipsoid. + * + * @param center The new center. + * @return An instance of this region for fluent builder design. + */ public EllipsoidRegion setCenter(KelpLocation center) { this.center = center; return this; } + /** + * Gets the center of this ellipsoid. + * + * @return The center point of this ellipsoid. + */ @Override public KelpLocation getCenter() { return this.center; } + /** + * Gets a set of all blocks which are on the regions surface. + * + * @return A set of blocks containing all blocks on the regions surface. + */ @Override public Set getSurfaceBlocks() { return getBlocks(true); } + /** + * Gets all blocks contained by this region. + * This can be used to visualize its shape. + * + * @return A set of all blocks contained by this region. + */ @Override public Set getBlocks() { return getBlocks(false); } + /** + * Gets all blocks contained by this region and optionally + * only the blocks contained by the surface. This method + * obeys all criteria set by the radius limiter. + * + * @param surfaceOnly Whether only surface blocks should be returned. + * @return All (surface) blocks of this region. + */ private Set getBlocks(boolean surfaceOnly) { Set output = Sets.newConcurrentHashSet(); double rX = xRadius + 0.5; @@ -189,11 +353,11 @@ private Set getBlocks(boolean surfaceOnly) { } } - + // if limiter is enabled, remove all blocks out of the allowed radius if (limitX > 0 || limitY > 0 || limitZ > 0) { output.parallelStream() .filter(block -> { - // if the location is excluded due to a limited radius + // TRUE if the location is excluded due to a limited radius boolean limitCriteriaX = false, limitCriteriaY = false, limitCriteriaZ = false; if (limitX > 0) { @@ -212,6 +376,9 @@ private Set getBlocks(boolean surfaceOnly) { }) .forEach(output::remove); + // if only the surface is desired, the region would + // have a big hole on the cut sides, which is why a slice is + // added back if (surfaceOnly) { if (limitX > 0) { output.addAll(getZSliceAt(center.getBlockX() + limitX)); @@ -232,6 +399,14 @@ private Set getBlocks(boolean surfaceOnly) { return output; } + /** + * Gets a slice of blocks of the ellipsoid + * which are on a specific height/y-Axis. The + * slice itself will be oriented along the x-axis. + * + * @param y The absolute y value of the blocks you want to get. + * @return All blocks of the desired slice. + */ public Set getYSliceAt(double y) { Set blocks = Sets.newConcurrentHashSet(); getBlocks().parallelStream() @@ -240,6 +415,14 @@ public Set getYSliceAt(double y) { return blocks; } + /** + * Gets a slice of blocks of the ellipsoid + * which are on a specific x-Axis. The + * slice itself will be oriented along the z-axis. + * + * @param x The absolute x value of the blocks you want to get. + * @return All blocks of the desired slice. + */ public Set getXSliceAt(double x) { Set blocks = Sets.newConcurrentHashSet(); getBlocks().parallelStream() @@ -248,6 +431,14 @@ public Set getXSliceAt(double x) { return blocks; } + /** + * Gets a slice of blocks of the ellipsoid + * which are on a specific z-Axis. The + * slice itself will be oriented along the x-axis. + * + * @param z The absolute z value of the blocks you want to get. + * @return All blocks of the desired slice. + */ public Set getZSliceAt(double z) { Set blocks = Sets.newConcurrentHashSet(); getBlocks().parallelStream() @@ -256,16 +447,38 @@ public Set getZSliceAt(double z) { return blocks; } + /** + * Gets all chunks covered by this region no matter if they + * are loaded or not. If you only want loaded chunks, + * use {@link #getLoadedChunks()} instead. + * + * @return A set of all chunks covered by this region. + */ @Override public Set getChunks() { return toCuboid().getChunks(); } + /** + * Gets all chunks that are loaded and covered by this region. + * If you want to include unloaded chunks as well, use {@link #getChunks()} + * instead. + * This method automatically applies the changes to the listener system + * if it is enabled for this region. + * + * @return A set of all loaded chunks covered by this region. + */ @Override public Set getLoadedChunks() { return toCuboid().getLoadedChunks(); } + /** + * Expands the current region by a certain multiplier. + * This method does not update the listeners for the region. + * + * @param amount The amount of expansion in blocks. + */ @Override protected void expandIgnoreListeners(double amount) { xRadius += amount; @@ -274,6 +487,14 @@ protected void expandIgnoreListeners(double amount) { updateMinMax(); } + /** + * Expands the current region into a specific direction by + * a given multiplier. + * This method does not update the listeners for the region. + * + * @param direction The direction to expand the region in. + * @param amount The amount of expansion in blocks. + */ @Override protected void expandIgnoreListeners(KelpBlockFace direction, double amount) { switch (direction) { @@ -295,11 +516,36 @@ protected void expandIgnoreListeners(KelpBlockFace direction, double amount) { updateMinMax(); } + /** + * Expands the current region into a specific direction by + * a given multiplier. + * + * This method automatically moves the ellipsoid up by the given + * amount to avoid that there will be an expansion in the opposite + * direction as well. + * + * This method automatically applies the changes to the listener system + * if it is enabled for this region. + * + * @param direction The direction to expand the region in. + * @param amount The amount of expansion in blocks. + */ public void expandAndMove(KelpBlockFace direction, double amount) { expand(direction, amount); move(direction.getDirection().multiply(amount / 2)); } + /** + * Expands the current region in the given axis. + * This method does not update the listeners for the region. + * + * @param negativeX The expansion on the negative x-axis (equal to west) + * @param positiveX The expansion on the positive x-axis (equal to east) + * @param negativeY The expansion on the negative y-axis (equal to down) + * @param positiveY The expansion on the positive y-axis (equal to up) + * @param negativeZ The expansion on the negative z-axis (equal to north) + * @param positiveZ The expansion on the positive z-axis (equal to south) + */ @Override protected void expandIgnoreListener(double negativeX, double positiveX, double negativeY, double positiveY, double negativeZ, double positiveZ) { this.xRadius = positiveX + negativeX; @@ -308,16 +554,49 @@ protected void expandIgnoreListener(double negativeX, double positiveX, double n updateMinMax(); } + /** + * Updates the {@code minPos} and {@code maxPos} according to the + * radius values. This should be executed every time you expand the + * region for example as the radius values are updated, but the + * outer points remain the same, which might break the listener system. + */ private void updateMinMax() { minPos = center.clone().subtract(xRadius, yRadius, zRadius); maxPos = center.clone().add(xRadius, yRadius, zRadius); } + /** + * Expands the current region in the given axis. + * + * This method automatically moves the ellipsoid up by the given + * amount to avoid that there will be an expansion in the opposite + * direction as well. + * + * This method automatically applies the changes to the listener system + * if it is enabled for this region. + * + * @param negativeX The expansion on the negative x-axis (equal to west) + * @param positiveX The expansion on the positive x-axis (equal to east) + * @param negativeY The expansion on the negative y-axis (equal to down) + * @param positiveY The expansion on the positive y-axis (equal to up) + * @param negativeZ The expansion on the negative z-axis (equal to north) + * @param positiveZ The expansion on the positive z-axis (equal to south) + */ public void expandAndMove(double negativeX, double positiveX, double negativeY, double positiveY, double negativeZ, double positiveZ) { expand(negativeX, positiveX, negativeY, positiveY, negativeZ, positiveZ); move(positiveX - negativeX, positiveY - negativeY, positiveZ - negativeZ); } + /** + * Determines whether the given location is contained by + * this region. This method does not check whether the world + * is equal but only the axis values. + * + * @param x The x-coordinate of the location to check. + * @param y The y-coordinate of the location to check. + * @param z The z-coordinate of the location to check. + * @return {@code true} if the location is contained by the region. + */ @Override public boolean contains(double x, double y, double z) { // if the location is excluded due to a limited radius @@ -338,24 +617,71 @@ public boolean contains(double x, double y, double z) { return getCostAt(x, y, z) <= 1 && !limitCriteriaX && !limitCriteriaY && !limitCriteriaZ; } + /** + * Gets the "cost" of a block at a given location. A location's cost + * is defined by {@code (x/xRadius)^2 + (y/yRadius)^2 + (z/zRadius)^2}, + * where x/y/z are defined by {@code x - center.x}, etc. + * + * This can be used to determine how far a block is away from the + * ellipsoid or in which layer it is roughly located. If the cost + * is smaller than or equal to 1, it is contained by the ellipsoid, + * otherwise it's not. + * + * @param location The location you want to get the cost of. + * @return The "cost" of the given location. + */ public double getCostAt(KelpLocation location) { return getCostAt(location.getX(), location.getY(), location.getZ()); } + /** + * Gets the "cost" of a block at a given location. A location's cost + * is defined by {@code (x/xRadius)^2 + (y/yRadius)^2 + (z/zRadius)^2}, + * where x/y/z are defined by {@code x - center.x}, etc. + * + * This can be used to determine how far a block is away from the + * ellipsoid or in which layer it is roughly located. If the cost + * is smaller than or equal to 1, it is contained by the ellipsoid, + * otherwise it's not. + * + * @param x The x-coordinate of the location you want to get the cost of. + * @param y The y-coordinate of the location you want to get the cost of. + * @param z The z-coordinate of the location you want to get the cost of. + * @return The "cost" of the given location. + */ public double getCostAt(double x, double y, double z) { return ((x - center.getX()) / xRadius) * ((x - center.getX()) / xRadius) + ((y - center.getY()) / yRadius) * ((y - center.getY()) / yRadius) + ((z - center.getZ()) / zRadius) * ((z - center.getZ()) / zRadius); } + /** + * Checks whether this ellipsoid is a sphere. + * For this to be true, all radius values have to be the same: + * {@code xRadius = yRadius = zRadius} + * + * @return {@code true} if all radius are equal. + */ public boolean isSphere() { return xRadius == yRadius && xRadius == zRadius; } + /** + * Checks whether this ellipsoid is a spheroid. + * For this to be true, at least two radius values have to be equal to each other. + * + * @return {@code true} if all radius are equal. + */ public boolean isSpheroid() { return xRadius == yRadius || xRadius == zRadius || yRadius == zRadius; } + /** + * Sets the radius of all axis to the given value. + * + * @param radius The radius to apply for all axis. + * @return An instance of the current region. + */ public EllipsoidRegion setRadius(double radius) { this.xRadius = radius; this.yRadius = radius; @@ -363,37 +689,63 @@ public EllipsoidRegion setRadius(double radius) { return this; } + /** + * Sets the radius of the x axis to the given value. + * + * @param xRadius The radius to apply on the x axis. + * @return An instance of the current region. + */ public EllipsoidRegion setXRadius(double xRadius) { this.xRadius = xRadius; return this; } + /** + * Sets the radius of the y axis to the given value. + * + * @param yRadius The radius to apply on the y axis. + * @return An instance of the current region. + */ public EllipsoidRegion setYRadius(double yRadius) { this.yRadius = yRadius; return this; } + /** + * Sets the radius of the z axis to the given value. + * + * @param zRadius The radius to apply on the z axis. + * @return An instance of the current region. + */ public EllipsoidRegion setZRadius(double zRadius) { this.zRadius = zRadius; return this; } + /** + * Gets the radius of this ellipsoid along the x axis. + * @return The radius along the x axis. + */ public double getXRadius() { return xRadius; } + /** + * Gets the radius of this ellipsoid along the y axis. + * @return The radius along the y axis. + */ public double getYRadius() { return yRadius; } + /** + * Gets the radius of this ellipsoid along the z axis. + * @return The radius along the z axis. + */ public double getZRadius() { return zRadius; } - public String getWorldName() { - return center.getWorldName(); - } - @Override public KelpRegion clone() { return EllipsoidRegion.create(this.center, xRadius, yRadius, zRadius); From 180592b495c08e84c356e147101e5f155c7a9abe Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 21:55:30 +0100 Subject: [PATCH 70/81] Add documentation for KelpRegionEvent --- .../kelp/core/event/kelpevent/KelpRegionEvent.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/event/kelpevent/KelpRegionEvent.java b/core/src/main/java/de/pxav/kelp/core/event/kelpevent/KelpRegionEvent.java index 7fa889c1..c2cc7f6f 100644 --- a/core/src/main/java/de/pxav/kelp/core/event/kelpevent/KelpRegionEvent.java +++ b/core/src/main/java/de/pxav/kelp/core/event/kelpevent/KelpRegionEvent.java @@ -3,14 +3,28 @@ import de.pxav.kelp.core.world.region.KelpRegion; import org.bukkit.event.Event; +/** + * A region event is any event that is related to a specific {@link KelpRegion}. + * + * In most cases you first have to enable listeners for your region using + * {@link KelpRegion#enableListeners()} to work with this region. + * + * @author pxav + */ public abstract class KelpRegionEvent extends Event { + // the region handled by this event protected KelpRegion region; public KelpRegionEvent(KelpRegion region) { this.region = region; } + /** + * Gets the region involved in the current event. + * + * @return The region involved in this event. + */ public KelpRegion getRegion() { return region; } From f983a9d7c468383d195eaebb320f8d97c20d6838 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 21:55:38 +0100 Subject: [PATCH 71/81] Add documentation for PlayerEnterRegionEvent --- .../event/kelpevent/PlayerEnterRegionEvent.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerEnterRegionEvent.java b/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerEnterRegionEvent.java index 6bdcbbaf..906324e5 100644 --- a/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerEnterRegionEvent.java +++ b/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerEnterRegionEvent.java @@ -4,10 +4,19 @@ import de.pxav.kelp.core.world.region.KelpRegion; import org.bukkit.event.HandlerList; +/** + * This event is triggered when a player enters a {@link KelpRegion} + * that has listeners enabled. + * + * The opposite of this event would be {@link PlayerLeaveRegionEvent} + * + * @author pxav + */ public class PlayerEnterRegionEvent extends KelpRegionEvent { private static final HandlerList handlers = new HandlerList(); + // player who entered the region private KelpPlayer player; public PlayerEnterRegionEvent(KelpRegion region, KelpPlayer player) { @@ -15,6 +24,11 @@ public PlayerEnterRegionEvent(KelpRegion region, KelpPlayer player) { this.player = player; } + /** + * Gets the player who entered the region. + * + * @return The player who entered the region. + */ public KelpPlayer getPlayer() { return player; } From 09493225bfeb8b303a9d8d5cf8c09f9507976349 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 21:55:46 +0100 Subject: [PATCH 72/81] Add documentation for PlayerLeaveRegionEvent --- .../event/kelpevent/PlayerLeaveRegionEvent.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerLeaveRegionEvent.java b/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerLeaveRegionEvent.java index 1cabc158..61a95d80 100644 --- a/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerLeaveRegionEvent.java +++ b/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerLeaveRegionEvent.java @@ -5,6 +5,14 @@ import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; +/** + * This event is triggered when a player leaves a {@link KelpRegion} + * that has listeners enabled. + * + * The opposite of this event would be {@link PlayerEnterRegionEvent} + * + * @author pxav + */ public class PlayerLeaveRegionEvent extends KelpRegionEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); @@ -17,6 +25,11 @@ public PlayerLeaveRegionEvent(KelpRegion region, KelpPlayer player) { this.player = player; } + /** + * Gets the player who left the region. + * + * @return The player who left the region. + */ public KelpPlayer getPlayer() { return player; } From f029c0fdb11f52066174be218b6b54f39f8bf9a6 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 21:56:32 +0100 Subject: [PATCH 73/81] Make PlayerLeaveRegionEvent not cancelable anymore --- .../core/event/kelpevent/PlayerLeaveRegionEvent.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerLeaveRegionEvent.java b/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerLeaveRegionEvent.java index 61a95d80..63b6e6e9 100644 --- a/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerLeaveRegionEvent.java +++ b/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerLeaveRegionEvent.java @@ -13,7 +13,7 @@ * * @author pxav */ -public class PlayerLeaveRegionEvent extends KelpRegionEvent implements Cancellable { +public class PlayerLeaveRegionEvent extends KelpRegionEvent { private static final HandlerList handlers = new HandlerList(); @@ -43,14 +43,4 @@ public static HandlerList getHandlerList() { return handlers; } - @Override - public boolean isCancelled() { - return cancelled; - } - - @Override - public void setCancelled(boolean cancelled) { - this.cancelled = cancelled; - } - } From 779f2401e5d912ce59dd2634f5fd09470bdb9469 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 21:57:54 +0100 Subject: [PATCH 74/81] Refactor: move region events to dedicated package --- .../event/kelpevent/{ => region}/KelpRegionEvent.java | 2 +- .../kelpevent/{ => region}/PlayerEnterRegionEvent.java | 2 +- .../kelpevent/{ => region}/PlayerLeaveRegionEvent.java | 4 +--- .../java/de/pxav/kelp/core/world/region/KelpRegion.java | 9 +++++---- .../kelp/core/world/region/KelpRegionRepository.java | 4 ++-- 5 files changed, 10 insertions(+), 11 deletions(-) rename core/src/main/java/de/pxav/kelp/core/event/kelpevent/{ => region}/KelpRegionEvent.java (93%) rename core/src/main/java/de/pxav/kelp/core/event/kelpevent/{ => region}/PlayerEnterRegionEvent.java (95%) rename core/src/main/java/de/pxav/kelp/core/event/kelpevent/{ => region}/PlayerLeaveRegionEvent.java (89%) diff --git a/core/src/main/java/de/pxav/kelp/core/event/kelpevent/KelpRegionEvent.java b/core/src/main/java/de/pxav/kelp/core/event/kelpevent/region/KelpRegionEvent.java similarity index 93% rename from core/src/main/java/de/pxav/kelp/core/event/kelpevent/KelpRegionEvent.java rename to core/src/main/java/de/pxav/kelp/core/event/kelpevent/region/KelpRegionEvent.java index c2cc7f6f..c989345e 100644 --- a/core/src/main/java/de/pxav/kelp/core/event/kelpevent/KelpRegionEvent.java +++ b/core/src/main/java/de/pxav/kelp/core/event/kelpevent/region/KelpRegionEvent.java @@ -1,4 +1,4 @@ -package de.pxav.kelp.core.event.kelpevent; +package de.pxav.kelp.core.event.kelpevent.region; import de.pxav.kelp.core.world.region.KelpRegion; import org.bukkit.event.Event; diff --git a/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerEnterRegionEvent.java b/core/src/main/java/de/pxav/kelp/core/event/kelpevent/region/PlayerEnterRegionEvent.java similarity index 95% rename from core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerEnterRegionEvent.java rename to core/src/main/java/de/pxav/kelp/core/event/kelpevent/region/PlayerEnterRegionEvent.java index 906324e5..f1baff09 100644 --- a/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerEnterRegionEvent.java +++ b/core/src/main/java/de/pxav/kelp/core/event/kelpevent/region/PlayerEnterRegionEvent.java @@ -1,4 +1,4 @@ -package de.pxav.kelp.core.event.kelpevent; +package de.pxav.kelp.core.event.kelpevent.region; import de.pxav.kelp.core.player.KelpPlayer; import de.pxav.kelp.core.world.region.KelpRegion; diff --git a/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerLeaveRegionEvent.java b/core/src/main/java/de/pxav/kelp/core/event/kelpevent/region/PlayerLeaveRegionEvent.java similarity index 89% rename from core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerLeaveRegionEvent.java rename to core/src/main/java/de/pxav/kelp/core/event/kelpevent/region/PlayerLeaveRegionEvent.java index 63b6e6e9..a9341101 100644 --- a/core/src/main/java/de/pxav/kelp/core/event/kelpevent/PlayerLeaveRegionEvent.java +++ b/core/src/main/java/de/pxav/kelp/core/event/kelpevent/region/PlayerLeaveRegionEvent.java @@ -1,8 +1,7 @@ -package de.pxav.kelp.core.event.kelpevent; +package de.pxav.kelp.core.event.kelpevent.region; import de.pxav.kelp.core.player.KelpPlayer; import de.pxav.kelp.core.world.region.KelpRegion; -import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** @@ -18,7 +17,6 @@ public class PlayerLeaveRegionEvent extends KelpRegionEvent { private static final HandlerList handlers = new HandlerList(); private KelpPlayer player; - private boolean cancelled; public PlayerLeaveRegionEvent(KelpRegion region, KelpPlayer player) { super(region); diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java index 6c8deba7..6282653a 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegion.java @@ -1,6 +1,7 @@ package de.pxav.kelp.core.world.region; import de.pxav.kelp.core.KelpPlugin; +import de.pxav.kelp.core.event.kelpevent.region.PlayerEnterRegionEvent; import de.pxav.kelp.core.player.KelpPlayer; import de.pxav.kelp.core.world.KelpBlock; import de.pxav.kelp.core.world.KelpChunk; @@ -20,7 +21,7 @@ * A region can be expanded and moved in the 3D-space and you can do operations * with its blocks. * - * You can also listen for region events, for example the {@link de.pxav.kelp.core.event.kelpevent.PlayerEnterRegionEvent}, + * You can also listen for region events, for example the {@link PlayerEnterRegionEvent}, * which is triggered each time a player enters a registered region. For a region to * be recognized by listeners, you have to enable listeners for it using * {@link #enableListeners()}, which can be disabled at every time using @@ -432,7 +433,7 @@ public UUID getRegionId() { /** * Enables all listeners for this region. - * This means that events such as {@link de.pxav.kelp.core.event.kelpevent.PlayerEnterRegionEvent} + * This means that events such as {@link PlayerEnterRegionEvent} * will be triggered. Region listeners might become relatively * performance intensive if you have many of them, so they are * disabled by default. @@ -449,7 +450,7 @@ public void enableListeners() { /** * Disables all listeners for this region. - * This means that events such as {@link de.pxav.kelp.core.event.kelpevent.PlayerEnterRegionEvent} + * This means that events such as {@link PlayerEnterRegionEvent} * won't be triggered anymore, which saves performance. * * Disabled listeners are the default for all regions. @@ -464,7 +465,7 @@ public void disableListeners() { /** * Checks whether listeners are currently enabled for this region. * Listeners are responsible for triggering events such as - * {@link de.pxav.kelp.core.event.kelpevent.PlayerEnterRegionEvent} + * {@link PlayerEnterRegionEvent} * * @return {@code true} if listeners are enabled. */ diff --git a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java index a96fd3ad..7802d00a 100644 --- a/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java +++ b/core/src/main/java/de/pxav/kelp/core/world/region/KelpRegionRepository.java @@ -2,8 +2,8 @@ import de.pxav.kelp.core.common.ConcurrentMultimap; import de.pxav.kelp.core.common.ConcurrentSetMultimap; -import de.pxav.kelp.core.event.kelpevent.PlayerEnterRegionEvent; -import de.pxav.kelp.core.event.kelpevent.PlayerLeaveRegionEvent; +import de.pxav.kelp.core.event.kelpevent.region.PlayerEnterRegionEvent; +import de.pxav.kelp.core.event.kelpevent.region.PlayerLeaveRegionEvent; import de.pxav.kelp.core.player.KelpPlayer; import de.pxav.kelp.core.scheduler.KelpSchedulerRepository; import de.pxav.kelp.core.scheduler.type.RepeatingScheduler; From 2920686a2b7bd65136601589dc65c547ed2096b3 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 22:12:37 +0100 Subject: [PATCH 75/81] Add documentation for ConcurrentMultimap --- .../kelp/core/common/ConcurrentMultimap.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/core/src/main/java/de/pxav/kelp/core/common/ConcurrentMultimap.java b/core/src/main/java/de/pxav/kelp/core/common/ConcurrentMultimap.java index bb6781c5..06dce0d7 100644 --- a/core/src/main/java/de/pxav/kelp/core/common/ConcurrentMultimap.java +++ b/core/src/main/java/de/pxav/kelp/core/common/ConcurrentMultimap.java @@ -5,16 +5,63 @@ import java.util.Collection; import java.util.Map; +/** + * This is a custom implementation of Guava's normal {@link Multimap} with the + * only difference that this offers thread-safety and some performance optimizations + * in some operations, while other operations are slightly slower than with a normal + * {@code Multimap}. + * + * @param The key type for each multimap entry + * @param The value type for each multimap entry. + */ public interface ConcurrentMultimap extends Multimap { + /** + * Checks whether a single key has all values + * contained by the given collection. + * + * @param iterable The collection to check. + * @return {@code true} of a single key had all of the values contained by the collection. + */ boolean containsValue(Collection iterable); + /** + * Gets the collection associated with the given + * key. If there is no entry for the given key, + * the given fallback collection will be returned. + * + * @param key The key to get the collection of. + * @param defaultCollection The fallback collection to return + * if there is no collection associated with the given key. + * @return The collection associated with the given key or the given fallback collection. + */ Collection getOrDefault(K key, Collection defaultCollection); + /** + * Gets the collection associated with the given + * key. If there is no entry for the given key, + * an empty collection (depending on the implementation) + * will be returned. + * + * @param key The key to get the collection of. + * @return The collection associated with the given key or an empty collection. + */ Collection getOrEmpty(K key); + /** + * Removes all entries with the given value. + * + * @param value The value to remove all entries with. + */ void removeWithValue(V value); + /** + * Takes a normal map and inserts its values + * into the multimap. If one of the contained keys + * already exists, it will simply be added to the collection. + * + * @param newMap The map to be added to the multimap. + */ void putAll(Map newMap); } From 11a321a5c269302148245964c6b0768f9571d05b Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 22:24:51 +0100 Subject: [PATCH 76/81] Bump plugin version to 0.3.2 --- core/src/main/java/de/pxav/kelp/core/KelpPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/de/pxav/kelp/core/KelpPlugin.java b/core/src/main/java/de/pxav/kelp/core/KelpPlugin.java index 54bec763..bf61ba98 100644 --- a/core/src/main/java/de/pxav/kelp/core/KelpPlugin.java +++ b/core/src/main/java/de/pxav/kelp/core/KelpPlugin.java @@ -35,7 +35,7 @@ * * @author pxav */ -@Plugin(name = "Kelp", version = "0.3.0") +@Plugin(name = "Kelp", version = "0.3.2") @Author("pxav") @Description("A cross version spigot framework.") @Singleton From c1ce780637509d3c7a2dce1168ba31dc17d82449 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 22:44:01 +0100 Subject: [PATCH 77/81] Bump project version to 0.3.2 --- core/pom.xml | 2 +- kelp-sql/pom.xml | 4 ++-- pom.xml | 2 +- testing-module/pom.xml | 4 ++-- v1_8_implementation/pom.xml | 4 ++-- v_1_14_implementation/pom.xml | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index f6ef3e8b..0fccfd18 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -5,7 +5,7 @@ com.github.pxav.kelp parent - 0.3.0 + 0.3.2 4.0.0 diff --git a/kelp-sql/pom.xml b/kelp-sql/pom.xml index 0a6f5860..f899b4b7 100644 --- a/kelp-sql/pom.xml +++ b/kelp-sql/pom.xml @@ -5,7 +5,7 @@ parent com.github.pxav.kelp - 0.3.0 + 0.3.2 4.0.0 @@ -20,7 +20,7 @@ com.github.pxav.kelp core - 0.3.0 + 0.3.2 provided diff --git a/pom.xml b/pom.xml index ff12e826..4a557119 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.github.pxav.kelp parent pom - 0.3.0 + 0.3.2 Kelp A cross-version spigot framework to avoid boilerplate code and make your plugin compatible with multiple spigot versions easily diff --git a/testing-module/pom.xml b/testing-module/pom.xml index 5e6f5a07..cb9f8beb 100644 --- a/testing-module/pom.xml +++ b/testing-module/pom.xml @@ -5,7 +5,7 @@ parent com.github.pxav.kelp - 0.3.0 + 0.3.2 4.0.0 @@ -45,7 +45,7 @@ com.github.pxav.kelp core - 0.3.0 + 0.3.2 provided diff --git a/v1_8_implementation/pom.xml b/v1_8_implementation/pom.xml index 14d88634..bc735fa8 100644 --- a/v1_8_implementation/pom.xml +++ b/v1_8_implementation/pom.xml @@ -5,7 +5,7 @@ com.github.pxav.kelp parent - 0.3.0 + 0.3.2 4.0.0 @@ -90,7 +90,7 @@ com.github.pxav.kelp core - 0.3.0 + 0.3.2 provided diff --git a/v_1_14_implementation/pom.xml b/v_1_14_implementation/pom.xml index cf257cab..649854f7 100644 --- a/v_1_14_implementation/pom.xml +++ b/v_1_14_implementation/pom.xml @@ -5,7 +5,7 @@ parent com.github.pxav.kelp - 0.3.0 + 0.3.2 4.0.0 @@ -57,7 +57,7 @@ com.github.pxav.kelp core - 0.3.0 + 0.3.2 org.spigotmc From 55939c9f590ad9f14b6df186fc285b3313d47649 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 22:44:18 +0100 Subject: [PATCH 78/81] Add WIP region tests to testing module --- .../testing/region/CreateRegionCommand.java | 27 +++++++++++++++ .../kelp/testing/region/KRegionCommand.java | 34 +++++++++++++++++++ .../pxav/kelp/testing/region/WandCommand.java | 22 ++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 testing-module/src/main/java/de/pxav/kelp/testing/region/CreateRegionCommand.java create mode 100644 testing-module/src/main/java/de/pxav/kelp/testing/region/KRegionCommand.java create mode 100644 testing-module/src/main/java/de/pxav/kelp/testing/region/WandCommand.java diff --git a/testing-module/src/main/java/de/pxav/kelp/testing/region/CreateRegionCommand.java b/testing-module/src/main/java/de/pxav/kelp/testing/region/CreateRegionCommand.java new file mode 100644 index 00000000..ad7b6f96 --- /dev/null +++ b/testing-module/src/main/java/de/pxav/kelp/testing/region/CreateRegionCommand.java @@ -0,0 +1,27 @@ +package de.pxav.kelp.testing.region; + +import de.pxav.kelp.core.command.CreateSubCommand; +import de.pxav.kelp.core.command.ExecutorType; +import de.pxav.kelp.core.command.KelpCommand; +import de.pxav.kelp.core.player.KelpPlayer; + +import javax.inject.Singleton; + +@Singleton +@CreateSubCommand(name = "create", executorType = ExecutorType.PLAYER_ONLY, parentCommand = KRegionCommand.class) +public class CreateRegionCommand extends KelpCommand { + + @Override + public void onCommandRegister() { + argumentsStartFromZero(true); + allowCustomParameters(true); + } + + @Override + public void onCommand(KelpPlayer player, String[] args) { + if (args.length == 0) { + player.sendMessage("§8[§2§8] §7Please select either §aCUBOID §7or §aELLIPSOID"); + } + } + +} diff --git a/testing-module/src/main/java/de/pxav/kelp/testing/region/KRegionCommand.java b/testing-module/src/main/java/de/pxav/kelp/testing/region/KRegionCommand.java new file mode 100644 index 00000000..2a44ad7c --- /dev/null +++ b/testing-module/src/main/java/de/pxav/kelp/testing/region/KRegionCommand.java @@ -0,0 +1,34 @@ +package de.pxav.kelp.testing.region; + +import de.pxav.kelp.core.command.CreateCommand; +import de.pxav.kelp.core.command.ExecutorType; +import de.pxav.kelp.core.command.KelpCommand; +import de.pxav.kelp.core.player.KelpPlayer; + +import javax.inject.Singleton; + +@Singleton +@CreateCommand(name = "kregion", executorType = ExecutorType.PLAYER_ONLY) +public class KRegionCommand extends KelpCommand { + + @Override + public void onCommandRegister() { + + } + + @Override + public void onCommand(KelpPlayer player, String[] args) { + if (args.length == 0) { + player.sendPrefixedMessages("§8[§2Kelp§8] ", + "§8§m----------------------------------", + "§7", + "§cWork in progress", + "§7/kregion wand", + "§7/kregion create ", + "§7/kregion edit", + "", + "§8§m----------------------------------"); + } + } + +} diff --git a/testing-module/src/main/java/de/pxav/kelp/testing/region/WandCommand.java b/testing-module/src/main/java/de/pxav/kelp/testing/region/WandCommand.java new file mode 100644 index 00000000..aacdb6c3 --- /dev/null +++ b/testing-module/src/main/java/de/pxav/kelp/testing/region/WandCommand.java @@ -0,0 +1,22 @@ +package de.pxav.kelp.testing.region; + +import de.pxav.kelp.core.command.CreateSubCommand; +import de.pxav.kelp.core.command.ExecutorType; +import de.pxav.kelp.core.command.KelpCommand; +import de.pxav.kelp.core.player.KelpPlayer; + +import javax.inject.Singleton; + +@Singleton +@CreateSubCommand(name = "wand", executorType = ExecutorType.PLAYER_ONLY, parentCommand = KRegionCommand.class) +public class WandCommand extends KelpCommand { + + @Override + public void onCommandRegister() { + super.onCommandRegister(); + } + + @Override + public void onCommand(KelpPlayer player, String[] args) {} + +} From 252b110751c15a9eda49dbdfadcbc498dd055ad2 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 22:44:55 +0100 Subject: [PATCH 79/81] Add basic and incomplete NPC tests to testing module --- .../de/pxav/kelp/testing/npc/NpcCommand.java | 45 ++++++++ .../kelp/testing/npc/NpcRemoveAllCommand.java | 28 +++++ .../kelp/testing/npc/NpcSpawnCommand.java | 57 ++++++++++ .../de/pxav/kelp/testing/npc/gui/NpcGui.java | 107 ++++++++++++++++++ .../kelp/testing/npc/gui/TitleLineGui.java | 11 ++ 5 files changed, 248 insertions(+) create mode 100644 testing-module/src/main/java/de/pxav/kelp/testing/npc/NpcCommand.java create mode 100644 testing-module/src/main/java/de/pxav/kelp/testing/npc/NpcRemoveAllCommand.java create mode 100644 testing-module/src/main/java/de/pxav/kelp/testing/npc/NpcSpawnCommand.java create mode 100644 testing-module/src/main/java/de/pxav/kelp/testing/npc/gui/NpcGui.java create mode 100644 testing-module/src/main/java/de/pxav/kelp/testing/npc/gui/TitleLineGui.java diff --git a/testing-module/src/main/java/de/pxav/kelp/testing/npc/NpcCommand.java b/testing-module/src/main/java/de/pxav/kelp/testing/npc/NpcCommand.java new file mode 100644 index 00000000..736f8340 --- /dev/null +++ b/testing-module/src/main/java/de/pxav/kelp/testing/npc/NpcCommand.java @@ -0,0 +1,45 @@ +package de.pxav.kelp.testing.npc; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import de.pxav.kelp.core.command.CreateCommand; +import de.pxav.kelp.core.command.ExecutorType; +import de.pxav.kelp.core.command.KelpCommand; +import de.pxav.kelp.core.npc.KelpNpc; +import de.pxav.kelp.core.player.KelpPlayer; + +import javax.inject.Singleton; +import java.util.UUID; + +@Singleton +@CreateCommand( + name = "knpc", + executorType = ExecutorType.PLAYER_ONLY) +public class NpcCommand extends KelpCommand { + + private static Multimap playerNpcs = HashMultimap.create(); + + @Override + public void onCommandRegister() { + permission("kelp.test.npc"); + noPlayerMessage("§cYou have to be a player to use this command"); + } + + @Override + public void onCommand(KelpPlayer player, String[] args) { + if (args.length == 0) { + player.sendPrefixedMessages("§8[§2Kelp§8] ", + "§8§m----------------------------------", + "§7", + "§7/knpc spawn", + "§7/knpc removeAll", + "", + "§8§m----------------------------------"); + } + } + + public static Multimap getPlayerNpcs() { + return playerNpcs; + } + +} diff --git a/testing-module/src/main/java/de/pxav/kelp/testing/npc/NpcRemoveAllCommand.java b/testing-module/src/main/java/de/pxav/kelp/testing/npc/NpcRemoveAllCommand.java new file mode 100644 index 00000000..70702235 --- /dev/null +++ b/testing-module/src/main/java/de/pxav/kelp/testing/npc/NpcRemoveAllCommand.java @@ -0,0 +1,28 @@ +package de.pxav.kelp.testing.npc; + +import de.pxav.kelp.core.command.CreateSubCommand; +import de.pxav.kelp.core.command.ExecutorType; +import de.pxav.kelp.core.command.KelpCommand; +import de.pxav.kelp.core.command.KelpConsoleSender; +import de.pxav.kelp.core.npc.KelpNpc; + +@CreateSubCommand(name = "removeAll", + executorType = ExecutorType.PLAYER_AND_CONSOLE, + parentCommand = NpcCommand.class) +public class NpcRemoveAllCommand extends KelpCommand { + + @Override + public void onCommandRegister() { + delegatePlayerToConsole(true); + inheritFromMainCommand(true); + } + + @Override + public void onCommand(KelpConsoleSender consoleSender, String[] args) { + NpcCommand.getPlayerNpcs().keySet().forEach(uuid + -> NpcCommand.getPlayerNpcs().get(uuid).forEach(KelpNpc::remove)); + NpcCommand.getPlayerNpcs().clear(); + consoleSender.sendMessage("§8[§2Kelp§8] §7All NPCs have been removed successfully."); + } + +} diff --git a/testing-module/src/main/java/de/pxav/kelp/testing/npc/NpcSpawnCommand.java b/testing-module/src/main/java/de/pxav/kelp/testing/npc/NpcSpawnCommand.java new file mode 100644 index 00000000..7fa25bf5 --- /dev/null +++ b/testing-module/src/main/java/de/pxav/kelp/testing/npc/NpcSpawnCommand.java @@ -0,0 +1,57 @@ +package de.pxav.kelp.testing.npc; + + + +import de.pxav.kelp.core.command.CreateSubCommand; +import de.pxav.kelp.core.command.ExecutorType; +import de.pxav.kelp.core.command.KelpCommand; +import de.pxav.kelp.core.event.kelpevent.npc.NpcInteractAction; +import de.pxav.kelp.core.npc.KelpNpc; +import de.pxav.kelp.core.player.KelpPlayer; +import de.pxav.kelp.core.scheduler.type.DelayedScheduler; +import de.pxav.kelp.testing.npc.gui.NpcGui; + +import javax.inject.Inject; + +@CreateSubCommand( + name = "spawn", + executorType = ExecutorType.PLAYER_ONLY, + parentCommand = NpcCommand.class) +public class NpcSpawnCommand extends KelpCommand { + + @Inject private NpcGui gui; + + @Override + public void onCommandRegister() { + inheritFromMainCommand(true); + } + + @Override + public void onCommand(KelpPlayer player, String[] args) { + KelpNpc npc = KelpNpc.create(); + + npc.location(player.getLocation()); + npc.player(player); + npc.customName("§2Kelp Demo NPC"); + npc.showCustomName(); + npc.onInteract(event -> { + if (event.getAction() == NpcInteractAction.RIGHT_CLICK) { + DelayedScheduler.create().withDelayOf(50).milliseconds().run(taskId -> { + gui.open(npc); + }); + + return; + } + + // todo if npc is attackable, play damage animation + }); + + npc.spawn(); + npc.lookTo(player.getLocation()); + + NpcCommand.getPlayerNpcs().put(player.getUUID(), npc); + player.sendMessage("§8[§2Kelp§8] §7An NPC has been spawned at your location."); + player.sendMessage("§8[§2Kelp§8] §7Right click it to change some settings."); + } + +} diff --git a/testing-module/src/main/java/de/pxav/kelp/testing/npc/gui/NpcGui.java b/testing-module/src/main/java/de/pxav/kelp/testing/npc/gui/NpcGui.java new file mode 100644 index 00000000..cabbaf92 --- /dev/null +++ b/testing-module/src/main/java/de/pxav/kelp/testing/npc/gui/NpcGui.java @@ -0,0 +1,107 @@ +package de.pxav.kelp.testing.npc.gui; + +import de.pxav.kelp.core.inventory.item.KelpItem; +import de.pxav.kelp.core.inventory.material.KelpMaterial; +import de.pxav.kelp.core.inventory.type.AnimatedInventory; +import de.pxav.kelp.core.inventory.widget.ItemWidget; +import de.pxav.kelp.core.inventory.widget.Pagination; +import de.pxav.kelp.core.npc.KelpNpc; +import de.pxav.kelp.core.player.KelpPlayer; +import de.pxav.kelp.core.player.prompt.PromptResponseType; +import de.pxav.kelp.core.scheduler.type.DelayedScheduler; +import org.bukkit.ChatColor; + +import java.util.concurrent.TimeUnit; + +public class NpcGui { + + private static final String descriptionLine = "§8§m----------------------------"; + + public void open(KelpNpc npc) { + KelpPlayer player = npc.getPlayer(); + AnimatedInventory inventory = AnimatedInventory.create(); + + inventory.rows(4); + + inventory.addWidget(ItemWidget.create() + .player(player) + .item(KelpItem.create() + .displayName("§6§lEDITING NPC") + .material(KelpMaterial.OAK_SIGN_ITEM) + .slot(4) + .addItemDescription( + "§8§m----------------------------", + "§7Name§8: §e" + npc.getCustomName(), + "§7Tab-Name§8: §e" + npc.getTabListName() + ))); + + inventory.addWidget(Pagination.create() + .player(player) + .contentSlots( + 10, 11, 12, 13, 14, 15, 16, + 19, 20, 21, 22, 23, 24, 25) + .contentItems( + KelpItem.create() + .displayName("§eEdit Custom Name") + .material(KelpMaterial.NAME_TAG) + .addItemDescription( + descriptionLine, + "§7Change the NPC's custom name") + .addListener(player, event -> { + player.closeInventory(); + player.openAnvilPrompt() + .initialText(npc.getCustomName().replace("§", "&")) + .sourceMaterial(KelpMaterial.NAME_TAG) + .withAsyncTimeout(60, TimeUnit.SECONDS, () -> {}, true) + .handle(response -> { + if (response.length() >= 16) { + player.sendMessage("§cGiven name is too long. Limit is 16 chars."); + return PromptResponseType.TRY_AGAIN; + } + + npc.customName(ChatColor.translateAlternateColorCodes('&', response)); + player.sendMessage("§aCustom name has been changed successfully."); + return PromptResponseType.ACCEPTED; + }); + }), + KelpItem.create() + .displayName("§cNPC is attackable") + .material(KelpMaterial.IRON_SWORD) + .addItemDescription( + descriptionLine, + "§7Toggle whether the NPC is attackable or not.", + "§c(coming soon)" + ), + KelpItem.create() + .displayName("§cChange title lines") + .material(KelpMaterial.PAINTING) + .addItemDescription( + descriptionLine, + "§7Change the NPC's text lines above its head" + ), + KelpItem.create() + .displayName("§eMake the NPC walk") + .material(KelpMaterial.RAIL) + .addItemDescription( + descriptionLine, + "§7Let the npc walk to a given target", + "§7or direction. No physics are implemented", + "§7yet." + ) + ) + .previousButton(KelpItem.create() + .displayName("§6§lPrevious page") + .slot(27) + .material(KelpMaterial.ARROW), + () -> {}) // do nothing when on first page + .nextButton(KelpItem.create() + .displayName("§6§lNext page") + .slot(35) + .material(KelpMaterial.ARROW), + () -> {}) // do nothing when on last page + ); + + player.openInventory(inventory); + } + +} diff --git a/testing-module/src/main/java/de/pxav/kelp/testing/npc/gui/TitleLineGui.java b/testing-module/src/main/java/de/pxav/kelp/testing/npc/gui/TitleLineGui.java new file mode 100644 index 00000000..ed185083 --- /dev/null +++ b/testing-module/src/main/java/de/pxav/kelp/testing/npc/gui/TitleLineGui.java @@ -0,0 +1,11 @@ +package de.pxav.kelp.testing.npc.gui; + +import de.pxav.kelp.core.npc.KelpNpc; + +public class TitleLineGui { + + public void openTitleLineEditor(KelpNpc npc) { + + } + +} From fde57a72f4c03e9e95582e89c39de105159eeae0 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 22:59:33 +0100 Subject: [PATCH 80/81] Add CHANGELOG for v0.3.2 --- CHANGELOG/kelp-v0.3.2.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 CHANGELOG/kelp-v0.3.2.md diff --git a/CHANGELOG/kelp-v0.3.2.md b/CHANGELOG/kelp-v0.3.2.md new file mode 100644 index 00000000..ae8dae7f --- /dev/null +++ b/CHANGELOG/kelp-v0.3.2.md @@ -0,0 +1,17 @@ +# v0.3.2 +> Release date: 19.03.2021 + +**The regions update**: +* Add basic region library to the kelp world library. + * Add supertype for regions `KelpRegion` + * Add region implementation `CuboidRegion` representing a cuboid area of blocks + * Add region implementation `EllipsoidRegion` representing ellipsoids including spheres and sphereoids + * Add new event type `KelpRegionEvent` + * Add `PlayerEnterRegionEvent` triggered when a player enters a region + * Add `PlayerLeaveRegionEvent` triggered when a player leaves a region +* Add new multimap type `ConcurrentMultimap` offering multimaps with thread-safety. + * `ConcurrentSetMultimap` using a set in the background + * `ConcurrentListMultimap` using a normal list in the background +* Add a custom implementation of bukkits block face: `KelpBlockFace` +* Add documentation to `KelpChunk` +* Add basic NPC and region tests to `testing-module` \ No newline at end of file From e32fe557a75dc3c52cdf8b8f44afa51433ae2520 Mon Sep 17 00:00:00 2001 From: pxav Date: Fri, 19 Mar 2021 23:03:29 +0100 Subject: [PATCH 81/81] Update version in README to 0.3.2 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9f8785ba..b88d803a 100644 --- a/README.md +++ b/README.md @@ -125,13 +125,13 @@ There are version implementations for the following version implementations avai com.github.pxav.kelp core - 0.3.0 + 0.3.2 ``` ### Gradle ```shell script -compile group: 'com.github.pxav.kelp', name: 'core', version: '0.3.0' +compile group: 'com.github.pxav.kelp', name: 'core', version: '0.3.2' ``` ### Other build tools