diff --git a/CHANGELOG/kelp-v0.3.0.md b/CHANGELOG/kelp-v0.3.0.md
new file mode 100644
index 00000000..74e531f0
--- /dev/null
+++ b/CHANGELOG/kelp-v0.3.0.md
@@ -0,0 +1,37 @@
+# v0.3.0
+> Release date: 27.02.2021
+
+**The world update**:
+* Add custom library to for world and location management:
+ * Add `KelpWorld` class replacing the normal `World` class by bukkit. It still does not offer all methods of a default bukkit world, but offers integration with other kelp world classes.
+ * Add `KelpLocation` offering a replacement for the normal bukkit `Location`. It offers some more methods for vector calculation for example.
+ * Add `KelpChunk`, which is a replacement for the normal bukkit `Chunk`. On top of the normal bukkit methods, it offers methods to work with neighbouring chunks.
+ * Add `KelpBlock`
+ * All the mentioned classes port methods from newer versions to older versions for example bone meal application for `KelpBlock` or checking whether a `KelpChunk` is a slime chunk. More method ports will follow.
+ * Add some util methods used by the kelp world classes:
+ * `WorldType` expresses whether a world is overworld, nether or the end
+ * `CardinalDirection` expresses in which direction a player or relative block is facing. It simplifies the work wit `yaw`
+ * `ExplosionPower` is basically just a wrapper for a float, but it contains some default values for default minecraft explosion types, which makes the code more readable. Instead of `4F`, you would write `ExplosionPower.TNT`
+ * Add functionality to force load chunks
+* The `KelpPlayer`, `KelpEntity`, `KelpNpc` class as well as the particle library are now using the new location system instead of the normal bukkit system.
+* Add `KelpDummyPlugin` which can be used to emulate a plugin (`KelpApplication`) when developing core features where providing a plugin instance is needed.
+* Fix some material sub ids of the above mentioned ones.
+* Add `MathUtils` class providing some useful mathematical functions
+* When tagging an item of type `AIR`, the method does not return `null` anymore but the original item stack without a tag. This should avoid unexpected NPEs in the future.
+* Documentation improvements
+* Fix #45: Item description of `KelpItem` was not rendered.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/README.md b/README.md
index 89ae06cc..9f8785ba 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.2.0
+ 0.3.0
```
### Gradle
```shell script
-compile group: 'com.github.pxav.kelp', name: 'core', version: '0.2.0'
+compile group: 'com.github.pxav.kelp', name: 'core', version: '0.3.0'
```
### Other build tools
diff --git a/core/pom.xml b/core/pom.xml
index 2d8fb227..f6ef3e8b 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -5,7 +5,7 @@
com.github.pxav.kelp
parent
- 0.2.0
+ 0.3.0
4.0.0
@@ -139,7 +139,7 @@
org.spigotmc
spigot-api
- 1.16.1-R0.1-SNAPSHOT
+ 1.16.5-R0.1-SNAPSHOT
provided
@@ -158,7 +158,7 @@
org.bukkit
craftbukkit
- 1.14.4-R0.1-SNAPSHOT
+ 1.16.5-R0.1-SNAPSHOT
provided
diff --git a/core/src/main/java/de/pxav/kelp/core/KelpDummyPlugin.java b/core/src/main/java/de/pxav/kelp/core/KelpDummyPlugin.java
new file mode 100644
index 00000000..1b659001
--- /dev/null
+++ b/core/src/main/java/de/pxav/kelp/core/KelpDummyPlugin.java
@@ -0,0 +1,18 @@
+package de.pxav.kelp.core;
+
+import de.pxav.kelp.core.application.KelpApplication;
+
+/**
+ * This class is used to pass a dummy plugin instance when developing
+ * inside the core module.
+ *
+ * For some things like {@link de.pxav.kelp.core.world.KelpChunk#addForceLoadFlag(Class)} you
+ * need to pass a {@link KelpApplication} class representing your plugin, but the kelp core
+ * obviously is no Kelp plugin, but a spigot plugin ({@link org.bukkit.plugin.java.JavaPlugin}).
+ *
+ * So if you ever run into such scenarios as a KelpCore developer, use this
+ * class.
+ *
+ * @author pxav
+ */
+public class KelpDummyPlugin extends KelpApplication {}
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 660dd3ef..54bec763 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.2.0")
+@Plugin(name = "Kelp", version = "0.3.0")
@Author("pxav")
@Description("A cross version spigot framework.")
@Singleton
diff --git a/core/src/main/java/de/pxav/kelp/core/common/MathUtils.java b/core/src/main/java/de/pxav/kelp/core/common/MathUtils.java
new file mode 100644
index 00000000..9d40cec1
--- /dev/null
+++ b/core/src/main/java/de/pxav/kelp/core/common/MathUtils.java
@@ -0,0 +1,56 @@
+package de.pxav.kelp.core.common;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * A small collection of some mathematical utils for Kelp.
+ * This class is functional, so all methods can be accessed statically.
+ *
+ * @author pxav
+ */
+public class MathUtils {
+
+ /**
+ * Checks if the given number x is even, which is
+ * equal to {@code x % 2 == 0}
+ *
+ * @param number The number to check.
+ * @return {@code true} if the number is even.
+ */
+ public static boolean isEven(int number) {
+ return number % 2 == 0;
+ }
+
+ /**
+ * Checks if the given number x is odd, which is
+ * equal to {@code x % 2 == 1}
+ *
+ * @param number The number to check.
+ * @return {@code true} if the number is odd.
+ */
+ public static boolean isOdd(int number) {
+ return number % 2 != 0;
+ }
+
+ /**
+ * Randomly calculates whether a certain chance has been
+ * fulfilled. If your {@code chance} is set to {@code 50},
+ * there will be a 50% chance that this method will return
+ * {@code true}. If it is {@code 5}, then there will be a
+ * {@code 5} per cent chance that this method will return
+ * {@code true}.
+ *
+ * @param chance The chance to use for random calculation.
+ * @return Either true or false depending on your luck and the provided chance.
+ */
+ public static boolean perCentChance(int chance) {
+ if (chance == 100) {
+ return true;
+ }
+ if (chance == 0) {
+ return false;
+ }
+ return ThreadLocalRandom.current().nextInt(0, 101) < chance;
+ }
+
+}
diff --git a/core/src/main/java/de/pxav/kelp/core/entity/KelpEntity.java b/core/src/main/java/de/pxav/kelp/core/entity/KelpEntity.java
index 7d6143fb..58ea851d 100644
--- a/core/src/main/java/de/pxav/kelp/core/entity/KelpEntity.java
+++ b/core/src/main/java/de/pxav/kelp/core/entity/KelpEntity.java
@@ -1,10 +1,10 @@
package de.pxav.kelp.core.entity;
import de.pxav.kelp.core.entity.version.EntityVersionTemplate;
-import org.bukkit.Bukkit;
+import de.pxav.kelp.core.world.KelpLocation;
+import de.pxav.kelp.core.world.KelpWorld;
import org.bukkit.Location;
import org.bukkit.Server;
-import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.util.Vector;
@@ -61,12 +61,21 @@ public KelpEntity entityType(KelpEntityType entityType) {
return this;
}
- public Location getInitialLocation() {
+ public Location getInitialBukkitLocation() {
return initialLocation;
}
- public KelpEntity initialLocation(Location currentLocation) {
- this.initialLocation = currentLocation;
+ public KelpLocation getInitialLocation() {
+ return KelpLocation.from(initialLocation);
+ }
+
+ public KelpEntity initialLocation(KelpLocation currentLocation) {
+ this.initialLocation = currentLocation.getBukkitLocation();
+ return this;
+ }
+
+ public KelpEntity initialLocation(Location location) {
+ this.initialLocation = location;
return this;
}
@@ -104,7 +113,7 @@ public Entity toBukkitEntity() {
*
* @return The current location of the current entity.
*/
- public Location getLocation() {
+ public KelpLocation getLocation() {
return entityVersionTemplate.getLocation(toBukkitEntity());
}
@@ -162,8 +171,8 @@ public boolean isOnGround() {
*
* @return The world where the entity is currently located.
*/
- public World getWorld() {
- return entityVersionTemplate.getWorld(toBukkitEntity());
+ public KelpWorld getWorld() {
+ return KelpWorld.from(getLocation().getWorldName());
}
/**
@@ -183,7 +192,7 @@ public KelpEntity setRotation(float yaw, float pitch) {
*
* @param to The location you want the entity to be teleported to.
*/
- public KelpEntity teleport(Location to) {
+ public KelpEntity teleport(KelpLocation to) {
entityVersionTemplate.teleport(toBukkitEntity(), to, PlayerTeleportEvent.TeleportCause.PLUGIN);
return this;
}
@@ -198,42 +207,7 @@ public KelpEntity teleport(Location to) {
* @param z The exact value of the location's z axis.
*/
public KelpEntity teleport(double x, double y, double z) {
- World world = entityVersionTemplate.getWorld(toBukkitEntity());
- Location to = new Location(world, x, y, z);
- teleport(to);
- return this;
- }
-
- /**
- * Teleports the entity to the location at the given
- * coordinates. As there is no world passed, this method
- * will use the current world of the entity.
- *
- * @param x The block value of the location's x axis.
- * @param y The block value of the location's y axis.
- * @param z The block value of the location's z axis.
- */
- public KelpEntity teleport(int x, int y, int z) {
- World world = entityVersionTemplate.getWorld(toBukkitEntity());
- Location to = new Location(world, x, y, z);
- teleport(to);
- return this;
- }
-
- /**
- * Teleports the entity to the location at the given
- * coordinates. As there is no world passed, this method
- * will use the current world of the entity.
- *
- * @param x The block value of the location's x axis.
- * @param y The block value of the location's y axis.
- * @param z The block value of the location's z axis.
- * @param yaw The yaw rotation of the entity.
- * @param pitch The location's pitch
- */
- public KelpEntity teleport(int x, int y, int z, float yaw, float pitch) {
- World world = entityVersionTemplate.getWorld(toBukkitEntity());
- Location to = new Location(world, x, y, z, yaw, pitch);
+ KelpLocation to = KelpLocation.from(getWorld(), x, y, z);
teleport(to);
return this;
}
@@ -250,8 +224,7 @@ public KelpEntity teleport(int x, int y, int z, float yaw, float pitch) {
* @param pitch The location's pitch
*/
public KelpEntity teleport(double x, double y, double z, float yaw, float pitch) {
- World world = entityVersionTemplate.getWorld(toBukkitEntity());
- Location to = new Location(world, x, y, z, yaw, pitch);
+ KelpLocation to = KelpLocation.from(getWorld(), x, y, z, yaw, pitch);
teleport(to);
return this;
}
@@ -267,40 +240,8 @@ public KelpEntity teleport(double x, double y, double z, float yaw, float pitch)
* @param yaw The yaw rotation of the entity.
* @param pitch The location's pitch
*/
- public KelpEntity teleport(World world, double x, double y, double z, float yaw, float pitch) {
- Location to = new Location(world, x, y, z, yaw, pitch);
- teleport(to);
- return this;
- }
-
- /**
- * Teleports the entity to the location at the given
- * coordinates.
- *
- * @param world The world where the entity should be teleported to.
- * @param x The block value of the location's x axis.
- * @param y The block value of the location's y axis.
- * @param z The block value of the location's z axis.
- * @param yaw The yaw rotation of the entity.
- * @param pitch The location's pitch
- */
- public KelpEntity teleport(World world, int x, int y, int z, float yaw, float pitch) {
- Location to = new Location(world, x, y, z, yaw, pitch);
- teleport(to);
- return this;
- }
-
- /**
- * Teleports the entity to the location at the given
- * coordinates.
- *
- * @param world The world where the entity should be teleported to.
- * @param x The block value of the location's x axis.
- * @param y The block value of the location's y axis.
- * @param z The block value of the location's z axis.
- */
- public KelpEntity teleport(World world, int x, int y, int z) {
- Location to = new Location(world, x, y, z);
+ public KelpEntity teleport(KelpWorld world, double x, double y, double z, float yaw, float pitch) {
+ KelpLocation to = KelpLocation.from(world, x, y, z, yaw, pitch);
teleport(to);
return this;
}
@@ -314,8 +255,8 @@ public KelpEntity teleport(World world, int x, int y, int z) {
* @param y The exact value of the location's y axis.
* @param z The exact value of the location's z axis.
*/
- public KelpEntity teleport(World world, double x, double y, double z) {
- Location to = new Location(world, x, y, z);
+ public KelpEntity teleport(KelpWorld world, double x, double y, double z) {
+ KelpLocation to = KelpLocation.from(world, x, y, z);
teleport(to);
return this;
}
@@ -332,39 +273,7 @@ public KelpEntity teleport(World world, double x, double y, double z) {
* @param pitch The location's pitch
*/
public KelpEntity teleport(String worldName, double x, double y, double z, float yaw, float pitch) {
- Location to = new Location(Bukkit.getWorld(worldName), x, y, z, yaw, pitch);
- teleport(to);
- return this;
- }
-
- /**
- * Teleports the entity to the location at the given
- * coordinates.
- *
- * @param worldName The name of the world where the entity should be teleported to.
- * @param x The block value of the location's x axis.
- * @param y The block value of the location's y axis.
- * @param z The block value of the location's z axis.
- * @param yaw The yaw rotation of the entity.
- * @param pitch The location's pitch
- */
- public KelpEntity teleport(String worldName, int x, int y, int z, float yaw, float pitch) {
- Location to = new Location(Bukkit.getWorld(worldName), x, y, z, yaw, pitch);
- teleport(to);
- return this;
- }
-
- /**
- * Teleports the entity to the location at the given
- * coordinates.
- *
- * @param worldName The name of the world where the entity should be teleported to.
- * @param x The block value of the location's x axis.
- * @param y The block value of the location's y axis.
- * @param z The block value of the location's z axis.
- */
- public KelpEntity teleport(String worldName, int x, int y, int z) {
- Location to = new Location(Bukkit.getWorld(worldName), x, y, z);
+ KelpLocation to = KelpLocation.from(worldName, x, y, z, yaw, pitch);
teleport(to);
return this;
}
@@ -379,7 +288,7 @@ public KelpEntity teleport(String worldName, int x, int y, int z) {
* @param z The exact value of the location's z axis.
*/
public KelpEntity teleport(String worldName, double x, double y, double z) {
- Location to = new Location(Bukkit.getWorld(worldName), x, y, z);
+ KelpLocation to = KelpLocation.from(worldName, x, y, z);
teleport(to);
return this;
}
diff --git a/core/src/main/java/de/pxav/kelp/core/entity/LivingKelpEntity.java b/core/src/main/java/de/pxav/kelp/core/entity/LivingKelpEntity.java
index e75b5c60..7dab04a2 100644
--- a/core/src/main/java/de/pxav/kelp/core/entity/LivingKelpEntity.java
+++ b/core/src/main/java/de/pxav/kelp/core/entity/LivingKelpEntity.java
@@ -2,6 +2,7 @@
import de.pxav.kelp.core.entity.version.EntityVersionTemplate;
import de.pxav.kelp.core.entity.version.LivingEntityVersionTemplate;
+import de.pxav.kelp.core.world.KelpLocation;
import org.bukkit.Location;
import org.bukkit.entity.LivingEntity;
@@ -43,7 +44,7 @@ public LivingEntity toBukkitLivingEntity() {
return bukkitLivingEntity;
}
- public Location getEyeLocation() {
+ public KelpLocation getEyeLocation() {
return this.livingEntityVersionTemplate.getEyeLocation(bukkitLivingEntity);
}
diff --git a/core/src/main/java/de/pxav/kelp/core/entity/type/DroppedItemEntity.java b/core/src/main/java/de/pxav/kelp/core/entity/type/DroppedItemEntity.java
index ca58ba1b..2b17d9d0 100644
--- a/core/src/main/java/de/pxav/kelp/core/entity/type/DroppedItemEntity.java
+++ b/core/src/main/java/de/pxav/kelp/core/entity/type/DroppedItemEntity.java
@@ -1,10 +1,13 @@
package de.pxav.kelp.core.entity.type;
+import de.pxav.kelp.core.KelpPlugin;
import de.pxav.kelp.core.entity.KelpEntity;
import de.pxav.kelp.core.entity.KelpEntityType;
+import de.pxav.kelp.core.entity.version.EntityTypeVersionTemplate;
import de.pxav.kelp.core.entity.version.EntityVersionTemplate;
import de.pxav.kelp.core.inventory.item.KelpItem;
import org.bukkit.Location;
+import org.bukkit.entity.Item;
/**
* A class description goes here.
@@ -16,6 +19,10 @@ public class DroppedItemEntity extends KelpEntity {
private KelpItem item;
private ItemDropType itemDropType;
+ public static DroppedItemEntity from(Item item) {
+ return (DroppedItemEntity) KelpPlugin.getInjector().getInstance(EntityTypeVersionTemplate.class).getKelpEntity(item);
+ }
+
public DroppedItemEntity() {}
public DroppedItemEntity(EntityVersionTemplate entityVersionTemplate, Object entity, int entityId, Location location, KelpItem item) {
diff --git a/core/src/main/java/de/pxav/kelp/core/entity/type/ItemDropType.java b/core/src/main/java/de/pxav/kelp/core/entity/type/ItemDropType.java
index 4799ce97..b79bd5ff 100644
--- a/core/src/main/java/de/pxav/kelp/core/entity/type/ItemDropType.java
+++ b/core/src/main/java/de/pxav/kelp/core/entity/type/ItemDropType.java
@@ -1,13 +1,27 @@
package de.pxav.kelp.core.entity.type;
/**
- * A class description goes here.
+ * This enum expresses how an item should be dropped.
+ * There are different modes an item can be dropped.
*
* @author pxav
*/
public enum ItemDropType {
+ /**
+ * Drops the item at the provided location without any
+ * random spread or offset. It is guaranteed that the item will
+ * land at the given location.
+ */
NORMAL,
+
+ /**
+ * Drops the item with a natural random offset. That means
+ * the location you provide for an item to land at won't be the
+ * exact location where it spawns but minecraft will calculate a
+ * random location within a small radius and drop it there, which
+ * gives that natural-looking effect.
+ */
NATURAL
}
diff --git a/core/src/main/java/de/pxav/kelp/core/entity/version/EntityVersionTemplate.java b/core/src/main/java/de/pxav/kelp/core/entity/version/EntityVersionTemplate.java
index 60731272..08435e61 100644
--- a/core/src/main/java/de/pxav/kelp/core/entity/version/EntityVersionTemplate.java
+++ b/core/src/main/java/de/pxav/kelp/core/entity/version/EntityVersionTemplate.java
@@ -4,6 +4,7 @@
import de.pxav.kelp.core.entity.KelpEntity;
import de.pxav.kelp.core.version.KelpVersion;
import de.pxav.kelp.core.version.SinceKelpVersion;
+import de.pxav.kelp.core.world.KelpLocation;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.World;
@@ -46,7 +47,7 @@ public abstract class EntityVersionTemplate {
* @param entity The entity whose location you want to get.
* @return The location of the given entity.
*/
- public abstract Location getLocation(Entity entity);
+ public abstract KelpLocation getLocation(Entity entity);
/**
* Sets the entity's velocity to the given vector.
@@ -102,14 +103,6 @@ public abstract class EntityVersionTemplate {
*/
public abstract boolean isOnGround(Entity entity);
- /**
- * Gets the current world of the entity.
- *
- * @param entity The entity whose world you want to get.
- * @return The world where the entity is currently located.
- */
- public abstract World getWorld(Entity entity);
-
/**
* Sets the rotation of the given entity. This does not
* affect the location x, y and z axes.
@@ -128,7 +121,7 @@ public abstract class EntityVersionTemplate {
* @param teleportCause The cause for the teleportation.
* @return {@code true} if the teleport action was successful.
*/
- public abstract boolean teleport(Entity entity, Location location, PlayerTeleportEvent.TeleportCause teleportCause);
+ public abstract boolean teleport(Entity entity, KelpLocation location, PlayerTeleportEvent.TeleportCause teleportCause);
/**
* Gets all entities within the given radius centered around
diff --git a/core/src/main/java/de/pxav/kelp/core/entity/version/LivingEntityVersionTemplate.java b/core/src/main/java/de/pxav/kelp/core/entity/version/LivingEntityVersionTemplate.java
index b7eba9e4..79427daf 100644
--- a/core/src/main/java/de/pxav/kelp/core/entity/version/LivingEntityVersionTemplate.java
+++ b/core/src/main/java/de/pxav/kelp/core/entity/version/LivingEntityVersionTemplate.java
@@ -1,6 +1,7 @@
package de.pxav.kelp.core.entity.version;
import de.pxav.kelp.core.application.KelpVersionTemplate;
+import de.pxav.kelp.core.world.KelpLocation;
import org.bukkit.Location;
import org.bukkit.entity.LivingEntity;
@@ -12,6 +13,6 @@
@KelpVersionTemplate
public abstract class LivingEntityVersionTemplate {
- public abstract Location getEyeLocation(LivingEntity livingEntity);
+ public abstract KelpLocation getEyeLocation(LivingEntity livingEntity);
}
diff --git a/core/src/main/java/de/pxav/kelp/core/inventory/item/KelpItem.java b/core/src/main/java/de/pxav/kelp/core/inventory/item/KelpItem.java
index 0d5d5e92..1d014a1a 100644
--- a/core/src/main/java/de/pxav/kelp/core/inventory/item/KelpItem.java
+++ b/core/src/main/java/de/pxav/kelp/core/inventory/item/KelpItem.java
@@ -298,6 +298,10 @@ public ItemStack getItemStack() {
itemStack = itemVersionTemplate.setDisplayName(itemStack, displayName);
}
+ if (this.itemDescription != null && !this.itemDescription.isEmpty()) {
+ itemVersionTemplate.setLore(itemStack, itemDescription);
+ }
+
// make the item unbreakable if needed.
if (this.unbreakable) {
itemStack = itemVersionTemplate.makeUnbreakable(itemStack);
diff --git a/core/src/main/java/de/pxav/kelp/core/inventory/material/KelpMaterial.java b/core/src/main/java/de/pxav/kelp/core/inventory/material/KelpMaterial.java
index b0d7ac2e..53704d77 100644
--- a/core/src/main/java/de/pxav/kelp/core/inventory/material/KelpMaterial.java
+++ b/core/src/main/java/de/pxav/kelp/core/inventory/material/KelpMaterial.java
@@ -1,6 +1,8 @@
package de.pxav.kelp.core.inventory.material;
+import de.pxav.kelp.core.KelpPlugin;
import de.pxav.kelp.core.version.KelpVersion;
+import org.bukkit.Material;
import java.util.ArrayList;
import java.util.Collection;
@@ -930,7 +932,7 @@ public enum KelpMaterial {
LAPIS_BLOCK(KelpVersion.MC_1_8_0),
LAPIS_LAZULI(KelpVersion.MC_1_8_0),
LAPIS_ORE(KelpVersion.MC_1_8_0),
- LARGE_FERN(KelpVersion.MC_1_8_0),
+ LARGE_FERN_LOWER(KelpVersion.MC_1_8_0),
LAVA(KelpVersion.MC_1_8_0),
LAVA_BUCKET(KelpVersion.MC_1_8_0),
LEAD(KelpVersion.MC_1_8_0),
@@ -1135,6 +1137,27 @@ public KelpVersion since() {
return since;
}
+ public static KelpMaterial from(Material bukkitMaterial) {
+ MaterialRepository repository = KelpPlugin.getInjector().getInstance(MaterialRepository.class);
+ return repository.getKelpMaterial(bukkitMaterial.toString());
+ }
+
+ public static KelpMaterial from(Material bukkitMaterial, int subId) {
+ MaterialRepository repository = KelpPlugin.getInjector().getInstance(MaterialRepository.class);
+ return repository.getKelpMaterial(bukkitMaterial.toString(), subId);
+ }
+
+ public static MaterialContainer convert(KelpMaterial kelpMaterial) {
+ MaterialRepository repository = KelpPlugin.getInjector().getInstance(MaterialRepository.class);
+ return repository.getBukkitMaterial(kelpMaterial);
+ }
+
+ public static Material convertUnsafe(KelpMaterial kelpMaterial) {
+ MaterialRepository repository = KelpPlugin.getInjector().getInstance(MaterialRepository.class);
+ String[] materialData = repository.getMaterial(kelpMaterial).split(":");
+ return Material.valueOf(materialData[0]);
+ }
+
/**
* Returns a collection of materials included in newer versions than
* the given one. So if you input 1.12 for example, only materials
diff --git a/core/src/main/java/de/pxav/kelp/core/inventory/version/ItemVersionTemplate.java b/core/src/main/java/de/pxav/kelp/core/inventory/version/ItemVersionTemplate.java
index a0312c66..22b970b9 100644
--- a/core/src/main/java/de/pxav/kelp/core/inventory/version/ItemVersionTemplate.java
+++ b/core/src/main/java/de/pxav/kelp/core/inventory/version/ItemVersionTemplate.java
@@ -7,6 +7,8 @@
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
+import java.util.List;
+
/**
* A class description goes here.
*
@@ -23,7 +25,7 @@ public abstract class ItemVersionTemplate {
public abstract ItemStack setDisplayName(ItemStack itemStack, String displayName);
- public abstract ItemStack setLore(ItemStack itemStack, String itemLore);
+ public abstract ItemStack setLore(ItemStack itemStack, List itemLore);
public abstract ItemStack makeUnbreakable(ItemStack itemStack);
diff --git a/core/src/main/java/de/pxav/kelp/core/npc/KelpNpc.java b/core/src/main/java/de/pxav/kelp/core/npc/KelpNpc.java
index 8a1293a5..0c642fec 100644
--- a/core/src/main/java/de/pxav/kelp/core/npc/KelpNpc.java
+++ b/core/src/main/java/de/pxav/kelp/core/npc/KelpNpc.java
@@ -12,6 +12,7 @@
import de.pxav.kelp.core.npc.activity.NpcActivity;
import de.pxav.kelp.core.npc.version.NpcVersionTemplate;
import de.pxav.kelp.core.player.KelpPlayer;
+import de.pxav.kelp.core.world.KelpLocation;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@@ -34,7 +35,7 @@ public class KelpNpc {
private KelpPlayer player;
- private Location currentLocation;
+ private KelpLocation currentLocation;
private KelpItem itemInHand;
private int entityId;
@@ -99,7 +100,7 @@ public static KelpNpc create() {
);
}
- public KelpNpc location(Location location) {
+ public KelpNpc location(KelpLocation location) {
this.currentLocation = location;
return this;
}
@@ -411,7 +412,7 @@ public KelpNpc onInteract(Consumer event) {
return this;
}
- public KelpNpc sleep(Location bedLocation) {
+ public KelpNpc sleep(KelpLocation bedLocation) {
this.sleeping = true;
npcVersionTemplate.sleep(this, bedLocation);
return this;
@@ -441,7 +442,7 @@ public KelpNpc wakeUp() {
* @param target The location where the NPC should look to.
* @return An instance of the current NPC object.
*/
- public KelpNpc lookTo(Location target) {
+ public KelpNpc lookTo(KelpLocation target) {
double xDiff = target.getX() - currentLocation.getX();
double yDiff = target.getY() - currentLocation.getY();
double zDiff = target.getZ() - currentLocation.getZ();
@@ -464,7 +465,7 @@ public void moveRelativeDistance(double x, double y, double z, float absoluteYaw
npcVersionTemplate.moveRelativeDistance(this, player.getBukkitPlayer(), x, y, z, absoluteYaw, absolutePitch);
}
- public void moveTo(Location target) {
+ public void moveTo(KelpLocation target) {
double x = target.getX() - currentLocation.getX();
double y = target.getY() - currentLocation.getY();
double z = target.getZ() - currentLocation.getZ();
@@ -472,7 +473,7 @@ public void moveTo(Location target) {
this.location(target);
}
- public void teleport(Location location) {
+ public void teleport(KelpLocation location) {
this.currentLocation = location;
npcVersionTemplate.teleport(this, currentLocation);
}
@@ -789,7 +790,7 @@ public KelpNpcMeta getNpcMeta() {
return npcMeta;
}
- public Location getLocation() {
+ public KelpLocation getLocation() {
return currentLocation;
}
diff --git a/core/src/main/java/de/pxav/kelp/core/npc/activity/AutoSpawnActivity.java b/core/src/main/java/de/pxav/kelp/core/npc/activity/AutoSpawnActivity.java
index 7e891c7e..cba43e30 100644
--- a/core/src/main/java/de/pxav/kelp/core/npc/activity/AutoSpawnActivity.java
+++ b/core/src/main/java/de/pxav/kelp/core/npc/activity/AutoSpawnActivity.java
@@ -2,6 +2,7 @@
import de.pxav.kelp.core.npc.KelpNpc;
import de.pxav.kelp.core.player.KelpPlayer;
+import de.pxav.kelp.core.world.KelpLocation;
import org.bukkit.Location;
public class AutoSpawnActivity extends NpcActivity {
@@ -20,7 +21,7 @@ public AutoSpawnActivity distanceThreshold(double distanceThreshold) {
@Override
public void onTick(KelpNpc kelpNpc) {
KelpPlayer player = kelpNpc.getPlayer();
- Location npcLocation = kelpNpc.getLocation();
+ KelpLocation npcLocation = kelpNpc.getLocation();
if (player.getLocation().distanceSquared(npcLocation) <= (distanceThreshold * distanceThreshold)
&& !kelpNpc.isSpawned()) {
diff --git a/core/src/main/java/de/pxav/kelp/core/npc/activity/LookToActivity.java b/core/src/main/java/de/pxav/kelp/core/npc/activity/LookToActivity.java
index d800035f..cca8d6d7 100644
--- a/core/src/main/java/de/pxav/kelp/core/npc/activity/LookToActivity.java
+++ b/core/src/main/java/de/pxav/kelp/core/npc/activity/LookToActivity.java
@@ -1,19 +1,20 @@
package de.pxav.kelp.core.npc.activity;
import de.pxav.kelp.core.npc.KelpNpc;
+import de.pxav.kelp.core.world.KelpLocation;
import org.bukkit.Location;
import java.util.function.Supplier;
public class LookToActivity extends NpcActivity {
- private Supplier target;
+ private Supplier target;
public static LookToActivity create() {
return new LookToActivity();
}
- public LookToActivity target(Supplier target) {
+ public LookToActivity target(Supplier target) {
this.target = target;
return this;
}
diff --git a/core/src/main/java/de/pxav/kelp/core/npc/activity/WalkToDirectionActivity.java b/core/src/main/java/de/pxav/kelp/core/npc/activity/WalkToDirectionActivity.java
index 6cf62d8a..7631e21a 100644
--- a/core/src/main/java/de/pxav/kelp/core/npc/activity/WalkToDirectionActivity.java
+++ b/core/src/main/java/de/pxav/kelp/core/npc/activity/WalkToDirectionActivity.java
@@ -2,6 +2,7 @@
import de.pxav.kelp.core.npc.KelpNpc;
import de.pxav.kelp.core.npc.MovementSpeed;
+import de.pxav.kelp.core.world.KelpLocation;
import org.bukkit.Location;
import org.bukkit.util.Vector;
@@ -10,7 +11,7 @@ public class WalkToDirectionActivity extends NpcActivity {
private Vector direction;
private double index = .01;
- private Location startLocation;
- private Location target;
+ private KelpLocation startLocation;
+ private KelpLocation target;
private boolean lookToTarget = true;
private boolean lastTick = false;
@@ -19,7 +20,7 @@ public static WalkToTargetActivity create() {
return new WalkToTargetActivity();
}
- public WalkToTargetActivity target(Location target) {
+ public WalkToTargetActivity target(KelpLocation target) {
this.target = target;
return this;
}
@@ -47,14 +48,13 @@ public void onTick(KelpNpc kelpNpc) {
}
direction.multiply(index);
- Location newLocation = startLocation.clone().add(direction);
+ KelpLocation newLocation = startLocation.clone().add(direction);
kelpNpc.moveTo(newLocation);
if (this.lookToTarget) {
kelpNpc.lookTo(target);
}
-
direction.normalize();
if (kelpNpc.getLocation().distance(target) < 1) {
diff --git a/core/src/main/java/de/pxav/kelp/core/npc/version/NpcVersionTemplate.java b/core/src/main/java/de/pxav/kelp/core/npc/version/NpcVersionTemplate.java
index d6312a4a..f66f5304 100644
--- a/core/src/main/java/de/pxav/kelp/core/npc/version/NpcVersionTemplate.java
+++ b/core/src/main/java/de/pxav/kelp/core/npc/version/NpcVersionTemplate.java
@@ -5,6 +5,7 @@
import de.pxav.kelp.core.npc.KelpNpc;
import de.pxav.kelp.core.npc.KelpNpcMeta;
import de.pxav.kelp.core.npc.NpcAnimation;
+import de.pxav.kelp.core.world.KelpLocation;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@@ -52,7 +53,7 @@ public abstract void moveRelativeDistance(KelpNpc npc,
float absoluteYaw,
float absolutePitch);
- public abstract void teleport(KelpNpc npc, Location location);
+ public abstract void teleport(KelpNpc npc, KelpLocation location);
public abstract void updateCustomName(KelpNpc npc);
@@ -62,7 +63,7 @@ public abstract void moveRelativeDistance(KelpNpc npc,
public abstract void makeCorpse(KelpNpc npc);
- public abstract void sleep(KelpNpc npc, Location bedLocation);
+ public abstract void sleep(KelpNpc npc, KelpLocation bedLocation);
public abstract void wakeUp(KelpNpc npc);
@@ -87,6 +88,7 @@ public abstract void moveRelativeDistance(KelpNpc npc,
* this very player.
*/
public abstract void refreshMetadata(KelpNpc npc, Player player);
+
public abstract void updateTab(KelpNpc npc, @Nullable GameProfile gameProfile);
}
diff --git a/core/src/main/java/de/pxav/kelp/core/particle/effect/ParticleLineEffect.java b/core/src/main/java/de/pxav/kelp/core/particle/effect/ParticleLineEffect.java
index b5633d99..bf476c74 100644
--- a/core/src/main/java/de/pxav/kelp/core/particle/effect/ParticleLineEffect.java
+++ b/core/src/main/java/de/pxav/kelp/core/particle/effect/ParticleLineEffect.java
@@ -2,6 +2,7 @@
import de.pxav.kelp.core.particle.type.ParticleType;
import de.pxav.kelp.core.player.KelpPlayer;
+import de.pxav.kelp.core.world.KelpLocation;
import org.bukkit.Location;
import org.bukkit.util.Vector;
@@ -15,8 +16,8 @@
public class ParticleLineEffect extends ParticleEffect {
private ParticleType particleType;
- private Location firstPoint;
- private Location secondPoint;
+ private KelpLocation firstPoint;
+ private KelpLocation secondPoint;
private double particleDensity;
ParticleLineEffect(ParticleEffectRepository particleEffectRepository) {
@@ -28,12 +29,12 @@ public ParticleLineEffect particleType(ParticleType particleType) {
return this;
}
- public ParticleLineEffect firstPoint(Location firstPoint) {
+ public ParticleLineEffect firstPoint(KelpLocation firstPoint) {
this.firstPoint = firstPoint;
return this;
}
- public ParticleLineEffect secondPoint(Location secondPoint) {
+ public ParticleLineEffect secondPoint(KelpLocation secondPoint) {
this.secondPoint = secondPoint;
return this;
}
@@ -50,7 +51,7 @@ public ParticleLineEffect changeLocationBy(double x, double y, double z) {
@Override
protected void playAnimationOnce(Collection player) {
- Location firstPointBackup = firstPoint.clone();
+ KelpLocation firstPointBackup = firstPoint.clone();
Vector line = secondPoint.clone().toVector().subtract(firstPointBackup.toVector());
double length = line.length();
diff --git a/core/src/main/java/de/pxav/kelp/core/player/KelpPlayer.java b/core/src/main/java/de/pxav/kelp/core/player/KelpPlayer.java
index 82f2eabe..39b94a1c 100644
--- a/core/src/main/java/de/pxav/kelp/core/player/KelpPlayer.java
+++ b/core/src/main/java/de/pxav/kelp/core/player/KelpPlayer.java
@@ -1,6 +1,7 @@
package de.pxav.kelp.core.player;
import com.google.common.base.Preconditions;
+import de.pxav.kelp.core.KelpPlugin;
import de.pxav.kelp.core.entity.KelpEntityType;
import de.pxav.kelp.core.entity.LivingKelpEntity;
import de.pxav.kelp.core.entity.version.EntityVersionTemplate;
@@ -23,6 +24,7 @@
import de.pxav.kelp.core.sidebar.SidebarRepository;
import de.pxav.kelp.core.sidebar.type.KelpSidebar;
import de.pxav.kelp.core.sound.KelpSound;
+import de.pxav.kelp.core.world.KelpLocation;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@@ -114,6 +116,23 @@ public KelpPlayer(Player bukkitPlayer,
this.anvilPromptVersionTemplate = anvilPromptVersionTemplate;
}
+ public static KelpPlayer from(UUID player) {
+ KelpPlayerRepository repository = KelpPlugin.getInjector().getInstance(KelpPlayerRepository.class);
+ return repository.getKelpPlayer(player);
+ }
+
+ public static KelpPlayer from(String name) {
+ Player player = Bukkit.getPlayer(name);
+ if (player == null) {
+ return null;
+ }
+ return KelpPlayer.from(player);
+ }
+
+ public static KelpPlayer from(Player player) {
+ return KelpPlayer.from(player.getUniqueId());
+ }
+
public SignPrompt openSignPrompt() {
return new SignPrompt(this.getBukkitPlayer(), this.signPromptVersionTemplate);
}
@@ -229,26 +248,26 @@ public KelpPlayer forceInventoryClose() {
}
public KelpPlayer playSound(KelpSound sound) {
- playerVersionTemplate.playSound(bukkitPlayer, sound, playerVersionTemplate.getLocation(bukkitPlayer), 3, 0);
+ playerVersionTemplate.playSound(bukkitPlayer, sound, getLocation(), 3, 0);
return this;
}
- public KelpPlayer playSound(KelpSound sound, Location location) {
+ public KelpPlayer playSound(KelpSound sound, KelpLocation location) {
playerVersionTemplate.playSound(bukkitPlayer, sound, location, 3, 0);
return this;
}
public KelpPlayer playSound(KelpSound sound, float volume) {
- playerVersionTemplate.playSound(bukkitPlayer, sound, playerVersionTemplate.getLocation(bukkitPlayer), volume, 0);
+ playerVersionTemplate.playSound(bukkitPlayer, sound, getLocation(), volume, 0);
return this;
}
public KelpPlayer playSound(KelpSound sound, float volume, float pitch) {
- playerVersionTemplate.playSound(bukkitPlayer, sound, playerVersionTemplate.getLocation(bukkitPlayer), volume, pitch);
+ playerVersionTemplate.playSound(bukkitPlayer, sound, getLocation(), volume, pitch);
return this;
}
- public KelpPlayer playSound(KelpSound sound, Location location, float volume, float pitch) {
+ public KelpPlayer playSound(KelpSound sound, KelpLocation location, float volume, float pitch) {
playerVersionTemplate.playSound(bukkitPlayer, sound, location, volume, pitch);
return this;
}
@@ -313,7 +332,7 @@ public KelpPlayer spawnParticle(ParticleType particleType, boolean longDistance,
return this;
}
- public KelpPlayer spawnParticle(ParticleType particleType, boolean longDistance, Location location, float offsetX, float offsetY, float offsetZ, int count, float particleData, Object generalData) {
+ public KelpPlayer spawnParticle(ParticleType particleType, boolean longDistance, KelpLocation location, float offsetX, float offsetY, float offsetZ, int count, float particleData, Object generalData) {
particleVersionTemplate.spawnParticle(this,
particleType,
longDistance,
@@ -351,12 +370,6 @@ public String getName() {
return bukkitPlayer.getName();
}
-// @Override
-// public KelpPlayer teleport(Location location) {
-// playerVersionTemplate.teleport(bukkitPlayer, location);
-// return this;
-// }
-
public KelpPlayer setHealth(int health) {
playerVersionTemplate.setHealth(bukkitPlayer, health);
return this;
@@ -408,7 +421,7 @@ public KelpPlayer setTabListName(String tabListName) {
// TODO TAB LIST HEADER AND FOOTER
- public KelpPlayer setCompassTarget(Location target) {
+ public KelpPlayer setCompassTarget(KelpLocation target) {
playerVersionTemplate.setCompassTarget(bukkitPlayer, target);
return this;
}
diff --git a/core/src/main/java/de/pxav/kelp/core/player/KelpPlayerRepository.java b/core/src/main/java/de/pxav/kelp/core/player/KelpPlayerRepository.java
index 8ce8f7a4..4e0605f1 100644
--- a/core/src/main/java/de/pxav/kelp/core/player/KelpPlayerRepository.java
+++ b/core/src/main/java/de/pxav/kelp/core/player/KelpPlayerRepository.java
@@ -236,7 +236,7 @@ private KelpPlayer newKelpPlayerFrom(Player bukkitPlayer) {
entityVersionTemplate,
livingEntityVersionTemplate,
playerVersionTemplate.getUniqueId(bukkitPlayer),
- entityVersionTemplate.getLocation(bukkitPlayer),
+ entityVersionTemplate.getLocation(bukkitPlayer).getBukkitLocation(),
entityVersionTemplate.getEntityId(bukkitPlayer)
);
}
diff --git a/core/src/main/java/de/pxav/kelp/core/player/PlayerVersionTemplate.java b/core/src/main/java/de/pxav/kelp/core/player/PlayerVersionTemplate.java
index ff033cd2..7cb9938d 100644
--- a/core/src/main/java/de/pxav/kelp/core/player/PlayerVersionTemplate.java
+++ b/core/src/main/java/de/pxav/kelp/core/player/PlayerVersionTemplate.java
@@ -5,7 +5,7 @@
import de.pxav.kelp.core.player.bossbar.BossBarStyle;
import de.pxav.kelp.core.player.message.InteractiveMessage;
import de.pxav.kelp.core.sound.KelpSound;
-import org.bukkit.*;
+import de.pxav.kelp.core.world.KelpLocation;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
@@ -70,7 +70,7 @@ public abstract class PlayerVersionTemplate {
* @param volume How loud the sound should be.
* @param pitch How strong the sound should be pitched.
*/
- public abstract void playSound(Player player, KelpSound sound, Location location, float volume, float pitch);
+ public abstract void playSound(Player player, KelpSound sound, KelpLocation location, float volume, float pitch);
/**
* Sets the player's health.
@@ -88,20 +88,6 @@ public abstract class PlayerVersionTemplate {
*/
public abstract UUID getUniqueId(Player player);
- /**
- * @param player The player whose location you want to get
- * @return The location the player is currently at.
- */
- public abstract Location getLocation(Player player);
-
- /**
- * Teleports the player to the given location.
- *
- * @param player The player you want to teleport.
- * @param location The location you want to teleport the player to.
- */
- public abstract void teleport(Player player, Location location);
-
/**
* Checks if the player is currently stuck in a cobweb.
*
@@ -250,7 +236,7 @@ public abstract class PlayerVersionTemplate {
* @param player The player whose target location you want to update.
* @param target The location, where the compass should point to.
*/
- public abstract void setCompassTarget(Player player, Location target);
+ public abstract void setCompassTarget(Player player, KelpLocation target);
/**
* Gets the compass target of the player.
@@ -260,9 +246,7 @@ public abstract class PlayerVersionTemplate {
* @param player The player whose compass target you want to set.
* @return The target location of the player's compass.
*/
- public abstract Location getCompassTarget(Player player);
-
- // TODO send raw message, handle conversations (Bukkit conversation API)
+ public abstract KelpLocation getCompassTarget(Player player);
/**
* Kicks the given player from the server.
@@ -875,7 +859,7 @@ public abstract class PlayerVersionTemplate {
* @param player The player you want to get the bed spawn location of.
* @return The spawn location, {@code null} if the player has not slept or location is invalid.
*/
- public abstract Location getBedSpawnLocation(Player player);
+ public abstract KelpLocation getBedSpawnLocation(Player player);
/**
* Sends the given player a message into their chat.
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
new file mode 100644
index 00000000..2ec689d1
--- /dev/null
+++ b/core/src/main/java/de/pxav/kelp/core/world/KelpBlock.java
@@ -0,0 +1,318 @@
+package de.pxav.kelp.core.world;
+
+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.version.BlockVersionTemplate;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.util.Vector;
+
+/**
+ * This class represents a block of a world. A block is the smallest unit
+ * a world consists of. Blocks are grouped together in 16x16 areas of the
+ * world (-> {@link KelpChunk}), which can be loaded and unloaded for
+ * optimal performance usage. A block can have a {@link KelpMaterial}
+ * which defines its hardness, collision and other properties.
+ *
+ * @author pxav
+ */
+public class KelpBlock {
+
+ private Block bukkitBlock;
+ private BlockVersionTemplate versionTemplate;
+
+ KelpBlock(Block bukkitBlock, BlockVersionTemplate versionTemplate) {
+ this.bukkitBlock = bukkitBlock;
+ this.versionTemplate = versionTemplate;
+ }
+
+ public static KelpBlock from(Block bukkitBlock) {
+ return new KelpBlock(
+ bukkitBlock,
+ KelpPlugin.getInjector().getInstance(BlockVersionTemplate.class)
+ );
+ }
+
+ /**
+ * Gets the {@link KelpWorld} this block is located in.
+ *
+ * @return The world this block is located in.
+ */
+ public KelpWorld getWorld() {
+ return KelpWorld.from(getBukkitBlock().getWorld());
+ }
+
+ /**
+ * Gets the name of the world this block is located in.
+ *
+ * @return The name of the world this block is located in.
+ */
+ public String getWorldName() {
+ return getBukkitBlock().getWorld().getName();
+ }
+
+ /**
+ * Gets the {@link KelpChunk} the given block is located in.
+ *
+ * @return The {@link KelpChunk} the given block is located in.
+ */
+ public KelpChunk getChunk() {
+ return versionTemplate.getChunk(this);
+ }
+
+ /**
+ * Gets the {@link KelpLocation} of the given block in its {@link KelpWorld}.
+ *
+ * @return The {@link KelpLocation} of this world.
+ */
+ public KelpLocation getLocation() {
+ return versionTemplate.getLocation(this);
+ }
+
+ /**
+ * Gets the block which is blow this block. This means it
+ * gets the block which has the same x and z coordinates, but a
+ * lower y coordinate.
+ *
+ * @return The block below this block.
+ */
+ public KelpBlock getBlockBelow() {
+ return getLocation().subtractY(1).getBlock();
+ }
+
+ /**
+ * Gets the block which is above this block. This means it
+ * gets the block which has the same x and z coordinates, but a
+ * higher y coordinate.
+ *
+ * @return The block above this block.
+ */
+ public KelpBlock getBlockAbove() {
+ return getLocation().addY(1).getBlock();
+ }
+
+ /**
+ * Gets the block which is south of this block in the same y-axis.
+ *
+ * @return The southern block of this block.
+ */
+ public KelpBlock getSouthernBlock() {
+ return getLocation().addZ(1).getBlock();
+ }
+
+ /**
+ * Gets the block which is north of this block in the same y-axis.
+ *
+ * @return The northern block of this block.
+ */
+ public KelpBlock getNorthernBlock() {
+ return getLocation().subtractZ(1).getBlock();
+ }
+
+ /**
+ * Gets the block which is west of this block in the same y-axis.
+ *
+ * @return The western block of this block.
+ */
+ public KelpBlock getWesternBlock() {
+ return getLocation().subtractX(1).getBlock();
+ }
+
+ /**
+ * Gets the block which is east of this block in the same y-axis.
+ *
+ * @return The eastern block of this block.
+ */
+ public KelpBlock getEasternBlock() {
+ return getLocation().addX(1).getBlock();
+ }
+
+ /**
+ * Gets the block which is north east of this block in the same y-axis.
+ *
+ * @return The north eastern block of this block.
+ */
+ public KelpBlock getNorthEasternBlock() {
+ return getLocation().add(1, 0, -1).getBlock();
+ }
+
+ /**
+ * Gets the block which is north west of this block in the same y-axis.
+ *
+ * @return The north western block of this block.
+ */
+ public KelpBlock getNorthWesternBlock() {
+ return getLocation().add(-1, 0, -1).getBlock();
+ }
+
+ /**
+ * Gets the block which is south west of this block in the same y-axis.
+ *
+ * @return The south western block of this block.
+ */
+ public KelpBlock getSouthWesternBlock() {
+ return getLocation().add(-1, 0, 1).getBlock();
+ }
+
+ /**
+ * Gets the block which is south east of this block in the same y-axis.
+ *
+ * @return The south eastern block of this block.
+ */
+ public KelpBlock getSouthEasternBlock() {
+ return getLocation().add(1, 0, 1).getBlock();
+ }
+
+ /**
+ * Gets the block relative to this block in a specific
+ * {@link CardinalDirection}.
+ *
+ * @param direction The direction of the block you want to get.
+ * @return The block relative to this block in the given direction.
+ */
+ public KelpBlock getRelative(CardinalDirection direction) {
+ switch (direction) {
+ case NORTH:
+ return getNorthernBlock();
+ case NORTH_EAST:
+ return getNorthEasternBlock();
+ case EAST:
+ return getEasternBlock();
+ case SOUTH_EAST:
+ return getSouthEasternBlock();
+ case SOUTH:
+ return getSouthernBlock();
+ case SOUTH_WEST:
+ return getSouthWesternBlock();
+ case WEST:
+ return getWesternBlock();
+ case NORTH_WEST:
+ return getNorthWesternBlock();
+ }
+ return null;
+ }
+
+ /**
+ * Gets the block in front of this block relative to the given direction.
+ *
+ * @param direction A {@link Vector} indicating the direction
+ * to determine which block is actually in front.
+ * @return The block in front of this block.
+ */
+ public KelpBlock getFrontBlock(Vector direction) {
+ KelpLocation location = getLocation();
+ location.setDirection(direction);
+
+ return getRelative(location.getCardinalDirection());
+ }
+
+ /**
+ * Gets the block behind this block relative to the given direction.
+ *
+ * @param direction A {@link Vector} indicating the direction
+ * to determine which block is actually behind.
+ * @return The block behind this block.
+ */
+ public KelpBlock getBackBlock(Vector direction) {
+ KelpLocation location = getLocation();
+ location.setDirection(direction.multiply(-1));
+
+ return getRelative(location.getCardinalDirection());
+ }
+
+ /**
+ * Gets the x-coordinate of this block in the world grid.
+ *
+ * @return The block's x-coordinate.
+ */
+ public int getX() {
+ return bukkitBlock.getX();
+ }
+
+ /**
+ * Gets the y-coordinate of this block in the world grid.
+ *
+ * @return The block's y-coordinate.
+ */
+ public int getY() {
+ return bukkitBlock.getY();
+ }
+
+ /**
+ * Gets the z-coordinate of this block in the world grid.
+ *
+ * @return The block's z-coordinate.
+ */
+ public int getZ() {
+ return bukkitBlock.getZ();
+ }
+
+ /**
+ * Gets the material of the current block. The material defines
+ * the block's general behavior including its hardness or
+ * collision.
+ *
+ * @return The {@link KelpMaterial} the given block is made of.
+ */
+ public KelpMaterial getMaterial() {
+ return versionTemplate.getMaterial(this);
+ }
+
+ /**
+ * Sets the material of the given block to the given {@link KelpMaterial}.
+ * So if you had an {@link KelpMaterial#AIR} block for example,
+ *
+ * @param material The new material you want the block to have.
+ */
+ public void setMaterial(KelpMaterial material) {
+ versionTemplate.setMaterial(this, material);
+ }
+
+ /**
+ * Simulates the application of bone meal on a block.
+ * This means if the block is a sapling for example, it
+ * 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. */
+ public void applyBoneMeal() {
+ versionTemplate.applyBoneMeal(this, BlockFace.UP);
+ }
+
+ /**
+ * Simulates the application of bone meal on a block.
+ * This means if the block is a sapling for example, it
+ * might grow to a tree or grass might spawn random flowers
+ * and so on.
+ *
+ * @param blockFace The face of the block to apply the bone meal on.
+ */
+ public void applyBoneMeal(BlockFace blockFace) {
+ versionTemplate.applyBoneMeal(this, blockFace);
+ }
+
+ /**
+ * Checks whether bone meal will have an effect on this block.
+ * This means it will check whether the block is a sapling
+ * or a plant that will grow if you right click it with bone
+ * meal. More information about that can be found
+ * in this wiki
+ * article
+ *
+ * @return {@code true} whether bone meal is applicable.
+ */
+ public boolean canApplyBoneMeal() {
+ return versionTemplate.canApplyBoneMeal(this);
+ }
+
+ /**
+ * Converts this block to a bukkit {@link Block}.
+ *
+ * @return The bukkit block instance of this block.
+ */
+ public Block getBukkitBlock() {
+ return bukkitBlock;
+ }
+
+}
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
new file mode 100644
index 00000000..9a35c3db
--- /dev/null
+++ b/core/src/main/java/de/pxav/kelp/core/world/KelpChunk.java
@@ -0,0 +1,172 @@
+package de.pxav.kelp.core.world;
+
+import de.pxav.kelp.core.KelpPlugin;
+import de.pxav.kelp.core.application.KelpApplication;
+import de.pxav.kelp.core.player.KelpPlayer;
+import de.pxav.kelp.core.world.version.ChunkVersionTemplate;
+import org.bukkit.Chunk;
+
+import java.util.Collection;
+import java.util.Set;
+
+public class KelpChunk {
+
+ private Chunk bukkitChunk;
+ private ChunkVersionTemplate versionTemplate;
+
+ KelpChunk(Chunk bukkitChunk, ChunkVersionTemplate versionTemplate) {
+ this.bukkitChunk = bukkitChunk;
+ this.versionTemplate = versionTemplate;
+ }
+
+ public static KelpChunk from(Chunk bukkitChunk) {
+ return new KelpChunk(
+ bukkitChunk,
+ KelpPlugin.getInjector().getInstance(ChunkVersionTemplate.class)
+ );
+ }
+
+ public KelpWorld getWorld() {
+ return versionTemplate.getWorld(this);
+ }
+
+ public int getX() {
+ return versionTemplate.getX(this);
+ }
+
+ public int getZ() {
+ return versionTemplate.getZ(this);
+ }
+
+ public KelpBlock getBlockAt(KelpLocation location) {
+ return versionTemplate.getBlockAt(this, location);
+ }
+
+ public boolean contains(KelpLocation location) {
+ return this.versionTemplate.contains(this, location);
+ }
+
+ public boolean contains(KelpBlock block) {
+ return this.versionTemplate.contains(this, block.getLocation());
+ }
+
+ public boolean isSlimeChunk() {
+ return versionTemplate.isSlimeChunk(this);
+ }
+
+ public Collection getPlayers() {
+ return versionTemplate.getPlayers(this);
+ }
+
+ public KelpLocation getCenter(int height) {
+ return KelpLocation.from(getWorld().getName(), getX() << 4, height, getZ() << 4).add(7, 0, 7);
+ }
+
+ public KelpChunk getWestEnclosedChunk() {
+ return getNorthWesternBlock(10).getLocation().add(-2, 0, 2).getChunk();
+ }
+
+ public KelpChunk getNorthEnclosedChunk() {
+ return getNorthEasternBlock(10).getLocation().add(-2, 0, -2).getChunk();
+ }
+
+ public KelpChunk getEastEnclosedChunk() {
+ return getNorthEasternBlock(10).getLocation().add(2, 0, 2).getChunk();
+ }
+
+ public KelpChunk getSouthEnclosedChunk() {
+ return getSouthWesternBlock(10).getLocation().add(2, 0, 2).getChunk();
+ }
+
+ public KelpChunk getSouthWestEnclosedChunk() {
+ return getSouthWesternBlock(10).getLocation().add(-2, 0, 2).getChunk();
+ }
+
+ public KelpChunk getNorthWestEnclosedChunk() {
+ return getNorthWesternBlock(10).getLocation().add(-2, 0, -2).getChunk();
+ }
+
+ public KelpChunk getNorthEastEnclosedChunk() {
+ return getNorthEasternBlock(10).getLocation().add(2, 0, -2).getChunk();
+ }
+
+ public KelpChunk getSouthEastEnclosedChunk() {
+ return getSouthEasternBlock(10).getLocation().add(2, 0, 2).getChunk();
+ }
+
+ public KelpBlock getNorthEasternBlock(int height) {
+ KelpLocation location = KelpLocation.from(
+ getWorld().getName(),
+ getX() * 16 + 15,
+ height,
+ getZ() * 16
+ );
+ return getWorld().getBlockAt(location);
+ }
+
+ public KelpBlock getNorthWesternBlock(int height) {
+ KelpLocation location = KelpLocation.from(
+ getWorld().getName(),
+ getX() * 16,
+ height,
+ getZ() * 16
+ );
+ return getWorld().getBlockAt(location);
+ }
+
+ public KelpBlock getSouthEasternBlock(int height) {
+ KelpLocation location = KelpLocation.from(
+ getWorld().getName(),
+ getX() * 16 + 15,
+ height,
+ getZ() * 16 + 15
+ );
+ return getWorld().getBlockAt(location);
+ }
+
+ public KelpBlock getSouthWesternBlock(int height) {
+ KelpLocation location = KelpLocation.from(
+ getWorld().getName(),
+ getX() * 16,
+ height,
+ getZ() * 16 + 15
+ );
+ return getWorld().getBlockAt(location);
+ }
+
+ public void load() {
+ versionTemplate.load(this);
+ }
+
+ public void unload() {
+ versionTemplate.unload(this);
+ }
+
+ public boolean isLoaded() {
+ return versionTemplate.isLoaded(this);
+ }
+
+ public void addForceLoadFlag(Class extends KelpApplication> plugin) {
+ versionTemplate.addForceLoadFlag(this, plugin);
+ }
+
+ public void removeForceLoadFlag(Class extends KelpApplication> plugin) {
+ versionTemplate.removeForceLoadFlag(this, plugin);
+ }
+
+ 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();
+ }
+
+ public Chunk getBukkitChunk() {
+ return bukkitChunk;
+ }
+}
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
new file mode 100644
index 00000000..47d0f749
--- /dev/null
+++ b/core/src/main/java/de/pxav/kelp/core/world/KelpLocation.java
@@ -0,0 +1,899 @@
+package de.pxav.kelp.core.world;
+
+import com.google.common.base.Preconditions;
+import de.pxav.kelp.core.world.util.CardinalDirection;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.util.NumberConversions;
+import org.bukkit.util.Vector;
+
+import java.io.Serializable;
+
+/**
+ * A {@code KelpLocation} is a point in a 3D-world. It basically is a replacement
+ * for the default {@link Location} of bukkit with some additional useful methods.
+ *
+ * You can easily convert those location types:
+ * - From a bukkit location to a KelpLocation: {@link #from(Location)}
+ * - From a KelpLocation to a bukkit location: {@link #getBukkitLocation()}
+ *
+ * @author pxav
+ */
+public class KelpLocation implements Serializable, Cloneable {
+
+ // name of the location's world
+ //
+ // There is no need to store the entire KelpWorld object,
+ // which is why it's not saved for performance reasons.
+ private String worldName = "world";
+
+ // axis of the location
+ private double x = 0.0D;
+ private double y = 0.0D;
+ private double z = 0.0D;
+ private float yaw = 0.0F;
+ private float pitch = 0.0F;
+
+ public static KelpLocation from(Location location) {
+ KelpLocation kelpLocation = new KelpLocation();
+
+ kelpLocation.setX(location.getX());
+ kelpLocation.setY(location.getY());
+ kelpLocation.setZ(location.getZ());
+ kelpLocation.setYaw(location.getYaw());
+ kelpLocation.setPitch(location.getPitch());
+
+ kelpLocation.setWorldName(location.getWorld().getName());
+
+ return kelpLocation;
+ }
+
+ public static KelpLocation from(String worldName, double x, double y, double z) {
+ Preconditions.checkNotNull(worldName);
+ KelpLocation location = new KelpLocation();
+
+ location.setX(x);
+ location.setY(y);
+ location.setZ(z);
+ location.setWorldName(worldName);
+
+ return location;
+ }
+
+ public static KelpLocation from(String worldName, double x, double y, double z, float yaw, float pitch) {
+ Preconditions.checkNotNull(worldName);
+ KelpLocation location = new KelpLocation();
+
+ location.setX(x);
+ location.setY(y);
+ location.setZ(z);
+ location.setYaw(yaw);
+ location.setPitch(pitch);
+ location.setWorldName(worldName);
+
+ return location;
+ }
+
+ public static KelpLocation from(KelpWorld world, double x, double y, double z, float yaw, float pitch) {
+ return from(world.getName(), x, y, z, yaw, pitch);
+ }
+
+ public static KelpLocation from(KelpWorld world, double x, double y, double z) {
+ return from(world.getName(), x, y, z);
+ }
+
+ public static KelpLocation from(World world, double x, double y, double z, float yaw, float pitch) {
+ return from(world.getName(), x, y, z, yaw, pitch);
+ }
+
+ public static KelpLocation from(World world, double x, double y, double z) {
+ return from(world.getName(), x, y, z);
+ }
+
+ public static KelpLocation create() {
+ return new KelpLocation();
+ }
+
+ /**
+ * Gets the name of the world this location is valid for.
+ *
+ * @return The name of the world of this location.
+ */
+ public String getWorldName() {
+ return worldName;
+ }
+
+ /**
+ * Sets the name of the world of this location.
+ * Note that changing a location's world while keeping
+ * the same coordinates, might lead to unintended world generation
+ * when working with the location. This might cause lag to the server.
+ *
+ * @param worldName The name of the new world for this location.
+ */
+ public void setWorldName(String worldName) {
+ this.worldName = worldName;
+ }
+
+ /**
+ * Gets the locations exact value on the world's X-axis.
+ *
+ * @return The locations value on the X-axis.
+ */
+ public double getX() {
+ return x;
+ }
+
+ /**
+ * 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.
+ */
+ public void setX(double x) {
+ this.x = x;
+ }
+
+ /**
+ * Gets the locations exact value on the world's Y-axis.
+ *
+ * @return The locations value on the Y-axis.
+ */
+ public double getY() {
+ return y;
+ }
+
+ /**
+ * 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.
+ */
+ public void setY(double y) {
+ this.y = y;
+ }
+
+ /**
+ * Gets the locations exact value on the world's Z-axis.
+ *
+ * @return The locations value on the Z-axis.
+ */
+ public double getZ() {
+ return z;
+ }
+
+ /**
+ * 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.
+ */
+ public void setZ(double z) {
+ this.z = z;
+ }
+
+ /**
+ * Gets the locations yaw. The yaw is part of the facing.
+ * If you are using this location as a block location, this value
+ * is likely {@code 0}, but if you are using it with a {@link de.pxav.kelp.core.player.KelpPlayer}
+ * or any other entity, this indicates the angle the player is looking at left and right
+ * (rotation around the y-axis).
+ *
+ * If the player is looking straight west, it is {@code 0F} and towards west it rises up to
+ * {@code 180F} in the north direction and up to {@code -180F} if you rotate in the other
+ * direction.
+ *
+ * You can calculate the yaw from a {@link Vector} if you need to using {@link #setDirection(Vector)},
+ * which will also set the {@code pitch}.
+ *
+ * @return The locations yaw (rotation around the y-axis).
+ */
+ public float getYaw() {
+ return yaw;
+ }
+
+ /**
+ * Sets the location's yaw (rotation around the y-Axis).
+ *
+ * @param yaw The new yaw to set for this location.
+ */
+ public void setYaw(float yaw) {
+ this.yaw = yaw;
+ }
+
+ /**
+ * Gets the pitch for this location. The pitch is the angle in which a player is looking
+ * up or down. If a player is looking right into the sky, his pitch is {@code -90F}, if
+ * they are looking at the ground their pitch is {@code 90F} and looking straight on stands
+ * for {@code 0F}. Block locations usually don't have a pitch (= 0F).
+ *
+ * You can calculate the pitch from a {@link Vector} if you need to using {@link #setDirection(Vector)},
+ * which will also set the {@code yaw}.
+ *
+ * @return The current location's pitch.
+ */
+ public float getPitch() {
+ return pitch;
+ }
+
+ /**
+ * 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.
+ */
+ public void setPitch(float pitch) {
+ this.pitch = pitch;
+ }
+
+ /**
+ * Gets the block X-location, which is a rounded value of the exact x-axis.
+ * Instead of {@code -456.223183497}, this will simply return {@code -456}
+ * as any more detail would be unnecessary when working with simple blocks.
+ *
+ * @return The block x-axis value of this location.
+ */
+ public int getBlockX() {
+ return Location.locToBlock(this.x);
+ }
+
+ /**
+ * Gets the block Y-location, which is a rounded value of the exact y-axis.
+ * Instead of {@code -87.223183497}, this will simply return {@code 87}
+ * as any more detail would be unnecessary when working with simple blocks.
+ *
+ * @return The block y-axis value of this location.
+ */
+ public int getBlockY() {
+ return Location.locToBlock(this.y);
+ }
+
+ /**
+ * Gets the block Z-location, which is a rounded value of the exact Z-axis.
+ * Instead of {@code -456.223183497}, this will simply return {@code -456}
+ * as any more detail would be unnecessary when working with simple blocks.
+ *
+ * @return The block Z-axis value of this location.
+ */
+ public int getBlockZ() {
+ return Location.locToBlock(this.z);
+ }
+
+ /**
+ * Sets the block X-value of this location.
+ *
+ * This will overwrite the exact x-value as well,
+ * so be careful if you still need the exact value.
+ *
+ * @param x The new block x-value of this location.
+ */
+ public void setBlockX(double x) {
+ this.x = Location.locToBlock(this.x);
+ }
+
+ /**
+ * Sets the block Y-value of this location.
+ *
+ * This will overwrite the exact x-value as well,
+ * so be careful if you still need the exact value.
+ *
+ * @param y The new block y-value of this location.
+ */
+ public void setBlockY(double y) {
+ this.y = Location.locToBlock(this.y);
+ }
+
+ /**
+ * Sets the block Z-value of this location.
+ *
+ * This will overwrite the exact x-value as well,
+ * so be careful if you still need the exact value.
+ *
+ * @param z The new block z-value of this location.
+ */
+ public void setBlockZ(double z) {
+ this.z = Location.locToBlock(this.z);
+ }
+
+ /**
+ * Gets the {@link KelpChunk} at the current Location.
+ *
+ * @return The chunk at the current location.
+ */
+ public KelpChunk getChunk() {
+ return KelpChunk.from(getBukkitLocation().getChunk());
+ }
+
+ /**
+ * Gets the {@link KelpBlock} at the current Location.
+ *
+ * @return The block at the current location.
+ */
+ public KelpBlock getBlock() {
+ return KelpBlock.from(getBukkitLocation().getBlock());
+ }
+
+ /**
+ * Adds the given values to the current axis values.
+ *
+ * Note that this method will overwrite the old values.
+ * If you still need them, {@link #clone()} the instance before
+ * and then add the values.
+ *
+ * @param x The x-coordinate to add.
+ * @param y The x-coordinate to add.
+ * @param z The x-coordinate to add.
+ * @return The current location object with the added values.
+ */
+ public KelpLocation add(double x, double y, double z) {
+ this.x += x;
+ this.y += y;
+ this.z += z;
+ return this;
+ }
+
+ /**
+ * Only adds the x-coordinate of this location.
+ * Other coordinates are not affected by this method.
+ *
+ * Note that this method will overwrite the old the value.
+ * If you still need them, {@link #clone()} the instance before
+ * and then add the values.
+ *
+ * @param x The x-coordinate to add.
+ * @return The current location object with the added value.
+ */
+ public KelpLocation addX(double x) {
+ this.x += x;
+ return this;
+ }
+
+ /**
+ * Only adds the y-coordinate of this location.
+ * Other coordinates are not affected by this method.
+ *
+ * Note that this method will overwrite the old the value.
+ * If you still need them, {@link #clone()} the instance before
+ * and then add the values.
+ *
+ * @param y The y-coordinate to add.
+ * @return The current location object with the added value.
+ */
+ public KelpLocation addY(double y) {
+ this.y += y;
+ return this;
+ }
+
+ /**
+ * Only adds the z-coordinate of this location.
+ * Other coordinates are not affected by this method.
+ *
+ * Note that this method will overwrite the old the value.
+ * If you still need them, {@link #clone()} the instance before
+ * and then add the values.
+ *
+ * @param z The z-coordinate to add.
+ * @return The current location object with the added value.
+ */
+ public KelpLocation addZ(double z) {
+ this.z += z;
+ return this;
+ }
+
+ /**
+ * Subtracts the given values from the current axis values.
+ *
+ * Note that this method will overwrite the old values.
+ * If you still need them, {@link #clone()} the instance before
+ * and then subtract the values.
+ *
+ * @param x The x-coordinate to subtract.
+ * @param y The x-coordinate to subtract.
+ * @param z The x-coordinate to subtract.
+ * @return The current location object with the subtracted values.
+ */
+ public KelpLocation subtract(double x, double y, double z) {
+ this.x -= x;
+ this.y -= y;
+ this.z -= z;
+ return this;
+ }
+
+ /**
+ * Only subtracts the x-coordinate of this location.
+ * Other values are not affected by this method.
+ *
+ * Note that this method will overwrite the old value.
+ * If you still need it, {@link #clone()} the instance before
+ * and then subtract the value.
+ *
+ * @param x The x-coordinate to subtract.
+ * @return The current location object with the subtracted value.
+ */
+ public KelpLocation subtractX(double x) {
+ this.x -= x;
+ return this;
+ }
+
+ /**
+ * Only subtracts the y-coordinate of this location.
+ * Other values are not affected by this method.
+ *
+ * Note that this method will overwrite the old value.
+ * If you still need it, {@link #clone()} the instance before
+ * and then subtract the value.
+ *
+ * @param y The y-coordinate to subtract.
+ * @return The current location object with the subtracted value.
+ */
+ public KelpLocation subtractY(double y) {
+ this.y -= y;
+ return this;
+ }
+
+ /**
+ * Only subtracts the z-coordinate of this location.
+ * Other values are not affected by this method.
+ *
+ * Note that this method will overwrite the old value.
+ * If you still need it, {@link #clone()} the instance before
+ * and then subtract the value.
+ *
+ * @param z The z-coordinate to subtract.
+ * @return The current location object with the subtracted value.
+ */
+ public KelpLocation subtractZ(double z) {
+ this.z -= z;
+ return this;
+ }
+
+ /**
+ * Converts the locations {@code yaw} and {@code pitch} to a {@link Vector}.
+ * This vector will point into the facing of the current location and will
+ * have the default length of {@code 1}.
+ *
+ * @return A vector pointing in this location's facing.
+ */
+ public Vector getDirection() {
+ Vector vector = new Vector();
+
+ double rotY = this.getPitch();
+ vector.setY(-Math.sin(Math.toRadians(rotY)));
+
+ double xz = Math.cos(Math.toRadians(rotY));
+ double rotX = this.getYaw();
+ vector.setX(-xz * Math.sin(Math.toRadians(rotX)));
+ vector.setZ(xz * Math.cos(Math.toRadians(rotX)));
+
+ return vector;
+ }
+
+ /**
+ * Gets the location in front of this location. The front is defined
+ * by the location's facing (yaw & pitch), so doing that with a block location
+ * does not make sense. Use {@link KelpBlock#getFrontBlock(Vector)} for this instead.
+ *
+ * As this method uses the pitch as well the front location returned by this method
+ * might be below this location if the facing is looking down or above this location
+ * if the facing up. If you want to get the front location on the same y-level,
+ * use one of the 'keep-height' methods such as {@link #getFrontLocationKeepHeight()}.
+ *
+ * @return The location in front of this location.
+ */
+ public KelpLocation getFrontLookLocation() {
+ return this.clone().add(getDirection());
+ }
+
+ /**
+ * Gets the location behind this location. The back is defined
+ * by the location's facing (yaw & pitch), so doing that with a block location
+ * does not make sense. Use {@link KelpBlock#getBackBlock(Vector)} for this instead.
+ *
+ * As this method uses the pitch as well the back location returned by this method
+ * might be below this location if the facing is looking up or above this location
+ * if the facing down. If you want to get the back location on the same y-level,
+ * use one of the 'keep-height' methods such as {@link #getBackLocationKeepHeight()}.
+ *
+ * @return The location in behind this location.
+ */
+ public KelpLocation getBackLookLocation() {
+ return this.clone().subtract(getDirection());
+ }
+
+ /**
+ * Gets the location behind this location. In this case, only the location's
+ * yaw is used for calculation, which means it does not matter if the player
+ * is currently looking up or down. The location will keep on the same y-level.
+ *
+ * If you want the pitch of the player to be respected as well in the calculation,
+ * then use {@link #getBackLookLocation()}.
+ *
+ * @return The location in behind this location on the same y-level.
+ */
+ public KelpLocation getBackLocationKeepHeight() {
+ double yawRadians = Math.PI * getYaw() / 180;
+ return this.add(Math.sin(yawRadians), 0, -Math.cos(yawRadians));
+ }
+
+ /**
+ * Gets the location in front of this location. In this case, only the location's
+ * yaw is used for calculation, which means it does not matter if the player
+ * is currently looking up or down. The location will keep on the same y-level.
+ *
+ * If you want the pitch of the player to be respected as well in the calculation,
+ * then use {@link #getFrontLookLocation()}.
+ *
+ * @return The location in in front of this location on the same y-level.
+ */
+ public KelpLocation getFrontLocationKeepHeight() {
+ double yawRadians = Math.PI * getYaw() / 180;
+ return this.subtract(Math.sin(yawRadians), 0, -Math.cos(yawRadians));
+ }
+
+ /**
+ * Gets the location behind this location with the given distance. In this case, only the location's
+ * yaw is used for calculation, which means it does not matter if the player
+ * is currently looking up or down. The location will keep on the same y-level.
+ *
+ * While {@link #getBackLocationKeepHeight()} will simply take the location one block behind
+ * this location, you can pass the distance from the original location here as well.
+ * {@code 3} for example will get the location three blocks behind this location.
+ *
+ * If you want the pitch of the player to be respected as well in the calculation,
+ * then use {@link #getBackLookLocation()}.
+ *
+ * @param distance How many blocks behind this location should the new location be?
+ * @return The location in behind this location on the same y-level.
+ */
+ public KelpLocation getBackLocationKeepHeight(double distance) {
+ double yawRadians = Math.PI * getYaw() / 180;
+ return this.add(distance * Math.sin(yawRadians), 0, -distance * Math.cos(yawRadians));
+ }
+
+ /**
+ * Gets the location in front of this location with the given distance. In this case, only the location's
+ * yaw is used for calculation, which means it does not matter if the player
+ * is currently looking up or down. The location will keep on the same y-level.
+ *
+ * While {@link #getFrontLocationKeepHeight()} will simply take the location one block in front of
+ * this location, you can pass the distance from the original location here as well.
+ * {@code 3} for example will get the location three blocks in front of this location.
+ *
+ * If you want the pitch of the player to be respected as well in the calculation,
+ * then use {@link #getFrontLookLocation()} ()}.
+ *
+ * @param distance How many blocks in front of this location should the new location be?
+ * @return The location in in front of this location on the same y-level.
+ */
+ public KelpLocation getFrontLocationKeepHeight(double distance) {
+ double yawRadians = Math.PI * getYaw() / 180;
+ return this.subtract(distance * Math.sin(yawRadians), 0, -distance * Math.cos(yawRadians));
+ }
+
+ /**
+ * Gets the {@link CardinalDirection} of this location's facing. For this, only the location's
+ * yaw will be used. For more information about carinal direction, check out the documentation
+ * of {@link CardinalDirection}
+ *
+ * @return The cardinal direction based on this location's facing.
+ */
+ public CardinalDirection getCardinalDirection() {
+ double rotation = (getYaw() - 180) % 360;
+
+ if (rotation < 0) {
+ rotation += 360.0;
+ }
+
+ if (0 <= rotation && rotation < 22.5) {
+ return CardinalDirection.NORTH;
+ } else if (22.5 <= rotation && rotation < 67.5) {
+ return CardinalDirection.NORTH_EAST;
+ } else if (67.5 <= rotation && rotation < 112.5) {
+ return CardinalDirection.EAST;
+ } else if (112.5 <= rotation && rotation < 157.5) {
+ return CardinalDirection.SOUTH_EAST;
+ } else if (157.5 <= rotation && rotation < 202.5) {
+ return CardinalDirection.SOUTH;
+ } else if (202.5 <= rotation && rotation < 247.5) {
+ return CardinalDirection.SOUTH_WEST;
+ } else if (247.5 <= rotation && rotation < 292.5) {
+ return CardinalDirection.WEST;
+ } else if (292.5 <= rotation && rotation < 337.5) {
+ return CardinalDirection.NORTH_WEST;
+ } else if (337.5 <= rotation && rotation < 360.0) {
+ return CardinalDirection.NORTH;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Sets the yaw and pitch value of this location based on any
+ * vector. The length of the vector is ignored for this operation.
+ *
+ * @param vector The vector to calculate the yaw and pitch value of.
+ * @return The current location object with the new yaw and pitch.
+ */
+ public KelpLocation setDirection(Vector vector) {
+ double _2PI = 6.283185307179586D;
+ double x = vector.getX();
+ double z = vector.getZ();
+ if (x == 0.0D && z == 0.0D) {
+ this.pitch = (float)(vector.getY() > 0.0D ? -90 : 90);
+ } else {
+ double theta = Math.atan2(-x, z);
+ this.yaw = (float)Math.toDegrees((theta + _2PI) % _2PI);
+ double x2 = NumberConversions.square(x);
+ double z2 = NumberConversions.square(z);
+ double xz = Math.sqrt(x2 + z2);
+ this.pitch = (float)Math.toDegrees(Math.atan(-vector.getY() / xz));
+ }
+ return this;
+ }
+
+ /**
+ * Creates a new {@link Vector} representing this location.
+ *
+ * @return A new vectors containing the coordinates of this location.
+ */
+ public Vector toVector() {
+ return new Vector(this.x, this.y, this.z);
+ }
+
+ /**
+ * Adds another location to this location. This will add all axis
+ * of the locations but not the facing.
+ *
+ * @param location The location to add to this location.
+ * @throws IllegalArgumentException if the worlds of the both locations are not the same.
+ * @return The new location with the added values.
+ */
+ public KelpLocation add(KelpLocation location) {
+ Preconditions.checkNotNull(location, "Cannot add 'null' to a KelpLocation");
+ if (!location.getWorldName().equalsIgnoreCase(getWorldName())) {
+ throw new IllegalArgumentException("Cannot add two locations of differing worlds!");
+ }
+
+ this.x += location.getX();
+ this.y += location.getY();
+ this.z += location.getZ();
+ return this;
+ }
+
+ /**
+ * Subtracts another location from this location. This will subtract all axis
+ * of the locations but not the facing.
+ *
+ * @param location The location to subtract from this location.
+ * @throws IllegalArgumentException if the worlds of the both locations are not the same.
+ * @return The new location with the subtracted values.
+ */
+ public KelpLocation subtract(KelpLocation location) {
+ Preconditions.checkNotNull(location, "Cannot subtract 'null' from a KelpLocation");
+ if (!location.getWorldName().equalsIgnoreCase(getWorldName())) {
+ throw new IllegalArgumentException("Cannot subtract two locations of differing worlds!");
+ }
+
+ this.x -= location.getX();
+ this.y -= location.getY();
+ this.z -= location.getZ();
+ return this;
+ }
+
+ /**
+ * Adds any vector to this location.
+ *
+ * @param vector Vector to add the axis of.
+ * @return The new location with the added values.
+ */
+ public KelpLocation add(Vector vector) {
+ this.x += vector.getX();
+ this.y += vector.getY();
+ this.z += vector.getZ();
+ return this;
+ }
+
+ /**
+ * Subtracts any vector from this location.
+ *
+ * @param vector Vector to subtract the axis from.
+ * @return The new location with the subtracted values.
+ */
+ public KelpLocation subtract(Vector vector) {
+ this.x -= vector.getX();
+ this.y -= vector.getY();
+ this.z -= vector.getZ();
+ return this;
+ }
+
+ /**
+ * Returns the magnitude of this location defined as {@code squareRootOf(x^2 + y^2 + z^2)}.
+ * The magnitude can be used to compare two locations with each other. A larger magnitude means
+ * the location is more right/up in the grid. As this method uses performance heavy square root
+ * operations, it is recommended to use {@link #magnitudeSquared()} instead of this method where possible.
+ *
+ * @return The location's magnitude.
+ */
+ public double magnitude() {
+ return Math.sqrt(this.magnitudeSquared());
+ }
+
+ /**
+ * Returns the squared magnitude of this location defined as {@code (x^2 + y^2 + z^2)}.
+ * The magnitude can be used to compare two locations with each other. A larger magnitude means
+ * the location is more right/up in the grid.
+ *
+ * @return The location's magnitude value squared.
+ */
+ public double magnitudeSquared() {
+ return NumberConversions.square(this.x) + NumberConversions.square(this.y) + NumberConversions.square(this.z);
+ }
+
+ /**
+ * Calculates the squared distance to another location in blocks^2.
+ *
+ * @param to The location to calculate the distance to.
+ * @return The squared distance between the blocks.
+ */
+ public double distanceSquared(KelpLocation to) {
+ Preconditions.checkNotNull(to, "Cannot measure distance between 'null' and a KelpLocation");
+ if (!to.getWorldName().equalsIgnoreCase(worldName)) {
+ throw new IllegalArgumentException("Cannot measure distance between KelpLocations from differing worlds");
+ }
+ return NumberConversions.square(
+ this.x - to.x)
+ + NumberConversions.square(this.y - to.y)
+ + NumberConversions.square(this.z - to.z
+ );
+ }
+
+ /**
+ * Calculates the distance to another location in blocks.
+ * Note that this method uses heavy square root operations to calculate
+ * the distance and it is recommended to use {@link #distanceSquared(KelpLocation)}
+ * where possible.
+ *
+ * @param to The location to measure the distance to.
+ * @return The distance between the two given locations.
+ */
+ public double distance(KelpLocation to) {
+ return Math.sqrt(this.distanceSquared(to));
+ }
+
+ /**
+ * Multiplies all the location's axis with a specific
+ * multiplier. This does not include yaw and pitch.
+ *
+ * @param multiplier The multiplier to multiply with.
+ * @return The current location with the new values.
+ */
+ public KelpLocation multiply(double multiplier) {
+ this.x *= multiplier;
+ this.y *= multiplier;
+ this.z *= multiplier;
+ return this;
+ }
+
+ /**
+ * Only multiplies the x axis with a specific multiplier.
+ *
+ * @param multiplier The multiplier to multiply the x-axis with.
+ * @return The current location with the updated values.
+ */
+ public KelpLocation multiplyX(double multiplier) {
+ this.x *= multiplier;
+ return this;
+ }
+
+ /**
+ * Only multiplies the z axis with a specific multiplier.
+ *
+ * @param multiplier The multiplier to multiply the z-axis with.
+ * @return The current location with the updated values.
+ */
+ public KelpLocation multiplyZ(double multiplier) {
+ this.z *= multiplier;
+ return this;
+ }
+
+ /**
+ * Multiplies only the x and z axis of this location with the given multiplier.
+ *
+ * @param multiplier The multiplier to multiply x and z with.
+ * @return The current location with the updated values.
+ */
+ public KelpLocation multiplyXZ(double multiplier) {
+ this.x *= multiplier;
+ this.z *= multiplier;
+ return this;
+ }
+
+ /**
+ * Multiplies this location with a {@link Vector}.
+ * This means the x, y, and z axis are multiplied with
+ * the vector's values.
+ *
+ * @param multiplier The vector to multiply with.
+ * @return The current location with the updated values.
+ */
+ public KelpLocation multiply(Vector multiplier) {
+ this.x *= multiplier.getX();
+ this.y *= multiplier.getY();
+ this.z *= multiplier.getZ();
+ return this;
+ }
+
+ /**
+ * Multiplies this location with another. This means
+ * the x, y and z values are multiplied with the other
+ * x, y and z values.
+ *
+ * @param multiplier The location to multiply with.
+ * @return The current location with the updated axis.
+ */
+ public KelpLocation multiply(KelpLocation multiplier) {
+ this.x *= multiplier.getX();
+ this.y *= multiplier.getY();
+ this.z *= multiplier.getZ();
+ return this;
+ }
+
+ /**
+ * Zeros all axis of the location. This sets the x, y, and z
+ * coordinate to {@code 0}.
+ *
+ * @return The new location value with all axis set to 0.
+ */
+ public KelpLocation zeroAxis() {
+ setX(0.0D);
+ setY(0.0D);
+ setZ(0.0D);
+ return this;
+ }
+
+ /**
+ * Zeros all values of the location except the world name.
+ * This sets all coordinate values (x, y, z) as well as facing
+ * values to {@code 0}.
+ *
+ * @return The new location with all numbers set to 0.
+ */
+ public KelpLocation zeroAll() {
+ zeroAxis();
+ return zeroLook();
+ }
+
+ /**
+ * Zeros the look values of the location.
+ * This means the yaw and pitch are set to {@code 0F}.
+ *
+ * @return The new location object with the updated yaw and pitch values.
+ */
+ public KelpLocation zeroLook() {
+ setYaw(0.0F);
+ setPitch(0.0F);
+ return this;
+ }
+
+ /**
+ * Creates an identical copy of this location object.
+ *
+ * @return The new location object with the same data as this object.
+ */
+ @Override
+ public KelpLocation clone() {
+ try {
+ return (KelpLocation) super.clone();
+ } catch (CloneNotSupportedException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * Converts this {@code KelpLocation} into a default bukkit {@link Location}.
+ *
+ * @return The newly created bukkit location.
+ */
+ public Location getBukkitLocation() {
+ return new Location(Bukkit.getWorld(worldName), x, y, z, yaw, pitch);
+ }
+
+}
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
new file mode 100644
index 00000000..5d9b4870
--- /dev/null
+++ b/core/src/main/java/de/pxav/kelp/core/world/KelpWorld.java
@@ -0,0 +1,616 @@
+package de.pxav.kelp.core.world;
+
+import de.pxav.kelp.core.KelpPlugin;
+import de.pxav.kelp.core.entity.type.DroppedItemEntity;
+import de.pxav.kelp.core.entity.type.ItemDropType;
+import de.pxav.kelp.core.inventory.item.KelpItem;
+import de.pxav.kelp.core.player.KelpPlayer;
+import de.pxav.kelp.core.world.util.ExplosionPower;
+import de.pxav.kelp.core.world.util.WorldType;
+import de.pxav.kelp.core.world.version.WorldVersionTemplate;
+import org.bukkit.Bukkit;
+import org.bukkit.Difficulty;
+import org.bukkit.World;
+
+import java.util.Collection;
+import java.util.UUID;
+
+/**
+ * This class represents a world on the server in which entities and players
+ * can move, blocks can be spawned and generated and chunks can be loaded
+ * and unloaded.
+ *
+ * A world is usually saved in the server's main folder and has to
+ * be loaded manually before it can be used (except the default {@code 'world'} of
+ * bukkit).
+ *
+ * This class is a version-independent replacement for the normal {@link World}
+ * provided by bukkit.
+ *
+ * @author pxav
+ */
+public class KelpWorld {
+
+ private String name;
+ private WorldType worldType;
+ private World bukkitWorld;
+
+ private WorldVersionTemplate versionTemplate;
+
+ public KelpWorld(World bukkitWorld, WorldVersionTemplate versionTemplate) {
+ this.bukkitWorld = bukkitWorld;
+ this.versionTemplate = versionTemplate;
+
+ this.name = bukkitWorld.getName();
+ }
+
+ public static KelpWorld from(String worldName) {
+ World world = Bukkit.getWorld(worldName);
+ if (world == null) {
+ return null;
+ }
+ return KelpWorld.from(world);
+ }
+
+ public static KelpWorld from(World world) {
+ return new KelpWorld(
+ world,
+ KelpPlugin.getInjector().getInstance(WorldVersionTemplate.class)
+ );
+ }
+
+ /**
+ * Converts this world into a bukkit {@link World} instance.
+ *
+ * @return The bukkit instance of this world.
+ */
+ public World getBukkitWorld() {
+ return bukkitWorld;
+ }
+
+ public WorldType getWorldType() {
+ if (this.worldType == null) {
+ this.worldType = versionTemplate.getWorldType(this);
+ }
+ return worldType;
+ }
+
+ /**
+ * Gets the {@link KelpBlock} at the given location of the world.
+ *
+ * @param location The exact location of the block you want to get.
+ * @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(KelpLocation location) {
+ return versionTemplate.getBlockAt(this, location);
+ }
+
+ /**
+ * Gets the chunk at the given location in this world.
+ * A chunk is a 16x16x256 (320 if you are on 1.17+)
+ * cuboid region minecraft uses to generate and handle the world. More information
+ * can be found in {@link KelpChunk}.
+ *
+ * @param location The location where the chunk is located.
+ * @return The chunk at the given location.
+ */
+ public KelpChunk getChunkAt(KelpLocation location) {
+ return versionTemplate.getChunkAt(this, location);
+ }
+
+ /**
+ * Gets the name of this world in the bukkit world registration.
+ *
+ * @return This world's name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Gets the spawn location of the given world. The spawn location is the
+ * center of a radius in which players can spawn when initially joining a
+ * new world.
+ *
+ * @return The world's spawn location.
+ */
+ public KelpLocation getSpawnLocation() {
+ return versionTemplate.getSpawnLocation(this);
+ }
+
+ /**
+ * Checks whether this world is able to generate structures such as temples or villages.
+ *
+ * @return {@code true} if the world can generate structures
+ */
+ public boolean canGenerateStructures() {
+ return versionTemplate.canGenerateStructures(this);
+ }
+
+ /**
+ * Creates an explosion at the given location. This explosion will be
+ * destructive, which means it will actually destroy blocks, but not
+ * ignite any fire.
+ *
+ * @param location The center of the explosion
+ * @param power The power of the explosion. More information on explosion powers can be
+ * found in {@link ExplosionPower}. This has no effect on the fact whether
+ * fire is ignited or blocks are broken.
+ * @return
+ */
+ public KelpWorld createDestructiveExplosion(KelpLocation location, ExplosionPower power) {
+ versionTemplate.createExplosion(this, location, power, true, false);
+ return this;
+ }
+
+ /**
+ * Creates an explosion at the given location. This explosion is only
+ * an effect, which means it won't break any blocks nor ignite fire.
+ *
+ * @param location The center of the explosion
+ * @param power The power of the explosion. More information on explosion powers can be
+ * found in {@link ExplosionPower}. This has no effect on the fact whether
+ * fire is ignited or blocks are broken.
+ * @return
+ */
+ public KelpWorld createEffectExplosion(KelpLocation location, ExplosionPower power) {
+ versionTemplate.createExplosion(this, location, power, false, false);
+ return this;
+ }
+
+ /**
+ * Creates an explosion at the given location. This explosion will be
+ * destructive, which means it will break blocks. It will furthermore ignite fire
+ * at some locations within the explosion radius.
+ *
+ * @param location The center of the explosion
+ * @param power The power of the explosion. More information on explosion powers can be
+ * found in {@link ExplosionPower}. This has no effect on the fact whether
+ * fire is ignited or blocks are broken.
+ * @return
+ */
+ public KelpWorld createDestructiveFireExplosion(KelpLocation location, ExplosionPower power) {
+ versionTemplate.createExplosion(this, location, power, true, true);
+ return this;
+ }
+
+ /**
+ * Creates an explosion at the given location. This explosion will not
+ * break any blocks but ignite fire within a specific radius of the center
+ * location.
+ *
+ * @param location The center of the explosion
+ * @param power The power of the explosion. More information on explosion powers can be
+ * found in {@link ExplosionPower}. This has no effect on the fact whether
+ * fire is ignited or blocks are broken.
+ * @return
+ */
+ public KelpWorld createFireExplosion(KelpLocation location, ExplosionPower power) {
+ versionTemplate.createExplosion(this, location, power, false, true);
+ return this;
+ }
+
+ /**
+ * Drops an item on the given world at the given location. This item will be
+ * visible for all players on the world. The drop will land on the given
+ * location as there won't be any natural offset by default. If you want that,
+ * use {@link #dropItemNaturally(KelpLocation, KelpItem)} instead.
+ *
+ * @param location The location from where the item should drop
+ * @param item The item you want to drop
+ * @return The item entity that has been spawned at the given location.
+ */
+ public DroppedItemEntity dropItem(KelpLocation location, KelpItem item) {
+ return versionTemplate.dropItem(this, location, item, ItemDropType.NORMAL);
+ }
+
+ /**
+ * Drops an item on the given world at the given location. This item will be
+ * visible for all players on the world. The drop will land on the given
+ * location as there won't be any natural offset by default. If you want that,
+ * use
+ *
+ * @param location The location from where the item should drop
+ * @param item The item you want to drop
+ * @return The item entity that has been spawned at the given location.
+ */
+ public DroppedItemEntity dropItemNaturally(KelpLocation location, KelpItem item) {
+ return versionTemplate.dropItem(this, location, item, ItemDropType.NATURAL);
+ }
+
+ /**
+ * Checks whether animals can spawn naturally on this world.
+ *
+ * @return {@code true} if animals can spawn by default.
+ */
+ public boolean animalsAllowed() {
+ return versionTemplate.animalsAllowed(this);
+ }
+
+ /**
+ * Checks whether monsters such as creepers or zombies
+ * can spawn naturally on this world.
+ *
+ * @return {@code true} if monsters can spawn by default.
+ */
+ public boolean monstersAllowed() {
+ return versionTemplate.monstersAllowed(this);
+ }
+
+ /**
+ * Gets the highest block at a given location.
+ *
+ * @param location The location containing the y-axis for the block check.
+ * @return The highest {@link KelpBlock} at the provided location of the world.
+ */
+ public KelpBlock getHighestBlockAt(KelpLocation location) {
+ return versionTemplate.getHighestBlockAt(this, location);
+ }
+
+ /**
+ * Gets a collection of all {@link KelpChunk}s that are currently loaded in
+ * this world. This includes forced loaded chunks as well as naturally loaded chunks.
+ *
+ * @return The collection of all chunks currently loaded in this world.
+ */
+ public Collection getLoadedChunks() {
+ return versionTemplate.getLoadedChunks(this);
+ }
+
+ /**
+ * Gets the difficulty of this current world. The difficulty gives an
+ * indication of how difficult survival gameplay is. {@code Peaceful} means
+ * players cannot become hungry and monsters won't spawn, while {@code hard}
+ * says that lots of monsters will spawn and players lose saturation really
+ * fast.
+ *
+ * @return The current difficulty of the world.
+ */
+ public Difficulty getDifficulty() {
+ return versionTemplate.getDifficulty(this);
+ }
+
+ /**
+ * Gets the humidity at a specific location in the world.
+ * The humidity is dependent on the biome at the given location.
+ *
+ * @param location The location to check the humidity at.
+ * @return The humidity value at the given location in the given world.
+ */
+ public double getHumidityAt(KelpLocation location) {
+ return versionTemplate.getHumidityAt(this, location);
+ }
+
+ /**
+ * Checks whether the spawn chunks should be kept in memory although
+ * no players are currently in the spawn area.
+ *
+ * @return {@code true} if the spawn chunks are kept in memory.
+ */
+ public boolean shouldKeepSpawnInMemory() {
+ return versionTemplate.shouldKeepSpawnInMemory(this);
+ }
+
+ /**
+ * Changes whether the spawn chunks of a world should be kept
+ * loaded/in memory although no players are currently in the spawn
+ * area. This might improve performance if you are commonly spawning
+ * new players to a world (such as in a server lobby).
+ *
+ * @param keep {@code true} if the spawn chunks should be kept loaded over the server runtime.
+ */
+ public KelpWorld keepSpawnInMemory(boolean keep) {
+ versionTemplate.setKeepSpawnInMemory(this, keep);
+ return this;
+ }
+
+ /**
+ * Gets the full time of a world. This is the full absolute
+ * in-game daytime.
+ *
+ * @return The full (absolute) time of the given world.
+ */
+ public long getFullTime() {
+ return versionTemplate.getFullTime(this);
+ }
+
+ /**
+ * Gets the full in-game time of this world from the initial world generation.
+ *
+ * @return The game time that has passed on the given world.
+ */
+ public long getGameTime() {
+ return versionTemplate.getGameTime(this);
+ }
+
+ /**
+ * Gets the relative in-game time of the given world.
+ * This time is calculated from the world's full time:
+ * {@code #getFullTime() % 24000L}.
+ *
+ * This time cannot be negative. If a negative value is returned from
+ * the above operation, a full day will be added to this value.
+ *
+ * @return The relative in-game time of the given world.
+ */
+ public long getTime() {
+ return versionTemplate.getTime(this);
+ }
+
+ /**
+ * Gets all players that are currently on the given world.
+ *
+ * @return A collection of all players that are currently on this world.
+ */
+ public Collection getPlayers() {
+ return versionTemplate.getPlayers(this);
+ }
+
+ /**
+ * Gets the current pvp mode of the world. If players are allowed to
+ * fight each other, this will return {@code true}.
+ *
+ * @return {@code true} if pvp is enabled on this world.
+ */
+ public boolean pvpEnabled() {
+ return versionTemplate.isPvPEnabled(this);
+ }
+
+ /**
+ * Allows PVP in this world. This allows players to fight
+ * against each other.
+ *
+ * @return
+ */
+ public KelpWorld enablePVP() {
+ versionTemplate.setPVP(this, true);
+ return this;
+ }
+
+ /**
+ * Allows PVP in this world. This prohibits players to fight
+ * against each other.
+ *
+ * @return
+ */
+ public KelpWorld disablePVP() {
+ versionTemplate.setPVP(this, false);
+ return this;
+ }
+
+ /**
+ * Sets the current pvp mode of the world. If players should be allowed
+ * to fight each other, set this to {@code true}.
+ *
+ * @param allow {@code true} if players should be able to fight each other.
+ */
+ public KelpWorld allowPVP(boolean allow) {
+ versionTemplate.setPVP(this, allow);
+ return this;
+ }
+
+ /**
+ * Gets the seed of the world generator of this world. A seed is a constant value
+ * used by the {@code Perlin noise} world generation algorithm to randomize
+ * biomes and structures. Seeds are usually compatible across different versions, but
+ * might not output the same result in differing versions due to new structures and
+ * biomes that have been added over time.
+ *
+ * @return The world's seed.
+ */
+ public long getSeed() {
+ return versionTemplate.getSeed(this);
+ }
+
+ /**
+ * Gets the sea level of this world.
+ *
+ * @return The world's sea level.
+ */
+ public int getSeaLevel() {
+ return versionTemplate.getSeaLevel(this);
+ }
+
+ /**
+ * Gets the temperature of a world at a specific location.
+ * Temperatures are depending on the biome and exact location (height specifically).
+ * A temperature indicates whether it can snow or rain at a specific location
+ * or whether there is any precipitation at all. The desert biome for example
+ * has a relatively high temperature of {@code 2.0d}, while Snowy Beach has {@code 0.05}
+ * for example.
+ *
+ * @param location The location you want to get the temperature at.
+ * @return The exact temperature at the given location in the current world.
+ */
+ public double getTemperatureAt(KelpLocation location) {
+ return versionTemplate.getTemperatureAt(this, location);
+ }
+
+ /**
+ * Gets the duration of the current thunder in the world in ticks.
+ *
+ * @return The thunder duration in ticks.
+ */
+ public int getThunderDuration() {
+ return versionTemplate.getThunderDuration(this);
+ }
+
+ /**
+ * Gets the unique id of this world.
+ *
+ * @return The world's {@link UUID}.
+ */
+ public UUID getUUID() {
+ return versionTemplate.getUUID(this);
+ }
+
+ /**
+ * Gets the duration of the current weather state in ticks.
+ * This counts for all weather types (including clear weather)
+ *
+ * @return The current weather duration in ticks.
+ */
+ public int getWeatherDuration() {
+ return versionTemplate.getWeatherDuration(this);
+ }
+
+ /**
+ * Checks whether there is a storm in the given world.
+ *
+ * @return {@code true} whether there is currently a storm in the given world.
+ */
+ public boolean hasStorm() {
+ return versionTemplate.hasStorm(this);
+ }
+
+ /**
+ * Checks if auto-save is enabled in this world.
+ * This would save the world to the server folder in a regular interval.
+ *
+ * @return {@code true} if auto-save is enabled.
+ */
+ public boolean hasAutoSave() {
+ return versionTemplate.hasAutoSave(this);
+ }
+
+ /**
+ * Checks whether there is clear weather in the given world.
+ * If there is clear whether, methods such as {@link #hasStorm()}
+ * are automatically {@code false}.
+ *
+ * @return {@code true} whether there is clear weather, {@code false} if there is any thunder or precipitation.
+ */
+ public boolean isWeatherClear() {
+ return versionTemplate.isWeatherClear(this);
+ }
+
+ /**
+ * Checks if there currently is a thunderstorm in the given world.
+ *
+ * @return {@code true} weather there currently is a thunder in the world.
+ */
+ public boolean isThundering() {
+ return versionTemplate.isThundering(this);
+ }
+
+ /**
+ * Sets the difficulty of the current world. The difficulty gives an
+ * indication of how difficult survival gameplay is. {@code Peaceful} means
+ * players cannot become hungry and monsters won't spawn, while {@code hard}
+ * says that lots of monsters will spawn and players lose saturation really
+ * fast.
+ *
+ * @param difficulty The difficulty you want to set.
+ */
+ public KelpWorld setDifficulty(Difficulty difficulty) {
+ versionTemplate.setDifficulty(this, difficulty);
+ return this;
+ }
+
+ /**
+ * Sets the full time of the current world. The full time is the absolute in-game
+ * daytime of a world. With this method you can also rewind the time, which could
+ * eventually break redstone clocks or other scheduled events, which is why
+ * you might want to use {@link #setTime(long)} depending on your use-case.
+ *
+ * @param fullTime The full time to set for this world.
+ */
+ public KelpWorld setFullTime(long fullTime) {
+ versionTemplate.setFullTime(this, fullTime);
+ return this;
+ }
+
+ /**
+ * Sets the spawn location of this world. This is the location, where
+ * players will spawn when they first join the world.
+ *
+ * By default, there is a 30x30 radius around the location, where players
+ * spawn randomly.
+ *
+ * @param spawnLocation The spawn location you want to set.
+ */
+ public KelpWorld setSpawnLocation(KelpLocation spawnLocation) {
+ versionTemplate.setSpawnLocation(this, spawnLocation);
+ return this;
+ }
+
+ /**
+ * Sets the relative in-game time of the given world.
+ * If you set a value that is smaller than the current time value,
+ * the server will automatically skip to the next day. Rewind
+ * is not possible with this method for safety reasons. If you need
+ * to do it though, use {@link #setFullTime(long)}.
+ *
+ * @param time The relative in-game time to set.
+ */
+ public KelpWorld setTime(long time) {
+ versionTemplate.setTime(this, time);
+ return this;
+ }
+
+ /**
+ * Sets the duration of the current thunderstorm in the world in ticks.
+ *
+ * @param duration The duration in ticks the thunder should take.
+ */
+ public KelpWorld setThunderDuration(int duration) {
+ versionTemplate.setThunderDuration(this, duration);
+ return this;
+ }
+
+ /**
+ * Sets the thunder state of a world. When set to {@code true} a new thunder
+ * will start or the current storm will continue.
+ *
+ * @param thundering {@code true} if you want to start a thunderstorm. {@code false} if you want to end the thunder.
+ */
+ public KelpWorld setThundering(boolean thundering) {
+ versionTemplate.setThundering(this, thundering);
+ return this;
+ }
+
+ /**
+ * Sets the storm state of a world. When set to {@code true} a new storm
+ * will start or the current storm will continue.
+ *
+ * @param storm {@code true} if you want to start a storm. {@code false} if you want to end the storm.
+ */
+ public KelpWorld setStorm(boolean storm) {
+ versionTemplate.setStorm(this, storm);
+ return this;
+ }
+
+ /**
+ * Spawns a lightning in the given world at the given location. This
+ * lightning will be visible for all players who are in the world and in range.
+ *
+ * This method will cause block and entity damage and might ignite fires at
+ * the point where the lightning strikes. If you just want to spawn the effect,
+ * use {@link #strikeLightningEffect(KelpLocation)}.
+ *
+ * @param location The exact location where the lightning should strike.
+ */
+ public KelpWorld strikeLightning(KelpLocation location) {
+ versionTemplate.strikeLightning(this, location, false);
+ return this;
+ }
+
+ /**
+ * Spawns a strike lightning in the given world at the given location. This
+ * lightning will be visible for all players who are in the world and in range.
+ *
+ * This method does not spawn fire at the location where the lightning
+ * strikes, nor does it damage or transform any entities (from pig to pigman
+ * for example). It simply spawns the effect of a lightning. If you want the
+ * lightning to make damange, use {@link #strikeLightning(KelpLocation)}
+ *
+ * @param location The exact location where the lightning should strike.
+ */
+ public KelpWorld strikeLightningEffect(KelpLocation location) {
+ versionTemplate.strikeLightning(this, location, true);
+ return this;
+ }
+
+}
diff --git a/core/src/main/java/de/pxav/kelp/core/world/util/CardinalDirection.java b/core/src/main/java/de/pxav/kelp/core/world/util/CardinalDirection.java
new file mode 100644
index 00000000..f11fb41b
--- /dev/null
+++ b/core/src/main/java/de/pxav/kelp/core/world/util/CardinalDirection.java
@@ -0,0 +1,37 @@
+package de.pxav.kelp.core.world.util;
+
+import de.pxav.kelp.core.world.KelpLocation;
+import org.bukkit.util.Vector;
+
+/**
+ * This util class is used to express cardinal directions
+ * of any {@link de.pxav.kelp.core.world.KelpLocation}.
+ *
+ * Cardinal directions depend on the rotation on the y-axis
+ * and are therefore calculated using a location's {@code yaw}.
+ * If you use the {@link KelpLocation#getCardinalDirection()} method,
+ * the given location has to have a {@code yaw} to be able to convert
+ * it to a {@code CardinalDirection}.
+ *
+ * You can also use a {@link org.bukkit.util.Vector} to determine
+ * the yaw, which can be done with {@link KelpLocation#setDirection(Vector)}.
+ *
+ * Cardinal directions can be used to express the facing of a player or
+ * a relative block for example. Given a block, you could use the direction
+ * with {@link de.pxav.kelp.core.world.KelpBlock#getRelative(CardinalDirection)}
+ * to get the neighbouring block in a specific direction.
+ *
+ * @author pxav
+ */
+public enum CardinalDirection {
+
+ NORTH,
+ NORTH_EAST,
+ EAST,
+ SOUTH_EAST,
+ SOUTH,
+ SOUTH_WEST,
+ WEST,
+ NORTH_WEST;
+
+}
diff --git a/core/src/main/java/de/pxav/kelp/core/world/util/ExplosionPower.java b/core/src/main/java/de/pxav/kelp/core/world/util/ExplosionPower.java
new file mode 100644
index 00000000..55c03534
--- /dev/null
+++ b/core/src/main/java/de/pxav/kelp/core/world/util/ExplosionPower.java
@@ -0,0 +1,69 @@
+package de.pxav.kelp.core.world.util;
+
+/**
+ * This class is used to determine the power of an explosion.
+ * Depending on which type of explosion is performed, those types
+ * have different powers. {@code TNT} for example has a power of {@code 4},
+ * while a creeper has just {@code 3}.
+ *
+ * This class basically just holds the float value for the power of the
+ * explosion, while providing a bunch of default values for some default
+ * Minecraft explosion types. They can be accessed with:
+ * {@code ExplosionPower.EXPLOSION_NAME}.
+ *
+ * To create a custom explosion power, you could use the static factory
+ * {@code ExplosionPower.custom(float power)}.
+ *
+ * The power can be passed when creating an explosion from
+ * {@link de.pxav.kelp.core.world.KelpWorld} for example.
+ *
+ * @author pxav
+ */
+public class ExplosionPower {
+
+ public static final ExplosionPower CREEPER = new ExplosionPower(3F);
+ public static final ExplosionPower CHARGED_CREEPER = new ExplosionPower(6F);
+
+ public static final ExplosionPower TNT = new ExplosionPower(4F);
+ public static final ExplosionPower UNDERWATER_TNT = new ExplosionPower(4F);
+
+ public static final ExplosionPower WITHER_HALF_HEALTH = new ExplosionPower(8F);
+ public static final ExplosionPower WITHER_SPAWN = new ExplosionPower(7F);
+ public static final ExplosionPower WITHER_KILL = new ExplosionPower(7F);
+ public static final ExplosionPower BLACK_WITHER_SKULL = new ExplosionPower(1F);
+ public static final ExplosionPower DANGEROUS_WITHER_SKULL = new ExplosionPower(1F);
+
+ public static final ExplosionPower BED_NETHER = new ExplosionPower(5F);
+ public static final ExplosionPower BED_END = new ExplosionPower(5F);
+
+ public static final ExplosionPower END_CRYSTAL = new ExplosionPower(6F);
+ public static final ExplosionPower RESPAWN_ANCHOR = new ExplosionPower(5F);
+ public static final ExplosionPower GHAST_FIREBALL = new ExplosionPower(1F);
+
+ /**
+ * Allows you to create an explosion with a custom power, which is not
+ * provided by the default list by minecraft.
+ *
+ * @param power The power of your custom explosion.
+ * @return The {@link ExplosionPower} object holding the given {@code power}
+ */
+ public static ExplosionPower custom(float power) {
+ return new ExplosionPower(power);
+ }
+
+ // the actual power of the explosion
+ private final float power;
+
+ ExplosionPower(float power) {
+ this.power = power;
+ }
+
+ /**
+ * Gets the explosion power of this explosion type.
+ * @return The explosion power value held by this object.
+ */
+ public float getPower() {
+ return power;
+ }
+
+}
diff --git a/core/src/main/java/de/pxav/kelp/core/world/util/WorldType.java b/core/src/main/java/de/pxav/kelp/core/world/util/WorldType.java
new file mode 100644
index 00000000..95f9a900
--- /dev/null
+++ b/core/src/main/java/de/pxav/kelp/core/world/util/WorldType.java
@@ -0,0 +1,29 @@
+package de.pxav.kelp.core.world.util;
+
+/**
+ * Represents the generator type of a {@link de.pxav.kelp.core.world.KelpWorld}.
+ * This can be either a normal overworld or the nether or the end.
+ * This is the equivalent to the normal {@link org.bukkit.World.Environment} provided
+ * by bukkit.
+ *
+ * @author pxav
+ */
+public enum WorldType {
+
+ /**
+ * The current world is an overworld.
+ */
+ NORMAL,
+
+ /**
+ * The current world is the end. This also changes the sky background
+ * to the purple background you might know.
+ */
+ THE_END,
+
+ /**
+ * The current world is the nether.
+ */
+ THE_NETHER
+
+}
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
new file mode 100644
index 00000000..acf50caf
--- /dev/null
+++ b/core/src/main/java/de/pxav/kelp/core/world/version/BlockVersionTemplate.java
@@ -0,0 +1,77 @@
+package de.pxav.kelp.core.world.version;
+
+import de.pxav.kelp.core.application.KelpVersionTemplate;
+import de.pxav.kelp.core.inventory.material.KelpMaterial;
+import de.pxav.kelp.core.world.KelpBlock;
+import de.pxav.kelp.core.world.KelpChunk;
+import de.pxav.kelp.core.world.KelpLocation;
+import org.bukkit.block.BlockFace;
+
+/**
+ * This version template is used for version-dependent operations
+ * related to bukkit's default {@link org.bukkit.block.Block}.
+ *
+ * @author pxav
+ */
+@KelpVersionTemplate
+public abstract class BlockVersionTemplate {
+
+ /**
+ * Gets the {@link KelpChunk} the given block is located in.
+ *
+ * @param block The block you want to get the chunk of.
+ * @return The {@link KelpChunk} the given block is located in.
+ */
+ public abstract KelpChunk getChunk(KelpBlock block);
+
+ /**
+ * Gets the {@link KelpLocation} of the given block. This also
+ * contains information like the current world.
+ *
+ * @param block The block you want to get the location of.
+ * @return The {@link KelpLocation} of this world.
+ */
+ public abstract KelpLocation getLocation(KelpBlock block);
+
+ /**
+ * Gets the material of the current block.
+ *
+ * @param block The block you want to get the material of.
+ * @return The {@link KelpMaterial} the given block is made of.
+ */
+ public abstract KelpMaterial getMaterial(KelpBlock block);
+
+ /**
+ * Sets the material of the given block to the given {@link KelpMaterial}.
+ * So if you had an {@link KelpMaterial#AIR} block for example,
+ *
+ * @param block The block you want to change the material of.
+ * @param material The new material you want the block to have.
+ */
+ public abstract void setMaterial(KelpBlock block, KelpMaterial material);
+
+ /**
+ * Checks whether bone meal will have an effect on this block.
+ * This means it will check whether the block is a sapling
+ * or a plant that will grow if you right click it with bone
+ * meal. More information about that can be found
+ * in this wiki
+ * article
+ *
+ * @param block The block you want to check the application state of.
+ * @return {@code true} whether bone meal is applicable.
+ */
+ public abstract boolean canApplyBoneMeal(KelpBlock block);
+
+ /**
+ * Simulates the application of bone meal on a block.
+ * This means if the block is a sapling for example, it
+ * might grow to a tree or grass might spawn random flowers
+ * and so on.
+ *
+ * @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);
+
+}
diff --git a/core/src/main/java/de/pxav/kelp/core/world/version/ChunkVersionTemplate.java b/core/src/main/java/de/pxav/kelp/core/world/version/ChunkVersionTemplate.java
new file mode 100644
index 00000000..ce5a0e29
--- /dev/null
+++ b/core/src/main/java/de/pxav/kelp/core/world/version/ChunkVersionTemplate.java
@@ -0,0 +1,171 @@
+package de.pxav.kelp.core.world.version;
+
+import de.pxav.kelp.core.application.KelpApplication;
+import de.pxav.kelp.core.application.KelpVersionTemplate;
+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 java.util.Collection;
+import java.util.Set;
+
+/**
+ * This version template is used to handle version-dependent operations
+ * of {@link KelpChunk}s.
+ *
+ * @author pxav
+ */
+@KelpVersionTemplate
+public abstract class ChunkVersionTemplate {
+
+ /**
+ * Determines whether the given location is inside the given chunk.
+ *
+ * @param chunk The chunk you want to check.
+ * @param location The location that should be located in the chunk.
+ * @return {@code true} if the location is inside the chunk.
+ */
+ public abstract boolean contains(KelpChunk chunk, KelpLocation location);
+
+ /**
+ * Gets a block at the given location inside the chunk. The passed location
+ * may not be from outside the chunk!
+ *
+ * @param chunk The chunk you want to get a block of.
+ * @param location The location where the block is located.
+ * @return The block at the given location.
+ */
+ public abstract KelpBlock getBlockAt(KelpChunk chunk, KelpLocation location);
+
+ //todo getEntities()
+
+ /**
+ * Gets a collection of all players that are currently inside the chunk.
+ *
+ * @param chunk The chunk you want to get the players of.
+ * @return A collection of all players that are currently inside the chunk.
+ */
+ public abstract Collection getPlayers(KelpChunk chunk);
+
+ /**
+ * Gets the world the given chunk is located in.
+ *
+ * @param chunk The chunk you want to get the world of.
+ * @return The world the given chunk is located in.
+ */
+ public abstract KelpWorld getWorld(KelpChunk chunk);
+
+ /**
+ * 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).
+ *
+ * @param chunk The chunk you want to get the X-coordinate of.
+ * @return The X coordinate on the world's chunk grid.
+ */
+ public abstract int getX(KelpChunk chunk);
+
+ /**
+ * 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).
+ *
+ * @param chunk The chunk you want to get the Z-coordinate of.
+ * @return The Z coordinate on the world's chunk grid.
+ */
+ public abstract int getZ(KelpChunk chunk);
+
+ /**
+ * Checks whether the given chunk is currently loaded. That
+ * means it checks whether tick operations are currently ran on
+ * this chunk.
+ *
+ * @param chunk The chunk you want to check if its loaded.
+ * @return {@code true} if the chunk is currently loaded.
+ */
+ public abstract boolean isLoaded(KelpChunk chunk);
+
+ /**
+ * 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(KelpChunk, Class)}
+ * to keep the chunk loaded until you manually unload it again. But please
+ * keep in mind that this might have bad performance impact.
+ *
+ * @param chunk The chunk to be loaded. If the chunk has not been loaded before,
+ * the world will be generated at that point.
+ */
+ public abstract void load(KelpChunk chunk);
+
+ /**
+ * 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(KelpChunk)}.
+ *
+ * @param chunk The chunk you want to unload.
+ */
+ public abstract void unload(KelpChunk chunk);
+
+ /**
+ * 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(KelpChunk, Class)}).
+ *
+ * If you call this method, {@link #unload(KelpChunk)} 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(KelpChunk, Class)}
+ * first.
+ *
+ * @param chunk The chunk you want to keep loaded.
+ * @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 abstract void addForceLoadFlag(KelpChunk chunk, Class extends KelpApplication> 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(KelpChunk, Class)}.
+ *
+ * @param chunk The chunk to remove the force load flag of.
+ * @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 abstract void removeForceLoadFlag(KelpChunk chunk, Class extends KelpApplication> plugin);
+
+ /**
+ * Gets all {@link KelpApplication}s that are currently forcing this chunk to keep loaded.
+ *
+ * @param chunk The chunk you want to get the plugins of.
+ * @return A set of all plugin main classes that force the chunk to keep loaded.
+ */
+ public abstract Set> getForceLoadFlagPlugins(KelpChunk chunk);
+
+ /**
+ * Determines whether slime entities can spawn in the given chunk.
+ *
+ * @param chunk The chunk you want to check.
+ * @return {@code true} if slime entities can spawn in that chunk.
+ */
+ public abstract boolean isSlimeChunk(KelpChunk chunk);
+
+}
diff --git a/core/src/main/java/de/pxav/kelp/core/world/version/LocationVersionTemplate.java b/core/src/main/java/de/pxav/kelp/core/world/version/LocationVersionTemplate.java
new file mode 100644
index 00000000..4310ec99
--- /dev/null
+++ b/core/src/main/java/de/pxav/kelp/core/world/version/LocationVersionTemplate.java
@@ -0,0 +1,7 @@
+package de.pxav.kelp.core.world.version;
+
+import de.pxav.kelp.core.application.KelpVersionTemplate;
+
+@KelpVersionTemplate
+public abstract class LocationVersionTemplate {
+}
diff --git a/core/src/main/java/de/pxav/kelp/core/world/version/WorldVersionTemplate.java b/core/src/main/java/de/pxav/kelp/core/world/version/WorldVersionTemplate.java
new file mode 100644
index 00000000..c8e2bb2e
--- /dev/null
+++ b/core/src/main/java/de/pxav/kelp/core/world/version/WorldVersionTemplate.java
@@ -0,0 +1,417 @@
+package de.pxav.kelp.core.world.version;
+
+import de.pxav.kelp.core.application.KelpVersionTemplate;
+import de.pxav.kelp.core.entity.type.DroppedItemEntity;
+import de.pxav.kelp.core.entity.type.ItemDropType;
+import de.pxav.kelp.core.inventory.item.KelpItem;
+import de.pxav.kelp.core.player.KelpPlayer;
+import de.pxav.kelp.core.world.*;
+import de.pxav.kelp.core.world.util.ExplosionPower;
+import de.pxav.kelp.core.world.util.WorldType;
+import org.bukkit.Difficulty;
+
+import java.util.Collection;
+import java.util.UUID;
+
+/**
+ * This class is used to handle version dependent operations
+ * with {@link KelpWorld}s.
+ *
+ * @author pxav
+ */
+@KelpVersionTemplate
+public abstract class WorldVersionTemplate {
+
+ /**
+ * Gets the spawn location of the given world. The spawn location is the
+ * center of a radius in which players can spawn when initially joining a
+ * new world.
+ *
+ * @param world The world you want to get the spawn location of.
+ * @return The world's spawn location.
+ */
+ public abstract KelpLocation getSpawnLocation(KelpWorld world);
+
+ /**
+ * Gets the chunk at the given location. A chunk is a 16x16x(256 or 320 depending on your version)
+ * cuboid region minecraft uses to generate and handle the world. More information
+ * can be found in {@link KelpChunk}.
+ *
+ * @param world The world from which you want to get the chunk of.
+ * @param location The location where the chunk is located.
+ * @return The chunk in the given world at the given location.
+ */
+ public abstract KelpChunk getChunkAt(KelpWorld world, KelpLocation location);
+
+ /**
+ * Checks whether this world is able to generate structures such as temples or villages.
+ *
+ * @param world The world you want to check.
+ * @return {@code true} if the world can generate structures
+ */
+ public abstract boolean canGenerateStructures(KelpWorld world);
+
+ /**
+ * Creates an explosion at the given location.
+ *
+ * @param world The world to spawn the explosion at.
+ * @param location The center of the explosion
+ * @param power The power of the explosion. More information on explosion powers can be
+ * found in {@link ExplosionPower}
+ * @param breakBlocks Whether the explosion should be able to break blocks.
+ * @param igniteFire Whether the explosion should be able to ignite a fire within its radius.
+ */
+ public abstract void createExplosion(KelpWorld world, KelpLocation location, ExplosionPower power, boolean breakBlocks, boolean igniteFire);
+
+ /**
+ * Drops an item on the given world at the given location. This item will be
+ * visible for all players on the world.
+ *
+ * @param world The world to spawn the item on.
+ * @param location The location from where the item should drop
+ * @param item The item you want to drop
+ * @param dropType Whether the item should be dropped normally or naturally.
+ * More information about the difference can be found in {@link ItemDropType}
+ * @return The item entity that has been spawned at the given location.
+ */
+ public abstract DroppedItemEntity dropItem(KelpWorld world, KelpLocation location, KelpItem item, ItemDropType dropType);
+
+ /**
+ * Checks whether animals can spawn naturally on this world.
+ *
+ * @param world The world you want to check animal spawns for.
+ * @return {@code true} if animals can spawn by default.
+ */
+ public abstract boolean animalsAllowed(KelpWorld world);
+
+ /**
+ * Checks whether monsters such as creepers or zombies
+ * can spawn naturally on this world.
+ *
+ * @param world The world you want to check monster spawns for.
+ * @return {@code true} if monsters can spawn by default.
+ */
+ public abstract boolean monstersAllowed(KelpWorld world);
+
+ /**
+ * Gets the {@link KelpBlock} at the given location of the world.
+ *
+ * @param world The world you want to get the block of.
+ * @param location The exact location of the block you want to get.
+ * @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 abstract KelpBlock getBlockAt(KelpWorld world, KelpLocation location);
+
+ /**
+ * Gets the highest block at a given location.
+ *
+ * @param world The world to check the location in.
+ * @param location The location containing the y-axis for the block check.
+ * @return The highest {@link KelpBlock} at the provided location of the world.
+ */
+ public abstract KelpBlock getHighestBlockAt(KelpWorld world, KelpLocation location);
+
+ /**
+ * Gets a collection of all {@link KelpChunk}s that are currently loaded in
+ * the given world. This includes forced loaded chunks as well as naturally loaded chunks.
+ *
+ * @param world The world to get the loaded chunks of.
+ * @return The collection of all chunks currently loaded in this world.
+ */
+ public abstract Collection getLoadedChunks(KelpWorld world);
+
+ /**
+ * Gets the difficulty of the current world. The difficulty gives an
+ * indication of how difficult survival gameplay is. {@code Peaceful} means
+ * players cannot become hungry and monsters won't spawn, while {@code hard}
+ * says that lots of monsters will spawn and players lose saturation really
+ * fast.
+ *
+ * @param world The world to get the difficulty of.
+ * @return The current difficulty of the world.
+ */
+ public abstract Difficulty getDifficulty(KelpWorld world);
+
+ /**
+ * Gets the environment/world type. The world type says which dimension
+ * is currently used by the world generator. This also affects the fog effects
+ * of the world. While in overworld ({@code NORMAL}) there won't be any
+ * special background or fog, the background of an {@code END} world
+ * will be purple for example.
+ *
+ * This is similar to the {@code #getEnvironment()} method of a normal
+ * bukkit world.
+ *
+ * @param world The world to get the world type of.
+ * @return The type of the given world.
+ */
+ public abstract WorldType getWorldType(KelpWorld world);
+
+ /**
+ * Gets the humidity at a specific location in the world.
+ * The humidity is dependent on the biome at the given location.
+ *
+ * @param world The world you want to check the humidity in.
+ * @param location The location to check the humidity at.
+ * @return The humidity value at the given location in the given world.
+ */
+ public abstract double getHumidityAt(KelpWorld world, KelpLocation location);
+
+ /**
+ * Checks whether the spawn chunks should be kept in memory although
+ * no players are currently in the spawn area.
+ *
+ * @param world The world you want to check the spawn chunks of.
+ * @return {@code true} if the spawn chunks are kept in memory.
+ */
+ public abstract boolean shouldKeepSpawnInMemory(KelpWorld world);
+
+ /**
+ * Changes whether the spawn chunks of a world should be kept
+ * loaded/in memory although no players are currently in the spawn
+ * area. This might improve performance if you are commonly spawning
+ * new players to a world (such as in a server lobby).
+ *
+ * @param world The world you want to set the spawn chunk settings of.
+ * @param keep {@code true} if the spawn chunks should be kept loaded over the server runtime.
+ */
+ public abstract void setKeepSpawnInMemory(KelpWorld world, boolean keep);
+
+ /**
+ * Gets the full time of a world. This is the full absolute
+ * in-game daytime.
+ *
+ * @param world The world you want to get the time of.
+ * @return The full (absolute) time of the given world.
+ */
+ public abstract long getFullTime(KelpWorld world);
+
+ /**
+ * Gets the full in-game time of this world from the initial world generation.
+ *
+ * @param world The world you want to get the game time of.
+ * @return The game time that has passed on the given world.
+ */
+ public abstract long getGameTime(KelpWorld world);
+
+ /**
+ * Gets the relative in-game time of the given world.
+ * This time is calculated from the world's full time:
+ * {@code #getFullTime() % 24000L}.
+ *
+ * This time cannot be negative. If a negative value is returned from
+ * the above operation, a full day will be added to this value.
+ *
+ * @param world The world you want to get the relative time of.
+ * @return The relative in-game time of the given world.
+ */
+ public abstract long getTime(KelpWorld world);
+
+ /**
+ * Gets all players that are currently on the given world.
+ *
+ * @param world The world you want to get the players of.
+ * @return A collection of all players that are currently on this world.
+ */
+ public abstract Collection getPlayers(KelpWorld world);
+
+ /**
+ * Gets the current pvp mode of the world. If players are allowed to
+ * fight each other, this will return {@code true}.
+ *
+ * @param world The world you want to check the pvp setting of.
+ * @return {@code true} if pvp is enabled on this world.
+ */
+ public abstract boolean isPvPEnabled(KelpWorld world);
+
+ /**
+ * Sets the current pvp mode of the world. If players should be allowed
+ * to fight each other, set this to {@code true}.
+ *
+ * @param world The world you want to set the pvp setting of.
+ * @param pvp {@code true} if players should be able to fight each other.
+ */
+ public abstract void setPVP(KelpWorld world, boolean pvp);
+
+ /**
+ * Gets the seed of the world generator of this world. A seed is a constant value
+ * used by the {@code Perlin noise} world generation algorithm to randomize
+ * biomes and structures. Seeds are usually compatible across different versions, but
+ * might not output the same result in differing versions due to new structures and
+ * biomes that have been added over time.
+ *
+ * @param world The world you want to get the seed of.
+ * @return The world's seed.
+ */
+ public abstract long getSeed(KelpWorld world);
+
+ /**
+ * Gets the sea level of this world.
+ *
+ * @param world The world you want to get the sea level of.
+ * @return The world's sea level.
+ */
+ public abstract int getSeaLevel(KelpWorld world);
+
+ /**
+ * Gets the temperature of a world at a specific location.
+ * Temperatures are depending on the biome and exact location (height specifically).
+ * A temperature indicates whether it can snow or rain at a specific location
+ * or whether there is any precipitation at all. The desert biome for example
+ * has a relatively high temperature of {@code 2.0d}, while Snowy Beach has {@code 0.05}
+ * for example.
+ *
+ * @param world The world you want to get the temperature in.
+ * @param location The location you want to get the temperature at.
+ * @return The exact temperature at the given location in the current world.
+ */
+ public abstract double getTemperatureAt(KelpWorld world, KelpLocation location);
+
+ /**
+ * Gets the duration of the current thunder in the world in ticks.
+ *
+ * @param world The world you want to get the thunder duration of.
+ * @return The thunder duration in ticks.
+ */
+ public abstract int getThunderDuration(KelpWorld world);
+
+ /**
+ * Gets the unique id of this world.
+ *
+ * @param world This world's unique id.
+ * @return The world's {@link UUID}.
+ */
+ public abstract UUID getUUID(KelpWorld world);
+
+ /**
+ * Gets the duration of the current weather state in ticks.
+ * This counts for all weather types (including clear weather)
+ *
+ * @param world The world to get the weather duration of.
+ * @return The current weather duration in ticks.
+ */
+ public abstract int getWeatherDuration(KelpWorld world);
+
+ /**
+ * Checks whether there is a storm in the given world.
+ *
+ * @param world The world you want to check.
+ * @return {@code true} whether there is currently a storm in the given world.
+ */
+ public abstract boolean hasStorm(KelpWorld world);
+
+ /**
+ * Checks if auto-save is enabled in this world.
+ * This would save the world to the server folder in a regular interval.
+ *
+ * @param world The world you want to get the auto-save mode of.
+ * @return {@code true} if auto-save is enabled.
+ */
+ public abstract boolean hasAutoSave(KelpWorld world);
+
+ /**
+ * Checks whether there is clear weather in the given world.
+ * If there is clear whether, methods such as {@link #hasStorm(KelpWorld)}
+ * are automatically {@code false}.
+ *
+ * @param world The world you want to check the weather of.
+ * @return {@code true} whether there is clear weather, {@code false} if there is any thunder or precipitation.
+ */
+ public abstract boolean isWeatherClear(KelpWorld world);
+
+ /**
+ * Checks if there currently is a thunderstorm in the given world.
+ *
+ * @param world The world you want to check the weather of.
+ * @return {@code true} weather there currently is a thunder in the world.
+ */
+ public abstract boolean isThundering(KelpWorld world);
+
+ /**
+ * Sets the difficulty of the current world. The difficulty gives an
+ * indication of how difficult survival gameplay is. {@code Peaceful} means
+ * players cannot become hungry and monsters won't spawn, while {@code hard}
+ * says that lots of monsters will spawn and players lose saturation really
+ * fast.
+ *
+ * @param world The world you want to set the difficulty of.
+ * @param difficulty The difficulty you want to set.
+ */
+ public abstract void setDifficulty(KelpWorld world, Difficulty difficulty);
+
+ /**
+ * Sets the full time of the current world. The full time is the absolute in-game
+ * daytime of a world. With this method you can also rewind the time, which could
+ * eventually break redstone clocks or other scheduled events, which is why
+ * you might want to use {@link #setTime(KelpWorld, long)} depending on your use-case.
+ *
+ * @param world The world to set the full time of.
+ * @param fullTime The full time to set for this world.
+ */
+ public abstract void setFullTime(KelpWorld world, long fullTime);
+
+ /**
+ * Sets the spawn location of this world. This is the location, where
+ * players will spawn when they first join the world.
+ *
+ * By default, there is a 30x30 radius around the location, where players
+ * spawn randomly.
+ *
+ * @param world The world you want to set the spawn location of.
+ * @param location The spawn location you want to set.
+ */
+ public abstract void setSpawnLocation(KelpWorld world, KelpLocation location);
+
+ /**
+ * Sets the storm state of a world. When set to {@code true} a new storm
+ * will start or the current storm will continue.
+ *
+ * @param world The world you want to set the storm state of.
+ * @param storm {@code true} if you want to start a storm. {@code false} if you want to end the storm.
+ */
+ public abstract void setStorm(KelpWorld world, boolean storm);
+
+ /**
+ * Sets the thunder state of a world. When set to {@code true} a new thunder
+ * will start or the current storm will continue.
+ *
+ * @param world The world you want to set the thunder state of.
+ * @param thunder {@code true} if you want to start a thunderstorm. {@code false} if you want to end the thunder.
+ */
+ public abstract void setThundering(KelpWorld world, boolean thunder);
+
+ /**
+ * Sets the duration of the current thunderstorm in the world in ticks.
+ *
+ * @param world The world you want to set the thunder duration of.
+ * @param duration The duration in ticks the thunder should take.
+ */
+ public abstract void setThunderDuration(KelpWorld world, int duration);
+
+ /**
+ * Sets the relative in-game time of the given world.
+ * If you set a value that is smaller than the current time value,
+ * the server will automatically skip to the next day. Rewind
+ * is not possible with this method for safety reasons. If you need
+ * to do it though, use {@link #setFullTime(KelpWorld, long)}.
+ *
+ * @param world The world you want to set the time of.
+ * @param time The relative in-game time to set.
+ */
+ public abstract void setTime(KelpWorld world, long time);
+
+ /**
+ * Spawns a lightning in the given world at the given location. This
+ * lightning will be visible for all players who are in the world and in range.
+ *
+ * @param world The world you want to spawn the lightning on.
+ * @param location The exact location where the lightning should strike.
+ * @param effect Whether the lightning should just be an effect or be a real lightning.
+ * A lightning effect does not cause fire, nor damage, nor pigs are transformed to pigmen, etc.
+ * If you set this to {@code false} a normal lightning will spawn and cause damage to players.
+ */
+ public abstract void strikeLightning(KelpWorld world, KelpLocation location, boolean effect);
+
+}
diff --git a/kelp-sql/pom.xml b/kelp-sql/pom.xml
index 49c64d42..0a6f5860 100644
--- a/kelp-sql/pom.xml
+++ b/kelp-sql/pom.xml
@@ -5,7 +5,7 @@
parent
com.github.pxav.kelp
- 0.2.0
+ 0.3.0
4.0.0
@@ -20,7 +20,7 @@
com.github.pxav.kelp
core
- 0.2.0
+ 0.3.0
provided
diff --git a/pom.xml b/pom.xml
index 30440dd9..ff12e826 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
com.github.pxav.kelp
parent
pom
- 0.2.0
+ 0.3.0
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 da89802d..5e6f5a07 100644
--- a/testing-module/pom.xml
+++ b/testing-module/pom.xml
@@ -5,7 +5,7 @@
parent
com.github.pxav.kelp
- 0.2.0
+ 0.3.0
4.0.0
@@ -45,7 +45,7 @@
com.github.pxav.kelp
core
- 0.2.0
+ 0.3.0
provided
diff --git a/v1_8_implementation/pom.xml b/v1_8_implementation/pom.xml
index 1a9dc224..14d88634 100644
--- a/v1_8_implementation/pom.xml
+++ b/v1_8_implementation/pom.xml
@@ -5,7 +5,7 @@
com.github.pxav.kelp
parent
- 0.2.0
+ 0.3.0
4.0.0
@@ -90,7 +90,7 @@
com.github.pxav.kelp
core
- 0.2.0
+ 0.3.0
provided
diff --git a/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/entity/VersionedEntity.java b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/entity/VersionedEntity.java
index 8a932bf1..dbd8732c 100644
--- a/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/entity/VersionedEntity.java
+++ b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/entity/VersionedEntity.java
@@ -8,6 +8,8 @@
import de.pxav.kelp.core.version.KelpVersion;
import de.pxav.kelp.core.version.SinceKelpVersion;
import de.pxav.kelp.core.version.Versioned;
+import de.pxav.kelp.core.world.KelpLocation;
+import de.pxav.kelp.core.world.KelpWorld;
import net.minecraft.server.v1_8_R3.Entity;
import org.bukkit.Location;
import org.bukkit.Server;
@@ -46,20 +48,20 @@ public class VersionedEntity extends EntityVersionTemplate {
*/
@Override
public void spawnEntity(KelpEntity entity) {
- CraftWorld craftWorld = (CraftWorld) entity.getInitialLocation().getWorld();
+ CraftWorld craftWorld = (CraftWorld) entity.getInitialBukkitLocation().getWorld();
if (entity.getEntityType() == KelpEntityType.DROPPED_ITEM) {
DroppedItemEntity kelpEntity = (DroppedItemEntity) entity;
if (kelpEntity.getItemDropType() == ItemDropType.NATURAL) {
- craftWorld.dropItemNaturally(entity.getInitialLocation(), kelpEntity.getItem().getItemStack());
+ craftWorld.dropItemNaturally(entity.getInitialBukkitLocation(), kelpEntity.getItem().getItemStack());
} else {
- craftWorld.dropItem(entity.getInitialLocation(), kelpEntity.getItem().getItemStack());
+ craftWorld.dropItem(entity.getInitialBukkitLocation(), kelpEntity.getItem().getItemStack());
}
return;
}
Entity minecraftEntity = (Entity) entity.getMinecraftEntity();
- ((Entity) entity.getMinecraftEntity()).setPositionRotation(entity.getInitialLocation().getX(), entity.getInitialLocation().getY(), entity.getInitialLocation().getZ(), entity.getInitialLocation().getYaw(), entity.getInitialLocation().getPitch());
+ ((Entity) entity.getMinecraftEntity()).setPositionRotation(entity.getInitialBukkitLocation().getX(), entity.getInitialBukkitLocation().getY(), entity.getInitialBukkitLocation().getZ(), entity.getInitialBukkitLocation().getYaw(), entity.getInitialBukkitLocation().getPitch());
craftWorld.addEntity(minecraftEntity, CreatureSpawnEvent.SpawnReason.CUSTOM);
}
@@ -76,8 +78,8 @@ public org.bukkit.entity.Entity toBukkitEntity(Object minecraftEntity) {
* @return The location of the given entity.
*/
@Override
- public Location getLocation(org.bukkit.entity.Entity entity) {
- return entity.getLocation();
+ public KelpLocation getLocation(org.bukkit.entity.Entity entity) {
+ return KelpLocation.from(entity.getLocation());
}
/**
@@ -136,17 +138,6 @@ public boolean isOnGround(org.bukkit.entity.Entity entity) {
return entity.isOnGround();
}
- /**
- * Gets the current world of the entity.
- *
- * @param entity The entity whose world you want to get.
- * @return The world where the entity is currently located.
- */
- @Override
- public World getWorld(org.bukkit.entity.Entity entity) {
- return entity.getWorld();
- }
-
/**
* Sets the rotation of the given entity. This does not
* affect the location x, y and z axes.
@@ -175,8 +166,8 @@ public void setRotation(org.bukkit.entity.Entity entity, float yaw, float pitch)
* @return {@code true} if the teleport action was successful.
*/
@Override
- public boolean teleport(org.bukkit.entity.Entity entity, Location location, PlayerTeleportEvent.TeleportCause teleportCause) {
- return entity.teleport(location, teleportCause);
+ public boolean teleport(org.bukkit.entity.Entity entity, KelpLocation location, PlayerTeleportEvent.TeleportCause teleportCause) {
+ return entity.teleport(location.getBukkitLocation(), teleportCause);
}
/**
diff --git a/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/entity/VersionedLivingEntity.java b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/entity/VersionedLivingEntity.java
index f3e47ee7..c2652e0d 100644
--- a/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/entity/VersionedLivingEntity.java
+++ b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/entity/VersionedLivingEntity.java
@@ -2,6 +2,7 @@
import de.pxav.kelp.core.entity.version.LivingEntityVersionTemplate;
import de.pxav.kelp.core.version.Versioned;
+import de.pxav.kelp.core.world.KelpLocation;
import org.bukkit.Location;
import org.bukkit.entity.LivingEntity;
@@ -12,8 +13,10 @@
*/
@Versioned
public class VersionedLivingEntity extends LivingEntityVersionTemplate {
+
@Override
- public Location getEyeLocation(LivingEntity livingEntity) {
- return livingEntity.getEyeLocation();
+ public KelpLocation getEyeLocation(LivingEntity livingEntity) {
+ return KelpLocation.from(livingEntity.getEyeLocation());
}
+
}
diff --git a/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/inventory/VersionedItem.java b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/inventory/VersionedItem.java
index 672b826c..2cc75797 100644
--- a/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/inventory/VersionedItem.java
+++ b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/inventory/VersionedItem.java
@@ -14,6 +14,8 @@
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
+import java.util.List;
+
/**
* A class description goes here.
*
@@ -81,8 +83,11 @@ public ItemStack setDisplayName(ItemStack itemStack, String displayName) {
}
@Override
- public ItemStack setLore(ItemStack itemStack, String itemLore) {
- return null;
+ public ItemStack setLore(ItemStack itemStack, List itemLore) {
+ ItemMeta itemMeta = itemStack.getItemMeta();
+ itemMeta.setLore(itemLore);
+ itemStack.setItemMeta(itemMeta);
+ return itemStack;
}
@Override
diff --git a/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/inventory/VersionedItemTag.java b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/inventory/VersionedItemTag.java
index 3c28d7ae..448892c1 100644
--- a/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/inventory/VersionedItemTag.java
+++ b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/inventory/VersionedItemTag.java
@@ -43,7 +43,7 @@ public ItemStack tagItem(ItemStack itemStack, String key, String value) {
Preconditions.checkNotNull(value);
if (itemStack.getType() == Material.AIR) {
this.logAirError();
- return null;
+ return itemStack;
}
net.minecraft.server.v1_8_R3.ItemStack nmsItemStack = CraftItemStack.asNMSCopy(itemStack);
@@ -58,13 +58,16 @@ public ItemStack tagItem(ItemStack itemStack, String key, String value) {
@Override
public ItemStack removeTag(ItemStack itemStack, String key) {
Preconditions.checkNotNull(itemStack);
+
if (itemStack.getType() == Material.AIR) {
this.logAirError();
- return null;
+ return itemStack;
}
net.minecraft.server.v1_8_R3.ItemStack nmsItemStack = CraftItemStack.asNMSCopy(itemStack);
- NBTTagCompound nbtTagCompound = nmsItemStack.getTag();
+ NBTTagCompound nbtTagCompound = nmsItemStack.getTag() == null
+ ? new NBTTagCompound()
+ : nmsItemStack.getTag();
nbtTagCompound.remove(key);
return CraftItemStack.asBukkitCopy(nmsItemStack);
}
diff --git a/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/inventory/VersionedMaterial.java b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/inventory/VersionedMaterial.java
index 2613c335..0b4d2aa6 100644
--- a/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/inventory/VersionedMaterial.java
+++ b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/inventory/VersionedMaterial.java
@@ -579,7 +579,7 @@ public void defineDefaults() {
materialRepository.addMaterial(KelpMaterial.LAPIS_BLOCK, Material.LAPIS_BLOCK.toString());
materialRepository.addMaterial(KelpMaterial.LAPIS_LAZULI, Material.INK_SACK.toString() + ":4");
materialRepository.addMaterial(KelpMaterial.LAPIS_ORE, Material.LAPIS_ORE.toString());
- materialRepository.addMaterial(KelpMaterial.LARGE_FERN, Material.DOUBLE_PLANT.toString() + ":3");
+ materialRepository.addMaterial(KelpMaterial.LARGE_FERN_LOWER, Material.DOUBLE_PLANT.toString() + ":3");
materialRepository.addMaterial(KelpMaterial.LAVA, Material.LAVA.toString());
materialRepository.addMaterial(KelpMaterial.LAVA_BUCKET, Material.LAVA_BUCKET.toString());
materialRepository.addMaterial(KelpMaterial.LEAD, Material.LEASH.toString());
@@ -709,7 +709,7 @@ public void defineDefaults() {
materialRepository.addMaterial(KelpMaterial.SUGAR_CANE, Material.SUGAR_CANE.toString());
materialRepository.addMaterial(KelpMaterial.SUGAR_CANE_BLOCK, Material.SUGAR_CANE_BLOCK.toString());
materialRepository.addMaterial(KelpMaterial.SUNFLOWER, Material.DOUBLE_PLANT.toString());
- materialRepository.addMaterial(KelpMaterial.TALL_GRASS, Material.LONG_GRASS.toString());
+ materialRepository.addMaterial(KelpMaterial.TALL_GRASS, Material.DOUBLE_PLANT.toString() + ":2");
materialRepository.addMaterial(KelpMaterial.TNT, Material.TNT.toString());
materialRepository.addMaterial(KelpMaterial.TNT_MINECART, Material.EXPLOSIVE_MINECART.toString());
materialRepository.addMaterial(KelpMaterial.TORCH, Material.TORCH.toString());
diff --git a/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/npc/VersionedNpc.java b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/npc/VersionedNpc.java
index 98044f6e..0689e389 100644
--- a/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/npc/VersionedNpc.java
+++ b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/npc/VersionedNpc.java
@@ -14,6 +14,7 @@
import de.pxav.kelp.core.npc.version.NpcVersionTemplate;
import de.pxav.kelp.core.reflect.ReflectionUtil;
import de.pxav.kelp.core.version.Versioned;
+import de.pxav.kelp.core.world.KelpLocation;
import net.minecraft.server.v1_8_R3.*;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@@ -170,7 +171,7 @@ public void moveRelativeDistance(KelpNpc npc, Player player, double x, double y,
}
@Override
- public void teleport(KelpNpc npc, Location location) {
+ public void teleport(KelpNpc npc, KelpLocation location) {
CraftPlayer craftPlayer = (CraftPlayer) npc.getPlayer().getBukkitPlayer();
PlayerConnection playerConnection = craftPlayer.getHandle().playerConnection;
@@ -235,7 +236,8 @@ public void updateCustomName(KelpNpc npc) {
@Override
public void updateTitleLines(KelpNpc npc) {
CraftPlayer player = (CraftPlayer) npc.getPlayer().getBukkitPlayer();
- WorldServer nmsWorld = ((CraftWorld) npc.getLocation().getWorld()).getHandle();
+ CraftWorld craftWorld = (CraftWorld) Bukkit.getWorld(npc.getLocation().getWorldName());
+ WorldServer nmsWorld = craftWorld.getHandle();
npc.getNpcMeta().getArmorStandEntityIds().forEach(current -> {
PacketPlayOutEntityDestroy armorStandDestroyPacket = new PacketPlayOutEntityDestroy(current);
@@ -325,7 +327,7 @@ public void makeCorpse(KelpNpc npc) {
CraftPlayer player = (CraftPlayer) npc.getPlayer().getBukkitPlayer();
Location bedLocation = new Location(
- npc.getLocation().getWorld(),
+ Bukkit.getWorld(npc.getLocation().getWorldName()),
npc.getLocation().getX(),
npc.getLocation().getY(),
npc.getLocation().getZ());
@@ -345,7 +347,7 @@ public void makeCorpse(KelpNpc npc) {
}
@Override
- public void sleep(KelpNpc npc, Location bedLocation) {
+ public void sleep(KelpNpc npc, KelpLocation bedLocation) {
CraftPlayer player = (CraftPlayer) npc.getPlayer().getBukkitPlayer();
BlockPosition blockPosition = new BlockPosition(
@@ -366,8 +368,11 @@ public void wakeUp(KelpNpc npc) {
playAnimation(npc, NpcAnimation.LEAVE_BED);
if (npc.isCorpse()) {
- Material serverMaterial = npc.getLocation().getBlock().getType();
- player.sendBlockChange(npc.getLocation(), serverMaterial, npc.getLocation().getBlock().getData());
+ Material serverMaterial = npc.getLocation().getBlock().getBukkitBlock().getType();
+ player.sendBlockChange(
+ npc.getLocation().getBukkitLocation(),
+ serverMaterial,
+ npc.getLocation().getBlock().getBukkitBlock().getData());
}
// walk back to initial location for a more smooth transition
diff --git a/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/player/VersionedPlayer.java b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/player/VersionedPlayer.java
index 2911ee83..bd3fd2e3 100644
--- a/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/player/VersionedPlayer.java
+++ b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/player/VersionedPlayer.java
@@ -17,6 +17,7 @@
import de.pxav.kelp.core.sound.KelpSound;
import de.pxav.kelp.core.sound.SoundRepository;
import de.pxav.kelp.core.version.Versioned;
+import de.pxav.kelp.core.world.KelpLocation;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.*;
import net.minecraft.server.v1_8_R3.*;
@@ -150,7 +151,7 @@ public void sendTabHeaderAndFooter(Player player, String header, String footer)
* @param pitch How strong the sound should be pitched.
*/
@Override
- public void playSound(Player player, KelpSound sound, Location location, float volume, float pitch) {
+ public void playSound(Player player, KelpSound sound, KelpLocation location, float volume, float pitch) {
Sound bukkitSound = Sound.valueOf(soundRepository.getSound(sound));
player.playSound(player.getLocation(), bukkitSound, volume, pitch);
}
@@ -168,17 +169,6 @@ public void setHealth(Player player, int health) {
player.setHealth(health);
}
- /**
- * Teleports the player to the given location.
- *
- * @param player The player you want to teleport.
- * @param location The location you want to teleport the player to.
- */
- @Override
- public void teleport(Player player, Location location) {
- player.teleport(location);
- }
-
/**
* @param player The player whose UUID you want to get.
* @return The player's uuid.
@@ -187,15 +177,6 @@ public UUID getUniqueId(Player player) {
return player.getUniqueId();
}
- /**
- * @param player The player whose location you want to get
- * @return The location the player is currently at.
- */
- @Override
- public Location getLocation(Player player) {
- return null;
- }
-
/**
* Checks if the player is currently located in water.
*
@@ -378,8 +359,8 @@ public void setPlayerListFooter(Player player, String footer) {
* @param target The location, where the compass should point to.
*/
@Override
- public void setCompassTarget(Player player, Location target) {
- player.setCompassTarget(target);
+ public void setCompassTarget(Player player, KelpLocation target) {
+ player.setCompassTarget(target.getBukkitLocation());
}
/**
@@ -391,8 +372,8 @@ public void setCompassTarget(Player player, Location target) {
* @return The target location of the player's compass.
*/
@Override
- public Location getCompassTarget(Player player) {
- return player.getCompassTarget();
+ public KelpLocation getCompassTarget(Player player) {
+ return KelpLocation.from(player.getCompassTarget());
}
/**
@@ -1135,8 +1116,8 @@ public void setWhitelisted(Player player, boolean whitelisted) {
* @return The spawn location, {@code null} if the player has not slept or location is invalid.
*/
@Override
- public Location getBedSpawnLocation(Player player) {
- return player.getBedSpawnLocation();
+ public KelpLocation getBedSpawnLocation(Player player) {
+ return KelpLocation.from(player.getBedSpawnLocation());
}
@Override
diff --git a/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/world/ForcedChunkLoader.java b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/world/ForcedChunkLoader.java
new file mode 100644
index 00000000..1a97f29a
--- /dev/null
+++ b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/world/ForcedChunkLoader.java
@@ -0,0 +1,154 @@
+package de.pxav.kelp.implementation1_8.world;
+
+import com.google.common.collect.Sets;
+import de.pxav.kelp.core.application.KelpApplication;
+import de.pxav.kelp.core.logger.KelpLogger;
+import de.pxav.kelp.core.logger.LogLevel;
+import de.pxav.kelp.core.scheduler.synchronize.ServerMainThread;
+import de.pxav.kelp.core.world.KelpChunk;
+import org.bukkit.Chunk;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.world.ChunkUnloadEvent;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * This class is responsible for keeping chunks loaded if
+ * they have a flag assigned by {@link KelpChunk#addForceLoadFlag(Class)}.
+ *
+ * Minecraft unloads chunks by default to save performance, which prevents
+ * crops from growing, etc. If you want to disable that, you can use this class.
+ *
+ * But you don't have to use it directly, it is recommended to use the
+ * interface provided by {@link KelpChunk}.
+ *
+ * @author pxav
+ */
+@Singleton
+public class ForcedChunkLoader {
+
+ private ConcurrentMap>> forceLoadedChunks = new ConcurrentHashMap<>();
+
+ private KelpLogger logger;
+
+ @Inject
+ public ForcedChunkLoader(KelpLogger logger) {
+ this.logger = logger;
+ }
+
+ /**
+ * Adds a chunk to the force load list. This will automatically load the
+ * chunk if it is not already loaded.
+ *
+ * @param plugin The plugin that added this loading flag.
+ * @param kelpChunk The chunk that should be kept loaded.
+ */
+ public void forceLoadChunk(Class extends KelpApplication> plugin, KelpChunk kelpChunk) {
+ Chunk chunk = kelpChunk.getBukkitChunk();
+ Set> plugins = forceLoadedChunks.getOrDefault(chunk, Sets.newHashSet());
+ plugins.add(plugin);
+ forceLoadedChunks.put(chunk, plugins);
+
+ // load the plugin sync, as it is not thread-safe
+ ServerMainThread.RunParallel.run(chunk::load);
+ }
+
+ /**
+ * Removes a force load flag from the given chunk by a specific plugin.
+ * The chunk might still be kept loaded if another plugin is loading it.
+ *
+ * @param plugin The plugin that removed the load flag.
+ * @param kelpChunk The chunk that should be removed.
+ */
+ public void removeForceLoadFlag(Class extends KelpApplication> plugin, KelpChunk kelpChunk) {
+ Chunk chunk = kelpChunk.getBukkitChunk();
+ Set> plugins = forceLoadedChunks.getOrDefault(chunk, Sets.newHashSet());
+ plugins.remove(plugin);
+
+ // if this was the last plugin keeping that chunk, remove the chunk from the list entirely
+ if (plugins.isEmpty()) {
+ forceLoadedChunks.remove(chunk);
+ return;
+ }
+ forceLoadedChunks.put(chunk, plugins);
+ }
+
+ /**
+ * Removes all force load flags for a plugin. Then, this plugin won't load
+ * any chunks anymore. But the chunks might still be loaded if another plugin
+ * loads them.
+ *
+ * @param plugin The plugin to remove from the list and eventually unload the chunks of.
+ */
+ public void removeForceLoadsFor(Class extends KelpApplication> plugin) {
+ for (Map.Entry>> entry : forceLoadedChunks.entrySet()) {
+ Set> plugins = entry.getValue();
+
+ if (plugins.contains(plugin)) {
+ plugins.remove(plugin);
+ if (plugins.isEmpty()) {
+ forceLoadedChunks.remove(entry.getKey());
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Gets all chunks that are force loaded by a specific plugin.
+ *
+ * @param plugin The plugin you want to get the force loaded chunks of.
+ * @return The set of chunks that are loaded by the given plugin.
+ */
+ public Set getForceLoadedChunksFor(Class extends KelpApplication> plugin) {
+ Set output = Sets.newHashSet();
+ for (Map.Entry>> entry : forceLoadedChunks.entrySet()) {
+ Set> plugins = entry.getValue();
+
+ if (plugins.contains(plugin)) {
+ output.add(KelpChunk.from(entry.getKey()));
+ }
+
+ }
+ return output;
+ }
+
+ /**
+ * Gets all plugins force loading a specific {@link KelpChunk}.
+ *
+ * @param chunk The chunk whose plugin should be queried.
+ * @return The plugins loading the given chunk.
+ */
+ public Set> getForceLoadingPluginsOf(KelpChunk chunk) {
+ return this.forceLoadedChunks.getOrDefault(chunk.getBukkitChunk(), Sets.newHashSet());
+ }
+
+ /**
+ * This listener method is triggered everytime the server
+ * tries to unload a chunk.
+ *
+ * If that chunk is force loaded by any plugin, the unload event
+ * is canceled and the chunk is kept loaded.
+ *
+ * @param event The detailed event data
+ */
+ @EventHandler
+ public void handleChunkUnload(ChunkUnloadEvent event) {
+ Chunk chunk = event.getChunk();
+
+ // if chunk in list, then cancel the event
+ for (Chunk current : forceLoadedChunks.keySet()) {
+ if (current.getX() == chunk.getX() && current.getZ() == chunk.getZ()) {
+ event.setCancelled(true);
+ logger.log(LogLevel.DEBUG, "Chunk " + chunk.getX() + ":" + chunk.getZ() + " has been prevented from unloading.");
+ }
+ }
+
+ }
+
+}
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
new file mode 100644
index 00000000..bce35e79
--- /dev/null
+++ b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/world/VersionedBlock.java
@@ -0,0 +1,338 @@
+package de.pxav.kelp.implementation1_8.world;
+
+import de.pxav.kelp.core.common.MathUtils;
+import de.pxav.kelp.core.inventory.item.KelpItem;
+import de.pxav.kelp.core.inventory.material.KelpMaterial;
+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.version.BlockVersionTemplate;
+import de.pxav.kelp.core.world.KelpBlock;
+import de.pxav.kelp.core.world.KelpChunk;
+import net.minecraft.server.v1_8_R3.BlockPosition;
+import net.minecraft.server.v1_8_R3.BlockSapling;
+import net.minecraft.server.v1_8_R3.ItemDye;
+import net.minecraft.server.v1_8_R3.ItemStack;
+import org.bukkit.Material;
+import org.bukkit.TreeType;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.block.BlockState;
+import org.bukkit.craftbukkit.v1_8_R3.CraftWorld;
+import org.bukkit.craftbukkit.v1_8_R3.block.CraftBlock;
+import org.bukkit.craftbukkit.v1_8_R3.inventory.CraftItemStack;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * This class implements all version-dependent methods
+ * of a {@link KelpBlock}.
+ *
+ * @author pxav
+ */
+@Versioned
+public class VersionedBlock extends BlockVersionTemplate {
+
+ /**
+ * Gets the {@link KelpChunk} the given block is located in.
+ *
+ * @param block The block you want to get the chunk of.
+ * @return The {@link KelpChunk} the given block is located in.
+ */
+ @Override
+ public KelpChunk getChunk(KelpBlock block) {
+ return KelpChunk.from(block.getBukkitBlock().getChunk());
+ }
+
+ /**
+ * Gets the {@link KelpLocation} of the given block. This also
+ * contains information like the current world.
+ *
+ * @param block The block you want to get the location of.
+ * @return The {@link KelpLocation} of this world.
+ */
+ @Override
+ public KelpLocation getLocation(KelpBlock block) {
+ return KelpLocation.from(block.getBukkitBlock().getLocation());
+ }
+
+ /**
+ * Gets the material of the current block.
+ *
+ * @param block The block you want to get the material of.
+ * @return The {@link KelpMaterial} the given block is made of.
+ */
+ @Override
+ public KelpMaterial getMaterial(KelpBlock block) {
+ // if block has a sub id
+ if (block.getBukkitBlock().getData() != 0) {
+ return KelpMaterial.from(block.getBukkitBlock().getType(), block.getBukkitBlock().getData());
+ }
+
+ // if the block has no special data to obey
+ return KelpMaterial.from(block.getBukkitBlock().getType());
+ }
+
+ /**
+ * Sets the material of the given block to the given {@link KelpMaterial}.
+ * So if you had an {@link KelpMaterial#AIR} block for example,
+ *
+ * @param block The block you want to change the material of.
+ * @param material The new material you want the block to have.
+ */
+ @Override
+ public void setMaterial(KelpBlock block, KelpMaterial material) {
+ MaterialContainer newMaterial = KelpMaterial.convert(material);
+ block.getBukkitBlock().setType(newMaterial.getBukkitMaterial());
+ if (newMaterial.getSubId() != 0) {
+ block.getBukkitBlock().setData(
+ (byte) newMaterial.getSubId()
+ );
+ }
+ }
+
+ /**
+ * Checks whether bone meal will have an effect on this block.
+ * This means it will check whether the block is a sapling
+ * or a plant that will grow if you right click it with bone
+ * meal. More information about that can be found
+ * in this wiki
+ * article
+ *
+ * @param block The block you want to check the application state of.
+ * @return {@code true} whether bone meal is applicable.
+ */
+ @Override
+ public boolean canApplyBoneMeal(KelpBlock block) {
+ // kelp material checks!
+ return false;
+ }
+
+ /**
+ * Simulates the application of bone meal on a block.
+ * This means if the block is a sapling for example, it
+ * might grow to a tree or grass might spawn random flowers
+ * and so on.
+ *
+ * @param kBlock The block you want to simulate the application of.
+ * @param blockFace The face of the block to apply the bone meal to.
+ */
+ @Override
+ public void applyBoneMeal(KelpBlock kBlock, BlockFace blockFace) {
+ Block block = kBlock.getBukkitBlock();
+
+ // cause those plant types to grow by 2 to 5 stages.
+ if (kBlock.getMaterial() == KelpMaterial.POTATOES
+ || kBlock.getMaterial() == KelpMaterial.CARROTS
+ || craftBlock(kBlock).getType() == Material.CROPS
+ || craftBlock(kBlock).getType() == Material.MELON_STEM
+ || craftBlock(kBlock).getType() == Material.PUMPKIN_STEM) {
+
+ // generate a random growth value between 2 and 5 which
+ // is equal to the natural bone meal growth
+ byte data = kBlock.getBukkitBlock().getData();
+ byte newData = (byte) (data + ThreadLocalRandom.current().nextInt(2, 5));
+
+ // a crop can only grow up to age 7, so set
+ // it back there if the value should exceed that number
+ if (newData > 7) {
+ newData = (byte) 7;
+ }
+
+ // update the block data
+ kBlock.getBukkitBlock().setData(newData);
+ return;
+ }
+
+ // drop item if player clicks on sunflower, lilac, peony or rose bush
+ if (kBlock.getBukkitBlock().getType() == Material.DOUBLE_PLANT) {
+ byte data = block.getData();
+ KelpMaterial material;
+
+ // if the block is the upper part of a plant, get
+ // the lower part to fetch the exact item dropped by
+ // this plant
+ if (data == 10) {
+ KelpBlock baseBlock = kBlock.getBlockBelow();
+ byte baseData = baseBlock.getBukkitBlock().getData();
+ material = getDoublePlantMaterial(baseData);
+
+ // if the block is the lower part of the plant
+ // immediately get the item material
+ } else {
+ material = getDoublePlantMaterial(data);
+ }
+
+ // if there could not be any material fetched,
+ // return.
+ if (material == KelpMaterial.AIR) {
+ return;
+ }
+
+ kBlock.getWorld().dropItemNaturally(
+ kBlock.getLocation(),
+ KelpItem.create()
+ .material(material)
+ .allowInteractions()
+ );
+ return;
+ }
+
+ // grow small fern and grass to tall gras and large fern
+ if (kBlock.getBukkitBlock().getType() == Material.LONG_GRASS
+ && kBlock.getBlockAbove().getMaterial() == KelpMaterial.AIR) {
+
+ byte data = kBlock.getBukkitBlock().getData();
+
+ // if it is small grass, build both tall grass blocks
+ if (data == 1) {
+ block.setType(Material.DOUBLE_PLANT);
+ block.setData((byte) 2);
+
+ Block above = kBlock.getBlockAbove().getBukkitBlock();
+ above.setType(Material.DOUBLE_PLANT);
+ above.setData((byte) 10);
+ }
+
+ // if it is small fern, build both large fern blocks
+ if (data == 2) {
+ block.setType(Material.DOUBLE_PLANT);
+ block.setData((byte) 3);
+
+ Block above = kBlock.getBlockAbove().getBukkitBlock();
+ above.setType(Material.DOUBLE_PLANT);
+ above.setData((byte) 10);
+ }
+ return;
+ }
+
+ // if a player clicks on a sapling there is a 45% chance
+ // that it will grow by 1 stage. When it is on the second stage,
+ // a real tree will grow
+ if (kBlock.getBukkitBlock().getType() == Material.SAPLING) {
+ byte data = kBlock.getBukkitBlock().getData();
+ boolean grow = MathUtils.perCentChance(45);
+
+ if (!grow) {
+ return;
+ }
+
+ boolean spawned = false;
+
+ // if the id is below 8, the tree is too young to
+ // grow immediately, so it is set to the next growth stage.
+
+ // if the id is higher than or equal to 8, try spawning
+ // the tree
+ switch (data) {
+ case 0: // oak
+ kBlock.getBukkitBlock().setData((byte) 8);
+ break;
+ case 1: // spruce
+ kBlock.getBukkitBlock().setData((byte) 9);
+ break;
+ case 2: // birch
+ kBlock.getBukkitBlock().setData((byte) 10);
+ break;
+ case 3: // jungle
+ kBlock.getBukkitBlock().setData((byte) 11);
+ break;
+ case 4: // acacia
+ kBlock.getBukkitBlock().setData((byte) 12);
+ break;
+ case 5: // dark oak
+ kBlock.getBukkitBlock().setData((byte) 13);
+ break;
+ case 8:
+ block.setType(Material.AIR);
+ TreeType type = MathUtils.perCentChance(10) ? TreeType.BIG_TREE : TreeType.TREE;
+ spawned = kBlock.getBukkitBlock().getWorld().generateTree(kBlock.getBukkitBlock().getLocation(), type);
+ break;
+ case 9:
+ block.setType(Material.AIR);
+ spawned = kBlock.getBukkitBlock().getWorld().generateTree(kBlock.getBukkitBlock().getLocation(), TreeType.REDWOOD);
+ break;
+ case 10:
+ block.setType(Material.AIR);
+ spawned = kBlock.getBukkitBlock().getWorld().generateTree(kBlock.getBukkitBlock().getLocation(), TreeType.BIRCH);
+ break;
+ case 11:
+ block.setType(Material.AIR);
+ spawned = kBlock.getBukkitBlock().getWorld().generateTree(kBlock.getBukkitBlock().getLocation(), TreeType.JUNGLE);
+ break;
+ case 12:
+ block.setType(Material.AIR);
+ spawned = kBlock.getBukkitBlock().getWorld().generateTree(kBlock.getBukkitBlock().getLocation(), TreeType.ACACIA);
+ break;
+ case 13:
+ block.setType(Material.AIR);
+ spawned = kBlock.getBukkitBlock().getWorld().generateTree(kBlock.getBukkitBlock().getLocation(), TreeType.DARK_OAK);
+ break;
+ }
+
+ // if there was not enough space for the tree, reset it
+ if (!spawned && data >= 8) {
+ block.setType(Material.SAPLING);
+ block.setData(data);
+ }
+
+ return;
+ }
+
+ // default case when player interacts with grass
+ // for example
+ ItemStack nmsStack = CraftItemStack.asNMSCopy(
+ new org.bukkit.inventory.ItemStack(Material.INK_SACK, 1, (short) 15)
+ );
+
+ CraftWorld world = (CraftWorld) kBlock.getWorld().getBukkitWorld();
+
+ ItemDye.a(nmsStack, world.getHandle(),
+ new BlockPosition(
+ kBlock.getX(),
+ kBlock.getY() - 1,
+ kBlock.getZ()
+ )
+ );
+
+ }
+
+ /**
+ * Converts the block data of a flower type double plant and converts
+ * it to the corresponding item which is dropped by the plant.
+ *
+ * @param data The data of the plant (either sub id of an item or
+ * data of a block)
+ * @return The kelp material of the plant.
+ */
+ private KelpMaterial getDoublePlantMaterial(byte data) {
+ KelpMaterial material = KelpMaterial.AIR;
+ switch (data) {
+ case 0:
+ material = KelpMaterial.SUNFLOWER;
+ break;
+ case 1:
+ material = KelpMaterial.LILAC;
+ break;
+ case 4:
+ material = KelpMaterial.ROSE_BUSH;
+ break;
+ case 5:
+ material = KelpMaterial.PEONY;
+ break;
+ }
+ return material;
+ }
+
+ /**
+ * Converts the given kelp block to a craftbukkit
+ * block allowing for better NMS integration.
+ *
+ * @param block The block to convert
+ * @return The final craftbukkit block
+ */
+ private CraftBlock craftBlock(KelpBlock block) {
+ return (CraftBlock) block.getBukkitBlock();
+ }
+
+}
diff --git a/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/world/VersionedChunk.java b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/world/VersionedChunk.java
new file mode 100644
index 00000000..e387bb23
--- /dev/null
+++ b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/world/VersionedChunk.java
@@ -0,0 +1,263 @@
+package de.pxav.kelp.implementation1_8.world;
+
+import com.google.common.collect.Lists;
+import de.pxav.kelp.core.application.KelpApplication;
+import de.pxav.kelp.core.inventory.material.KelpMaterial;
+import de.pxav.kelp.core.player.KelpPlayer;
+import de.pxav.kelp.core.version.Versioned;
+import de.pxav.kelp.core.world.*;
+import de.pxav.kelp.core.world.version.ChunkVersionTemplate;
+import net.minecraft.server.v1_8_R3.EntityHuman;
+import org.bukkit.craftbukkit.v1_8_R3.CraftChunk;
+import org.bukkit.entity.Player;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.util.*;
+
+/**
+ * Implements all version-dependent operations of {@link KelpChunk}.
+ *
+ * @author pxav
+ */
+@Versioned
+@Singleton
+public class VersionedChunk extends ChunkVersionTemplate {
+
+ @Inject private ForcedChunkLoader forcedChunkLoader;
+
+ /**
+ * Determines whether the given location is inside the given chunk.
+ *
+ * @param chunk The chunk you want to check.
+ * @param location The location that should be located in the chunk.
+ * @return {@code true} if the location is inside the chunk.
+ */
+ @Override
+ public boolean contains(KelpChunk chunk, KelpLocation location) {
+ KelpLocation firstPosition = chunk.getNorthEasternBlock(0).getLocation();
+ KelpLocation secondPosition = chunk.getSouthWesternBlock(256).getLocation();
+
+ double maxX = Math.max(firstPosition.getX(), secondPosition.getX());
+ double minX = Math.min(firstPosition.getX(), secondPosition.getX());
+
+ double maxY = Math.max(firstPosition.getY(), secondPosition.getY());
+ double minY = Math.min(firstPosition.getY(), secondPosition.getY());
+
+ double maxZ = Math.max(firstPosition.getZ(), secondPosition.getZ());
+ double minZ = Math.min(firstPosition.getZ(), secondPosition.getZ());
+
+ if(location.getX() <= maxX && location.getX() >= minX) {
+ if(location.getY() <= maxY && location.getY() >= minY)
+ return location.getZ() <= maxZ && location.getZ() >= minZ;
+ }
+
+ return false;
+ }
+
+ /**
+ * Gets a block at the given location inside the chunk. The passed location
+ * may not be from outside the chunk!
+ *
+ * @param chunk The chunk you want to get a block of.
+ * @param location The location where the block is located.
+ * @return The block at the given location.
+ */
+ @Override
+ public KelpBlock getBlockAt(KelpChunk chunk, KelpLocation location) {
+ return KelpBlock.from(chunk.getBukkitChunk().getBlock(
+ location.getBlockX(),
+ location.getBlockY(),
+ location.getBlockZ())
+ );
+ }
+
+ /**
+ * Gets a collection of all players that are currently inside the chunk.
+ *
+ * @param chunk The chunk you want to get the players of.
+ * @return A collection of all players that are currently inside the chunk.
+ */
+ @Override
+ public Collection getPlayers(KelpChunk chunk) {
+ Collection output = Lists.newArrayList();
+ net.minecraft.server.v1_8_R3.Chunk nmsChunk = craftChunk(chunk).getHandle();
+
+ Arrays.stream(nmsChunk.entitySlices).forEach(entitySlice -> {
+ Object[] entityArray = entitySlice.toArray();
+
+ for (Object entityObject : entityArray) {
+ if (entityObject instanceof EntityHuman) {
+ EntityHuman entityHuman = (EntityHuman) entityObject;
+ Player player = (Player) entityHuman.getBukkitEntity();
+ KelpPlayer kelpPlayer = KelpPlayer.from(player);
+ output.add(kelpPlayer);
+ }
+ }
+
+ });
+
+ return output;
+ }
+
+ /**
+ * Gets the world the given chunk is located in.
+ *
+ * @param chunk The chunk you want to get the world of.
+ * @return The world the given chunk is located in.
+ */
+ @Override
+ public KelpWorld getWorld(KelpChunk chunk) {
+ return KelpWorld.from(chunk.getBukkitChunk().getWorld());
+ }
+
+ /**
+ * 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).
+ *
+ * @param chunk The chunk you want to get the X-coordinate of.
+ * @return The X coordinate on the world's chunk grid.
+ */
+ @Override
+ public int getX(KelpChunk chunk) {
+ return chunk.getBukkitChunk().getX();
+ }
+
+ /**
+ * 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).
+ *
+ * @param chunk The chunk you want to get the Z-coordinate of.
+ * @return The Z coordinate on the world's chunk grid.
+ */
+ @Override
+ public int getZ(KelpChunk chunk) {
+ return chunk.getBukkitChunk().getZ();
+ }
+
+ /**
+ * Checks whether the given chunk is currently loaded. That
+ * means it checks whether tick operations are currently ran on
+ * this chunk.
+ *
+ * @param chunk The chunk you want to check if its loaded.
+ * @return {@code true} if the chunk is currently loaded.
+ */
+ @Override
+ public boolean isLoaded(KelpChunk chunk) {
+ return chunk.getBukkitChunk().isLoaded();
+ }
+
+ /**
+ * 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(KelpChunk, Class)}
+ * to keep the chunk loaded until you manually unload it again. But please
+ * keep in mind that this might have bad performance impact.
+ *
+ * @param chunk The chunk to be loaded. If the chunk has not been loaded before,
+ * the world will be generated at that point.
+ */
+ @Override
+ public void load(KelpChunk chunk) {
+ chunk.getBukkitChunk().load();
+ }
+
+ /**
+ * 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(KelpChunk)}.
+ *
+ * @param chunk The chunk you want to unload.
+ */
+ @Override
+ public void unload(KelpChunk chunk) {
+ chunk.getBukkitChunk().unload();
+ }
+
+ /**
+ * 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(KelpChunk, Class)}).
+ *
+ * If you call this method, {@link #unload(KelpChunk)} 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(KelpChunk, Class)}
+ * first.
+ *
+ * @param chunk The chunk you want to keep loaded.
+ * @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.
+ */
+ @Override
+ public void addForceLoadFlag(KelpChunk chunk, Class extends KelpApplication> plugin) {
+ forcedChunkLoader.forceLoadChunk(plugin, chunk);
+ }
+
+ /**
+ * 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(KelpChunk, Class)}.
+ *
+ * @param chunk The chunk to remove the force load flag of.
+ * @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.
+ */
+ @Override
+ public void removeForceLoadFlag(KelpChunk chunk, Class extends KelpApplication> plugin) {
+ forcedChunkLoader.removeForceLoadFlag(plugin, chunk);
+ }
+
+ /**
+ * Gets all {@link KelpApplication}s that are currently forcing this chunk to keep loaded.
+ *
+ * @param chunk The chunk you want to get the plugins of.
+ * @return A set of all plugin main classes that force the chunk to keep loaded.
+ */
+ @Override
+ public Set> getForceLoadFlagPlugins(KelpChunk chunk) {
+ return forcedChunkLoader.getForceLoadingPluginsOf(chunk);
+ }
+
+ /**
+ * Determines whether slime entities can spawn in the given chunk.
+ *
+ * @param chunk The chunk you want to check.
+ * @return {@code true} if slime entities can spawn in that chunk.
+ */
+ @Override
+ public boolean isSlimeChunk(KelpChunk chunk) {
+ long seed = chunk.getBukkitChunk().getWorld().getSeed();
+ int x = chunk.getX();
+ int y = chunk.getZ();
+ return (new Random(
+ seed
+ + ((long) x * x * 4987142) + (x * 5947611L)
+ + (long) y * y * 4392871L + (y * 389711L) ^ 987234911L)
+ ).nextInt(10) == 0;
+ }
+
+ private CraftChunk craftChunk(KelpChunk chunk) {
+ return (CraftChunk) chunk.getBukkitChunk();
+ }
+
+}
diff --git a/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/world/VersionedLocation.java b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/world/VersionedLocation.java
new file mode 100644
index 00000000..1b9e6fee
--- /dev/null
+++ b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/world/VersionedLocation.java
@@ -0,0 +1,8 @@
+package de.pxav.kelp.implementation1_8.world;
+
+import de.pxav.kelp.core.version.Versioned;
+import de.pxav.kelp.core.world.version.LocationVersionTemplate;
+
+@Versioned
+public class VersionedLocation extends LocationVersionTemplate {
+}
diff --git a/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/world/VersionedWorld.java b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/world/VersionedWorld.java
new file mode 100644
index 00000000..1cfce2e2
--- /dev/null
+++ b/v1_8_implementation/src/main/java/de/pxav/kelp/implementation1_8/world/VersionedWorld.java
@@ -0,0 +1,603 @@
+package de.pxav.kelp.implementation1_8.world;
+
+import com.google.common.collect.Lists;
+import de.pxav.kelp.core.entity.type.DroppedItemEntity;
+import de.pxav.kelp.core.entity.type.ItemDropType;
+import de.pxav.kelp.core.inventory.item.KelpItem;
+import de.pxav.kelp.core.player.KelpPlayer;
+import de.pxav.kelp.core.version.Versioned;
+import de.pxav.kelp.core.world.*;
+import de.pxav.kelp.core.world.util.ExplosionPower;
+import de.pxav.kelp.core.world.util.WorldType;
+import de.pxav.kelp.core.world.version.WorldVersionTemplate;
+import net.minecraft.server.v1_8_R3.EntityHuman;
+import org.bukkit.Chunk;
+import org.bukkit.Difficulty;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.craftbukkit.v1_8_R3.CraftWorld;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.entity.Player;
+
+import java.util.Collection;
+import java.util.UUID;
+
+/**
+ * This class implements all version-dependent methods
+ * of {@link KelpWorld}.
+ *
+ * @author pxav
+ */
+@Versioned
+public class VersionedWorld extends WorldVersionTemplate {
+
+ /**
+ * Gets the spawn location of the given world. The spawn location is the
+ * center of a radius in which players can spawn when initially joining a
+ * new world.
+ *
+ * @param world The world you want to get the spawn location of.
+ * @return The world's spawn location.
+ */
+ @Override
+ public KelpLocation getSpawnLocation(KelpWorld world) {
+ return KelpLocation.from(world.getBukkitWorld().getSpawnLocation());
+ }
+
+ /**
+ * Gets the chunk at the given location. A chunk is a 16x16x(256 or 320 depending on your version)
+ * cuboid region minecraft uses to generate and handle the world. More information
+ * can be found in {@link KelpChunk}.
+ *
+ * @param world The world from which you want to get the chunk of.
+ * @param location The location where the chunk is located.
+ * @return The chunk in the given world at the given location.
+ */
+ @Override
+ public KelpChunk getChunkAt(KelpWorld world, KelpLocation location) {
+ return KelpChunk.from(world.getBukkitWorld().getChunkAt(location.getBukkitLocation()));
+ }
+
+ /**
+ * Checks whether this world is able to generate structures such as temples or villages.
+ *
+ * @param world The world you want to check.
+ * @return {@code true} if the world can generate structures
+ */
+ @Override
+ public boolean canGenerateStructures(KelpWorld world) {
+ return world.getBukkitWorld().canGenerateStructures();
+ }
+
+ /**
+ * Creates an explosion at the given location.
+ *
+ * @param world The world to spawn the explosion at.
+ * @param location The center of the explosion
+ * @param power The power of the explosion. More information on explosion powers can be
+ * found in {@link ExplosionPower}
+ * @param breakBlocks Whether the explosion should be able to break blocks.
+ * @param igniteFire Whether the explosion should be able to ignite a fire within its radius.
+ */
+ @Override
+ public void createExplosion(KelpWorld world, KelpLocation location, ExplosionPower power, boolean breakBlocks, boolean igniteFire) {
+ Location bukkitLocation = location.getBukkitLocation();
+ world.getBukkitWorld().createExplosion(
+ bukkitLocation.getX(),
+ bukkitLocation.getY(),
+ bukkitLocation.getZ(),
+ power.getPower(),
+ igniteFire,
+ breakBlocks
+ );
+ }
+
+ /**
+ * Drops an item on the given world at the given location. This item will be
+ * visible for all players on the world.
+ *
+ * @param world The world to spawn the item on.
+ * @param location The location from where the item should drop
+ * @param item The item you want to drop
+ * @param dropType Whether the item should be dropped normally or naturally.
+ * More information about the difference can be found in {@link ItemDropType}
+ * @return The item entity that has been spawned at the given location.
+ */
+ @Override
+ public DroppedItemEntity dropItem(KelpWorld world, KelpLocation location, KelpItem item, ItemDropType dropType) {
+ if (dropType == ItemDropType.NORMAL) {
+ return DroppedItemEntity.from(world.getBukkitWorld()
+ .dropItem(
+ location.getBukkitLocation(),
+ item.getItemStack())
+ );
+ } else if (dropType == ItemDropType.NATURAL) {
+ return DroppedItemEntity.from(world.getBukkitWorld()
+ .dropItemNaturally(
+ location.getBukkitLocation(),
+ item.getItemStack())
+ );
+ }
+ return null;
+ }
+
+ /**
+ * Checks whether animals can spawn naturally on this world.
+ *
+ * @param world The world you want to check animal spawns for.
+ * @return {@code true} if animals can spawn by default.
+ */
+ @Override
+ public boolean animalsAllowed(KelpWorld world) {
+ return world.getBukkitWorld().getAllowAnimals();
+ }
+
+ /**
+ * Checks whether monsters such as creepers or zombies
+ * can spawn naturally on this world.
+ *
+ * @param world The world you want to check monster spawns for.
+ * @return {@code true} if monsters can spawn by default.
+ */
+ @Override
+ public boolean monstersAllowed(KelpWorld world) {
+ return world.getBukkitWorld().getAllowMonsters();
+ }
+
+ /**
+ * Gets the {@link KelpBlock} at the given location of the world.
+ *
+ * @param world The world you want to get the block of.
+ * @param location The exact location of the block you want to get.
+ * @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}
+ */
+ @Override
+ public KelpBlock getBlockAt(KelpWorld world, KelpLocation location) {
+ return KelpBlock.from(world.getBukkitWorld().getBlockAt(location.getBukkitLocation()));
+ }
+
+ /**
+ * Gets the highest block at a given location.
+ *
+ * @param world The world to check the location in.
+ * @param location The location containing the y-axis for the block check.
+ * @return The highest {@link KelpBlock} at the provided location of the world.
+ */
+ @Override
+ public KelpBlock getHighestBlockAt(KelpWorld world, KelpLocation location) {
+ return KelpBlock.from(world.getBukkitWorld().getHighestBlockAt(location.getBukkitLocation()));
+ }
+
+ /**
+ * Gets a collection of all {@link KelpChunk}s that are currently loaded in
+ * the given world. This includes forced loaded chunks as well as naturally loaded chunks.
+ *
+ * @param world The world to get the loaded chunks of.
+ * @return The collection of all chunks currently loaded in this world.
+ */
+ @Override
+ public Collection getLoadedChunks(KelpWorld world) {
+ Collection chunks = Lists.newArrayList();
+ for (Chunk loadedChunk : world.getBukkitWorld().getLoadedChunks()) {
+ chunks.add(KelpChunk.from(loadedChunk));
+ }
+ return chunks;
+ }
+
+ /**
+ * Gets the difficulty of the current world. The difficulty gives an
+ * indication of how difficult survival gameplay is. {@code Peaceful} means
+ * players cannot become hungry and monsters won't spawn, while {@code hard}
+ * says that lots of monsters will spawn and players lose saturation really
+ * fast.
+ *
+ * @param world The world to get the difficulty of.
+ * @return The current difficulty of the world.
+ */
+ @Override
+ public Difficulty getDifficulty(KelpWorld world) {
+ return world.getBukkitWorld().getDifficulty();
+ }
+
+ /**
+ * Gets the environment/world type. The world type says which dimension
+ * is currently used by the world generator. This also affects the fog effects
+ * of the world. While in overworld ({@code NORMAL}) there won't be any
+ * special background or fog, the background of an {@code END} world
+ * will be purple for example.
+ *
+ * This is similar to the {@code #getEnvironment()} method of a normal
+ * bukkit world.
+ *
+ * @param world The world to get the world type of.
+ * @return The type of the given world.
+ */
+ @Override
+ public WorldType getWorldType(KelpWorld world) {
+ World.Environment environment = world.getBukkitWorld().getEnvironment();
+ switch (environment) {
+ case NETHER:
+ return WorldType.THE_NETHER;
+ case THE_END:
+ return WorldType.THE_END;
+ case NORMAL:
+ return WorldType.NORMAL;
+ }
+ return WorldType.NORMAL;
+ }
+
+ /**
+ * Gets the humidity at a specific location in the world.
+ * The humidity is dependent on the biome at the given location.
+ *
+ * @param world The world you want to check the humidity in.
+ * @param location The location to check the humidity at.
+ * @return The humidity value at the given location in the given world.
+ */
+ @Override
+ public double getHumidityAt(KelpWorld world, KelpLocation location) {
+ return world.getBukkitWorld().getHumidity((int) location.getX(), (int) location.getZ());
+ }
+
+ /**
+ * Checks whether the spawn chunks should be kept in memory although
+ * no players are currently in the spawn area.
+ *
+ * @param world The world you want to check the spawn chunks of.
+ * @return {@code true} if the spawn chunks are kept in memory.
+ */
+ @Override
+ public boolean shouldKeepSpawnInMemory(KelpWorld world) {
+ return world.getBukkitWorld().getKeepSpawnInMemory();
+ }
+
+ /**
+ * Changes whether the spawn chunks of a world should be kept
+ * loaded/in memory although no players are currently in the spawn
+ * area. This might improve performance if you are commonly spawning
+ * new players to a world (such as in a server lobby).
+ *
+ * @param world The world you want to set the spawn chunk settings of.
+ * @param keep {@code true} if the spawn chunks should be kept loaded over the server runtime.
+ */
+ @Override
+ public void setKeepSpawnInMemory(KelpWorld world, boolean keep) {
+ world.getBukkitWorld().setKeepSpawnInMemory(keep);
+ }
+
+ /**
+ * Gets the full time of a world. This is the full absolute
+ * in-game daytime.
+ *
+ * @param world The world you want to get the time of.
+ * @return The full (absolute) time of the given world.
+ */
+ @Override
+ public long getFullTime(KelpWorld world) {
+ return world.getBukkitWorld().getFullTime();
+ }
+
+ /**
+ * Gets the full in-game time of this world from the initial world generation.
+ *
+ * @param world The world you want to get the game time of.
+ * @return The game time that has passed on the given world.
+ */
+ @Override
+ public long getGameTime(KelpWorld world) {
+ return ((CraftWorld)world.getBukkitWorld()).getHandle().getWorldData().getTime();
+ }
+
+ /**
+ * Gets the relative in-game time of the given world.
+ * This time is calculated from the world's full time:
+ * {@code #getFullTime() % 24000L}.
+ *
+ * This time cannot be negative. If a negative value is returned from
+ * the above operation, a full day will be added to this value.
+ *
+ * @param world The world you want to get the relative time of.
+ * @return The relative in-game time of the given world.
+ */
+ @Override
+ public long getTime(KelpWorld world) {
+ return world.getBukkitWorld().getTime();
+ }
+
+ /**
+ * Gets all players that are currently on the given world.
+ *
+ * @param world The world you want to get the players of.
+ * @return A collection of all players that are currently on this world.
+ */
+ @Override
+ public Collection getPlayers(KelpWorld world) {
+ Collection output = Lists.newArrayList();
+ for (EntityHuman human : craftWorld(world).getHandle().players) {
+ HumanEntity bukkitEntity = human.getBukkitEntity();
+ if (bukkitEntity instanceof Player) {
+ output.add(KelpPlayer.from((Player) bukkitEntity));
+ }
+ }
+ return output;
+ }
+
+ /**
+ * Gets the current pvp mode of the world. If players are allowed to
+ * fight each other, this will return {@code true}.
+ *
+ * @param world The world you want to check the pvp setting of.
+ * @return {@code true} if pvp is enabled on this world.
+ */
+ @Override
+ public boolean isPvPEnabled(KelpWorld world) {
+ return world.getBukkitWorld().getPVP();
+ }
+
+ /**
+ * Sets the current pvp mode of the world. If players should be allowed
+ * to fight each other, set this to {@code true}.
+ *
+ * @param world The world you want to set the pvp setting of.
+ * @param pvp {@code true} if players should be able to fight each other.
+ */
+ @Override
+ public void setPVP(KelpWorld world, boolean pvp) {
+ world.getBukkitWorld().setPVP(pvp);
+ }
+
+ /**
+ * Gets the seed of the world generator of this world. A seed is a constant value
+ * used by the {@code Perlin noise} world generation algorithm to randomize
+ * biomes and structures. Seeds are usually compatible across different versions, but
+ * might not output the same result in differing versions due to new structures and
+ * biomes that have been added over time.
+ *
+ * @param world The world you want to get the seed of.
+ * @return The world's seed.
+ */
+ @Override
+ public long getSeed(KelpWorld world) {
+ return world.getBukkitWorld().getSeed();
+ }
+
+ /**
+ * Gets the sea level of this world.
+ *
+ * @param world The world you want to get the sea level of.
+ * @return The world's sea level.
+ */
+ @Override
+ public int getSeaLevel(KelpWorld world) {
+ return world.getBukkitWorld().getSeaLevel();
+ }
+
+ /**
+ * Gets the temperature of a world at a specific location.
+ * Temperatures are depending on the biome and exact location (height specifically).
+ * A temperature indicates whether it can snow or rain at a specific location
+ * or whether there is any precipitation at all. The desert biome for example
+ * has a relatively high temperature of {@code 2.0d}, while Snowy Beach has {@code 0.05}
+ * for example.
+ *
+ * @param world The world you want to get the temperature in.
+ * @param location The location you want to get the temperature at.
+ * @return The exact temperature at the given location in the current world.
+ */
+ @Override
+ public double getTemperatureAt(KelpWorld world, KelpLocation location) {
+ return world.getBukkitWorld().getTemperature((int) location.getX(), (int) location.getZ());
+ }
+
+ /**
+ * Gets the duration of the current thunder in the world in ticks.
+ *
+ * @param world The world you want to get the thunder duration of.
+ * @return The thunder duration in ticks.
+ */
+ @Override
+ public int getThunderDuration(KelpWorld world) {
+ return world.getBukkitWorld().getThunderDuration();
+ }
+
+ /**
+ * Gets the unique id of this world.
+ *
+ * @param world This world's unique id.
+ * @return The world's {@link UUID}.
+ */
+ @Override
+ public UUID getUUID(KelpWorld world) {
+ return world.getBukkitWorld().getUID();
+ }
+
+ /**
+ * Gets the duration of the current weather state in ticks.
+ * This counts for all weather types (including clear weather)
+ *
+ * @param world The world to get the weather duration of.
+ * @return The current weather duration in ticks.
+ */
+ @Override
+ public int getWeatherDuration(KelpWorld world) {
+ return world.getBukkitWorld().getWeatherDuration();
+ }
+
+ /**
+ * Checks whether there is a storm in the given world.
+ *
+ * @param world The world you want to check.
+ * @return {@code true} whether there is currently a storm in the given world.
+ */
+ @Override
+ public boolean hasStorm(KelpWorld world) {
+ return world.getBukkitWorld().hasStorm();
+ }
+
+ /**
+ * Checks if auto-save is enabled in this world.
+ * This would save the world to the server folder in a regular interval.
+ *
+ * @param world The world you want to get the auto-save mode of.
+ * @return {@code true} if auto-save is enabled.
+ */
+ @Override
+ public boolean hasAutoSave(KelpWorld world) {
+ return world.getBukkitWorld().isAutoSave();
+ }
+
+ /**
+ * Checks whether there is clear weather in the given world.
+ * If there is clear whether, methods such as {@link #hasStorm(KelpWorld)}
+ * are automatically {@code false}.
+ *
+ * @param world The world you want to check the weather of.
+ * @return {@code true} whether there is clear weather, {@code false} if there is any thunder or precipitation.
+ */
+ @Override
+ public boolean isWeatherClear(KelpWorld world) {
+ return !(world.getBukkitWorld().hasStorm() || world.getBukkitWorld().isThundering());
+ }
+
+ /**
+ * Checks if there currently is a thunderstorm in the given world.
+ *
+ * @param world The world you want to check the weather of.
+ * @return {@code true} weather there currently is a thunder in the world.
+ */
+ @Override
+ public boolean isThundering(KelpWorld world) {
+ return world.getBukkitWorld().isThundering();
+ }
+
+ /**
+ * Sets the difficulty of the current world. The difficulty gives an
+ * indication of how difficult survival gameplay is. {@code Peaceful} means
+ * players cannot become hungry and monsters won't spawn, while {@code hard}
+ * says that lots of monsters will spawn and players lose saturation really
+ * fast.
+ *
+ * @param world The world you want to set the difficulty of.
+ * @param difficulty The difficulty you want to set.
+ */
+ @Override
+ public void setDifficulty(KelpWorld world, Difficulty difficulty) {
+ world.getBukkitWorld().setDifficulty(difficulty);
+ }
+
+ /**
+ * Sets the full time of the current world. The full time is the absolute in-game
+ * daytime of a world. With this method you can also rewind the time, which could
+ * eventually break redstone clocks or other scheduled events, which is why
+ * you might want to use {@link #setTime(KelpWorld, long)} depending on your use-case.
+ *
+ * @param world The world to set the full time of.
+ * @param fullTime The full time to set for this world.
+ */
+ @Override
+ public void setFullTime(KelpWorld world, long fullTime) {
+ world.getBukkitWorld().setFullTime(fullTime);
+ }
+
+ /**
+ * Sets the spawn location of this world. This is the location, where
+ * players will spawn when they first join the world.
+ *
+ * By default, there is a 30x30 radius around the location, where players
+ * spawn randomly.
+ *
+ * @param world The world you want to set the spawn location of.
+ * @param location The spawn location you want to set.
+ */
+ @Override
+ public void setSpawnLocation(KelpWorld world, KelpLocation location) {
+ world.getBukkitWorld().setSpawnLocation(
+ location.getBukkitLocation().getBlockX(),
+ location.getBukkitLocation().getBlockY(),
+ location.getBukkitLocation().getBlockZ()
+ );
+ }
+
+ /**
+ * Sets the storm state of a world. When set to {@code true} a new storm
+ * will start or the current storm will continue.
+ *
+ * @param world The world you want to set the storm state of.
+ * @param storm {@code true} if you want to start a storm. {@code false} if you want to end the storm.
+ */
+ @Override
+ public void setStorm(KelpWorld world, boolean storm) {
+ world.getBukkitWorld().setStorm(storm);
+ }
+
+ /**
+ * Sets the thunder state of a world. When set to {@code true} a new thunder
+ * will start or the current storm will continue.
+ *
+ * @param world The world you want to set the thunder state of.
+ * @param thunder {@code true} if you want to start a thunderstorm. {@code false} if you want to end the thunder.
+ */
+ @Override
+ public void setThundering(KelpWorld world, boolean thunder) {
+ world.getBukkitWorld().setThundering(thunder);
+ }
+
+ /**
+ * Sets the duration of the current thunderstorm in the world in ticks.
+ *
+ * @param world The world you want to set the thunder duration of.
+ * @param duration The duration in ticks the thunder should take.
+ */
+ @Override
+ public void setThunderDuration(KelpWorld world, int duration) {
+ world.getBukkitWorld().setThunderDuration(duration);
+ }
+
+ /**
+ * Sets the relative in-game time of the given world.
+ * If you set a value that is smaller than the current time value,
+ * the server will automatically skip to the next day. Rewind
+ * is not possible with this method for safety reasons. If you need
+ * to do it though, use {@link #setFullTime(KelpWorld, long)}.
+ *
+ * @param world The world you want to set the time of.
+ * @param time The relative in-game time to set.
+ */
+ @Override
+ public void setTime(KelpWorld world, long time) {
+ world.getBukkitWorld().setTime(time);
+ }
+
+ /**
+ * Spawns a lightning in the given world at the given location. This
+ * lightning will be visible for all players who are in the world and in range.
+ *
+ * @param world The world you want to spawn the lightning on.
+ * @param location The exact location where the lightning should strike.
+ * @param effect Whether the lightning should just be an effect or be a real lightning.
+ * A lightning effect does not cause fire, nor damage, nor pigs are transformed to pigmen, etc.
+ * If you set this to {@code false} a normal lightning will spawn and cause damage to players.
+ */
+ @Override
+ public void strikeLightning(KelpWorld world, KelpLocation location, boolean effect) {
+ if (effect) {
+ world.getBukkitWorld().strikeLightningEffect(location.getBukkitLocation());
+ return;
+ }
+ world.getBukkitWorld().strikeLightning(location.getBukkitLocation());
+ }
+
+ /**
+ * Converts a given {@link KelpWorld} to a craft bukkit world.
+ * This allows easier NMS integration into the implementation.
+ *
+ * @param world The world to convert.
+ * @return The final craft world
+ */
+ private CraftWorld craftWorld(KelpWorld world) {
+ return ((CraftWorld) world.getBukkitWorld());
+ }
+
+}
diff --git a/v_1_14_implementation/pom.xml b/v_1_14_implementation/pom.xml
index 6d7cc8f1..cf257cab 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.2.0
+ 0.3.0
4.0.0
@@ -57,7 +57,7 @@
com.github.pxav.kelp
core
- 0.2.0
+ 0.3.0
org.spigotmc
diff --git a/v_1_14_implementation/src/main/java/de/pxav/kelp/implementation1_14/inventory/VersionedItem.java b/v_1_14_implementation/src/main/java/de/pxav/kelp/implementation1_14/inventory/VersionedItem.java
index f696cb00..1caf2868 100644
--- a/v_1_14_implementation/src/main/java/de/pxav/kelp/implementation1_14/inventory/VersionedItem.java
+++ b/v_1_14_implementation/src/main/java/de/pxav/kelp/implementation1_14/inventory/VersionedItem.java
@@ -7,6 +7,8 @@
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
+import java.util.List;
+
/**
* A class description goes here.
*
@@ -34,7 +36,7 @@ public ItemStack setDisplayName(ItemStack itemStack, String displayName) {
}
@Override
- public ItemStack setLore(ItemStack itemStack, String itemLore) {
+ public ItemStack setLore(ItemStack itemStack, List itemLore) {
return null;
}