diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index 3fd2793..35cc0b4 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -26,5 +26,15 @@
       <option name="name" value="sonatype" />
       <option name="url" value="https://oss.sonatype.org/content/groups/public/" />
     </remote-repository>
+    <remote-repository>
+      <option name="id" value="maven2" />
+      <option name="name" value="maven2" />
+      <option name="url" value="https://hub.spigotmc.org/nexus/content/groups/public/" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="maven" />
+      <option name="name" value="maven" />
+      <option name="url" value="https://repo.aikar.co/content/groups/aikar/" />
+    </remote-repository>
   </component>
 </project>
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 242f4dc..02d0572 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -4,6 +4,7 @@
     <modules>
       <module fileurl="file://$PROJECT_DIR$/.idea/modules/PixelBlocks.main.iml" filepath="$PROJECT_DIR$/.idea/modules/PixelBlocks.main.iml" />
       <module fileurl="file://$PROJECT_DIR$/.idea/modules/eu.mhsl.minecraft.PixelBlocks.main.iml" filepath="$PROJECT_DIR$/.idea/modules/eu.mhsl.minecraft.PixelBlocks.main.iml" />
+      <module fileurl="file://$PROJECT_DIR$/.idea/modules/minecraft.PixelBlocks.main.iml" filepath="$PROJECT_DIR$/.idea/modules/minecraft.PixelBlocks.main.iml" />
       <module fileurl="file://$PROJECT_DIR$/.idea/modules/projects.PixelBlocks.main.iml" filepath="$PROJECT_DIR$/.idea/modules/projects.PixelBlocks.main.iml" />
     </modules>
   </component>
diff --git a/.idea/modules/minecraft.PixelBlocks.main.iml b/.idea/modules/minecraft.PixelBlocks.main.iml
new file mode 100644
index 0000000..3cf00db
--- /dev/null
+++ b/.idea/modules/minecraft.PixelBlocks.main.iml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module version="4">
+  <component name="FacetManager">
+    <facet type="minecraft" name="Minecraft">
+      <configuration>
+        <autoDetectTypes>
+          <platformType>PAPER</platformType>
+        </autoDetectTypes>
+        <projectReimportVersion>1</projectReimportVersion>
+      </configuration>
+    </facet>
+  </component>
+</module>
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index ecb55f6..beb9a10 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,6 @@
 plugins {
     id 'java'
+    id 'com.gradleup.shadow' version '8.3.1'
 }
 
 group = 'eu.mhsl.minecraft'
@@ -15,10 +16,14 @@ repositories {
         name = "sonatype"
         url = "https://oss.sonatype.org/content/groups/public/"
     }
+    maven {
+        url = "https://repo.aikar.co/content/groups/aikar/"
+    }
 }
 
 dependencies {
     compileOnly "io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT"
+    implementation "co.aikar:taskchain-bukkit:3.7.2"
 }
 
 def targetJavaVersion = 21
@@ -31,14 +36,6 @@ java {
     }
 }
 
-tasks.withType(JavaCompile).configureEach {
-    options.encoding = 'UTF-8'
-
-    if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) {
-        options.release.set(targetJavaVersion)
-    }
-}
-
 processResources {
     def props = [version: version]
     inputs.properties props
@@ -49,8 +46,12 @@ processResources {
 }
 
 tasks.register('copyJarToTestServer', Exec) {
-    dependsOn jar
-    mustRunAfter jar
-
-    commandLine 'cp', 'build/libs/PixelBlocks-1.0-SNAPSHOT.jar', '/home/elias/Dokumente/mcTestServer/plugins/pixelblocks.jar'
+    commandLine 'cp', 'build/libs/PixelBlocks-1.0-SNAPSHOT-all.jar', '/home/elias/Dokumente/mcTestServer/plugins/pixelblocks.jar'
 }
+
+shadowJar {
+    relocate 'co.aikar.taskchain', 'eu.mhsl.minecraft.pixelblocks.taskchain'
+}
+
+jar.dependsOn shadowJar
+copyJarToTestServer.dependsOn jar
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/PixelBlocksPlugin.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/Main.java
similarity index 52%
rename from src/main/java/eu/mhsl/minecraft/pixelblocks/PixelBlocksPlugin.java
rename to src/main/java/eu/mhsl/minecraft/pixelblocks/Main.java
index ef0c9bb..f60f0d7 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/PixelBlocksPlugin.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/Main.java
@@ -1,5 +1,8 @@
 package eu.mhsl.minecraft.pixelblocks;
 
+import co.aikar.taskchain.BukkitTaskChainFactory;
+import co.aikar.taskchain.TaskChain;
+import co.aikar.taskchain.TaskChainFactory;
 import eu.mhsl.minecraft.pixelblocks.commands.CreatePixelBlockCommand;
 import eu.mhsl.minecraft.pixelblocks.commands.ExitWorldCommand;
 import eu.mhsl.minecraft.pixelblocks.listeners.*;
@@ -14,36 +17,45 @@ import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
+import java.util.logging.Logger;
 
-public final class PixelBlocksPlugin extends JavaPlugin {
-    public static PixelBlocksPlugin plugin;
-    public static PixelBlockConfiguration configuration;
-    public static PixelBlockDatabase database;
+public final class Main extends JavaPlugin {
+    private static Main plugin;
+    private static PixelBlockConfiguration configuration;
+    private static PixelBlockDatabase database;
 
-    public static List<PixelBlock> pixelBlocks = new ArrayList<>();
+    private static TaskChainFactory taskFactory;
+
+    public final static List<PixelBlock> pixelBlocks = new ArrayList<>();
+
+    public static <T> TaskChain<T> sharedChain(String name) {
+        return taskFactory.newSharedChain(name);
+    }
 
     @Override
     public void onLoad() {
-        PixelBlocksPlugin.plugin = this;
+        Main.plugin = this;
 
         FileConfiguration config = this.getConfig();
         PixelBlockConfiguration.setDefaults(config);
         this.saveConfig();
 
-        PixelBlocksPlugin.configuration = new PixelBlockConfiguration(
-                config.getInt(PixelBlockConfiguration.Keys.PixelsPerBlock.getKey()),
-                config.getDouble(PixelBlockConfiguration.Keys.HitboxOffset.getKey()),
-                config.getBoolean(PixelBlockConfiguration.Keys.OnlyBreakableByOwners.getKey()),
-                config.getBoolean(PixelBlockConfiguration.Keys.OnlyEditableByOwners.getKey())
+        Main.configuration = new PixelBlockConfiguration(
+            config.getInt(PixelBlockConfiguration.Keys.PixelsPerBlock.getKey()),
+            config.getBoolean(PixelBlockConfiguration.Keys.OnlyBreakableByOwners.getKey()),
+            config.getBoolean(PixelBlockConfiguration.Keys.OnlyEditableByOwners.getKey())
         );
 
         File databaseFile = new File(plugin.getDataFolder(), "blocks.db");
-        PixelBlocksPlugin.database = new PixelBlockDatabase("jdbc:sqlite:" + databaseFile);
+        Main.database = new PixelBlockDatabase("jdbc:sqlite:" + databaseFile);
     }
 
     @Override
     public void onEnable() {
+        Main.taskFactory = BukkitTaskChainFactory.create(this);
+        getLogger().info("Start constructing blocks from Database...");
         database.loadPixelBlocks();
+        getLogger().info("Construction done!");
 
         Listener[] listeners = {
             new EnterPixelBlockListener(),
@@ -52,7 +64,6 @@ public final class PixelBlocksPlugin extends JavaPlugin {
             new PlacePixelBlockListener(),
             new PreventInventorysListener(),
             new ExitPixelWorldListener(),
-            new CraftPixelBlockListener(),
             new PreventIllegalBlocksListener(),
             new BreakPixelBlockListener(),
             new PlacePixelListener(),
@@ -60,10 +71,12 @@ public final class PixelBlocksPlugin extends JavaPlugin {
             new PreventLiquidsFlowListener(),
             new PreventPistonsListener(),
             new PreventEntityPlacementListener(),
-            new DiscoverRecipesListener()
+            new DiscoverRecipesListener(),
+            new QuitWhileInPixelBlockListener(),
+            new PreventGrowthListener()
         };
 
-        for (Listener listener : listeners) {
+        for(Listener listener : listeners) {
             getServer().getPluginManager().registerEvents(listener, plugin);
         }
 
@@ -75,10 +88,27 @@ public final class PixelBlocksPlugin extends JavaPlugin {
 
     @Override
     public void onDisable() {
+        Bukkit.getOnlinePlayers().forEach(QuitWhileInPixelBlockListener::kickPlayerOutOfWorld);
         try {
             database.close();
-        } catch (SQLException e) {
+        } catch(SQLException e) {
             throw new RuntimeException("Failed disabling", e);
         }
     }
+
+    public static Main plugin() {
+        return plugin;
+    }
+
+    public static Logger logger() {
+        return plugin.getLogger();
+    }
+
+    public static PixelBlockDatabase database() {
+        return database;
+    }
+
+    public static PixelBlockConfiguration configuration() {
+        return configuration;
+    }
 }
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/PixelBlockConfiguration.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/PixelBlockConfiguration.java
index 803e288..383fa0e 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/PixelBlockConfiguration.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/PixelBlockConfiguration.java
@@ -4,14 +4,12 @@ import org.bukkit.configuration.file.FileConfiguration;
 import org.jetbrains.annotations.NotNull;
 
 public record PixelBlockConfiguration(
-        int pixelsPerBlock,
-        double hitboxOffset,
-        boolean onlyBreakableByOwner,
-        boolean onlyEditableByOwner
+    int pixelsPerBlock,
+    boolean onlyBreakableByOwner,
+    boolean onlyEditableByOwner
 ) {
     public static void setDefaults(FileConfiguration config) {
         config.addDefault(Keys.PixelsPerBlock.key, 16);
-        config.addDefault(Keys.HitboxOffset.key, 0.005);
         config.addDefault(Keys.OnlyBreakableByOwners.key, false);
         config.addDefault(Keys.OnlyEditableByOwners.key, true);
         config.options().copyDefaults(true);
@@ -19,11 +17,11 @@ public record PixelBlockConfiguration(
 
     public enum Keys {
         PixelsPerBlock("pixelsPerBlock"),
-        HitboxOffset("hitboxOffset"),
         OnlyBreakableByOwners("onlyBreakableByOwners"),
         OnlyEditableByOwners("onlyEditableByOwners");
 
         private final String key;
+
         Keys(@NotNull String key) {
             this.key = key;
         }
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/PixelBlockDatabase.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/PixelBlockDatabase.java
index 19128e3..9ba530f 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/PixelBlockDatabase.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/PixelBlockDatabase.java
@@ -1,13 +1,11 @@
 package eu.mhsl.minecraft.pixelblocks;
 
-import eu.mhsl.minecraft.pixelblocks.utils.Direction;
 import eu.mhsl.minecraft.pixelblocks.pixelblock.PixelBlock;
+import eu.mhsl.minecraft.pixelblocks.utils.Direction;
 import org.bukkit.Bukkit;
 import org.bukkit.Location;
 
 import java.sql.*;
-import java.util.ArrayList;
-import java.util.List;
 import java.util.UUID;
 
 public class PixelBlockDatabase {
@@ -15,9 +13,7 @@ public class PixelBlockDatabase {
 
     private final PreparedStatement getAllPixelBlocks;
     private final PreparedStatement deletePixelBlock;
-    private final PreparedStatement insertNewPixelBlock;
-    private final PreparedStatement updateExistingPixelBlock;
-
+    private final PreparedStatement insertOrReplacePixelBlock;
 
     public PixelBlockDatabase(String url) {
         try {
@@ -25,134 +21,81 @@ public class PixelBlockDatabase {
             this.db = DriverManager.getConnection(url);
 
             this.db.createStatement().execute(
-                    "CREATE TABLE IF NOT EXISTS pixelblocks (" +
-                            "uuid CHAR(36) PRIMARY KEY, " +
-                            "owner CHAR(36), " +
-                            "locationWorldName CHAR(36), " +
-                            "locationX DOUBLE, " +
-                            "locationY DOUBLE, " +
-                            "locationZ DOUBLE, " +
-                            "entryLocationWorldName CHAR(36), " +
-                            "entryLocationX DOUBLE, " +
-                            "entryLocationY DOUBLE, " +
-                            "entryLocationZ DOUBLE, " +
-                            "direction CHAR(36)" +
-                            ")"
+                "CREATE TABLE IF NOT EXISTS pixelblocks (" +
+                    "uuid CHAR(36) PRIMARY KEY, " +
+                    "owner CHAR(36), " +
+                    "locationWorldName CHAR(36), " +
+                    "locationX DOUBLE, " +
+                    "locationY DOUBLE, " +
+                    "locationZ DOUBLE, " +
+                    "entryLocationWorldName CHAR(36), " +
+                    "entryLocationX DOUBLE, " +
+                    "entryLocationY DOUBLE, " +
+                    "entryLocationZ DOUBLE, " +
+                    "direction CHAR(36)" +
+                    ")"
             );
 
             this.deletePixelBlock = this.db.prepareStatement("DELETE FROM pixelblocks WHERE uuid = ?");
             this.getAllPixelBlocks = this.db.prepareStatement("SELECT * FROM pixelblocks");
-            this.insertNewPixelBlock = this.db.prepareStatement(
-            "INSERT INTO pixelblocks(uuid, owner, " +
-                "locationWorldName, locationX, locationY, locationZ, " +
-                "entryLocationWorldName, entryLocationX, entryLocationY, entryLocationZ, direction) " +
-                "VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
+            this.insertOrReplacePixelBlock = this.db.prepareStatement(
+                "INSERT OR REPLACE INTO pixelblocks(uuid, owner, " +
+                    "locationWorldName, locationX, locationY, locationZ, " +
+                    "entryLocationWorldName, entryLocationX, entryLocationY, entryLocationZ, direction) " +
+                    "VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"
             );
-            this.updateExistingPixelBlock = this.db.prepareStatement(
-            "UPDATE pixelblocks " +
-                "SET owner=?, locationWorldName=?, locationX=?, locationY=?, locationZ=?," +
-                "entryLocationWorldName=?, entryLocationX=?, entryLocationY=?, entryLocationZ=?, direction=? " +
-                "WHERE uuid=?;"
-            );
-        } catch (SQLException | RuntimeException | ClassNotFoundException e) {
-            throw new RuntimeException("Failed to load Database", e);
+
+        } catch(SQLException | RuntimeException | ClassNotFoundException e) {
+            throw new RuntimeException("Error while initializing database", e);
         }
     }
 
     public void close() throws SQLException {
         deletePixelBlock.close();
         getAllPixelBlocks.close();
-        insertNewPixelBlock.close();
-        updateExistingPixelBlock.close();
+        insertOrReplacePixelBlock.close();
         db.close();
     }
 
     public void deletePixelBlock(PixelBlock pixelBlock) {
-        Bukkit.getScheduler().runTaskAsynchronously(PixelBlocksPlugin.plugin, () -> {
+        Bukkit.getScheduler().runTaskAsynchronously(Main.plugin(), () -> {
             try {
-                this.deletePixelBlock.setString(1, pixelBlock.blockUUID.toString());
+                this.deletePixelBlock.setString(1, pixelBlock.getBlockUUID().toString());
                 this.deletePixelBlock.executeUpdate();
-                PixelBlocksPlugin.plugin.getLogger().info("DB: Deleted PixelBlock: " + pixelBlock.blockUUID);
-            } catch (SQLException e) {
-                throw new RuntimeException("Failed to delete PixelBlock", e);
+            } catch(SQLException e) {
+                throw new RuntimeException("Failed to delete PixelBlock from the database", e);
             }
         });
     }
 
     public void savePixelBlock(PixelBlock pixelBlock) {
-        Bukkit.getScheduler().runTask(PixelBlocksPlugin.plugin, () -> {
-            List<UUID> storedPixelBlocks = new ArrayList<>();
-
+        Bukkit.getScheduler().runTask(Main.plugin(), () -> {
             try {
-                ResultSet pixelBlocksResult = this.getAllPixelBlocks.executeQuery();
-                while (pixelBlocksResult.next()) {
-                    storedPixelBlocks.add(UUID.fromString(pixelBlocksResult.getString("uuid")));
-                }
-            } catch (SQLException e) {
-                throw new RuntimeException("Failed to fetch PixelBlock list", e);
-            }
+                this.insertOrReplacePixelBlock.setString(1, pixelBlock.getBlockUUID().toString());
+                this.insertOrReplacePixelBlock.setString(2, pixelBlock.getOwnerUUID().toString());
 
-            if(!storedPixelBlocks.contains(pixelBlock.blockUUID)) {
-                // create new entry if it does not exist
-                try {
-                    this.insertNewPixelBlock.setString(1, pixelBlock.blockUUID.toString());
-                    this.insertNewPixelBlock.setString(2, pixelBlock.ownerUUID.toString());
+                this.insertOrReplacePixelBlock.setString(3, pixelBlock.getPixelBlockLocation().getWorld().getName());
+                this.insertOrReplacePixelBlock.setDouble(4, pixelBlock.getPixelBlockLocation().getX());
+                this.insertOrReplacePixelBlock.setDouble(5, pixelBlock.getPixelBlockLocation().getY());
+                this.insertOrReplacePixelBlock.setDouble(6, pixelBlock.getPixelBlockLocation().getZ());
 
-                    this.insertNewPixelBlock.setString(3, pixelBlock.pixelBlockLocation.getWorld().getName());
-                    this.insertNewPixelBlock.setDouble(4, pixelBlock.pixelBlockLocation.getX());
-                    this.insertNewPixelBlock.setDouble(5, pixelBlock.pixelBlockLocation.getY());
-                    this.insertNewPixelBlock.setDouble(6, pixelBlock.pixelBlockLocation.getZ());
-
-                    if(pixelBlock.lastEntryLocation != null) {
-                        this.insertNewPixelBlock.setString(7, pixelBlock.lastEntryLocation.getWorld().getName());
-                        this.insertNewPixelBlock.setDouble(8, pixelBlock.lastEntryLocation.getX());
-                        this.insertNewPixelBlock.setDouble(9, pixelBlock.lastEntryLocation.getY());
-                        this.insertNewPixelBlock.setDouble(10, pixelBlock.lastEntryLocation.getZ());
-                    } else {
-                        this.insertNewPixelBlock.setString(7, Bukkit.getWorlds().getFirst().getName());
-                        this.insertNewPixelBlock.setDouble(8, Bukkit.getWorlds().getFirst().getSpawnLocation().getX());
-                        this.insertNewPixelBlock.setDouble(9, Bukkit.getWorlds().getFirst().getSpawnLocation().getY());
-                        this.insertNewPixelBlock.setDouble(10, Bukkit.getWorlds().getFirst().getSpawnLocation().getZ());
-                    }
-
-                    this.insertNewPixelBlock.setString(11, pixelBlock.facingDirection.toString());
-
-                    this.insertNewPixelBlock.executeUpdate();
-                    PixelBlocksPlugin.plugin.getLogger().info("DB: Created PixelBlock: " + pixelBlock.blockUUID);
-                } catch (SQLException e) {
-                    throw new RuntimeException("Failed to save PixelBlock", e);
+                if(pixelBlock.hasLastEntryLocation()) {
+                    this.insertOrReplacePixelBlock.setString(7, pixelBlock.getLastEntryLocation().getWorld().getName());
+                    this.insertOrReplacePixelBlock.setDouble(8, pixelBlock.getLastEntryLocation().getX());
+                    this.insertOrReplacePixelBlock.setDouble(9, pixelBlock.getLastEntryLocation().getY());
+                    this.insertOrReplacePixelBlock.setDouble(10, pixelBlock.getLastEntryLocation().getZ());
+                } else {
+                    this.insertOrReplacePixelBlock.setString(7, pixelBlock.getPixelBlockLocation().getWorld().getName());
+                    this.insertOrReplacePixelBlock.setDouble(8, pixelBlock.getPixelBlockLocation().getX());
+                    this.insertOrReplacePixelBlock.setDouble(9, pixelBlock.getPixelBlockLocation().getY());
+                    this.insertOrReplacePixelBlock.setDouble(10, pixelBlock.getPixelBlockLocation().getZ());
                 }
 
-            } else {
-                // update existing entry
-                try {
-                    this.updateExistingPixelBlock.setString(1, pixelBlock.ownerUUID.toString());
+                this.insertOrReplacePixelBlock.setString(11, pixelBlock.getFacingDirection().toString());
 
-                    this.updateExistingPixelBlock.setString(2, pixelBlock.pixelBlockLocation.getWorld().getName());
-                    this.updateExistingPixelBlock.setDouble(3, pixelBlock.pixelBlockLocation.getX());
-                    this.updateExistingPixelBlock.setDouble(4, pixelBlock.pixelBlockLocation.getY());
-                    this.updateExistingPixelBlock.setDouble(5, pixelBlock.pixelBlockLocation.getZ());
-
-                    if(pixelBlock.lastEntryLocation != null) {
-                        this.updateExistingPixelBlock.setString(6, pixelBlock.lastEntryLocation.getWorld().getName());
-                        this.updateExistingPixelBlock.setDouble(7, pixelBlock.lastEntryLocation.getX());
-                        this.updateExistingPixelBlock.setDouble(8, pixelBlock.lastEntryLocation.getY());
-                        this.updateExistingPixelBlock.setDouble(9, pixelBlock.lastEntryLocation.getZ());
-                    } else {
-                        this.updateExistingPixelBlock.setString(6, Bukkit.getWorlds().getFirst().getName());
-                        this.updateExistingPixelBlock.setDouble(7, Bukkit.getWorlds().getFirst().getSpawnLocation().getX());
-                        this.updateExistingPixelBlock.setDouble(8, Bukkit.getWorlds().getFirst().getSpawnLocation().getY());
-                        this.updateExistingPixelBlock.setDouble(9, Bukkit.getWorlds().getFirst().getSpawnLocation().getZ());
-                    }
-
-                    this.updateExistingPixelBlock.setString(10, pixelBlock.blockUUID.toString());
-                    this.updateExistingPixelBlock.setString(11, pixelBlock.facingDirection.toString());
-
-                    this.updateExistingPixelBlock.executeUpdate();
-                    PixelBlocksPlugin.plugin.getLogger().info("DB: Updated PixelBlock: " + pixelBlock.blockUUID);
-                } catch (SQLException e) {
-                    throw new RuntimeException("Failed updating PixelBlock", e);
-                }
+                this.insertOrReplacePixelBlock.executeUpdate();
+            } catch(SQLException e) {
+                throw new RuntimeException("Failed to create or update PixelBlock in the database", e);
             }
         });
     }
@@ -161,7 +104,7 @@ public class PixelBlockDatabase {
         try {
             ResultSet allPixelBlocks = this.getAllPixelBlocks.executeQuery();
 
-            while (allPixelBlocks.next()) {
+            while(allPixelBlocks.next()) {
                 Location blockLocation = new Location(
                     Bukkit.getWorld(allPixelBlocks.getString("locationWorldName")),
                     allPixelBlocks.getDouble("locationX"),
@@ -176,17 +119,16 @@ public class PixelBlockDatabase {
                     allPixelBlocks.getDouble("entryLocationZ")
                 );
 
-                PixelBlock block = new PixelBlock(
-                    blockLocation,
-                    UUID.fromString(allPixelBlocks.getString("owner")),
+                Main.pixelBlocks.add(PixelBlock.fromExisting(
                     UUID.fromString(allPixelBlocks.getString("uuid")),
-                    Direction.valueOf(allPixelBlocks.getString("direction"))
-                );
-                block.setLastEntryLocation(entryLocation);
-                PixelBlocksPlugin.plugin.getLogger().info("DB: Loaded PixelBlock: " + block.blockUUID);
+                    UUID.fromString(allPixelBlocks.getString("owner")),
+                    blockLocation,
+                    Direction.valueOf(allPixelBlocks.getString("direction")),
+                    entryLocation
+                ));
             }
-        } catch (SQLException e) {
-            throw new RuntimeException("Failed loading PixelBlocks", e);
+        } catch(SQLException e) {
+            throw new RuntimeException("Failed loading PixelBlocks from the database", e);
         }
     }
 }
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/PixelBlockItem.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/PixelBlockItem.java
index 7f52d37..495de65 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/PixelBlockItem.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/PixelBlockItem.java
@@ -11,30 +11,53 @@ import org.bukkit.inventory.ItemStack;
 import org.bukkit.inventory.Recipe;
 import org.bukkit.inventory.ShapedRecipe;
 import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.persistence.PersistentDataType;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.UUID;
 
 public class PixelBlockItem {
-    public static UUID unusedBlockID = UUID.fromString("98fdf0ae-c3ab-4ef7-ae25-efd518d600de");
+    public static final NamespacedKey recipeKey = new NamespacedKey(Main.plugin(), "pixelblock");
+    public static NamespacedKey idProperty = new NamespacedKey(Main.plugin(), "id");
+    public static NamespacedKey ownerProperty = new NamespacedKey(Main.plugin(), "owner");
+
     public static final String itemTexture = "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzE5NGU5ZTc3NTdkMDZkNmY1ZTViZTg0NTQ4YTdjYjUyMTczZDY4Y2NmODAyZDIxMTI3NWQzMWNkYmEwYTA2ZSJ9fX0=";
-    public static final NamespacedKey recipeKey = new NamespacedKey(PixelBlocksPlugin.plugin, "pixelblock");
+
+    public record BlockInfo(UUID id, @Nullable UUID owner) {
+        public boolean hasOwner() {
+            return owner != null;
+        }
+    }
+
+    public static @Nullable BlockInfo getBlockInfo(ItemStack item) {
+        PersistentDataContainer container = item.getItemMeta().getPersistentDataContainer();
+        if(!container.has(idProperty)) return null;
+        UUID blockId = UUID.fromString(Objects.requireNonNull(container.get(idProperty, PersistentDataType.STRING)));
+        UUID ownerId = container.has(ownerProperty)
+            ? UUID.fromString(Objects.requireNonNull(container.get(ownerProperty, PersistentDataType.STRING)))
+            : null;
+        return new BlockInfo(blockId, ownerId);
+    }
 
     public static @NotNull ItemStack getBlockAsItem(@NotNull PixelBlock block) {
-        String ownerName = Optional.ofNullable(Bukkit.getOfflinePlayer(block.ownerUUID).getName()).orElseGet(() -> block.ownerUUID.toString());
+        String ownerName = Optional.ofNullable(Bukkit.getOfflinePlayer(block.getOwnerUUID()).getName()).orElseGet(() -> block.getOwnerUUID().toString());
 
         ItemStack itemStack = HeadUtil.getCustomTextureHead(itemTexture);
         ItemMeta meta = itemStack.getItemMeta();
-        meta.itemName(Component.text(block.blockUUID.toString()));
+        meta.setMaxStackSize(1);
+        meta.getPersistentDataContainer().set(idProperty, PersistentDataType.STRING, block.getBlockUUID().toString());
+        meta.getPersistentDataContainer().set(ownerProperty, PersistentDataType.STRING, block.getOwnerUUID().toString());
         meta.displayName(Component.text("Pixelblock von " + ownerName));
         meta.lore(List.of(
             Component.text(ownerName + " ist der Besitzer dieses Blocks."),
             Component.text("Klicke auf den gesetzten Block, um diesen zu bearbeiten!"),
-            Component.text(block.blockUUID.toString()).color(NamedTextColor.DARK_GRAY)
+            Component.text(block.getBlockUUID().toString()).color(NamedTextColor.DARK_GRAY)
         ));
-        meta.setEnchantmentGlintOverride(true);
         itemStack.setItemMeta(meta);
 
         return itemStack;
@@ -43,13 +66,13 @@ public class PixelBlockItem {
     public static @NotNull ItemStack getEmptyPixelBlock() {
         ItemStack item = HeadUtil.getCustomTextureHead(itemTexture);
         ItemMeta meta = item.getItemMeta();
+        meta.setMaxStackSize(1);
         meta.displayName(Component.text("Leerer Pixelblock"));
         meta.lore(List.of(
             Component.text("Der erste Spieler, der den Block platziert wird zum Besitzer des Blocks."),
             Component.text("Klicke auf den gesetzten Block, um diesen zu bearbeiten!")
         ));
-        meta.setEnchantmentGlintOverride(true);
-        meta.itemName(Component.text(unusedBlockID.toString()));
+        meta.getPersistentDataContainer().set(idProperty, PersistentDataType.STRING, UUID.randomUUID().toString());
         item.setItemMeta(meta);
         return item;
     }
@@ -61,7 +84,7 @@ public class PixelBlockItem {
         recipe.setIngredient('B', Material.DIAMOND_BLOCK);
         recipe.setIngredient('C', Material.HEART_OF_THE_SEA);
         recipe.setIngredient('D', Material.GRASS_BLOCK);
-        recipe.setIngredient('E', Material.GLOW_BERRIES);
+        recipe.setIngredient('E', Material.END_CRYSTAL);
         return recipe;
     }
 }
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/commands/CreatePixelBlockCommand.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/commands/CreatePixelBlockCommand.java
index 4b8c69b..0ca7f20 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/commands/CreatePixelBlockCommand.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/commands/CreatePixelBlockCommand.java
@@ -1,8 +1,8 @@
 package eu.mhsl.minecraft.pixelblocks.commands;
 
+import eu.mhsl.minecraft.pixelblocks.pixelblock.PixelBlock;
 import eu.mhsl.minecraft.pixelblocks.pixelblock.PixelBlockWorld;
 import eu.mhsl.minecraft.pixelblocks.utils.Direction;
-import eu.mhsl.minecraft.pixelblocks.pixelblock.PixelBlock;
 import org.bukkit.Location;
 import org.bukkit.World;
 import org.bukkit.command.Command;
@@ -24,13 +24,12 @@ public class CreatePixelBlockCommand implements CommandExecutor {
             }
 
             Location playerLocation = p.getLocation();
-            PixelBlock block = new PixelBlock(
-                playerLocation,
-                p.getUniqueId(),
+            PixelBlock.createPixelBlock(
                 UUID.randomUUID(),
+                p.getUniqueId(),
+                playerLocation.toBlockLocation(),
                 Direction.south
             );
-            block.place(playerLocation, Direction.south);
         }
         return true;
     }
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/commands/ExitWorldCommand.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/commands/ExitWorldCommand.java
index 10a4442..c6e33c6 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/commands/ExitWorldCommand.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/commands/ExitWorldCommand.java
@@ -9,7 +9,7 @@ import org.bukkit.command.CommandSender;
 import org.bukkit.entity.Player;
 import org.jetbrains.annotations.NotNull;
 
-import java.util.*;
+import java.util.Objects;
 
 public class ExitWorldCommand implements CommandExecutor {
     @Override
@@ -23,7 +23,7 @@ public class ExitWorldCommand implements CommandExecutor {
 
             PixelBlock currentPixelBlock = PixelBlock.getPixelBlockFromBlockWorld(playerWorld);
             Objects.requireNonNull(currentPixelBlock);
-            p.teleport(currentPixelBlock.lastEntryLocation);
+            currentPixelBlock.exitBlock(p);
         }
         return true;
     }
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/BreakPixelBlockListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/BreakPixelBlockListener.java
index d9863cb..5d1be1c 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/BreakPixelBlockListener.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/BreakPixelBlockListener.java
@@ -1,6 +1,5 @@
 package eu.mhsl.minecraft.pixelblocks.listeners;
 
-import eu.mhsl.minecraft.pixelblocks.PixelBlocksPlugin;
 import eu.mhsl.minecraft.pixelblocks.pixelblock.PixelBlock;
 import io.papermc.paper.event.player.PrePlayerAttackEntityEvent;
 import org.bukkit.Location;
@@ -10,10 +9,10 @@ import org.bukkit.event.Listener;
 
 public class BreakPixelBlockListener implements Listener {
     @EventHandler
-    static void destroyPixelBlock(PrePlayerAttackEntityEvent event) {
+    public void destroyPixelBlock(PrePlayerAttackEntityEvent event) {
         if(!(event.getAttacked() instanceof Interaction)) return;
 
-        Location blockLocation = event.getAttacked().getLocation().add(0, PixelBlocksPlugin.configuration.hitboxOffset(), 0).toBlockLocation();
+        Location blockLocation = event.getAttacked().getLocation().toBlockLocation();
         PixelBlock pixelBlock = PixelBlock.getPixelBlockFromPlacedLocation(blockLocation);
         if(pixelBlock == null) return;
         pixelBlock.destroy(event.getPlayer());
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/BreakPixelListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/BreakPixelListener.java
index be48781..0dcbddd 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/BreakPixelListener.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/BreakPixelListener.java
@@ -7,7 +7,7 @@ import org.bukkit.event.block.BlockBreakEvent;
 
 public class BreakPixelListener implements Listener {
     @EventHandler
-    static void onBlockBreak(BlockBreakEvent event) {
+    public void onBlockBreak(BlockBreakEvent event) {
         EventCanceling.shouldCancelInPixelBlock(
             event,
             event.getBlock().getWorld(),
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/CraftPixelBlockListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/CraftPixelBlockListener.java
deleted file mode 100644
index 0201d53..0000000
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/CraftPixelBlockListener.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package eu.mhsl.minecraft.pixelblocks.listeners;
-
-import eu.mhsl.minecraft.pixelblocks.PixelBlockItem;
-import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
-import org.bukkit.event.EventHandler;
-import org.bukkit.event.Listener;
-import org.bukkit.event.inventory.CraftItemEvent;
-import org.bukkit.inventory.ItemStack;
-import org.bukkit.inventory.meta.ItemMeta;
-
-import java.util.Objects;
-
-public class CraftPixelBlockListener implements Listener {
-    @EventHandler
-    static void onCraftItem(CraftItemEvent event) {
-        ItemStack craftedItem = event.getCurrentItem();
-        Objects.requireNonNull(craftedItem);
-
-        ItemMeta craftedItemMeta = craftedItem.getItemMeta();
-        String itemName = PlainTextComponentSerializer.plainText().serialize(craftedItemMeta.itemName());
-
-        if(!itemName.equals(PixelBlockItem.unusedBlockID.toString())) return;
-        if(event.isShiftClick()) event.setCancelled(true);
-    }
-}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/DiscoverRecipesListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/DiscoverRecipesListener.java
index 6efb61c..b46ffc2 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/DiscoverRecipesListener.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/DiscoverRecipesListener.java
@@ -1,12 +1,12 @@
 package eu.mhsl.minecraft.pixelblocks.listeners;
 
+import eu.mhsl.minecraft.pixelblocks.Main;
 import eu.mhsl.minecraft.pixelblocks.PixelBlockItem;
-import eu.mhsl.minecraft.pixelblocks.PixelBlocksPlugin;
 import org.bukkit.Material;
 import org.bukkit.entity.Player;
 import org.bukkit.event.EventHandler;
 import org.bukkit.event.Listener;
-import org.bukkit.event.inventory.*;
+import org.bukkit.event.inventory.InventoryClickEvent;
 import org.bukkit.inventory.ItemStack;
 
 import java.util.List;
@@ -17,10 +17,10 @@ public class DiscoverRecipesListener implements Listener {
     public void shouldDiscover(InventoryClickEvent event) {
         ItemStack clickedItem = event.getCurrentItem();
         if(clickedItem == null) return;
-        if(!List.of(Material.HEART_OF_THE_SEA, Material.GLOW_BERRIES).contains(clickedItem.getType())) return;
         if(!(event.getWhoClicked() instanceof Player player)) return;
+        if(!List.of(Material.HEART_OF_THE_SEA, Material.END_CRYSTAL).contains(clickedItem.getType())) return;
         if(player.hasDiscoveredRecipe(PixelBlockItem.recipeKey)) return;
-        PixelBlocksPlugin.plugin.getLogger().log(Level.INFO, String.format("%s unlocked tne PixelBlock recipe!", player.getName()));
+        Main.logger().log(Level.INFO, String.format("%s unlocked tne PixelBlock recipe!", player.getName()));
         player.discoverRecipe(PixelBlockItem.recipeKey);
     }
 }
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/EnterPixelBlockListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/EnterPixelBlockListener.java
index 13f3c32..43d864a 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/EnterPixelBlockListener.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/EnterPixelBlockListener.java
@@ -1,8 +1,7 @@
 package eu.mhsl.minecraft.pixelblocks.listeners;
 
-import eu.mhsl.minecraft.pixelblocks.PixelBlocksPlugin;
 import eu.mhsl.minecraft.pixelblocks.pixelblock.PixelBlock;
-import org.bukkit.*;
+import org.bukkit.Location;
 import org.bukkit.entity.Interaction;
 import org.bukkit.event.EventHandler;
 import org.bukkit.event.Listener;
@@ -10,14 +9,13 @@ import org.bukkit.event.player.PlayerInteractEntityEvent;
 
 public class EnterPixelBlockListener implements Listener {
     @EventHandler
-    static void enterPixelBlock(PlayerInteractEntityEvent event) {
+    public void enterPixelBlock(PlayerInteractEntityEvent event) {
         if(!(event.getRightClicked() instanceof Interaction)) return;
 
         Location interactionLocation = event
-                .getRightClicked()
-                .getLocation()
-                .add(0, PixelBlocksPlugin.configuration.hitboxOffset(), 0)
-                .toBlockLocation();
+            .getRightClicked()
+            .getLocation()
+            .toBlockLocation();
         PixelBlock pixelBlock = PixelBlock.getPixelBlockFromPlacedLocation(interactionLocation);
         if(pixelBlock == null) return;
         pixelBlock.enterBlock(event.getPlayer());
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/ExitPixelWorldListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/ExitPixelWorldListener.java
index 904af61..638f94a 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/ExitPixelWorldListener.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/ExitPixelWorldListener.java
@@ -5,31 +5,19 @@ import eu.mhsl.minecraft.pixelblocks.pixelblock.PixelBlockWorld;
 import org.bukkit.World;
 import org.bukkit.event.EventHandler;
 import org.bukkit.event.Listener;
-import org.bukkit.event.player.PlayerChangedWorldEvent;
 import org.bukkit.event.player.PlayerPortalEvent;
 
 import java.util.Objects;
 
 public class ExitPixelWorldListener implements Listener {
     @EventHandler
-    static void onPlayerChangeWorld(PlayerChangedWorldEvent event) {
-        World changingFrom = event.getFrom();
-        if(!PixelBlockWorld.isPixelWorld(changingFrom)) return;
-
-        PixelBlock pixelBlock = PixelBlock.getPixelBlockFromBlockWorld(changingFrom);
-        Objects.requireNonNull(pixelBlock);
-        pixelBlock.updateEntities();
-    }
-
-    @EventHandler
-    static void onPlayerPortal(PlayerPortalEvent event) {
+    public void onPlayerPortal(PlayerPortalEvent event) {
         World pixelBlockWorld = event.getFrom().getWorld();
-
         if(!PixelBlockWorld.isPixelWorld(pixelBlockWorld)) return;
 
         event.setCancelled(true);
         PixelBlock pixelBlock = PixelBlock.getPixelBlockFromBlockWorld(pixelBlockWorld);
         Objects.requireNonNull(pixelBlock);
-        event.getPlayer().teleport(pixelBlock.lastEntryLocation);
+        pixelBlock.exitBlock(event.getPlayer());
     }
 }
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/FallOutOfPixelBlockListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/FallOutOfPixelBlockListener.java
index 856d4b6..3a4a660 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/FallOutOfPixelBlockListener.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/FallOutOfPixelBlockListener.java
@@ -11,7 +11,7 @@ import java.util.Objects;
 
 public class FallOutOfPixelBlockListener implements Listener {
     @EventHandler
-    static void onPlayerMove(PlayerMoveEvent event) {
+    public void onPlayerMove(PlayerMoveEvent event) {
         Player player = event.getPlayer();
         if(player.getLocation().y() > -65) return;
         if(!PixelBlockWorld.isPixelWorld(player.getWorld())) return;
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PlacePixelBlockListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PlacePixelBlockListener.java
index 5e34565..bf73dae 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PlacePixelBlockListener.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PlacePixelBlockListener.java
@@ -1,12 +1,11 @@
 package eu.mhsl.minecraft.pixelblocks.listeners;
 
 import eu.mhsl.minecraft.pixelblocks.PixelBlockItem;
-import eu.mhsl.minecraft.pixelblocks.PixelBlocksPlugin;
 import eu.mhsl.minecraft.pixelblocks.pixelblock.PixelBlock;
 import eu.mhsl.minecraft.pixelblocks.pixelblock.PixelBlockWorld;
 import eu.mhsl.minecraft.pixelblocks.utils.Direction;
 import net.kyori.adventure.text.Component;
-import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
+import net.kyori.adventure.text.format.NamedTextColor;
 import org.bukkit.Location;
 import org.bukkit.Material;
 import org.bukkit.World;
@@ -14,23 +13,17 @@ import org.bukkit.event.EventHandler;
 import org.bukkit.event.Listener;
 import org.bukkit.event.block.BlockPlaceEvent;
 import org.bukkit.inventory.ItemStack;
-import org.bukkit.inventory.meta.ItemMeta;
-
-import java.util.UUID;
 
 public class PlacePixelBlockListener implements Listener {
     @EventHandler
-    static void onBlockPlace(BlockPlaceEvent event) {
+    public void onBlockPlace(BlockPlaceEvent event) {
         ItemStack usedItem = event.getItemInHand();
-        ItemMeta usedItemMeta = usedItem.getItemMeta();
-        Component displayName = usedItemMeta.displayName();
-        if(displayName == null) return;
-        if(!displayName.toString().contains("Pixelblock")) return;
-        if(!usedItemMeta.getEnchantmentGlintOverride()) return;
+        PixelBlockItem.BlockInfo info = PixelBlockItem.getBlockInfo(usedItem);
+        if(info == null) return;
 
         World playerWorld = event.getPlayer().getWorld();
         if(PixelBlockWorld.isPixelWorld(playerWorld)) {
-            event.getPlayer().sendMessage("In Pixelblöcken kann kein Pixelblock erstellt werden.");
+            event.getPlayer().sendMessage(Component.text("In Pixelblöcken kann kein Pixelblock platziert werden.", NamedTextColor.RED));
             event.setCancelled(true);
             return;
         }
@@ -39,34 +32,50 @@ public class PlacePixelBlockListener implements Listener {
         playerWorld.getBlockAt(newBlockLocation).setType(Material.AIR);
 
         Direction direction = Direction.vectorToDirection(event.getPlayer().getLocation().getDirection());
-        String itemName = PlainTextComponentSerializer.plainText().serialize(usedItemMeta.itemName());
 
-        PixelBlock pixelBlock;
-        if(itemName.equals(PixelBlockItem.unusedBlockID.toString())) {
-            pixelBlock = new PixelBlock(
-                newBlockLocation,
-                event.getPlayer().getUniqueId(),
-                UUID.randomUUID(),
-                direction
-            );
-        } else {
-            UUID itemUUID = UUID.fromString(itemName);
-            pixelBlock = PixelBlocksPlugin.pixelBlocks.stream()
-                .filter(block -> block.blockUUID.equals(itemUUID))
-                .findFirst()
-                .orElseGet(() -> new PixelBlock(
-                    newBlockLocation,
-                    event.getPlayer().getUniqueId(),
-                    itemUUID,
-                    direction
-                ));
-        }
-
-        try {
-            pixelBlock.place(newBlockLocation, direction);
-        } catch (IllegalArgumentException e) {
-            event.setCancelled(true);
-            event.getPlayer().sendMessage(Component.text(e.getMessage()));
-        }
+        PixelBlock.createPixelBlock(
+            info.id(),
+            info.hasOwner() ? info.owner() : event.getPlayer().getUniqueId(),
+            newBlockLocation,
+            direction
+        );
+//        if(!info.hasOwner()) {
+//            pixelBlock = PixelBlock.createPixelBlock(
+//                UUID.randomUUID(),
+//                event.getPlayer().getUniqueId(),
+//                newBlockLocation,
+//                Direction.south
+//            );
+////            pixelBlock = new PixelBlock(
+////                newBlockLocation,
+////                event.getPlayer().getUniqueId(),
+////                UUID.randomUUID(),
+////                direction
+////            );
+//        } else {
+//            UUID itemUUID = info.id();
+//            pixelBlock = PixelBlock.createPixelBlock(
+//                UUID.randomUUID(),
+//                even.getUniqueId(),
+//                playerLocation.toBlockLocation(),
+//                Direction.south
+//            );
+////            pixelBlock = Main.pixelBlocks.stream()
+////                .filter(block -> block.getBlockUUID().equals(itemUUID))
+////                .findFirst()
+////                .orElseGet(() -> new PixelBlock(
+////                    newBlockLocation,
+////                    event.getPlayer().getUniqueId(),
+////                    itemUUID,
+////                    direction
+////                ));
+//        }
+//
+//        try {
+//            pixelBlock.place(newBlockLocation, direction);
+//        } catch (IllegalArgumentException e) {
+//            event.setCancelled(true);
+//            event.getPlayer().sendMessage(Component.text(e.getMessage()));
+//        }
     }
 }
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PlacePixelListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PlacePixelListener.java
index 3130016..ba5d54a 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PlacePixelListener.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PlacePixelListener.java
@@ -8,7 +8,7 @@ import org.bukkit.event.player.PlayerBucketEmptyEvent;
 
 public class PlacePixelListener implements Listener {
     @EventHandler
-    static void onBlockPlace(BlockPlaceEvent event) {
+    public void onBlockPlace(BlockPlaceEvent event) {
         EventCanceling.shouldCancelInPixelBlock(
             event,
             event.getBlock().getWorld(),
@@ -17,11 +17,11 @@ public class PlacePixelListener implements Listener {
     }
 
     @EventHandler
-    static void onBuketEmpty(PlayerBucketEmptyEvent event) {
+    public void onBuketEmpty(PlayerBucketEmptyEvent event) {
         EventCanceling.shouldCancelInPixelBlock(
-                event,
-                event.getBlock().getWorld(),
-                pixelBlock -> !pixelBlock.getPixelWorld().allowPlacements(event.getBlock().getLocation())
+            event,
+            event.getBlock().getWorld(),
+            pixelBlock -> !pixelBlock.getPixelWorld().allowPlacements(event.getBlock().getLocation())
         );
     }
 }
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventEntityPlacementListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventEntityPlacementListener.java
index 9e672ab..12e4929 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventEntityPlacementListener.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventEntityPlacementListener.java
@@ -7,7 +7,7 @@ import org.bukkit.event.entity.EntityPlaceEvent;
 
 public class PreventEntityPlacementListener implements Listener {
     @EventHandler
-    public static void preventPlace(EntityPlaceEvent event) {
+    public void preventPlace(EntityPlaceEvent event) {
         EventCanceling.cancelIfInPixelWorld(event, event.getBlock().getWorld());
     }
 }
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventGrowthListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventGrowthListener.java
new file mode 100644
index 0000000..ce227b8
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventGrowthListener.java
@@ -0,0 +1,23 @@
+package eu.mhsl.minecraft.pixelblocks.listeners;
+
+import eu.mhsl.minecraft.pixelblocks.pixelblock.PixelBlock;
+import eu.mhsl.minecraft.pixelblocks.pixelblock.PixelBlockWorld;
+import org.bukkit.Material;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.BlockFertilizeEvent;
+
+import java.util.Objects;
+
+public class PreventGrowthListener implements Listener {
+    @EventHandler
+    public void inBoneMeal(BlockFertilizeEvent event) {
+        if(!PixelBlockWorld.isPixelWorld(event.getBlock().getWorld())) return;
+        PixelBlock pixelBlock = PixelBlock.getPixelBlockFromBlockWorld(event.getBlock().getWorld());
+        Objects.requireNonNull(pixelBlock);
+
+        event.getBlocks().stream()
+            .filter(blockState -> !pixelBlock.getPixelWorld().allowPlacements(blockState.getBlock().getLocation()))
+            .forEach(blockState -> blockState.setType(Material.AIR));
+    }
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventHopperActionsListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventHopperActionsListener.java
index d349a40..2219bc9 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventHopperActionsListener.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventHopperActionsListener.java
@@ -8,7 +8,7 @@ import org.bukkit.event.inventory.InventoryType;
 
 public class PreventHopperActionsListener implements Listener {
     @EventHandler
-    static void onInventoryPickupItem(InventoryPickupItemEvent event) {
+    public void onInventoryPickupItem(InventoryPickupItemEvent event) {
         EventCanceling.shouldCancelInPixelBlock(
             event,
             event.getItem().getWorld(),
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventIllegalBlocksListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventIllegalBlocksListener.java
index 1b56d95..e0a779f 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventIllegalBlocksListener.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventIllegalBlocksListener.java
@@ -10,22 +10,22 @@ import org.bukkit.event.entity.EntityExplodeEvent;
 
 public class PreventIllegalBlocksListener implements Listener {
     @EventHandler
-    static void onBlockExplode(BlockExplodeEvent event) {
+    public void onBlockExplode(BlockExplodeEvent event) {
         EventCanceling.cancelIfInPixelWorld(event, event.getBlock().getWorld());
     }
 
     @EventHandler
-    static void onCreatureSpawn(CreatureSpawnEvent event) {
+    public void onCreatureSpawn(CreatureSpawnEvent event) {
         EventCanceling.cancelIfInPixelWorld(event, event.getLocation().getWorld());
     }
 
     @EventHandler
-    static void onEntityDamage(EntityDamageEvent event) {
+    public void onEntityDamage(EntityDamageEvent event) {
         EventCanceling.cancelIfInPixelWorld(event, event.getEntity().getWorld());
     }
 
     @EventHandler
-    static void onEntityExplode(EntityExplodeEvent event) {
+    public void onEntityExplode(EntityExplodeEvent event) {
         EventCanceling.cancelIfInPixelWorld(event, event.getLocation().getWorld());
     }
 }
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventInventorysListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventInventorysListener.java
index dcec263..ea2339e 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventInventorysListener.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventInventorysListener.java
@@ -3,14 +3,16 @@ package eu.mhsl.minecraft.pixelblocks.listeners;
 import eu.mhsl.minecraft.pixelblocks.utils.EventCanceling;
 import org.bukkit.event.EventHandler;
 import org.bukkit.event.Listener;
-import org.bukkit.event.inventory.*;
+import org.bukkit.event.inventory.InventoryInteractEvent;
+import org.bukkit.event.inventory.InventoryOpenEvent;
+import org.bukkit.event.inventory.InventoryType;
 import org.bukkit.inventory.CraftingInventory;
 import org.bukkit.inventory.Inventory;
 import org.bukkit.inventory.PlayerInventory;
 
 public class PreventInventorysListener implements Listener {
     @EventHandler
-    static void onInventoryOpen(InventoryOpenEvent event) {
+    public void onInventoryOpen(InventoryOpenEvent event) {
         EventCanceling.shouldCancelInPixelBlock(
             event,
             event.getPlayer().getWorld(),
@@ -19,7 +21,7 @@ public class PreventInventorysListener implements Listener {
     }
 
     @EventHandler
-    static void onInventoryInteract(InventoryInteractEvent event) {
+    public void onInventoryInteract(InventoryInteractEvent event) {
         EventCanceling.shouldCancelInPixelBlock(
             event,
             event.getWhoClicked().getWorld(),
@@ -29,7 +31,7 @@ public class PreventInventorysListener implements Listener {
 
     private static boolean isDisallowedInventory(Inventory inventory) {
         return !(inventory instanceof PlayerInventory
-                || inventory instanceof CraftingInventory
-                || inventory.getType() == InventoryType.ENDER_CHEST);
+            || inventory instanceof CraftingInventory
+            || inventory.getType() == InventoryType.ENDER_CHEST);
     }
 }
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventLiquidsFlowListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventLiquidsFlowListener.java
index 79ccc4a..4e5e5b3 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventLiquidsFlowListener.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventLiquidsFlowListener.java
@@ -7,11 +7,11 @@ import org.bukkit.event.block.BlockFromToEvent;
 
 public class PreventLiquidsFlowListener implements Listener {
     @EventHandler
-    public static void onLiquidFlow(BlockFromToEvent event) {
+    public void onLiquidFlow(BlockFromToEvent event) {
         EventCanceling.shouldCancelInPixelBlock(
-                event,
-                event.getToBlock().getWorld(),
-                pixelBlock -> !pixelBlock.getPixelWorld().allowPlacements(event.getToBlock().getLocation())
+            event,
+            event.getToBlock().getWorld(),
+            pixelBlock -> !pixelBlock.getPixelWorld().allowPlacements(event.getToBlock().getLocation())
         );
     }
 }
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventPistonsListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventPistonsListener.java
index f9e8b28..57cc363 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventPistonsListener.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventPistonsListener.java
@@ -7,13 +7,13 @@ import org.bukkit.event.block.BlockPistonExtendEvent;
 
 public class PreventPistonsListener implements Listener {
     @EventHandler
-    public static void blockPistons(BlockPistonExtendEvent event) {
+    public void blockPistons(BlockPistonExtendEvent event) {
         EventCanceling.shouldCancelInPixelBlock(
             event,
             event.getBlock().getWorld(),
             (pixelBlock) -> event.getBlocks().stream()
                 .anyMatch(block -> !pixelBlock.getPixelWorld()
-                        .allowPlacements(block.getLocation().add(event.getDirection().getDirection())))
+                    .allowPlacements(block.getLocation().add(event.getDirection().getDirection())))
         );
     }
 }
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/QuitWhileInPixelBlockListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/QuitWhileInPixelBlockListener.java
new file mode 100644
index 0000000..ca03ed2
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/QuitWhileInPixelBlockListener.java
@@ -0,0 +1,26 @@
+package eu.mhsl.minecraft.pixelblocks.listeners;
+
+import eu.mhsl.minecraft.pixelblocks.pixelblock.PixelBlock;
+import eu.mhsl.minecraft.pixelblocks.pixelblock.PixelBlockWorld;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerQuitEvent;
+
+import java.util.Objects;
+
+public class QuitWhileInPixelBlockListener implements Listener {
+    @EventHandler
+    public void onLeave(PlayerQuitEvent event) {
+        kickPlayerOutOfWorld(event.getPlayer());
+    }
+
+    public static void kickPlayerOutOfWorld(Player player) {
+        World pixelBlockWorld = player.getLocation().getWorld();
+        if(!PixelBlockWorld.isPixelWorld(pixelBlockWorld)) return;
+        PixelBlock pixelBlock = PixelBlock.getPixelBlockFromBlockWorld(pixelBlockWorld);
+        Objects.requireNonNull(pixelBlock);
+        pixelBlock.exitBlock(player);
+    }
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/Pixel.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/Pixel.java
deleted file mode 100644
index 7c9e655..0000000
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/Pixel.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package eu.mhsl.minecraft.pixelblocks.pixelblock;
-
-import org.bukkit.Location;
-import org.bukkit.World;
-import org.bukkit.block.data.BlockData;
-import org.bukkit.entity.BlockDisplay;
-import org.bukkit.entity.Entity;
-import org.bukkit.entity.EntityType;
-import org.bukkit.util.Transformation;
-import org.jetbrains.annotations.NotNull;
-
-import javax.annotation.Nullable;
-import java.util.Objects;
-import java.util.UUID;
-
-public class Pixel {
-    public final Location relativeLocation;
-    public final BlockData blockData;
-    private final double scale;
-
-    private @Nullable UUID uuid;
-
-    public Pixel(@NotNull Location relativeLocation, @NotNull BlockData blockData, double scale) {
-        this.relativeLocation = new Location(
-            relativeLocation.getWorld(),
-            relativeLocation.x(),
-            relativeLocation.y(),
-            relativeLocation.z()
-        );
-        this.blockData = blockData;
-        this.scale = scale;
-    }
-
-    public void place(@NotNull Location spawnBlockLocation) {
-        World world = spawnBlockLocation.getWorld();
-        double positionX = spawnBlockLocation.x() + (relativeLocation.x()*scale);
-        double positionY = spawnBlockLocation.y() + (relativeLocation.y()*scale);
-        double positionZ = spawnBlockLocation.z() + (relativeLocation.z()*scale);
-
-        Location spawnLocation = new Location(world, positionX, positionY, positionZ);
-
-        BlockDisplay entity = (BlockDisplay) world.spawnEntity(spawnLocation, EntityType.BLOCK_DISPLAY);
-        this.uuid = entity.getUniqueId();
-
-        entity.setBlock(blockData);
-
-        Transformation transform = entity.getTransformation();
-        transform.getScale().set(scale);
-        entity.setTransformation(transform);
-    }
-
-    public void destroy() {
-        Objects.requireNonNull(this.uuid);
-        Entity pixelEntity = this.relativeLocation.getWorld().getEntity(this.uuid);
-        if(pixelEntity != null) pixelEntity.remove();
-    }
-}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlock.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlock.java
index 3170042..bd539f0 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlock.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlock.java
@@ -1,53 +1,53 @@
 package eu.mhsl.minecraft.pixelblocks.pixelblock;
 
+import co.aikar.taskchain.TaskChain;
+import eu.mhsl.minecraft.pixelblocks.Main;
 import eu.mhsl.minecraft.pixelblocks.PixelBlockItem;
-import eu.mhsl.minecraft.pixelblocks.PixelBlocksPlugin;
 import eu.mhsl.minecraft.pixelblocks.utils.Direction;
-import eu.mhsl.minecraft.pixelblocks.utils.MinMaxUtil;
-import org.bukkit.*;
-import org.bukkit.block.data.BlockData;
-import org.bukkit.entity.*;
-import org.bukkit.inventory.ItemStack;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import org.bukkit.Chunk;
+import org.bukkit.Location;
+import org.bukkit.Sound;
+import org.bukkit.World;
+import org.bukkit.entity.Item;
+import org.bukkit.entity.Player;
 import org.bukkit.util.Vector;
 import org.jetbrains.annotations.NotNull;
 
 import javax.annotation.Nullable;
-import java.io.File;
-import java.util.*;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
 
 public class PixelBlock {
-    private final PixelBlockWorld pixelWorld;
+    private boolean exists = true;
+    private PixelBlockWorld pixelWorld;
 
-    private final float hitboxOffset = (float) PixelBlocksPlugin.configuration.hitboxOffset();
-    private final int pixelsPerBlock = PixelBlocksPlugin.configuration.pixelsPerBlock();
+    private final Location pixelBlockLocation;
+    private final Direction facingDirection;
+    private Pixels pixels;
+    private List<PixelBlockWorld.PixelData> pixelData;
 
-    public Location pixelBlockLocation;
-    public Direction facingDirection;
-    public ArrayList<Pixel> pixels = new ArrayList<>();
+    private PixelBlockHitbox hitbox;
+    private PixelBlockPlaceholder placeholder;
 
-    public Interaction hitbox;
-    public ItemDisplay barrier;
-
-    public long lastEntryTime;
-    public Location lastEntryLocation;
-    public UUID ownerUUID;
-    public UUID blockUUID;
-
-    public static @NotNull String getWorldName(@NotNull PixelBlock pixelBlock) {
-        return PixelBlocksPlugin.plugin.getDataFolder().getPath() + File.separator + pixelBlock.blockUUID;
-    }
+    private Location lastEntryLocation;
+    private final UUID ownerUUID;
+    private final UUID blockUUID;
 
     public static @Nullable PixelBlock getPixelBlockFromBlockWorld(World world) {
-        return PixelBlocksPlugin.pixelBlocks.stream()
-                .filter(block -> block.blockUUID.equals(getUUIDFromWorld(world)))
-                .findFirst()
-                .orElse(null);
+        return Main.pixelBlocks.stream()
+            .filter(block -> block.blockUUID.equals(getUUIDFromWorld(world)))
+            .findFirst()
+            .orElse(null);
     }
 
     public static @Nullable UUID getUUIDFromWorld(@NotNull World world) {
         try {
             return UUID.fromString(List.of(world.getName().split("/")).getLast());
-        } catch (IllegalArgumentException e) {
+        } catch(IllegalArgumentException e) {
             return null;
         }
     }
@@ -57,226 +57,219 @@ public class PixelBlock {
         searchLocation.setPitch(0);
         searchLocation.setYaw(0);
 
-        return PixelBlocksPlugin.pixelBlocks.stream()
-                .filter(block -> block.pixelBlockLocation.equals(searchLocation))
-                .findFirst()
-                .orElse(null);
+        return Main.pixelBlocks.stream()
+            .filter(block -> Objects.equals(block.pixelBlockLocation, searchLocation))
+            .findFirst()
+            .orElse(null);
     }
 
-    public PixelBlock(Location originLocation, UUID ownerUUID, UUID blockUUID, Direction direction) {
-        PixelBlocksPlugin.pixelBlocks.add(this);
+    public static PixelBlock fromExisting(UUID blockUUID, UUID ownerUUID, Location pixelBlockLocation, Direction direction, Location lastEntryLocation) {
+        return new PixelBlock(blockUUID, ownerUUID, pixelBlockLocation, direction, lastEntryLocation);
+    }
 
-        this.ownerUUID = ownerUUID;
+    private PixelBlock(UUID blockUUID, UUID ownerUUID, Location pixelBlockLocation, Direction direction, Location lastEntryLocation) {
         this.blockUUID = blockUUID;
-
-        this.pixelBlockLocation = originLocation.toBlockLocation();
+        this.ownerUUID = ownerUUID;
+        this.pixelBlockLocation = pixelBlockLocation;
         this.pixelBlockLocation.setYaw(0);
         this.pixelBlockLocation.setPitch(0);
+        this.facingDirection = direction;
+        this.lastEntryLocation = lastEntryLocation;
 
+        try {
+            this.pixelWorld = new PixelBlockWorld(this);
+            this.pixelData = this.pixelWorld.getPixels(this.facingDirection);
+            this.pixels = new Pixels(this);
+            this.placeholder = new PixelBlockPlaceholder(this);
+            this.hitbox = new PixelBlockHitbox(this);
+
+            Main.logger().info(String.format("Loaded existing pixelblock '%s'", this.blockUUID));
+        } catch(Exception e) {
+            Main.logger().info(String.format("Failed initializing existing pixelblock '%s': %s", this.blockUUID, e.getMessage()));
+        }
+    }
+
+    public static PixelBlock createPixelBlock(UUID blockUUID, UUID ownerUUID, Location pixelBlockLocation, Direction direction) {
+        return new PixelBlock(blockUUID, ownerUUID, pixelBlockLocation, direction);
+    }
+
+    private PixelBlock(UUID blockUUID, UUID ownerUUID, Location pixelBlockLocation, Direction direction) {
+        if(Main.pixelBlocks.stream().anyMatch(pixelBlock -> pixelBlock.getBlockUUID().equals(blockUUID)))
+            throw new IllegalStateException(String.format("PixelBlock '%s' ist bereits in der Welt vorhanden!", blockUUID));
+
+        this.blockUUID = blockUUID;
+        this.ownerUUID = ownerUUID;
+        this.pixelBlockLocation = pixelBlockLocation;
+        this.pixelBlockLocation.setYaw(0);
+        this.pixelBlockLocation.setPitch(0);
         this.facingDirection = direction;
 
-        this.pixelWorld = new PixelBlockWorld(this);
+        this.getBlockTaskChain()
+            .sync(() -> {
+                this.pixelWorld = new PixelBlockWorld(this);
+                this.pixelData = this.pixelWorld.getPixels(this.facingDirection);
+                this.pixels = new Pixels(this);
+                this.placeholder = new PixelBlockPlaceholder(this);
+                this.hitbox = new PixelBlockHitbox(this);
+            })
+            .execute();
+
+        this.scheduleEntityUpdate();
+
+        this.getBlockTaskChain()
+            .async(() -> {
+                Main.database().savePixelBlock(this);
+                Main.pixelBlocks.add(this);
+            })
+            .execute();
+    }
+
+    public <T> TaskChain<T> getBlockTaskChain() {
+        return Main.sharedChain(this.blockUUID.toString());
     }
 
     public void enterBlock(@NotNull Player player) {
-        if(PixelBlocksPlugin.configuration.onlyEditableByOwner() && !player.getUniqueId().equals(ownerUUID)) {
-            player.sendMessage("Dieser Pixelblock gehört nicht dir!");
+        if(Main.configuration().onlyEditableByOwner() && !player.getUniqueId().equals(ownerUUID)) {
+            player.sendMessage(Component.text("Dieser Pixelblock gehört nicht dir!", NamedTextColor.RED));
             return;
         }
 
-        this.lastEntryLocation = player.getLocation();
-        this.lastEntryTime = System.currentTimeMillis();
-        PixelBlocksPlugin.database.savePixelBlock(this);
-
-        player.teleport(this.pixelWorld.getSpawnLocation());
+        getBlockTaskChain()
+            .async(() -> {
+                this.lastEntryLocation = player.getLocation();
+                Main.database().savePixelBlock(this);
+            })
+            .sync(() -> {
+                if(!exists) return;
+                player.teleport(this.pixelWorld.getSpawnLocation());
+            })
+            .current(() -> Main.logger()
+                .info(String.format("'%s' entered PixelBlock '%s' at %s", player.getName(), this.blockUUID, this.pixelBlockLocation.toString())))
+            .execute();
     }
 
-    public void setLastEntryLocation(Location lastEntryLocation) {
-        this.lastEntryLocation = lastEntryLocation;
+    public void exitBlock(@NotNull Player player) {
+        this.getBlockTaskChain()
+            .sync(() -> player.teleport(this.lastEntryLocation != null ? this.lastEntryLocation : this.pixelBlockLocation))
+            .sync(() -> this.pixelData = this.pixelWorld.getPixels(this.facingDirection))
+            .current(() -> Main.logger().info(String.format("%s exited PixelBlock", player.getName())))
+            .delay(1)
+            .execute();
+
+        this.scheduleEntityUpdate();
     }
 
-    public void spawnInteraction(boolean fullBlock) {
-        if(fullBlock) {
-            hitbox = (Interaction) pixelBlockLocation.getWorld().spawnEntity(
-                    pixelBlockLocation.clone().add(0.5, -hitboxOffset, 0.5),
-                    EntityType.INTERACTION
-            );
-            hitbox.setInteractionHeight(1 + 2*hitboxOffset);
-            hitbox.setInteractionWidth(1 + 2*hitboxOffset);
-        } else {
-            double startingX = MinMaxUtil.getMinProperty(this.pixels, pixel -> pixel.relativeLocation.getX());
-            double startingY = MinMaxUtil.getMinProperty(this.pixels, pixel -> pixel.relativeLocation.getY());
-            double startingZ = MinMaxUtil.getMinProperty(this.pixels, pixel -> pixel.relativeLocation.getZ());
-
-            double endingX = MinMaxUtil.getMaxProperty(this.pixels, pixel -> pixel.relativeLocation.getX());
-            double endingY = MinMaxUtil.getMaxProperty(this.pixels, pixel -> pixel.relativeLocation.getY());
-            double endingZ = MinMaxUtil.getMaxProperty(this.pixels, pixel -> pixel.relativeLocation.getZ());
-
-            Location spawnLocation = pixelBlockLocation.clone().add(
-                    ((startingX+endingX+1)/2)/pixelsPerBlock,
-                    (startingY/pixelsPerBlock)-hitboxOffset,
-                    ((startingZ+endingZ+1)/2)/pixelsPerBlock
-                );
-
-            float height = (float) (endingY-startingY+1)/pixelsPerBlock + 2*hitboxOffset;
-
-            float width;
-            if((endingX-startingX) > (endingZ-startingZ)) {
-                width = (float) (endingX-startingX+1)/pixelsPerBlock + 2*hitboxOffset;
-            } else {
-                width = (float) (endingZ-startingZ+1)/pixelsPerBlock + 2*hitboxOffset;
-            }
-
-            if(spawnLocation.getX()+width/2 > this.pixelBlockLocation.getX()+1) {
-                spawnLocation.subtract((spawnLocation.getX()+width/2)-(this.pixelBlockLocation.getX()+1), 0, 0);
-            }
-            if(spawnLocation.getX()-width/2 < this.pixelBlockLocation.getX()) {
-                spawnLocation.add(this.pixelBlockLocation.getX()-(spawnLocation.getX()-width/2), 0, 0);
-            }
-
-            if(spawnLocation.getZ()+width/2 > this.pixelBlockLocation.getZ()+1) {
-                spawnLocation.subtract(0, 0, (spawnLocation.getZ()+width/2)-(this.pixelBlockLocation.getZ()+1));
-            }
-            if(spawnLocation.getZ()-width/2 < this.pixelBlockLocation.getZ()) {
-                spawnLocation.add(0, 0, this.pixelBlockLocation.getZ()-(spawnLocation.getZ()-width/2));
-            }
-
-            hitbox = (Interaction) pixelBlockLocation.getWorld().spawnEntity(
-                spawnLocation,
-                EntityType.INTERACTION
-            );
-
-            hitbox.setInteractionHeight(height);
-            hitbox.setInteractionWidth(width);
-        }
+    private void scheduleEntityUpdate() {
+        this.getBlockTaskChain()
+            .sync(this::ensureChunksLoaded)
+            .sync(this::removeEntities)
+            .delay(1)
+            .async(() -> Collections.shuffle(this.pixelData))
+            .delay(1)
+            .sync(this::ensureChunksLoaded)
+            .syncLast((pixelData) -> {
+                this.pixels.spawn();
+                this.hitbox.spawn();
+                this.placeholder.spawn();
+            })
+            .execute();
     }
 
-    public void updateEntities() {
-        Bukkit.getScheduler().runTask(PixelBlocksPlugin.plugin, () -> {
-            this.clearEntities();
-
-            for (int x = 0; x < pixelsPerBlock; x++) {
-                for (int y = 0; y < pixelsPerBlock; y++) {
-                    for (int z = 0; z < pixelsPerBlock; z++) {
-                        Location relativeLocation = new Location(pixelBlockLocation.getWorld(), x, y, z);
-                        Location blockLocation = this.pixelWorld.getBuildOrigin();
-                        switch (this.facingDirection) {
-                            case south -> blockLocation.add(relativeLocation.x(), relativeLocation.y(), relativeLocation.z());
-                            case north -> blockLocation.add((pixelsPerBlock-1)-relativeLocation.x(), relativeLocation.y(), (pixelsPerBlock-1)-relativeLocation.z());
-                            case east -> blockLocation.add((pixelsPerBlock-1)-relativeLocation.z(), relativeLocation.y(), relativeLocation.x());
-                            case west -> blockLocation.add(relativeLocation.z(), relativeLocation.y(), (pixelsPerBlock-1)-relativeLocation.x());
-                        }
-                        BlockData block = blockLocation.getBlock().getBlockData();
-
-                        if(block.getMaterial() != Material.AIR) {
-                            Pixel newPixel = new Pixel(relativeLocation, block, ((double) 1 /pixelsPerBlock));
-                            pixels.add(newPixel);
-                        }
-                    }
-                }
-            }
-
-            for(Pixel pixel : this.pixels) {
-                pixel.place(this.pixelBlockLocation);
-            }
-
-            if(this.pixels.size() < 5) {
-                Location relativeLocation = new Location(pixelBlockLocation.getWorld(), 0, 0, 0);
-                BlockData block = Material.GRAY_STAINED_GLASS.createBlockData();
-                Pixel newPixel = new Pixel(relativeLocation, block, 1);
-                pixels.add(newPixel);
-                newPixel.place(this.pixelBlockLocation);
-
-                Location itemDisplayLocation = this.pixelBlockLocation.clone().add(0.5, 0.5, 0.5);
-                this.barrier = (ItemDisplay) this.pixelBlockLocation.getWorld().spawnEntity(
-                        itemDisplayLocation,
-                        EntityType.ITEM_DISPLAY
-                );
-                this.barrier.setItemStack(ItemStack.of(Material.BARRIER));
-
-                spawnInteraction(true);
-            } else {
-                spawnInteraction(false);
-            }
-        });
-    }
-
-    public void place(Location placeLocation, Direction direction) {
-        Location newLocation = placeLocation.toBlockLocation();
-        newLocation.setPitch(0);
-        newLocation.setYaw(0);
-
-        @Nullable PixelBlock blockAtLocation = PixelBlock.getPixelBlockFromPlacedLocation(newLocation);
-        if(blockAtLocation != null && blockAtLocation != this) throw new IllegalArgumentException("Es können nicht mehrere Pixelblöcke ineinander platziert werden.");
-
-        this.pixelBlockLocation = newLocation;
-        this.facingDirection = direction;
-        updateEntities();
-        PixelBlocksPlugin.database.savePixelBlock(this);
-    }
+//    public void place(Location placeLocation, Direction direction) {
+//        Location newLocation = placeLocation.toBlockLocation();
+//        newLocation.setPitch(0);
+//        newLocation.setYaw(0);
+//
+//        @Nullable PixelBlock blockAtLocation = PixelBlock.getPixelBlockFromPlacedLocation(newLocation);
+//        if(blockAtLocation != null && blockAtLocation != this) throw new IllegalArgumentException("Es können nicht mehrere Pixelblöcke ineinander platziert werden.");
+//
+//        Main.logger().info(String.format("Placing PixelBlock '%s' at %s", this.blockUUID, placeLocation));
+//        this.pixelBlockLocation = newLocation;
+//        this.facingDirection = direction;
+//        updateEntities();
+//        Main.database.savePixelBlock(this);
+//
+//        this.placeholder = PixelBlockPlaceholder.newPlaceholder(this);
+//    }
 
     public void destroy(Player destroyedBy) {
-        if(PixelBlocksPlugin.configuration.onlyBreakableByOwner() && !destroyedBy.getUniqueId().equals(ownerUUID)) {
+        if(!this.exists) return;
+        if(Main.configuration().onlyBreakableByOwner() && !destroyedBy.getUniqueId().equals(ownerUUID)) {
             destroyedBy.sendMessage("Dieser Pixelblock gehört nicht dir!");
             return;
         }
 
         this.pixelWorld.getPlayersInWorld().forEach(p -> {
-            p.sendMessage("Der Pixelblock wurde von einem anderen Spieler abgebaut.");
+            p.sendMessage(Component.text("Der Pixelblock wurde von einem anderen Spieler abgebaut!", NamedTextColor.RED));
             p.teleport(this.lastEntryLocation);
         });
 
+        Main.logger().info(String.format("Destroying PixelBlock '%s' at %s", this.blockUUID, pixelBlockLocation));
+        this.exists = false;
+
         this.pixelWorld.getEntitiesInWorld().stream()
             .filter(entity -> entity instanceof Item)
             .forEach(entity -> entity.teleport(this.lastEntryLocation));
 
-        this.clearEntities();
-        PixelBlocksPlugin.database.deletePixelBlock(this);
-        this.pixelBlockLocation.getWorld().playSound(this.pixelBlockLocation, Sound.BLOCK_COPPER_BULB_BREAK, 1.0F, 30);
-        this.pixelBlockLocation.getWorld().dropItem(this.pixelBlockLocation.add(new Vector(0.5, 0.5, 0.5)), PixelBlockItem.getBlockAsItem(this));
+        this.getBlockTaskChain()
+            .sync(() -> {
+                this.removeEntities();
+                World world = this.pixelBlockLocation.getWorld();
+                world.playSound(this.pixelBlockLocation, Sound.BLOCK_COPPER_BULB_BREAK, 1.0F, 30);
+                world.dropItem(this.pixelBlockLocation.add(new Vector(0.5, 0.5, 0.5)), PixelBlockItem.getBlockAsItem(this));
+            })
+            .async(() -> {
+                Main.database().deletePixelBlock(this);
+                Main.pixelBlocks.remove(this);
+            })
+            .execute();
     }
 
-    private void clearEntities() {
+    private void removeEntities() {
+        this.ensureChunksLoaded();
+        this.pixels.destroy();
+        this.placeholder.destroy();
+        this.hitbox.destroy();
+    }
+
+    private void ensureChunksLoaded() {
         Chunk chunk = this.pixelBlockLocation.getChunk();
-        if(!chunk.isEntitiesLoaded()) {
-            chunk.load(true);
+        if(!chunk.isLoaded() || !chunk.isEntitiesLoaded()) {
+            Main.logger().info(String.format("Loading chunk '%d, %d' for pixelblock '%s'", chunk.getX(), chunk.getZ(), this.blockUUID));
+            chunk.load();
             chunk.getEntities();
         }
-
-        this.pixels.forEach(Pixel::destroy);
-        this.pixels.clear();
-
-        if(hitbox != null) {
-            this.hitbox.remove();
-            this.hitbox = null;
-        }
-
-        if(barrier != null) {
-            this.barrier.remove();
-            this.barrier = null;
-        }
-
-        this.pixelBlockLocation.getWorld().getEntities().stream()
-                .filter(this::isRelevantEntity)
-                .filter(entity -> entity.getLocation()
-                        .add(0, hitboxOffset, 0)
-                        .toBlockLocation()
-                        .equals(this.pixelBlockLocation))
-                .forEach(Entity::remove);
     }
 
-    private boolean isRelevantEntity(Entity entity) {
-        return entity.getType().equals(EntityType.BLOCK_DISPLAY)
-                || entity.getType().equals(EntityType.INTERACTION)
-                || entity.getType().equals(EntityType.ITEM_DISPLAY);
-    }
-
-
     public @NotNull PixelBlockWorld getPixelWorld() {
         return pixelWorld;
     }
 
-    public int getPixelsPerBlock() {
-        return pixelsPerBlock;
+    public Location getPixelBlockLocation() {
+        return pixelBlockLocation.clone();
+    }
+
+    public Direction getFacingDirection() {
+        return facingDirection;
+    }
+
+    public boolean hasLastEntryLocation() {
+        return this.lastEntryLocation != null;
+    }
+
+    public List<PixelBlockWorld.PixelData> getPixelData() {
+        return pixelData;
+    }
+
+    public Location getLastEntryLocation() {
+        return lastEntryLocation.clone();
+    }
+
+    public UUID getOwnerUUID() {
+        return ownerUUID;
+    }
+
+    public UUID getBlockUUID() {
+        return blockUUID;
     }
 }
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlockHitbox.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlockHitbox.java
new file mode 100644
index 0000000..5d9e522
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlockHitbox.java
@@ -0,0 +1,99 @@
+package eu.mhsl.minecraft.pixelblocks.pixelblock;
+
+import eu.mhsl.minecraft.pixelblocks.Main;
+import eu.mhsl.minecraft.pixelblocks.utils.MinMaxUtil;
+import org.bukkit.Location;
+import org.bukkit.NamespacedKey;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.EntityType;
+import org.bukkit.entity.Interaction;
+import org.bukkit.persistence.PersistentDataType;
+
+import java.util.List;
+import java.util.Objects;
+
+public class PixelBlockHitbox {
+    private static final NamespacedKey hitboxOfTag = new NamespacedKey(Main.plugin(), "hitbox_of");
+
+    private final PixelBlock parentBlock;
+
+    public PixelBlockHitbox(PixelBlock parentBlock) {
+        this.parentBlock = parentBlock;
+    }
+
+    public void spawn() {
+        Location absoluteLocation = this.parentBlock.getPixelBlockLocation();
+        List<PixelBlockWorld.PixelData> pixels = this.parentBlock.getPixelData();
+        int pixelsPerBlock = Main.configuration().pixelsPerBlock();
+
+        Interaction interaction;
+        if (pixels.size() <= 5) {
+            interaction = (Interaction) absoluteLocation.getWorld().spawnEntity(
+                absoluteLocation.clone().add(0.5, -0, 0.5),
+                EntityType.INTERACTION
+            );
+            interaction.setInteractionHeight(1);
+            interaction.setInteractionWidth(1);
+        } else {
+            double startingX = MinMaxUtil.getMinProperty(pixels, pixel -> pixel.relativeLocation().getX());
+            double startingY = MinMaxUtil.getMinProperty(pixels, pixel -> pixel.relativeLocation().getY());
+            double startingZ = MinMaxUtil.getMinProperty(pixels, pixel -> pixel.relativeLocation().getZ());
+
+            double endingX = MinMaxUtil.getMaxProperty(pixels, pixel -> pixel.relativeLocation().getX());
+            double endingY = MinMaxUtil.getMaxProperty(pixels, pixel -> pixel.relativeLocation().getY());
+            double endingZ = MinMaxUtil.getMaxProperty(pixels, pixel -> pixel.relativeLocation().getZ());
+
+            Location spawnLocation = absoluteLocation.clone().add(
+                ((startingX+endingX)/2)/pixelsPerBlock,
+                (startingY/pixelsPerBlock)-0,
+                ((startingZ+endingZ)/2)/pixelsPerBlock
+            );
+
+            float height = (float) (endingY-startingY+1)/pixelsPerBlock;
+
+            float width;
+            if((endingX-startingX) > (endingZ-startingZ)) {
+                width = (float) (endingX-startingX+1)/pixelsPerBlock;
+            } else {
+                width = (float) (endingZ-startingZ+1)/pixelsPerBlock;
+            }
+
+            if(spawnLocation.getX()+width/2 > absoluteLocation.getX()+1) {
+                spawnLocation.subtract((spawnLocation.getX()+width/2)-(absoluteLocation.getX()+1), 0, 0);
+            }
+            if(spawnLocation.getX()-width/2 < absoluteLocation.getX()) {
+                spawnLocation.add(absoluteLocation.getX()-(spawnLocation.getX()-width/2), 0, 0);
+            }
+
+            if(spawnLocation.getZ()+width/2 > absoluteLocation.getZ()+1) {
+                spawnLocation.subtract(0, 0, (spawnLocation.getZ()+width/2)-(absoluteLocation.getZ()+1));
+            }
+            if(spawnLocation.getZ()-width/2 < absoluteLocation.getZ()) {
+                spawnLocation.add(0, 0, absoluteLocation.getZ()-(spawnLocation.getZ()-width/2));
+            }
+
+            interaction = (Interaction) absoluteLocation.getWorld().spawnEntity(
+                spawnLocation,
+                EntityType.INTERACTION
+            );
+
+            interaction.setInteractionHeight(height);
+            interaction.setInteractionWidth(width);
+        }
+
+        interaction.getPersistentDataContainer()
+            .set(hitboxOfTag, PersistentDataType.STRING, this.parentBlock.getBlockUUID().toString());
+    }
+
+
+    public void destroy() {
+        this.parentBlock.getPixelBlockLocation().getNearbyEntitiesByType(Interaction.class, 1)
+            .stream()
+            .filter(interaction -> interaction.getPersistentDataContainer().has(hitboxOfTag))
+            .filter(interaction -> Objects.equals(
+                interaction.getPersistentDataContainer().get(hitboxOfTag, PersistentDataType.STRING),
+                parentBlock.getBlockUUID().toString()
+            ))
+            .forEach(Entity::remove);
+    }
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlockInteraction.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlockInteraction.java
deleted file mode 100644
index 19a1863..0000000
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlockInteraction.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package eu.mhsl.minecraft.pixelblocks.pixelblock;
-
-public class PixelBlockInteraction {
-
-}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlockPlaceholder.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlockPlaceholder.java
new file mode 100644
index 0000000..dd35dad
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlockPlaceholder.java
@@ -0,0 +1,86 @@
+package eu.mhsl.minecraft.pixelblocks.pixelblock;
+
+import eu.mhsl.minecraft.pixelblocks.Main;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.World;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.EntityType;
+import org.bukkit.entity.ItemDisplay;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.persistence.PersistentDataHolder;
+import org.bukkit.persistence.PersistentDataType;
+import org.bukkit.util.Transformation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+
+public class PixelBlockPlaceholder {
+    private static final NamespacedKey placeholderOfTag = new NamespacedKey(Main.plugin(), "placeholder_of");
+
+    private final PixelBlock parentBlock;
+
+    public PixelBlockPlaceholder(PixelBlock parentBlock) {
+        this.parentBlock = parentBlock;
+    }
+
+    public void spawn() {
+        if(parentBlock.getPixelData().size() > 5) return;
+
+        List<ItemDisplay> placeholders = new ArrayList<>();
+        Location pixelBlockLocation = parentBlock.getPixelBlockLocation();
+        UUID parentBlockUUID = parentBlock.getBlockUUID();
+
+        World pixelBlockWorld = pixelBlockLocation.getWorld();
+        Location itemDisplayLocation = pixelBlockLocation.add(0.5, 0.5, 0.5);
+
+        for(int i = 0; i <= 90; i += 90) {
+            ItemDisplay verticalCore = (ItemDisplay) pixelBlockWorld.spawnEntity(
+                itemDisplayLocation,
+                EntityType.ITEM_DISPLAY
+            );
+            verticalCore.setRotation(i, 0);
+            placeholders.add(verticalCore);
+        }
+
+        ItemDisplay horizontalCore = (ItemDisplay) pixelBlockWorld.spawnEntity(
+            itemDisplayLocation,
+            EntityType.ITEM_DISPLAY
+        );
+        horizontalCore.setRotation(0, 90);
+        placeholders.add(horizontalCore);
+
+        placeholders.forEach(coreDisplay -> {
+            coreDisplay.setItemStack(ItemStack.of(Material.END_CRYSTAL));
+            Transformation transform = coreDisplay.getTransformation();
+            transform.getScale().set(0.5);
+            coreDisplay.setTransformation(transform);
+        });
+
+        ItemDisplay displayContainer = (ItemDisplay) pixelBlockWorld.spawnEntity(
+            itemDisplayLocation,
+            EntityType.ITEM_DISPLAY
+        );
+        displayContainer.setItemStack(ItemStack.of(Material.WHITE_STAINED_GLASS));
+        placeholders.add(displayContainer);
+
+        placeholders.stream()
+            .map(PersistentDataHolder::getPersistentDataContainer)
+            .forEach(container -> container.set(placeholderOfTag, PersistentDataType.STRING, parentBlockUUID.toString()));
+    }
+
+    public void destroy() {
+        this.parentBlock.getPixelBlockLocation()
+            .getNearbyEntitiesByType(ItemDisplay.class, 1)
+            .stream()
+            .filter(itemDisplay -> itemDisplay.getPersistentDataContainer().has(placeholderOfTag))
+            .filter(itemDisplay -> Objects.equals(
+                itemDisplay.getPersistentDataContainer().get(placeholderOfTag, PersistentDataType.STRING),
+                parentBlock.getBlockUUID().toString()
+            ))
+            .forEach(Entity::remove);
+    }
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlockWorld.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlockWorld.java
index 4f34c22..4521494 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlockWorld.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlockWorld.java
@@ -1,29 +1,31 @@
 package eu.mhsl.minecraft.pixelblocks.pixelblock;
 
-import eu.mhsl.minecraft.pixelblocks.PixelBlocksPlugin;
+import eu.mhsl.minecraft.pixelblocks.Main;
+import eu.mhsl.minecraft.pixelblocks.utils.Direction;
 import eu.mhsl.minecraft.pixelblocks.utils.LocationUtil;
 import org.bukkit.*;
+import org.bukkit.block.data.BlockData;
 import org.bukkit.entity.Entity;
 import org.bukkit.entity.Player;
 import org.bukkit.generator.ChunkGenerator;
+import org.bukkit.util.Vector;
 import org.jetbrains.annotations.NotNull;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 import java.util.Random;
 
-import static eu.mhsl.minecraft.pixelblocks.PixelBlocksPlugin.plugin;
-
 public class PixelBlockWorld {
     private final PixelBlock parentPixelBlock;
     private final World world;
 
     int worldGrassBorderWidth = 10;
-    int pixelsPerBlock = PixelBlocksPlugin.configuration.pixelsPerBlock();
+    int pixelsPerBlock = Main.configuration().pixelsPerBlock();
 
     public static boolean isPixelWorld(@NotNull World world) {
-        return world.getName().startsWith(plugin.getDataFolder().getPath());
+        return world.getName().startsWith(Main.plugin().getDataFolder().getPath());
     }
 
     public static @NotNull List<World> getOtherWorlds() {
@@ -44,12 +46,12 @@ public class PixelBlockWorld {
         Location origin = getBuildOrigin();
         int offset = pixelsPerBlock - 1;
         return blockLocation.x() >= origin.x() && blockLocation.x() <= origin.x() + offset
-                && blockLocation.z() >= origin.z() && blockLocation.z() <= origin.z() + offset
-                && blockLocation.y() >= origin.y() && blockLocation.y() <= origin.y() + offset;
+            && blockLocation.z() >= origin.z() && blockLocation.z() <= origin.z() + offset
+            && blockLocation.y() >= origin.y() && blockLocation.y() <= origin.y() + offset;
     }
 
     public @NotNull String getWorldPathName() {
-        return PixelBlocksPlugin.plugin.getDataFolder().getPath() + File.separator + "worlds" + File.separator + this.parentPixelBlock.blockUUID;
+        return Main.plugin().getDataFolder().getPath() + File.separator + "worlds" + File.separator + this.parentPixelBlock.getBlockUUID();
     }
 
     public @NotNull Location getSpawnLocation() {
@@ -57,7 +59,7 @@ public class PixelBlockWorld {
     }
 
     public @NotNull Location getPortalLocation() {
-        return this.getBuildOrigin().add((double) pixelsPerBlock/2 -2, 0, -worldGrassBorderWidth+3);
+        return this.getBuildOrigin().add((double) pixelsPerBlock / 2 - 2, 0, -worldGrassBorderWidth + 3);
     }
 
     public @NotNull List<Player> getPlayersInWorld() {
@@ -88,11 +90,46 @@ public class PixelBlockWorld {
         return getBorderOrigin().add(worldGrassBorderWidth + pixelsPerBlock, 0, worldGrassBorderWidth + pixelsPerBlock);
     }
 
+    public record PixelData(Vector relativeLocation, BlockData block, double scale) {
+    }
+
+    public List<PixelData> getPixels(Direction direction) {
+        List<PixelData> pixelData = new ArrayList<>();
+
+        for(int x = 0; x < pixelsPerBlock; x++) {
+            for(int y = 0; y < pixelsPerBlock; y++) {
+                for(int z = 0; z < pixelsPerBlock; z++) {
+                    Location relativeLocation = new Location(world, x, y, z);
+
+                    Location blockLocation = this.getBuildOrigin();
+                    switch(direction) {
+                        case south ->
+                            blockLocation.add(relativeLocation.x(), relativeLocation.y(), relativeLocation.z());
+                        case north ->
+                            blockLocation.add((pixelsPerBlock - 1) - relativeLocation.x(), relativeLocation.y(), (pixelsPerBlock - 1) - relativeLocation.z());
+                        case east ->
+                            blockLocation.add((pixelsPerBlock - 1) - relativeLocation.z(), relativeLocation.y(), relativeLocation.x());
+                        case west ->
+                            blockLocation.add(relativeLocation.z(), relativeLocation.y(), (pixelsPerBlock - 1) - relativeLocation.x());
+                    }
+                    BlockData block = blockLocation.getBlock().getBlockData();
+
+                    if(!block.getMaterial().isEmpty()) {
+                        pixelData.add(new PixelData(relativeLocation.toVector(), block, (double) 1 / pixelsPerBlock));
+                    }
+                }
+            }
+        }
+
+        return pixelData;
+    }
+
     private World loadOrCreatePixelWorld() {
         final WorldCreator worldCreator = new WorldCreator(getWorldPathName());
 
         worldCreator.type(WorldType.FLAT);
-        worldCreator.generator(new ChunkGenerator() {});
+        worldCreator.generator(new ChunkGenerator() {
+        });
 
         World world = Bukkit.createWorld(worldCreator);
         Objects.requireNonNull(world);
@@ -107,32 +144,32 @@ public class PixelBlockWorld {
 
         WorldBorder worldBorder = world.getWorldBorder();
         worldBorder.setCenter(getBuildOrigin().add((double) pixelsPerBlock / 2, 0, (double) pixelsPerBlock / 2));
-        worldBorder.setSize(pixelsPerBlock + (2*worldGrassBorderWidth));
+        worldBorder.setSize(pixelsPerBlock + (2 * worldGrassBorderWidth));
         worldBorder.setWarningDistance(0);
         worldBorder.setDamageAmount(0);
         return world;
     }
 
     private void setBuildingPlatform() {
-        Bukkit.getScheduler().runTask(PixelBlocksPlugin.plugin, () -> {
-            for (int x = 0; x < (pixelsPerBlock+2) + 2 * worldGrassBorderWidth; x++) {
-                for (int z = 0; z < (pixelsPerBlock+2) + 2 * worldGrassBorderWidth; z++) {
+        Bukkit.getScheduler().runTask(Main.plugin(), () -> {
+            for(int x = 0; x < (pixelsPerBlock + 2) + 2 * worldGrassBorderWidth; x++) {
+                for(int z = 0; z < (pixelsPerBlock + 2) + 2 * worldGrassBorderWidth; z++) {
                     getPlatformOrigin().add(x, 0, z).getBlock().setType(Material.GRASS_BLOCK);
                 }
             }
-            for (int x = 0; x < (pixelsPerBlock+2) + 2 * worldGrassBorderWidth; x++) {
-                for (int z = 0; z < (pixelsPerBlock+2) + 2 * worldGrassBorderWidth; z++) {
+            for(int x = 0; x < (pixelsPerBlock + 2) + 2 * worldGrassBorderWidth; x++) {
+                for(int z = 0; z < (pixelsPerBlock + 2) + 2 * worldGrassBorderWidth; z++) {
                     getPlatformOrigin().add(x, -1, z).getBlock().setType(Material.DIRT);
                 }
             }
 
-            for (int x = 0; x < (pixelsPerBlock+2); x++) {
-                for (int z = 0; z < (pixelsPerBlock+2); z++) {
+            for(int x = 0; x < (pixelsPerBlock + 2); x++) {
+                for(int z = 0; z < (pixelsPerBlock + 2); z++) {
                     Location currentLocation = getBorderOrigin().add(x, 0, z);
 
-                    if (currentLocation.x() == getBorderOrigin().x() || currentLocation.z() == getBorderOrigin().z()) {
+                    if(currentLocation.x() == getBorderOrigin().x() || currentLocation.z() == getBorderOrigin().z()) {
                         currentLocation.getBlock().setType(Material.RED_CONCRETE);
-                    } else if (currentLocation.x() == getBorderOrigin().x() + (pixelsPerBlock+1) || currentLocation.z() == getBorderOrigin().z() + (pixelsPerBlock+1)) {
+                    } else if(currentLocation.x() == getBorderOrigin().x() + (pixelsPerBlock + 1) || currentLocation.z() == getBorderOrigin().z() + (pixelsPerBlock + 1)) {
                         currentLocation.getBlock().setType(Material.RED_CONCRETE);
                     }
                 }
@@ -140,36 +177,39 @@ public class PixelBlockWorld {
 
             Random random = new Random();
             LocationUtil.iterateBlocks(getPlatformOrigin().add(1, 1, 1), getPlatformOriginEnd().add(0, 1, 0), location -> {
-                if (allowPlacements(location)) return;
-                if (!location.clone().subtract(0, 1, 0).getBlock().getType().equals(Material.GRASS_BLOCK)) return;
-                if (!location.getBlock().getType().equals(Material.AIR)) return;
+                if(allowPlacements(location)) return;
+                if(!location.clone().subtract(0, 1, 0).getBlock().getType().equals(Material.GRASS_BLOCK)) return;
 
-                if (random.nextInt(10) == 0) {
-                    Material[] flowers = {
-                            Material.DANDELION,
-                            Material.POPPY,
-                            Material.BLUE_ORCHID,
-                            Material.ALLIUM,
-                            Material.AZURE_BLUET,
-                            Material.RED_TULIP,
-                            Material.ORANGE_TULIP,
-                            Material.WHITE_TULIP,
-                            Material.CORNFLOWER,
-                            Material.LILY_OF_THE_VALLEY,
-                    };
+                List<Material> flowers = List.of(
+                    Material.DANDELION,
+                    Material.POPPY,
+                    Material.BLUE_ORCHID,
+                    Material.ALLIUM,
+                    Material.AZURE_BLUET,
+                    Material.RED_TULIP,
+                    Material.ORANGE_TULIP,
+                    Material.WHITE_TULIP,
+                    Material.CORNFLOWER,
+                    Material.LILY_OF_THE_VALLEY,
+                    Material.SHORT_GRASS,
+                    Material.TALL_GRASS
+                );
+                if(flowers.contains(location.getBlock().getType())) location.getBlock().setType(Material.AIR);
+                if(!location.getBlock().getType().equals(Material.AIR)) return;
 
-                    Material randomFlower = flowers[random.nextInt(flowers.length)];
+                if(random.nextInt(30) == 0) {
+                    Material randomFlower = flowers.get(random.nextInt(flowers.size()));
                     location.getBlock().setType(randomFlower);
                 }
             });
 
-            for (int x = 0; x < 4; x++) {
-                for (int y = 0; y < 5; y++) {
+            for(int x = 0; x < 4; x++) {
+                for(int y = 0; y < 5; y++) {
                     getPortalLocation().add(x, y, 0).getBlock().setType(Material.OBSIDIAN);
                 }
             }
-            for (int x = 1; x < 3; x++) {
-                for (int y = 1; y < 4; y++) {
+            for(int x = 1; x < 3; x++) {
+                for(int y = 1; y < 4; y++) {
                     getPortalLocation().add(x, y, 0).getBlock().setType(Material.NETHER_PORTAL);
                 }
             }
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/Pixels.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/Pixels.java
new file mode 100644
index 0000000..32d6501
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/Pixels.java
@@ -0,0 +1,62 @@
+package eu.mhsl.minecraft.pixelblocks.pixelblock;
+
+import eu.mhsl.minecraft.pixelblocks.Main;
+import eu.mhsl.minecraft.pixelblocks.utils.ListUtil;
+import org.bukkit.Location;
+import org.bukkit.NamespacedKey;
+import org.bukkit.entity.BlockDisplay;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.EntityType;
+import org.bukkit.persistence.PersistentDataType;
+import org.bukkit.util.Transformation;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+import java.util.Objects;
+
+public class Pixels {
+    private static final NamespacedKey pixelOfTag = new NamespacedKey(Main.plugin(), "pixel_of");
+
+    private final @NotNull PixelBlock parentBlock;
+
+    public Pixels(@NotNull PixelBlock parentBlock) {
+        this.parentBlock = parentBlock;
+    }
+
+    public void spawn() {
+        ListUtil.splitListInParts(10, this.parentBlock.getPixelData())
+            .forEach(pixels -> parentBlock.getBlockTaskChain()
+                .delay(1)
+                .sync(() -> pixels.forEach(this::spawnPixel))
+                .execute());
+    }
+
+    private void spawnPixel(PixelBlockWorld.PixelData pixelData) {
+        Location pixelBlockLocation = this.parentBlock.getPixelBlockLocation();
+        Location pixelLocation = pixelBlockLocation.add(pixelData.relativeLocation().multiply(pixelData.scale()));
+        BlockDisplay entity = (BlockDisplay) pixelBlockLocation.getWorld().spawnEntity(pixelLocation, EntityType.BLOCK_DISPLAY);
+
+        entity.setBlock(pixelData.block());
+        Transformation transform = entity.getTransformation();
+        transform.getScale().set(pixelData.scale());
+        entity.setTransformation(transform);
+        entity.getPersistentDataContainer().set(pixelOfTag, PersistentDataType.STRING, this.parentBlock.getBlockUUID().toString());
+    }
+
+    public void destroy() {
+        List<BlockDisplay> entities = parentBlock.getPixelBlockLocation().getNearbyEntitiesByType(BlockDisplay.class, 1)
+            .stream()
+            .filter(blockDisplay -> blockDisplay.getPersistentDataContainer().has(pixelOfTag))
+            .filter(blockDisplay -> Objects.equals(
+                blockDisplay.getPersistentDataContainer().get(pixelOfTag, PersistentDataType.STRING),
+                parentBlock.getBlockUUID().toString()
+            ))
+            .toList();
+
+        ListUtil.splitListInParts(10, entities)
+            .forEach(pixels -> parentBlock.getBlockTaskChain()
+                .delay(1)
+                .sync(() -> pixels.forEach(Entity::remove))
+                .execute());
+    }
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/utils/ListUtil.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/utils/ListUtil.java
new file mode 100644
index 0000000..d51b49d
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/utils/ListUtil.java
@@ -0,0 +1,19 @@
+package eu.mhsl.minecraft.pixelblocks.utils;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+public class ListUtil {
+    public static <T> List<List<T>> splitListInParts(int parts, List<T> list) {
+        int actualParts = Math.min(parts, list.size());
+        int chunkSize = (int) Math.ceil((double) list.size() / actualParts);
+        return IntStream.range(0, actualParts)
+            .mapToObj(i -> list.stream()
+                .skip((long) i * chunkSize)
+                .limit(chunkSize)
+                .collect(Collectors.toList()))
+            .toList();
+    }
+
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/utils/LocationUtil.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/utils/LocationUtil.java
index 83bc1f1..990ea31 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/utils/LocationUtil.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/utils/LocationUtil.java
@@ -7,7 +7,7 @@ import java.util.function.Consumer;
 
 public class LocationUtil {
     public static void iterateBlocks(Location loc1, Location loc2, Consumer<Location> action) {
-        if (loc1.getWorld() != loc2.getWorld()) {
+        if(loc1.getWorld() != loc2.getWorld()) {
             throw new IllegalArgumentException("Locations must be in the same world");
         }
 
@@ -19,9 +19,9 @@ public class LocationUtil {
         int minZ = Math.min(loc1.getBlockZ(), loc2.getBlockZ());
         int maxZ = Math.max(loc1.getBlockZ(), loc2.getBlockZ());
 
-        for (int x = minX; x <= maxX; x++) {
-            for (int y = minY; y <= maxY; y++) {
-                for (int z = minZ; z <= maxZ; z++) {
+        for(int x = minX; x <= maxX; x++) {
+            for(int y = minY; y <= maxY; y++) {
+                for(int z = minZ; z <= maxZ; z++) {
                     action.accept(new Location(world, x, y, z));
                 }
             }
diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/utils/MinMaxUtil.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/utils/MinMaxUtil.java
index 861752e..f7ef999 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelblocks/utils/MinMaxUtil.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/utils/MinMaxUtil.java
@@ -8,6 +8,7 @@ public class MinMaxUtil {
     public static <I, O extends Comparable<O>> O getMinProperty(List<I> list, Function<I, O> supplier) {
         return supplier.apply(list.stream().min(Comparator.comparing(supplier)).orElseThrow());
     }
+
     public static <I, O extends Comparable<O>> O getMaxProperty(List<I> list, Function<I, O> supplier) {
         return supplier.apply(list.stream().max(Comparator.comparing(supplier)).orElseThrow());
     }
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 3ec46db..0f5cb33 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -1,6 +1,6 @@
 name: PixelBlocks
 version: '${version}'
-main: eu.mhsl.minecraft.pixelblocks.PixelBlocksPlugin
+main: eu.mhsl.minecraft.pixelblocks.Main
 api-version: '1.21'
 commands:
   createpixelblock: