Compare commits

...

35 Commits

Author SHA1 Message Date
7a41145b8c fixed banner rotation and double bed issues,
added DestroyPixelBlocksCommand
2025-07-14 19:32:22 +02:00
36dd0535c3 fixed more orientation errors when placing from other directions 2025-07-14 16:02:37 +02:00
0bfb031212 fixed some orientation errors 2025-07-14 00:06:44 +02:00
8851f3c032 added givepixelblock command 2025-01-01 17:58:10 +01:00
e45bbfc07b fixed crafting advancement and crafting bug 2024-12-29 16:33:38 +01:00
224f4a9f1e fixed pixelblocks being placable in protected regions 2024-12-24 18:06:43 +01:00
fc043a2529 changed annotation for place pixelblock listener 2024-12-24 12:20:08 +01:00
3ed98d0d9e changed annotation for break pixelblock listener 2024-12-24 12:17:32 +01:00
9807f737f8 removed unnecessary logging 2024-12-19 09:43:01 +01:00
a82945909d removed dead code 2024-12-19 00:50:23 +01:00
b9cef8f567 added block rotation 2024-12-19 00:34:23 +01:00
d3c5a6c6b0 fixed race condition 2024-12-18 23:34:45 +01:00
8bb2d382b9 fixed various bugs 2024-12-14 14:59:42 +01:00
88b55d4d7c Merge pull request 'feature-remove-db' (#8) from feature-remove-db into main
Reviewed-on: #8
2024-10-19 13:25:56 +00:00
8a6577afa0 removed offset, fixed hitbox 2024-10-19 15:24:05 +02:00
744f1dd6e2 fixed pixel duplication because of unloaded chunks 2024-10-15 21:25:11 +02:00
46782b97c8 fixed flowers generational stacking on server start 2024-10-15 21:12:16 +02:00
03c26bc2f9 code reformat 2024-10-15 20:40:45 +02:00
796bee9696 further variable encapsulation 2024-10-15 20:37:36 +02:00
b8bcb5e580 fixed double item dropping and fixed exitWorld command 2024-10-15 20:03:14 +02:00
c3bf1943a5 refactored to not store entities 2024-10-13 18:38:05 +02:00
2810db2e93 refactoring 2024-10-13 16:26:41 +02:00
70c7059e43 refactored pixelblock and fully utilizing taskchains 2024-10-12 03:03:30 +02:00
503c596616 refactored hitbox and placeholders 2024-10-12 01:00:55 +02:00
ab58b291c8 added defering of pixel spawning 2024-10-11 20:03:15 +02:00
4bda427693 wip: refactoring with persistent storage containers 2024-10-10 22:08:48 +02:00
9c68308eeb preventing growth outside of pixel block borders 2024-10-09 13:34:31 +02:00
8d70c4b7b3 refactored hitbox logic to external class 2024-10-05 23:13:13 +02:00
32e6eb259e prevent players in pixelblock wile being offline 2024-10-05 22:40:22 +02:00
154c1cf936 changed placeholder 2024-10-05 22:24:27 +02:00
156b7e6b61 added logging 2024-10-05 19:24:36 +02:00
39af3589e3 restructured sql queries 2024-10-04 23:34:51 +02:00
92a3edc0b6 added listener to exit pixelblock when leaving the server 2024-10-04 21:02:48 +02:00
ab71f09f8a added and implemented taskchain 2024-10-04 20:42:17 +02:00
4f4a6aef10 wip: added persistent data tags on items 2024-09-03 21:35:23 +02:00
43 changed files with 1097 additions and 670 deletions

View File

@ -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>

1
.idea/modules.xml generated
View File

@ -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>

View File

@ -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>

View File

@ -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"
compileOnly "io.papermc.paper:paper-api:1.21.7-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/lars/Documents/Minecraft/Server/pixelblocks/plugins/PixelBlocks-1.0-SNAPSHOT-all.jar'
}
shadowJar {
relocate 'co.aikar.taskchain', 'eu.mhsl.minecraft.pixelblocks.taskchain'
}
jar.dependsOn shadowJar
copyJarToTestServer.dependsOn jar

View File

@ -0,0 +1,119 @@
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.DestroyPixelBlocksCommand;
import eu.mhsl.minecraft.pixelblocks.commands.ExitWorldCommand;
import eu.mhsl.minecraft.pixelblocks.commands.GivePixelBlockCommand;
import eu.mhsl.minecraft.pixelblocks.listeners.*;
import eu.mhsl.minecraft.pixelblocks.pixelblock.PixelBlock;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.logging.Logger;
public final class Main extends JavaPlugin {
private static Main plugin;
private static PixelBlockConfiguration configuration;
private static PixelBlockDatabase database;
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() {
Main.plugin = this;
FileConfiguration config = this.getConfig();
PixelBlockConfiguration.setDefaults(config);
this.saveConfig();
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");
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(),
new FallOutOfPixelBlockListener(),
new BreakPixelListener(),
new PlacePixelBlockListener(),
new PreventInventorysListener(),
new ExitPixelWorldListener(),
new PreventIllegalBlocksListener(),
new BreakPixelBlockListener(),
new PlacePixelListener(),
new PreventHopperActionsListener(),
new PreventLiquidsFlowListener(),
new PreventRedstoneListener(),
new PreventEntityPlacementListener(),
new DiscoverRecipesListener(),
new QuitWhileInPixelBlockListener(),
new PreventGrowthListener(),
new CraftPixelBlockListener()
};
for(Listener listener : listeners) {
getServer().getPluginManager().registerEvents(listener, plugin);
}
Objects.requireNonNull(getCommand("createpixelblock")).setExecutor(new CreatePixelBlockCommand());
Objects.requireNonNull(getCommand("givepixelblock")).setExecutor(new GivePixelBlockCommand());
Objects.requireNonNull(getCommand("exitworld")).setExecutor(new ExitWorldCommand());
Objects.requireNonNull(getCommand("destroypixelblocks")).setExecutor(new DestroyPixelBlocksCommand());
Bukkit.addRecipe(PixelBlockItem.getRecipe());
}
@Override
public void onDisable() {
Bukkit.getOnlinePlayers().forEach(QuitWhileInPixelBlockListener::kickPlayerOutOfWorld);
try {
database.close();
} 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;
}
}

View File

@ -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;
}

View File

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

View File

@ -11,30 +11,55 @@ 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 String itemTexture = "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzE5NGU5ZTc3NTdkMDZkNmY1ZTViZTg0NTQ4YTdjYjUyMTczZDY4Y2NmODAyZDIxMTI3NWQzMWNkYmEwYTA2ZSJ9fX0=";
public static final NamespacedKey recipeKey = new NamespacedKey(PixelBlocksPlugin.plugin, "pixelblock");
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 UUID emptyBlockUUID = UUID.fromString("98fdf0ae-c3ab-4ef7-ae25-efd518d600de");
public static final String itemTexture = "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQ" +
"ubmV0L3RleHR1cmUvYzE5NGU5ZTc3NTdkMDZkNmY1ZTViZTg0NTQ4YTdjYjUyMTczZDY4Y2NmODAyZDIxMTI3NWQzMWNkYmEwYTA2ZSJ9fX0=";
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 +68,14 @@ public class PixelBlockItem {
public static @NotNull ItemStack getEmptyPixelBlock() {
ItemStack item = HeadUtil.getCustomTextureHead(itemTexture);
ItemMeta meta = item.getItemMeta();
meta.setMaxStackSize(1);
meta.itemName(Component.text(emptyBlockUUID.toString()));
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, emptyBlockUUID.toString());
item.setItemMeta(meta);
return item;
}
@ -61,7 +87,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;
}
}

View File

@ -1,84 +0,0 @@
package eu.mhsl.minecraft.pixelblocks;
import eu.mhsl.minecraft.pixelblocks.commands.CreatePixelBlockCommand;
import eu.mhsl.minecraft.pixelblocks.commands.ExitWorldCommand;
import eu.mhsl.minecraft.pixelblocks.listeners.*;
import eu.mhsl.minecraft.pixelblocks.pixelblock.PixelBlock;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public final class PixelBlocksPlugin extends JavaPlugin {
public static PixelBlocksPlugin plugin;
public static PixelBlockConfiguration configuration;
public static PixelBlockDatabase database;
public static List<PixelBlock> pixelBlocks = new ArrayList<>();
@Override
public void onLoad() {
PixelBlocksPlugin.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())
);
File databaseFile = new File(plugin.getDataFolder(), "blocks.db");
PixelBlocksPlugin.database = new PixelBlockDatabase("jdbc:sqlite:" + databaseFile);
}
@Override
public void onEnable() {
database.loadPixelBlocks();
Listener[] listeners = {
new EnterPixelBlockListener(),
new FallOutOfPixelBlockListener(),
new BreakPixelListener(),
new PlacePixelBlockListener(),
new PreventInventorysListener(),
new ExitPixelWorldListener(),
new CraftPixelBlockListener(),
new PreventIllegalBlocksListener(),
new BreakPixelBlockListener(),
new PlacePixelListener(),
new PreventHopperActionsListener(),
new PreventLiquidsFlowListener(),
new PreventPistonsListener(),
new PreventEntityPlacementListener(),
new DiscoverRecipesListener()
};
for (Listener listener : listeners) {
getServer().getPluginManager().registerEvents(listener, plugin);
}
Objects.requireNonNull(getCommand("createpixelblock")).setExecutor(new CreatePixelBlockCommand());
Objects.requireNonNull(getCommand("exitworld")).setExecutor(new ExitWorldCommand());
Bukkit.addRecipe(PixelBlockItem.getRecipe());
}
@Override
public void onDisable() {
try {
database.close();
} catch (SQLException e) {
throw new RuntimeException("Failed disabling", e);
}
}
}

View File

@ -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;
}

View File

@ -0,0 +1,19 @@
package eu.mhsl.minecraft.pixelblocks.commands;
import eu.mhsl.minecraft.pixelblocks.Main;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class DestroyPixelBlocksCommand implements CommandExecutor {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if(sender instanceof Player p) {
Main.pixelBlocks.forEach(pixelBlock -> pixelBlock.destroy(Bukkit.getPlayer(pixelBlock.getOwnerUUID())));
}
return true;
}
}

View File

@ -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;
}

View File

@ -0,0 +1,40 @@
package eu.mhsl.minecraft.pixelblocks.commands;
import eu.mhsl.minecraft.pixelblocks.PixelBlockItem;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.UUID;
import static eu.mhsl.minecraft.pixelblocks.PixelBlockItem.getEmptyPixelBlock;
public class GivePixelBlockCommand implements CommandExecutor {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if(sender instanceof Player p) {
ItemStack result = getEmptyPixelBlock();
ItemMeta itemMeta = result.getItemMeta();
PersistentDataContainer dataContainer = itemMeta.getPersistentDataContainer();
if(!dataContainer.has(PixelBlockItem.idProperty)) return false;
String currentId = dataContainer.get(PixelBlockItem.idProperty, PersistentDataType.STRING);
Objects.requireNonNull(currentId);
if(!UUID.fromString(currentId).equals(PixelBlockItem.emptyBlockUUID)) return false;
dataContainer.set(PixelBlockItem.idProperty, PersistentDataType.STRING, args[0]);
result.setItemMeta(itemMeta);
p.getInventory().addItem(result);
}
return true;
}
}

View File

@ -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());

View File

@ -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(),

View File

@ -1,25 +1,30 @@
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 org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.Objects;
import java.util.UUID;
public class CraftPixelBlockListener implements Listener {
@EventHandler
static void onCraftItem(CraftItemEvent event) {
ItemStack craftedItem = event.getCurrentItem();
Objects.requireNonNull(craftedItem);
public void onCraft(CraftItemEvent event) {
ItemStack result = event.getInventory().getResult();
if(result == null) return;
ItemMeta itemMeta = result.getItemMeta();
PersistentDataContainer dataContainer = itemMeta.getPersistentDataContainer();
if(!dataContainer.has(PixelBlockItem.idProperty)) return;
String currentId = dataContainer.get(PixelBlockItem.idProperty, PersistentDataType.STRING);
Objects.requireNonNull(currentId);
if(!UUID.fromString(currentId).equals(PixelBlockItem.emptyBlockUUID)) return;
ItemMeta craftedItemMeta = craftedItem.getItemMeta();
String itemName = PlainTextComponentSerializer.plainText().serialize(craftedItemMeta.itemName());
if(!itemName.equals(PixelBlockItem.unusedBlockID.toString())) return;
if(event.isShiftClick()) event.setCancelled(true);
dataContainer.set(PixelBlockItem.idProperty, PersistentDataType.STRING, UUID.randomUUID().toString());
result.setItemMeta(itemMeta);
}
}

View File

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

View File

@ -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());

View File

@ -5,31 +5,28 @@ 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.entity.EntityPortalEvent;
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());
}
@EventHandler
public void onEntityPortal(EntityPortalEvent event) {
World pixelBlockWorld = event.getFrom().getWorld();
if(!PixelBlockWorld.isPixelWorld(pixelBlockWorld)) return;
event.setCancelled(true);
}
}

View File

@ -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;

View File

@ -1,36 +1,30 @@
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;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
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) {
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
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 +33,12 @@ 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
);
}
}

View File

@ -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())
);
}
}

View File

@ -4,10 +4,16 @@ import eu.mhsl.minecraft.pixelblocks.utils.EventCanceling;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityPlaceEvent;
import org.bukkit.event.entity.ProjectileLaunchEvent;
public class PreventEntityPlacementListener implements Listener {
@EventHandler
public static void preventPlace(EntityPlaceEvent event) {
public void preventPlace(EntityPlaceEvent event) {
EventCanceling.cancelIfInPixelWorld(event, event.getBlock().getWorld());
}
@EventHandler
public void preventProjectile(ProjectileLaunchEvent event) {
EventCanceling.cancelIfInPixelWorld(event, event.getEntity().getWorld());
}
}

View File

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

View File

@ -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(),

View File

@ -1,31 +1,40 @@
package eu.mhsl.minecraft.pixelblocks.listeners;
import eu.mhsl.minecraft.pixelblocks.utils.EventCanceling;
import org.bukkit.Material;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockExplodeEvent;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.hanging.HangingPlaceEvent;
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 onItemFrame(HangingPlaceEvent event) {
if(event.getItemStack() == null || event.getPlayer() == null) return;
if(!(event.getItemStack().getType().equals(Material.ITEM_FRAME) || event.getItemStack().getType().equals(Material.GLOW_ITEM_FRAME))) return;
EventCanceling.cancelIfInPixelWorld(event, event.getPlayer().getWorld());
}
@EventHandler
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());
}
}

View File

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

View File

@ -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())
);
}
}

View File

@ -1,19 +0,0 @@
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.block.BlockPistonExtendEvent;
public class PreventPistonsListener implements Listener {
@EventHandler
public static 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())))
);
}
}

View File

@ -0,0 +1,27 @@
package eu.mhsl.minecraft.pixelblocks.listeners;
import eu.mhsl.minecraft.pixelblocks.pixelblock.PixelBlockWorld;
import eu.mhsl.minecraft.pixelblocks.utils.EventCanceling;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPistonExtendEvent;
import org.bukkit.event.block.BlockRedstoneEvent;
public class PreventRedstoneListener implements Listener {
@EventHandler
public void preventRedstone(BlockRedstoneEvent event) {
boolean isInPixelBlock = PixelBlockWorld.isPixelWorld(event.getBlock().getWorld());
if(isInPixelBlock) event.setNewCurrent(0);
}
@EventHandler
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())))
);
}
}

View File

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

View File

@ -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();
}
}

View File

@ -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 isAccessible = false;
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,205 @@ 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);
this.getBlockTaskChain().sync(() -> this.isAccessible = true).execute();
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();
this.getBlockTaskChain().sync(() -> this.isAccessible = true).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(() -> Main.database().savePixelBlock(this))
.sync(() -> {
if(!this.isAccessible) 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);
}
}
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);
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 destroy(Player destroyedBy) {
if(PixelBlocksPlugin.configuration.onlyBreakableByOwner() && !destroyedBy.getUniqueId().equals(ownerUUID)) {
if(!this.isAccessible) 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.isAccessible = 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;
}
}

View File

@ -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+0.5)/pixelsPerBlock,
(startingY/pixelsPerBlock)-0,
((startingZ+endingZ)/2+0.5)/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);
}
}

View File

@ -1,5 +0,0 @@
package eu.mhsl.minecraft.pixelblocks.pixelblock;
public class PixelBlockInteraction {
}

View File

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

View File

@ -1,29 +1,35 @@
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.BlockState;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Directional;
import org.bukkit.block.data.Rotatable;
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 org.jetbrains.annotations.Nullable;
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 +50,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 +63,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 +94,49 @@ public class PixelBlockWorld {
return getBorderOrigin().add(worldGrassBorderWidth + pixelsPerBlock, 0, worldGrassBorderWidth + pixelsPerBlock);
}
public record PixelData(Vector relativeLocation, BlockData block, @Nullable Directional directional, @Nullable Rotatable rotatable, BlockState state, 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 blockData = blockLocation.getBlock().getBlockData();
@Nullable Directional directional = blockData instanceof Directional face ? face : null;
@Nullable Rotatable rotatable = blockData instanceof Rotatable rotation ? rotation : null;
BlockState state = blockLocation.getBlock().getState();
if(!blockData.getMaterial().isAir()) {
pixelData.add(new PixelData(relativeLocation.toVector(), blockData, directional, rotatable, state, (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 +151,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 +184,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);
}
}

View File

@ -0,0 +1,122 @@
package eu.mhsl.minecraft.pixelblocks.pixelblock;
import eu.mhsl.minecraft.pixelblocks.Main;
import eu.mhsl.minecraft.pixelblocks.utils.Direction;
import eu.mhsl.minecraft.pixelblocks.utils.ListUtil;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Banner;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.data.Directional;
import org.bukkit.block.data.Rotatable;
import org.bukkit.block.data.type.Bed;
import org.bukkit.block.data.type.Chest;
import org.bukkit.block.data.type.EnderChest;
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.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector3d;
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()));
Vector3d centerOffset = new Vector3d(0.5, 0.5, 0.5).mul(pixelData.scale());
BlockDisplay entity = (BlockDisplay) pixelBlockLocation.getWorld().spawnEntity(
pixelLocation.add(Vector.fromJOML(centerOffset)),
EntityType.BLOCK_DISPLAY
);
if(!(pixelData.directional() instanceof Bed bed && bed.getPart().equals(Bed.Part.FOOT))) entity.setBlock(pixelData.block());
this.setEntityDirection(entity, pixelData.directional());
this.setEntityRotation(entity, pixelData.rotatable(), pixelData.state());
Transformation transform = entity.getTransformation();
transform.getScale().set(pixelData.scale());
transform.getTranslation().set(centerOffset.mul(-1));
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());
}
private void setEntityRotation(Entity entity, @Nullable Rotatable rotatable, BlockState blockState) {
if(rotatable == null) return;
Vector rotation = rotatable.getRotation().getDirection();
float yaw = (float) Math.toDegrees(Math.atan2(rotation.getX(), -rotation.getZ()));
if(blockState instanceof Banner) yaw += 180;
entity.setRotation(entity.getYaw()+yaw, entity.getPitch());
}
private void setEntityDirection(Entity entity, @Nullable Directional direction) {
Direction blockDirection = parentBlock.getFacingDirection();
float angle = switch (blockDirection) {
case Direction.north -> 180;
case Direction.south -> 0;
case Direction.west -> 90;
case Direction.east -> -90;
};
entity.setRotation(entity.getYaw()+angle, entity.getPitch());
if (!(direction instanceof EnderChest || direction instanceof Chest || direction instanceof Bed)) return;
BlockFace blockFace = direction.getFacing();
float yaw = switch (blockFace) {
case NORTH -> 180;
case SOUTH -> 0;
case WEST -> 90;
case EAST -> -90;
default -> entity.getLocation().getYaw();
};
float pitch = switch (blockFace) {
case UP -> 90;
case DOWN -> -90;
default -> 0;
};
entity.setRotation(entity.getYaw()+yaw, entity.getPitch()+pitch);
}
}

View File

@ -1,18 +1,23 @@
package eu.mhsl.minecraft.pixelblocks.utils;
import eu.mhsl.minecraft.pixelblocks.Main;
import eu.mhsl.minecraft.pixelblocks.pixelblock.PixelBlock;
import eu.mhsl.minecraft.pixelblocks.pixelblock.PixelBlockWorld;
import org.bukkit.World;
import org.bukkit.event.Cancellable;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.function.Function;
public class EventCanceling {
public static void shouldCancelInPixelBlock(Cancellable event, World world, Function<PixelBlock, Boolean> callback) {
if(!PixelBlockWorld.isPixelWorld(world)) return;
PixelBlock pixelBlock = PixelBlock.getPixelBlockFromBlockWorld(world);
Objects.requireNonNull(pixelBlock);
@Nullable PixelBlock pixelBlock = PixelBlock.getPixelBlockFromBlockWorld(world);
if(pixelBlock == null) {
Main.logger().warning("Cancelling place event because PixelBlock could not be found: " + world.getName());
event.setCancelled(true);
return;
}
if(callback.apply(pixelBlock)) event.setCancelled(true);
}

View File

@ -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();
}
}

View File

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

View File

@ -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());
}

View File

@ -1,7 +1,9 @@
name: PixelBlocks
version: '${version}'
main: eu.mhsl.minecraft.pixelblocks.PixelBlocksPlugin
main: eu.mhsl.minecraft.pixelblocks.Main
api-version: '1.21'
commands:
createpixelblock:
exitworld:
givepixelblock:
destroypixelblocks: