diff --git a/.mvn/maven-git-versioning-extension.xml b/.mvn/maven-git-versioning-extension.xml index 11980355ba..1677a00c94 100644 --- a/.mvn/maven-git-versioning-extension.xml +++ b/.mvn/maven-git-versioning-extension.xml @@ -11,14 +11,6 @@ ${commit.short}-Insider - - test/(.+) - ${ref}-${commit.short} - - - feat/(.+) - ${ref}-${commit.short} - ([0-9].*)> ${1} diff --git a/pom.xml b/pom.xml index 8947ba1f4c..4ee9306fe2 100644 --- a/pom.xml +++ b/pom.xml @@ -294,6 +294,13 @@ provided + + org.apache.logging.log4j + log4j-core + 2.24.1 + provided + + com.github.StarWishsama.dough diff --git a/src/main/java/city/norain/slimefun4/SlimefunExtended.java b/src/main/java/city/norain/slimefun4/SlimefunExtended.java index 57589b4f36..624185737a 100644 --- a/src/main/java/city/norain/slimefun4/SlimefunExtended.java +++ b/src/main/java/city/norain/slimefun4/SlimefunExtended.java @@ -3,12 +3,16 @@ import city.norain.slimefun4.compatibillty.VersionedEvent; import city.norain.slimefun4.listener.SlimefunMigrateListener; import city.norain.slimefun4.utils.EnvUtil; +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import com.zaxxer.hikari.pool.HikariPool; import io.github.bakedlibs.dough.versions.MinecraftVersion; import io.github.bakedlibs.dough.versions.UnknownServerVersionException; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import java.util.logging.Level; import javax.annotation.Nonnull; import lombok.Getter; +import org.apache.logging.log4j.core.config.Configurator; public final class SlimefunExtended { private static SlimefunMigrateListener migrateListener = new SlimefunMigrateListener(); @@ -22,6 +26,12 @@ public final class SlimefunExtended { private static void checkDebug() { if ("true".equals(System.getProperty("slimefun.database.debug"))) { databaseDebugMode = true; + + Slimefun.getSQLProfiler().start(); + } else { + Configurator.setLevel(HikariConfig.class.getName(), org.apache.logging.log4j.Level.OFF); + Configurator.setLevel(HikariDataSource.class.getName(), org.apache.logging.log4j.Level.OFF); + Configurator.setLevel(HikariPool.class.getName(), org.apache.logging.log4j.Level.OFF); } } diff --git a/src/main/java/city/norain/slimefun4/api/menu/UniversalMenu.java b/src/main/java/city/norain/slimefun4/api/menu/UniversalMenu.java new file mode 100644 index 0000000000..c5f7491342 --- /dev/null +++ b/src/main/java/city/norain/slimefun4/api/menu/UniversalMenu.java @@ -0,0 +1,73 @@ +package city.norain.slimefun4.api.menu; + +import java.util.UUID; +import javax.annotation.Nonnull; +import lombok.Getter; +import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; +import me.mrCookieSlime.Slimefun.api.inventory.DirtyChestMenu; +import org.bukkit.Location; +import org.bukkit.inventory.ItemStack; + +/** + * This class represents a universal chest menu + * which a menu located by certain identify id instead of location. + */ +public class UniversalMenu extends DirtyChestMenu { + @Getter + private final UUID uuid; + + public UniversalMenu(@Nonnull UniversalMenuPreset preset, @Nonnull UUID uuid) { + this(preset, uuid, (Location) null); + } + + public UniversalMenu(@Nonnull UniversalMenuPreset preset, @Nonnull UUID uuid, Location lastPresent) { + super(preset); + this.uuid = uuid; + + preset.clone(this, lastPresent); + this.getContents(); + } + + public UniversalMenu( + @Nonnull UniversalMenuPreset preset, @Nonnull UUID uuid, Location lastPresent, ItemStack[] contents) { + super(preset); + this.uuid = uuid; + + for (int i = 0; i < contents.length; i++) { + var item = contents[i]; + if (item == null || item.getType().isAir()) { + continue; + } + addItem(i, item); + } + + preset.clone(this, lastPresent); + this.getContents(); + } + + public UniversalMenu(@Nonnull UniversalMenuPreset preset, @Nonnull UUID uuid, ItemStack[] contents) { + this(preset, uuid, null, contents); + } + + public void update(@Nonnull Location lastPresent) { + ((UniversalMenuPreset) preset).clone(this, lastPresent); + } + + /** + * This method drops the contents of this {@link BlockMenu} on the ground at the given + * {@link Location}. + * + * @param l Where to drop these items + * @param slots The slots of items that should be dropped + */ + public void dropItems(Location l, int... slots) { + for (int slot : slots) { + ItemStack item = getItemInSlot(slot); + + if (item != null) { + l.getWorld().dropItemNaturally(l, item); + replaceExistingItem(slot, null); + } + } + } +} diff --git a/src/main/java/city/norain/slimefun4/api/menu/UniversalMenuPreset.java b/src/main/java/city/norain/slimefun4/api/menu/UniversalMenuPreset.java new file mode 100644 index 0000000000..50fb73a937 --- /dev/null +++ b/src/main/java/city/norain/slimefun4/api/menu/UniversalMenuPreset.java @@ -0,0 +1,75 @@ +package city.norain.slimefun4.api.menu; + +import com.xzavier0722.mc.plugin.slimefun4.storage.util.StorageCacheUtils; +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import me.mrCookieSlime.Slimefun.api.inventory.BlockMenuPreset; +import me.mrCookieSlime.Slimefun.api.inventory.DirtyChestMenu; +import org.bukkit.Location; +import org.bukkit.block.Block; + +public abstract class UniversalMenuPreset extends BlockMenuPreset { + /** + * Creates a new ChestMenu with the specified + * Title + * + * @param title The title of the Menu + */ + public UniversalMenuPreset(@Nonnull String id, @Nonnull String title) { + super(id, title); + } + + public void newInstance(@Nonnull UniversalMenu menu, @Nonnull Block b) { + // This method can optionally be overridden by implementations + } + + @Override + protected void clone(@Nonnull DirtyChestMenu menu) { + if (menu instanceof UniversalMenu universalMenu) { + var uniData = StorageCacheUtils.getUniversalBlock(universalMenu.getUuid()); + + if (uniData == null) { + return; + } + + clone(universalMenu, uniData.getLastPresent().toLocation()); + } + } + + protected void clone(@Nonnull UniversalMenu menu, @Nonnull Location lastPresent) { + menu.setPlayerInventoryClickable(true); + + for (int slot : occupiedSlots) { + menu.addItem(slot, getItemInSlot(slot)); + } + + if (getSize() > -1) { + menu.addItem(getSize() - 1, null); + } + + newInstance(menu, lastPresent.getBlock()); + + for (int slot = 0; slot < 54; slot++) { + if (getMenuClickHandler(slot) != null) { + menu.addMenuClickHandler(slot, getMenuClickHandler(slot)); + } + } + + menu.addMenuOpeningHandler(getMenuOpeningHandler()); + menu.addMenuCloseHandler(getMenuCloseHandler()); + } + + @Nullable public static UniversalMenuPreset getPreset(@Nullable String id) { + if (id == null) { + return null; + } else { + var preset = Slimefun.getRegistry().getMenuPresets().get(id); + if (preset instanceof UniversalMenuPreset uniPreset) { + return uniPreset; + } else { + return null; + } + } + } +} diff --git a/src/main/java/city/norain/slimefun4/utils/ClassUtil.java b/src/main/java/city/norain/slimefun4/utils/ClassUtil.java new file mode 100644 index 0000000000..ea25c7cd37 --- /dev/null +++ b/src/main/java/city/norain/slimefun4/utils/ClassUtil.java @@ -0,0 +1,16 @@ +package city.norain.slimefun4.utils; + +import lombok.experimental.UtilityClass; + +@UtilityClass +public class ClassUtil { + public String getCallerClass() { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + + if (stackTrace.length > 3) { + return stackTrace[3].getClassName(); + } else { + return null; + } + } +} diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/mysql/MysqlAdapter.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/mysql/MysqlAdapter.java index 18ae9ffb44..28f8bb655b 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/mysql/MysqlAdapter.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/mysql/MysqlAdapter.java @@ -14,6 +14,8 @@ import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.FIELD_PLAYER_UUID; import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.FIELD_RESEARCH_KEY; import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.FIELD_SLIMEFUN_ID; +import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.FIELD_UNIVERSAL_TRAITS; +import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.FIELD_UNIVERSAL_UUID; import com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlCommonAdapter; import com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlUtils; @@ -39,6 +41,7 @@ public void initStorage(DataType type) { blockDataTable = SqlUtils.mapTable(DataScope.BLOCK_DATA, config.tablePrefix()); blockInvTable = SqlUtils.mapTable(DataScope.BLOCK_INVENTORY, config.tablePrefix()); chunkDataTable = SqlUtils.mapTable(DataScope.CHUNK_DATA, config.tablePrefix()); + universalInvTable = SqlUtils.mapTable(DataScope.UNIVERSAL_INVENTORY, config.tablePrefix()); createBlockStorageTables(); } } @@ -124,6 +127,9 @@ private void createBlockStorageTables() { createBlockDataTable(); createBlockInvTable(); createChunkDataTable(); + createUniversalInventoryTable(); + createUniversalRecordTable(); + createUniversalDataTable(); } private void createProfileTable() { @@ -312,4 +318,65 @@ private void createBlockInvTable() { + ")" + ");"); } + + private void createUniversalInventoryTable() { + executeSql("CREATE TABLE IF NOT EXISTS " + + universalInvTable + + "(" + + FIELD_UNIVERSAL_UUID + + " CHAR(64) NOT NULL, " + + FIELD_INVENTORY_SLOT + + " TINYINT UNSIGNED NOT NULL, " + + FIELD_INVENTORY_ITEM + + " CHAR(64) NOT NULL," + + "PRIMARY KEY (" + + FIELD_UNIVERSAL_UUID + + ", " + + FIELD_INVENTORY_SLOT + + ")" + + ");"); + } + + private void createUniversalRecordTable() { + executeSql("CREATE TABLE IF NOT EXISTS " + + universalRecordTable + + "(" + + FIELD_UNIVERSAL_UUID + + " CHAR(64) NOT NULL, " + + FIELD_SLIMEFUN_ID + + " CHAR(64) NOT NULL, " + + FIELD_UNIVERSAL_TRAITS + + " CHAR(64) NOT NULL, " + + "PRIMARY KEY (" + + FIELD_UNIVERSAL_UUID + + ")" + + ");"); + } + + private void createUniversalDataTable() { + executeSql("CREATE TABLE IF NOT EXISTS " + + universalDataTable + + "(" + + FIELD_UNIVERSAL_UUID + + " CHAR(64) NOT NULL, " + + FIELD_DATA_KEY + + " CHAR(64) NOT NULL, " + + FIELD_DATA_VALUE + + " TEXT NOT NULL, " + + "FOREIGN KEY (" + + FIELD_UNIVERSAL_UUID + + ") " + + "REFERENCES " + + universalRecordTable + + "(" + + FIELD_UNIVERSAL_UUID + + ") " + + "ON UPDATE CASCADE ON DELETE CASCADE, " + + "PRIMARY KEY (" + + FIELD_UNIVERSAL_UUID + + ", " + + FIELD_DATA_KEY + + ")" + + ");"); + } } diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/postgresql/PostgreSqlAdapter.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/postgresql/PostgreSqlAdapter.java index e1b550552e..8d4ab96c7a 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/postgresql/PostgreSqlAdapter.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/postgresql/PostgreSqlAdapter.java @@ -14,6 +14,8 @@ import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.FIELD_PLAYER_UUID; import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.FIELD_RESEARCH_KEY; import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.FIELD_SLIMEFUN_ID; +import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.FIELD_UNIVERSAL_TRAITS; +import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.FIELD_UNIVERSAL_UUID; import com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlCommonAdapter; import com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlUtils; @@ -41,6 +43,9 @@ public void initStorage(DataType type) { blockDataTable = SqlUtils.mapTable(DataScope.BLOCK_DATA, config.tablePrefix()); blockInvTable = SqlUtils.mapTable(DataScope.BLOCK_INVENTORY, config.tablePrefix()); chunkDataTable = SqlUtils.mapTable(DataScope.CHUNK_DATA, config.tablePrefix()); + universalInvTable = SqlUtils.mapTable(DataScope.UNIVERSAL_INVENTORY, config.tablePrefix()); + universalDataTable = SqlUtils.mapTable(DataScope.UNIVERSAL_DATA, config.tablePrefix()); + universalRecordTable = SqlUtils.mapTable(DataScope.UNIVERSAL_RECORD, config.tablePrefix()); createBlockStorageTables(); } } @@ -136,6 +141,9 @@ private void createBlockStorageTables() { createBlockDataTable(); createBlockInvTable(); createChunkDataTable(); + createUniversalInventoryTable(); + createUniversalRecordTable(); + createUniversalDataTable(); } private void createProfileTable() { @@ -328,4 +336,65 @@ private void createBlockInvTable() { + ")" + ");"); } + + private void createUniversalInventoryTable() { + executeSql("CREATE TABLE IF NOT EXISTS " + + universalInvTable + + "(" + + FIELD_UNIVERSAL_UUID + + " UUID NOT NULL, " + + FIELD_INVENTORY_SLOT + + " TINYINT UNSIGNED NOT NULL, " + + FIELD_INVENTORY_ITEM + + " TEXT NOT NULL," + + "PRIMARY KEY (" + + FIELD_UNIVERSAL_UUID + + ", " + + FIELD_INVENTORY_SLOT + + ")" + + ");"); + } + + private void createUniversalRecordTable() { + executeSql("CREATE TABLE IF NOT EXISTS " + + universalRecordTable + + "(" + + FIELD_UNIVERSAL_UUID + + " UUID NOT NULL, " + + FIELD_SLIMEFUN_ID + + " TEXT NOT NULL, " + + FIELD_UNIVERSAL_TRAITS + + " TEXT NOT NULL, " + + "PRIMARY KEY (" + + FIELD_UNIVERSAL_UUID + + ")" + + ");"); + } + + private void createUniversalDataTable() { + executeSql("CREATE TABLE IF NOT EXISTS " + + universalDataTable + + "(" + + FIELD_UNIVERSAL_UUID + + " UUID NOT NULL, " + + FIELD_DATA_KEY + + " VARCHAR(64) NOT NULL, " + + FIELD_DATA_VALUE + + " TEXT NOT NULL, " + + "FOREIGN KEY (" + + FIELD_UNIVERSAL_UUID + + ") " + + "REFERENCES " + + universalRecordTable + + "(" + + FIELD_UNIVERSAL_UUID + + ") " + + "ON UPDATE CASCADE ON DELETE CASCADE, " + + "PRIMARY KEY (" + + FIELD_UNIVERSAL_UUID + + ", " + + FIELD_DATA_KEY + + ")" + + ");"); + } } diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/sqlcommon/SqlCommonAdapter.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/sqlcommon/SqlCommonAdapter.java index 332257d88f..63fe239000 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/sqlcommon/SqlCommonAdapter.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/sqlcommon/SqlCommonAdapter.java @@ -12,7 +12,13 @@ public abstract class SqlCommonAdapter implements IDataSourceAdapter { protected HikariDataSource ds; protected String profileTable, researchTable, backpackTable, bpInvTable; - protected String blockRecordTable, blockDataTable, chunkDataTable, blockInvTable; + protected String blockRecordTable, + blockDataTable, + universalRecordTable, + universalDataTable, + chunkDataTable, + blockInvTable, + universalInvTable; protected T config; @Override @@ -56,6 +62,9 @@ protected String mapTable(DataScope scope) { case CHUNK_DATA -> chunkDataTable; case BLOCK_DATA -> blockDataTable; case BLOCK_RECORD -> blockRecordTable; + case UNIVERSAL_INVENTORY -> universalInvTable; + case UNIVERSAL_RECORD -> universalRecordTable; + case UNIVERSAL_DATA -> universalDataTable; case NONE -> throw new IllegalArgumentException("NONE cannot be a storage data scope!"); }; } @@ -72,5 +81,8 @@ public void shutdown() { blockRecordTable = null; chunkDataTable = null; blockInvTable = null; + universalInvTable = null; + universalDataTable = null; + universalRecordTable = null; } } diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/sqlcommon/SqlConstants.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/sqlcommon/SqlConstants.java index 49c32ad6fa..77af94134a 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/sqlcommon/SqlConstants.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/sqlcommon/SqlConstants.java @@ -9,6 +9,9 @@ public interface SqlConstants { String TABLE_NAME_BLOCK_DATA = "block_data"; String TABLE_NAME_CHUNK_DATA = "chunk_data"; String TABLE_NAME_BLOCK_INVENTORY = "block_inventory"; + String TABLE_NAME_UNIVERSAL_INVENTORY = "universal_inventory"; + String TABLE_NAME_UNIVERSAL_RECORD = "universal_record"; + String TABLE_NAME_UNIVERSAL_DATA = "universal_data"; String FIELD_PLAYER_UUID = "p_uuid"; String FIELD_PLAYER_NAME = "p_name"; @@ -29,4 +32,8 @@ public interface SqlConstants { String FIELD_DATA_KEY = "data_key"; String FIELD_DATA_VALUE = "data_val"; + + String FIELD_UNIVERSAL_UUID = "universal_uuid"; + + String FIELD_UNIVERSAL_TRAITS = "universal_traits"; } diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/sqlcommon/SqlUtils.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/sqlcommon/SqlUtils.java index 02cab7d1d2..d9a9d189f9 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/sqlcommon/SqlUtils.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/sqlcommon/SqlUtils.java @@ -14,6 +14,8 @@ import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.FIELD_PLAYER_UUID; import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.FIELD_RESEARCH_KEY; import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.FIELD_SLIMEFUN_ID; +import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.FIELD_UNIVERSAL_TRAITS; +import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.FIELD_UNIVERSAL_UUID; import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.TABLE_NAME_BACKPACK; import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.TABLE_NAME_BACKPACK_INVENTORY; import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.TABLE_NAME_BLOCK_DATA; @@ -22,6 +24,9 @@ import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.TABLE_NAME_CHUNK_DATA; import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.TABLE_NAME_PLAYER_PROFILE; import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.TABLE_NAME_PLAYER_RESEARCH; +import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.TABLE_NAME_UNIVERSAL_DATA; +import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.TABLE_NAME_UNIVERSAL_INVENTORY; +import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.TABLE_NAME_UNIVERSAL_RECORD; import com.xzavier0722.mc.plugin.slimefun4.storage.common.DataScope; import com.xzavier0722.mc.plugin.slimefun4.storage.common.FieldKey; @@ -58,6 +63,8 @@ public class SqlUtils { fieldMap.put(FieldKey.SLIMEFUN_ID, FIELD_SLIMEFUN_ID); fieldMap.put(FieldKey.DATA_KEY, FIELD_DATA_KEY); fieldMap.put(FieldKey.DATA_VALUE, FIELD_DATA_VALUE); + fieldMap.put(FieldKey.UNIVERSAL_UUID, FIELD_UNIVERSAL_UUID); + fieldMap.put(FieldKey.UNIVERSAL_TRAITS, FIELD_UNIVERSAL_TRAITS); mapper = new FieldMapper<>(fieldMap); } @@ -71,6 +78,9 @@ public static String mapTable(DataScope scope) { case BLOCK_DATA -> TABLE_NAME_BLOCK_DATA; case CHUNK_DATA -> TABLE_NAME_CHUNK_DATA; case BLOCK_INVENTORY -> TABLE_NAME_BLOCK_INVENTORY; + case UNIVERSAL_INVENTORY -> TABLE_NAME_UNIVERSAL_INVENTORY; + case UNIVERSAL_RECORD -> TABLE_NAME_UNIVERSAL_RECORD; + case UNIVERSAL_DATA -> TABLE_NAME_UNIVERSAL_DATA; case NONE -> throw new IllegalArgumentException("NONE cannot be a storage data scope!"); }; } diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/sqlite/SqliteAdapter.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/sqlite/SqliteAdapter.java index 37e071714d..e11f5b1f52 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/sqlite/SqliteAdapter.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/adapter/sqlite/SqliteAdapter.java @@ -14,6 +14,8 @@ import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.FIELD_PLAYER_UUID; import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.FIELD_RESEARCH_KEY; import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.FIELD_SLIMEFUN_ID; +import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.FIELD_UNIVERSAL_TRAITS; +import static com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlConstants.FIELD_UNIVERSAL_UUID; import city.norain.slimefun4.timings.entry.SQLEntry; import com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlCommonAdapter; @@ -118,6 +120,9 @@ private void createBlockStorageTables() { createBlockDataTable(); createBlockInvTable(); createChunkDataTable(); + createUniversalInventoryTable(); + createUniversalRecordTable(); + createUniversalDataTable(); } private void createProfileTable() { @@ -313,6 +318,67 @@ private void createBlockInvTable() { + ");"); } + private void createUniversalInventoryTable() { + executeSql("CREATE TABLE IF NOT EXISTS " + + SqlUtils.mapTable(DataScope.UNIVERSAL_INVENTORY) + + "(" + + FIELD_UNIVERSAL_UUID + + " CHAR(64) NOT NULL, " + + FIELD_INVENTORY_SLOT + + " TINYINT UNSIGNED NOT NULL, " + + FIELD_INVENTORY_ITEM + + " TEXT NOT NULL," + + "PRIMARY KEY (" + + FIELD_UNIVERSAL_UUID + + ", " + + FIELD_INVENTORY_SLOT + + ")" + + ");"); + } + + private void createUniversalRecordTable() { + executeSql("CREATE TABLE IF NOT EXISTS " + + SqlUtils.mapTable(DataScope.UNIVERSAL_RECORD) + + "(" + + FIELD_UNIVERSAL_UUID + + " CHAR(64) NOT NULL, " + + FIELD_SLIMEFUN_ID + + " CHAR(64) NOT NULL, " + + FIELD_UNIVERSAL_TRAITS + + " CHAR(64) NOT NULL, " + + "PRIMARY KEY (" + + FIELD_UNIVERSAL_UUID + + ")" + + ");"); + } + + private void createUniversalDataTable() { + executeSql("CREATE TABLE IF NOT EXISTS " + + SqlUtils.mapTable(DataScope.UNIVERSAL_DATA) + + "(" + + FIELD_UNIVERSAL_UUID + + " CHAR(64) NOT NULL, " + + FIELD_DATA_KEY + + " CHAR(64) NOT NULL, " + + FIELD_DATA_VALUE + + " TEXT NOT NULL, " + + "FOREIGN KEY (" + + FIELD_UNIVERSAL_UUID + + ") " + + "REFERENCES " + + SqlUtils.mapTable(DataScope.UNIVERSAL_RECORD) + + "(" + + FIELD_UNIVERSAL_UUID + + ") " + + "ON UPDATE CASCADE ON DELETE CASCADE, " + + "PRIMARY KEY (" + + FIELD_UNIVERSAL_UUID + + ", " + + FIELD_DATA_KEY + + ")" + + ");"); + } + public synchronized void executeSql(String sql) { super.executeSql(sql); } diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/common/DataScope.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/common/DataScope.java index 5795346ec6..9cb322da71 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/common/DataScope.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/common/DataScope.java @@ -9,7 +9,10 @@ public enum DataScope { BLOCK_RECORD(new FieldKey[] {FieldKey.LOCATION}), BLOCK_DATA(new FieldKey[] {FieldKey.LOCATION, FieldKey.DATA_KEY}), CHUNK_DATA(new FieldKey[] {FieldKey.CHUNK, FieldKey.DATA_KEY}), - BLOCK_INVENTORY(new FieldKey[] {FieldKey.LOCATION, FieldKey.INVENTORY_SLOT}); + BLOCK_INVENTORY(new FieldKey[] {FieldKey.LOCATION, FieldKey.INVENTORY_SLOT}), + UNIVERSAL_RECORD(new FieldKey[] {FieldKey.UNIVERSAL_UUID}), + UNIVERSAL_DATA(new FieldKey[] {FieldKey.UNIVERSAL_UUID, FieldKey.DATA_KEY}), + UNIVERSAL_INVENTORY(new FieldKey[] {FieldKey.UNIVERSAL_UUID, FieldKey.INVENTORY_SLOT}); private final FieldKey[] primaryKeys; diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/common/FieldKey.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/common/FieldKey.java index 4dfd86b370..c7044b44e3 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/common/FieldKey.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/common/FieldKey.java @@ -20,7 +20,14 @@ public enum FieldKey { SLIMEFUN_ID, DATA_KEY, - DATA_VALUE; + DATA_VALUE, + + /** + * Represents uuid of universal inventory + */ + UNIVERSAL_UUID, + + UNIVERSAL_TRAITS; private final boolean isNumType; diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/common/RecordSet.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/common/RecordSet.java index 3c67ffb70d..a2f571a5a3 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/common/RecordSet.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/common/RecordSet.java @@ -4,9 +4,12 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.UUID; import javax.annotation.ParametersAreNonnullByDefault; +import lombok.ToString; import org.bukkit.inventory.ItemStack; +@ToString public class RecordSet { private final Map data; private boolean readonly = false; @@ -56,6 +59,11 @@ public ItemStack getItemStack(FieldKey key) { return DataUtils.string2ItemStack(data.get(key)); } + @ParametersAreNonnullByDefault + public UUID getUUID(FieldKey key) { + return UUID.fromString(data.get(key)); + } + public boolean getBoolean(FieldKey key) { return getInt(key) == 1; } diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/ADataContainer.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/ADataContainer.java new file mode 100644 index 0000000000..3787d07152 --- /dev/null +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/ADataContainer.java @@ -0,0 +1,82 @@ +package com.xzavier0722.mc.plugin.slimefun4.storage.controller; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +public abstract class ADataContainer { + private final String key; + private final Map data; + private volatile boolean isDataLoaded = false; + + @ParametersAreNonnullByDefault + public ADataContainer(String key) { + this.key = key; + data = new ConcurrentHashMap<>(); + } + + @ParametersAreNonnullByDefault + public ADataContainer(String key, ADataContainer other) { + this.key = key; + this.data = other.data; + this.isDataLoaded = other.isDataLoaded; + } + + public boolean isDataLoaded() { + return isDataLoaded; + } + + protected String getCacheInternal(String key) { + return data.get(key); + } + + protected void setIsDataLoaded(boolean isDataLoaded) { + this.isDataLoaded = isDataLoaded; + } + + protected void setCacheInternal(String key, String val, boolean override) { + if (override) { + data.put(key, val); + } else { + data.putIfAbsent(key, val); + } + } + + protected String removeCacheInternal(String key) { + return data.remove(key); + } + + protected void checkData() { + if (!isDataLoaded) { + throw new IllegalStateException("Unable to access unloaded data!"); + } + } + + @Nonnull + public Map getAllData() { + checkData(); + return Collections.unmodifiableMap(data); + } + + @Nonnull + public Set getDataKeys() { + checkData(); + return Collections.unmodifiableSet(data.keySet()); + } + + @Nullable public String getData(String key) { + checkData(); + return getCacheInternal(key); + } + + @Nonnull + public String getKey() { + return key; + } + + public abstract void setData(String key, String val); +} diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/ASlimefunDataContainer.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/ASlimefunDataContainer.java index e7a6f84a69..2faba0b9de 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/ASlimefunDataContainer.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/ASlimefunDataContainer.java @@ -1,82 +1,23 @@ package com.xzavier0722.mc.plugin.slimefun4.storage.controller; -import java.util.Collections; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.ParametersAreNonnullByDefault; +import lombok.Getter; +import lombok.Setter; -public abstract class ASlimefunDataContainer { - private final String key; - private final Map data; - private volatile boolean isDataLoaded = false; +public abstract class ASlimefunDataContainer extends ADataContainer { + @Getter + private final String sfId; - @ParametersAreNonnullByDefault - public ASlimefunDataContainer(String key) { - this.key = key; - data = new ConcurrentHashMap<>(); - } - - @ParametersAreNonnullByDefault - public ASlimefunDataContainer(String key, ASlimefunDataContainer other) { - this.key = key; - this.data = other.data; - this.isDataLoaded = other.isDataLoaded; - } - - public boolean isDataLoaded() { - return isDataLoaded; - } - - protected String getCacheInternal(String key) { - return data.get(key); - } - - protected void setIsDataLoaded(boolean isDataLoaded) { - this.isDataLoaded = isDataLoaded; - } - - protected void setCacheInternal(String key, String val, boolean override) { - if (override) { - data.put(key, val); - } else { - data.putIfAbsent(key, val); - } - } - - protected String removeCacheInternal(String key) { - return data.remove(key); - } - - protected void checkData() { - if (!isDataLoaded) { - throw new IllegalStateException("Unable to access unloaded data!"); - } - } + @Setter + @Getter + private volatile boolean pendingRemove = false; - @Nonnull - public Map getAllData() { - checkData(); - return Collections.unmodifiableMap(data); + public ASlimefunDataContainer(String key, String sfId) { + super(key); + this.sfId = sfId; } - @Nonnull - public Set getDataKeys() { - checkData(); - return Collections.unmodifiableSet(data.keySet()); + public ASlimefunDataContainer(String key, ADataContainer other, String sfId) { + super(key, other); + this.sfId = sfId; } - - @Nullable public String getData(String key) { - checkData(); - return getCacheInternal(key); - } - - @Nonnull - public String getKey() { - return key; - } - - public abstract void setData(String key, String val); } diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/BlockDataController.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/BlockDataController.java index c54320ad18..e097ae654e 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/BlockDataController.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/BlockDataController.java @@ -1,5 +1,8 @@ package com.xzavier0722.mc.plugin.slimefun4.storage.controller; +import city.norain.slimefun4.api.menu.UniversalMenu; +import city.norain.slimefun4.api.menu.UniversalMenuPreset; +import city.norain.slimefun4.utils.InventoryUtil; import com.xzavier0722.mc.plugin.slimefun4.storage.adapter.IDataSourceAdapter; import com.xzavier0722.mc.plugin.slimefun4.storage.callback.IAsyncReadCallback; import com.xzavier0722.mc.plugin.slimefun4.storage.common.DataScope; @@ -8,6 +11,8 @@ import com.xzavier0722.mc.plugin.slimefun4.storage.common.RecordKey; import com.xzavier0722.mc.plugin.slimefun4.storage.common.RecordSet; import com.xzavier0722.mc.plugin.slimefun4.storage.common.ScopeKey; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.attributes.UniversalBlock; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.attributes.UniversalDataTrait; import com.xzavier0722.mc.plugin.slimefun4.storage.event.SlimefunChunkDataLoadEvent; import com.xzavier0722.mc.plugin.slimefun4.storage.task.DelayedSavingLooperTask; import com.xzavier0722.mc.plugin.slimefun4.storage.task.DelayedTask; @@ -17,11 +22,14 @@ import io.github.bakedlibs.dough.collections.Pair; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.logging.Level; @@ -38,26 +46,73 @@ import org.bukkit.plugin.Plugin; import org.bukkit.scheduler.BukkitTask; +/** + * 方块数据控制器 + *

+ * 用于管理区块中的 Slimefun 方块数据 + *

+ * {@link SlimefunBlockData} + * {@link SlimefunUniversalData} + * + * @author Xzavier0722 + * @author NoRainCity + */ public class BlockDataController extends ADataController { - + /** + * 延迟写数据任务队列 + */ private final Map delayedWriteTasks; + /** + * 区块数据缓存 + */ private final Map loadedChunk; + /** + * 通用数据缓存 + */ + private final Map loadedUniversalData; + /** + * 方块物品栏快照 + */ private final Map>> invSnapshots; + /** + * 全局控制器加载数据锁 + * + * {@link ScopedLock} + */ private final ScopedLock lock; + /** + * 延时加载模式标志 + */ private boolean enableDelayedSaving = false; + private int delayedSecond = 0; private BukkitTask looperTask; + /** + * 区块数据加载模式 + * {@link ChunkDataLoadMode} + */ private ChunkDataLoadMode chunkDataLoadMode; + /** + * 初始化加载中标志 + */ private boolean initLoading = false; BlockDataController() { super(DataType.BLOCK_STORAGE); delayedWriteTasks = new HashMap<>(); loadedChunk = new ConcurrentHashMap<>(); + loadedUniversalData = new ConcurrentHashMap<>(); invSnapshots = new ConcurrentHashMap<>(); lock = new ScopedLock(); } + /** + * 初始化数据控制器 + * + * @param dataAdapter 使用的 {@link IDataSourceAdapter} + * @param maxReadThread 最大数据库读线程数 + * @param maxWriteThread 最大数据库写线程数 + */ @Override public void init(IDataSourceAdapter dataAdapter, int maxReadThread, int maxWriteThread) { super.init(dataAdapter, maxReadThread, maxWriteThread); @@ -65,13 +120,29 @@ public void init(IDataSourceAdapter dataAdapter, int maxReadThread, int maxWr initLoadData(); } + /** + * 初始化加载数据 + */ private void initLoadData() { switch (chunkDataLoadMode) { case LOAD_WITH_CHUNK -> loadLoadedChunks(); case LOAD_ON_STARTUP -> loadLoadedWorlds(); } + + Bukkit.getScheduler() + .runTaskLater( + Slimefun.instance(), + () -> { + initLoading = true; + loadUniversalRecord(); + initLoading = false; + }, + 1); } + /** + * 加载所有服务器已加载的世界中的数据 + */ private void loadLoadedWorlds() { Bukkit.getScheduler() .runTaskLater( @@ -86,6 +157,9 @@ private void loadLoadedWorlds() { 1); } + /** + * 加载所有服务器已加载的世界区块中的数据 + */ private void loadLoadedChunks() { Bukkit.getScheduler() .runTaskLater( @@ -102,10 +176,17 @@ private void loadLoadedChunks() { 1); } + /** + * 初始化延时加载任务 + * + * @param p 插件实例 + * @param delayedSecond 首次执行延时 + * @param forceSavePeriod 强制保存周期 + */ public void initDelayedSaving(Plugin p, int delayedSecond, int forceSavePeriod) { checkDestroy(); if (delayedSecond < 1 || forceSavePeriod < 1) { - throw new IllegalArgumentException("Second must be greater than 0!"); + throw new IllegalArgumentException("save period second must be greater than 0!"); } enableDelayedSaving = true; this.delayedSecond = delayedSecond; @@ -137,20 +218,56 @@ public void setDelayedSavingEnable(boolean isEnable) { } /** - * Creates a new slimefun block data at specific location + * 在指定位置新建方块 * - * @param l slimefun block location {@link Location} - * @param sfId slimefun block id {@link SlimefunItem#getId()} - * @return {@link SlimefunBlockData} + * @param l Slimefun 方块位置 {@link Location} + * @param sfId Slimefun 物品 ID {@link SlimefunItem#getId()} + * @return 方块数据, 由于 {@link SlimefunItem} 的不同会返回两种数据中的一种 + * {@link SlimefunBlockData} + * {@link SlimefunUniversalData} */ @Nonnull - public SlimefunBlockData createBlock(Location l, String sfId) { + public ASlimefunDataContainer createBlock(Location l, String sfId) { checkDestroy(); - var re = getChunkDataCache(l.getChunk(), true).createBlockData(l, sfId); - if (Slimefun.getRegistry().getTickerBlocks().contains(sfId)) { - Slimefun.getTickerTask().enableTicker(l); + var sfItem = SlimefunItem.getById(sfId); + + if (sfItem instanceof UniversalBlock) { + var re = createUniversalBlockData(l, sfId); + if (Slimefun.getRegistry().getTickerBlocks().contains(sfId)) { + Slimefun.getTickerTask().enableTicker(l, re.getUUID()); + } + return re; + } else { + var re = getChunkDataCache(l.getChunk(), true).createBlockData(l, sfId); + if (Slimefun.getRegistry().getTickerBlocks().contains(sfId)) { + Slimefun.getTickerTask().enableTicker(l); + } + return re; } - return re; + } + + @Nonnull + @ParametersAreNonnullByDefault + public SlimefunUniversalBlockData createUniversalBlockData(Location l, String sfId) { + checkDestroy(); + + var uuid = UUID.randomUUID(); + var uniData = new SlimefunUniversalBlockData(uuid, sfId, l); + + uniData.setIsDataLoaded(true); + + loadedUniversalData.put(uuid, uniData); + + var preset = UniversalMenuPreset.getPreset(sfId); + if (preset != null) { + uniData.setMenu(new UniversalMenu(preset, uuid, l)); + } + + Slimefun.getDatabaseManager() + .getBlockDataController() + .saveUniversalData(uuid, sfId, Set.of(UniversalDataTrait.BLOCK, UniversalDataTrait.INVENTORY)); + + return uniData; } void saveNewBlock(Location l, String sfId) { @@ -169,6 +286,27 @@ void saveNewBlock(Location l, String sfId) { scheduleWriteTask(scopeKey, key, data, true); } + /** + * Save certain universal data + * + * @param uuid universal data uuid + * @param sfId the item universal data represents + */ + void saveUniversalData(UUID uuid, String sfId, Set traits) { + var key = new RecordKey(DataScope.UNIVERSAL_RECORD); + + var data = new RecordSet(); + data.put(FieldKey.UNIVERSAL_UUID, uuid.toString()); + data.put(FieldKey.SLIMEFUN_ID, sfId); + data.put( + FieldKey.UNIVERSAL_TRAITS, + String.join(",", traits.stream().map(Enum::name).toList())); + + var scopeKey = new UUIDKey(DataScope.NONE, uuid); + removeDelayedBlockDataUpdates(scopeKey); // Shouldn't have.. But for safe.. + scheduleWriteTask(scopeKey, key, data, true); + } + /** * Remove slimefun block data at specific location * @@ -179,6 +317,15 @@ public void removeBlock(Location l) { var removed = getChunkDataCache(l.getChunk(), true).removeBlockData(l); if (removed == null) { + getUniversalBlockDataFromCache(l) + .ifPresentOrElse(data -> removeUniversalBlockData(data.getUUID(), l), () -> { + if (Bukkit.isPrimaryThread()) { + Slimefun.getBlockDataService() + .getUniversalDataUUID(l.getBlock()) + .ifPresent(uuid -> removeUniversalBlockData(uuid, l)); + } + }); + return; } @@ -196,6 +343,55 @@ public void removeBlock(Location l) { } } + public void removeBlockData(Location l) { + checkDestroy(); + + var removed = getChunkDataCache(l.getChunk(), true).removeBlockData(l); + + if (removed == null || !removed.isDataLoaded()) { + return; + } + + var menu = removed.getBlockMenu(); + if (menu != null) { + InventoryUtil.closeInventory(menu.toInventory()); + } + + if (Slimefun.getRegistry().getTickerBlocks().contains(removed.getSfId())) { + Slimefun.getTickerTask().disableTicker(l); + } + } + + public void removeUniversalBlockData(UUID uuid, Location lastPresent) { + checkDestroy(); + + var toRemove = loadedUniversalData.get(uuid); + + if (toRemove == null) { + return; + } + + if (!toRemove.isDataLoaded()) { + return; + } + + if (toRemove instanceof SlimefunUniversalBlockData ubd) { + toRemove.setPendingRemove(true); + removeUniversalBlockDirectly(uuid); + + var menu = ubd.getMenu(); + if (menu != null) { + menu.lock(); + } + + if (Slimefun.getRegistry().getTickerBlocks().contains(toRemove.getSfId())) { + Slimefun.getTickerTask().disableTicker(lastPresent); + } + + loadedUniversalData.remove(uuid); + } + } + void removeBlockDirectly(Location l) { checkDestroy(); var scopeKey = new LocationKey(DataScope.NONE, l); @@ -206,6 +402,16 @@ void removeBlockDirectly(Location l) { scheduleDeleteTask(scopeKey, key, true); } + void removeUniversalBlockDirectly(UUID uuid) { + checkDestroy(); + var scopeKey = new UUIDKey(DataScope.NONE, uuid); + removeDelayedBlockDataUpdates(scopeKey); + + var key = new RecordKey(DataScope.UNIVERSAL_RECORD); + key.addCondition(FieldKey.UNIVERSAL_UUID, uuid.toString()); + scheduleDeleteTask(scopeKey, key, true); + } + /** * Get slimefun block data at specific location * @@ -245,9 +451,9 @@ public SlimefunBlockData getBlockData(Location l) { } /** - * Get slimefun block data at specific location async + * Get slimefun block data at specific location asynchronous * - * @param l slimefun block location {@link Location} + * @param l slimefun block location {@link Location} * @param callback operation when block data fetched {@link IAsyncReadCallback} */ public void getBlockDataAsync(Location l, IAsyncReadCallback callback) { @@ -264,6 +470,77 @@ public SlimefunBlockData getBlockDataFromCache(Location l) { return getBlockDataFromCache(LocationUtils.getChunkKey(l.getChunk()), LocationUtils.getLocKey(l)); } + /** + * Get slimefun universal data + * + * @param uuid universal data uuid {@link UUID} + */ + @Nullable public SlimefunUniversalBlockData getUniversalBlockData(@Nonnull UUID uuid) { + checkDestroy(); + + var key = new RecordKey(DataScope.UNIVERSAL_RECORD); + key.addCondition(FieldKey.UNIVERSAL_UUID, uuid.toString()); + key.addField(FieldKey.SLIMEFUN_ID); + + var result = getData(key); + + if (result.isEmpty()) { + return null; + } + + var newData = new SlimefunUniversalBlockData(uuid, result.get(0).get(FieldKey.SLIMEFUN_ID)); + + Arrays.stream(result.get(0).get(FieldKey.UNIVERSAL_TRAITS).split(",")).forEach(tname -> { + for (UniversalDataTrait trait : UniversalDataTrait.values()) { + if (trait.name().equals(tname)) { + newData.getTraits().add(trait); + } + } + }); + + return newData; + } + + /** + * Get slimefun universal data asynchronous + * + * @param uuid universal data uuid {@link UUID} + * @param callback operation when block data fetched {@link IAsyncReadCallback} + */ + public void getUniversalBlockData(@Nonnull UUID uuid, IAsyncReadCallback callback) { + scheduleReadTask(() -> invokeCallback(callback, getUniversalBlockData(uuid))); + } + + /** + * Get slimefun universal data from cache + * + * @param uuid universal data uuid {@link UUID} + */ + @Nullable public SlimefunUniversalBlockData getUniversalBlockDataFromCache(@Nonnull UUID uuid) { + checkDestroy(); + + var cache = loadedUniversalData.get(uuid); + + return cache == null + ? getUniversalBlockData(uuid) + : (cache instanceof SlimefunUniversalBlockData ubd ? ubd : null); + } + + /** + * Get slimefun universal data from cache by location + * + * @param l Slimefun block location {@link Location} + */ + public Optional getUniversalBlockDataFromCache(@Nonnull Location l) { + checkDestroy(); + + return loadedUniversalData.values().stream() + .filter(uniData -> uniData instanceof SlimefunUniversalBlockData ubd + && ubd.getLastPresent().toLocation().equals(l)) + .map(data -> (SlimefunUniversalBlockData) data) + .findFirst(); + } + /** * Move block data to specific location *

@@ -363,6 +640,8 @@ public void loadChunk(Chunk chunk, boolean isNewChunk) { loadChunkData(chunkData); + // 按区块加载方块数据 + var key = new RecordKey(DataScope.BLOCK_RECORD); key.addField(FieldKey.LOCATION); key.addField(FieldKey.SLIMEFUN_ID); @@ -384,6 +663,7 @@ public void loadChunk(Chunk chunk, boolean isNewChunk) { scheduleReadTask(() -> loadBlockData(blockData)); } }); + Bukkit.getPluginManager().callEvent(new SlimefunChunkDataLoadEvent(chunkData)); } @@ -407,6 +687,43 @@ public void loadWorld(World world) { Level.INFO, "世界 {0} 数据加载完成, 耗时 {1}ms", new Object[] {worldName, (System.currentTimeMillis() - start)}); } + public void loadUniversalRecord() { + var uniKey = new RecordKey(DataScope.UNIVERSAL_RECORD); + uniKey.addField(FieldKey.UNIVERSAL_UUID); + uniKey.addField(FieldKey.SLIMEFUN_ID); + uniKey.addField(FieldKey.UNIVERSAL_TRAITS); + + getData(uniKey).forEach(data -> { + var sfId = data.get(FieldKey.SLIMEFUN_ID); + var sfItem = SlimefunItem.getById(sfId); + + if (sfItem == null) { + return; + } + + var uuid = data.getUUID(FieldKey.UNIVERSAL_UUID); + var traitsData = data.get(FieldKey.UNIVERSAL_TRAITS); + var traits = new HashSet(); + + if (traitsData != null && !traitsData.isBlank()) { + for (String traitStr : traitsData.split(",")) { + try { + traits.add(UniversalDataTrait.valueOf(traitStr.toUpperCase())); + } catch (IllegalArgumentException ignored) { + } + } + } + + var uniData = traits.contains(UniversalDataTrait.BLOCK) + ? new SlimefunUniversalBlockData(uuid, sfId) + : new SlimefunUniversalData(uuid, sfId); + + traits.forEach(t -> uniData.getTraits().add(t)); + + scheduleReadTask(() -> loadUniversalData(uniData)); + }); + } + private void loadChunkData(SlimefunChunkData chunkData) { if (chunkData.isDataLoaded()) { return; @@ -447,33 +764,45 @@ public void loadBlockData(SlimefunBlockData blockData) { return; } - getData(key) - .forEach(recordSet -> blockData.setCacheInternal( - recordSet.get(FieldKey.DATA_KEY), - DataUtils.blockDataDebase64(recordSet.get(FieldKey.DATA_VALUE)), - false)); - blockData.setIsDataLoaded(true); - - var menuPreset = BlockMenuPreset.getPreset(blockData.getSfId()); - if (menuPreset != null) { - var menuKey = new RecordKey(DataScope.BLOCK_INVENTORY); - menuKey.addCondition(FieldKey.LOCATION, blockData.getKey()); - menuKey.addField(FieldKey.INVENTORY_SLOT); - menuKey.addField(FieldKey.INVENTORY_ITEM); - - var inv = new ItemStack[54]; - getData(menuKey) - .forEach(record -> inv[record.getInt(FieldKey.INVENTORY_SLOT)] = - record.getItemStack(FieldKey.INVENTORY_ITEM)); - blockData.setBlockMenu(new BlockMenu(menuPreset, blockData.getLocation(), inv)); - - var content = blockData.getMenuContents(); - if (content != null) { - invSnapshots.put(blockData.getKey(), InvStorageUtils.getInvSnapshot(content)); + var sfItem = SlimefunItem.getById(blockData.getSfId()); + var universal = sfItem instanceof UniversalBlock; + + var kvData = getData(key); + + var menuKey = new RecordKey(DataScope.BLOCK_INVENTORY); + menuKey.addCondition(FieldKey.LOCATION, blockData.getKey()); + menuKey.addField(FieldKey.INVENTORY_SLOT); + menuKey.addField(FieldKey.INVENTORY_ITEM); + + var invData = getData(menuKey); + + if (universal) { + migrateUniversalData(blockData.getLocation(), blockData.getSfId(), kvData, invData); + } else { + kvData.forEach(recordSet -> blockData.setCacheInternal( + recordSet.get(FieldKey.DATA_KEY), + DataUtils.blockDataDebase64(recordSet.get(FieldKey.DATA_VALUE)), + false)); + + blockData.setIsDataLoaded(true); + + var menuPreset = BlockMenuPreset.getPreset(blockData.getSfId()); + + if (menuPreset != null) { + var inv = new ItemStack[54]; + + invData.forEach(record -> + inv[record.getInt(FieldKey.INVENTORY_SLOT)] = record.getItemStack(FieldKey.INVENTORY_ITEM)); + + blockData.setBlockMenu(new BlockMenu(menuPreset, blockData.getLocation(), inv)); + + var content = blockData.getMenuContents(); + if (content != null) { + invSnapshots.put(blockData.getKey(), InvStorageUtils.getInvSnapshot(content)); + } } } - var sfItem = SlimefunItem.getById(blockData.getSfId()); if (sfItem != null && sfItem.isTicking()) { Slimefun.getTickerTask().enableTicker(blockData.getLocation()); } @@ -495,6 +824,89 @@ public void loadBlockDataAsync( invokeCallback(callback, blockDataList); } + @ParametersAreNonnullByDefault + public void loadUniversalData(SlimefunUniversalData uniData) { + if (uniData.isDataLoaded()) { + return; + } + + var key = new RecordKey(DataScope.UNIVERSAL_DATA); + key.addCondition(FieldKey.UNIVERSAL_UUID, uniData.getKey()); + key.addField(FieldKey.DATA_KEY); + key.addField(FieldKey.DATA_VALUE); + + lock.lock(key); + + try { + if (uniData.isDataLoaded()) { + return; + } + + getData(key) + .forEach(recordSet -> uniData.setCacheInternal( + recordSet.get(FieldKey.DATA_KEY), + DataUtils.blockDataDebase64(recordSet.get(FieldKey.DATA_VALUE)), + false)); + + uniData.setIsDataLoaded(true); + + loadedUniversalData.putIfAbsent(uniData.getUUID(), uniData); + + if (uniData.hasTrait(UniversalDataTrait.INVENTORY)) { + var menuPreset = UniversalMenuPreset.getPreset(uniData.getSfId()); + if (menuPreset != null) { + var menuKey = new RecordKey(DataScope.UNIVERSAL_INVENTORY); + menuKey.addCondition(FieldKey.UNIVERSAL_UUID, uniData.getKey()); + menuKey.addField(FieldKey.INVENTORY_SLOT); + menuKey.addField(FieldKey.INVENTORY_ITEM); + + var inv = new ItemStack[54]; + + getData(menuKey) + .forEach(recordSet -> inv[recordSet.getInt(FieldKey.INVENTORY_SLOT)] = + recordSet.getItemStack(FieldKey.INVENTORY_ITEM)); + + var location = uniData.hasTrait(UniversalDataTrait.BLOCK) + ? ((SlimefunUniversalBlockData) uniData) + .getLastPresent() + .toLocation() + : null; + + uniData.setMenu(new UniversalMenu(menuPreset, uniData.getUUID(), location, inv)); + + var content = uniData.getMenuContents(); + if (content != null) { + invSnapshots.put(uniData.getKey(), InvStorageUtils.getInvSnapshot(content)); + } + } + } + + if (uniData.hasTrait(UniversalDataTrait.BLOCK)) { + var sfItem = SlimefunItem.getById(uniData.getSfId()); + + if (sfItem != null && sfItem.isTicking()) { + Slimefun.getTickerTask() + .enableTicker( + ((SlimefunUniversalBlockData) uniData) + .getLastPresent() + .toLocation(), + uniData.getUUID()); + } + } + } finally { + lock.unlock(key); + } + } + + @ParametersAreNonnullByDefault + public void loadUniversalDataAsync( + SlimefunUniversalData uniData, IAsyncReadCallback callback) { + scheduleReadTask(() -> { + loadUniversalData(uniData); + invokeCallback(callback, uniData); + }); + } + public SlimefunChunkData getChunkData(Chunk chunk) { checkDestroy(); loadChunk(chunk, false); @@ -520,6 +932,21 @@ public void saveAllBlockInventories() { })); } + public void saveAllUniversalInventories() { + var uniData = new HashSet<>(loadedUniversalData.values()); + uniData.forEach(data -> { + if (data.isPendingRemove() || !data.isDataLoaded()) { + return; + } + var menu = data.getMenu(); + if (menu == null || !menu.isDirty()) { + return; + } + + saveUniversalInventory(data); + }); + } + public void saveBlockInventory(SlimefunBlockData blockData) { var newInv = blockData.getMenuContents(); List> lastSave; @@ -591,6 +1018,29 @@ public void removeAllDataInWorldAsync(World world, Runnable onFinishedCallback) }); } + public void saveUniversalInventory(@Nonnull SlimefunUniversalData universalData) { + var menu = universalData.getMenu(); + var universalID = universalData.getUUID(); + + var newInv = menu.getContents(); + List> lastSave; + if (newInv == null) { + lastSave = invSnapshots.remove(universalID.toString()); + if (lastSave == null) { + return; + } + } else { + lastSave = invSnapshots.put(universalID.toString(), InvStorageUtils.getInvSnapshot(newInv)); + } + + var changed = InvStorageUtils.getChangedSlots(lastSave, newInv); + if (changed.isEmpty()) { + return; + } + + changed.forEach(slot -> scheduleDelayedUniversalInvUpdate(universalID, menu, slot)); + } + public Set getAllLoadedChunkData(World world) { var prefix = world.getName() + ";"; var re = new HashSet(); @@ -648,9 +1098,47 @@ private void scheduleBlockInvUpdate(ScopeKey scopeKey, RecordKey reqKey, String } } + /** + * Save universal inventory by async way + * + * @param uuid Universal Inventory UUID + * @param menu Universal menu + * @param slot updated item slot + */ + private void scheduleDelayedUniversalInvUpdate(UUID uuid, UniversalMenu menu, int slot) { + var scopeKey = new UUIDKey(DataScope.NONE, uuid); + var reqKey = new RecordKey(DataScope.UNIVERSAL_INVENTORY); + reqKey.addCondition(FieldKey.UNIVERSAL_UUID, uuid.toString()); + reqKey.addCondition(FieldKey.INVENTORY_SLOT, slot + ""); + reqKey.addField(FieldKey.INVENTORY_ITEM); + + if (enableDelayedSaving) { + scheduleDelayedUpdateTask( + new LinkedKey(scopeKey, reqKey), + () -> scheduleUniversalInvUpdate(scopeKey, reqKey, uuid, menu.getContents(), slot)); + } else { + scheduleUniversalInvUpdate(scopeKey, reqKey, uuid, menu.getContents(), slot); + } + } + + private void scheduleUniversalInvUpdate(ScopeKey scopeKey, RecordKey reqKey, UUID uuid, ItemStack[] inv, int slot) { + var item = inv != null && slot < inv.length ? inv[slot] : null; + + if (item == null) { + scheduleDeleteTask(scopeKey, reqKey, true); + } else { + var data = new RecordSet(); + data.put(FieldKey.UNIVERSAL_UUID, uuid.toString()); + data.put(FieldKey.INVENTORY_SLOT, slot + ""); + data.put(FieldKey.INVENTORY_ITEM, item); + scheduleWriteTask(scopeKey, reqKey, data, true); + } + } + @Override public void shutdown() { saveAllBlockInventories(); + saveAllUniversalInventories(); if (enableDelayedSaving) { looperTask.cancel(); executeAllDelayedTasks(); @@ -672,6 +1160,21 @@ void scheduleDelayedBlockDataUpdate(SlimefunBlockData blockData, String key) { } } + void scheduleDelayedUniversalDataUpdate(SlimefunUniversalData universalData, String key) { + var scopeKey = new UUIDKey(DataScope.NONE, universalData.getKey()); + var reqKey = new RecordKey(DataScope.UNIVERSAL_DATA); + reqKey.addCondition(FieldKey.UNIVERSAL_UUID, universalData.getKey()); + reqKey.addCondition(FieldKey.DATA_KEY, key); + if (enableDelayedSaving) { + scheduleDelayedUpdateTask( + new LinkedKey(scopeKey, reqKey), + () -> scheduleUniversalDataUpdate( + scopeKey, reqKey, universalData.getKey(), key, universalData.getData(key))); + } else { + scheduleUniversalDataUpdate(scopeKey, reqKey, universalData.getKey(), key, universalData.getData(key)); + } + } + private void removeDelayedBlockDataUpdates(ScopeKey scopeKey) { synchronized (delayedWriteTasks) { delayedWriteTasks @@ -693,6 +1196,19 @@ private void scheduleBlockDataUpdate(ScopeKey scopeKey, RecordKey reqKey, String } } + private void scheduleUniversalDataUpdate(ScopeKey scopeKey, RecordKey reqKey, String uuid, String key, String val) { + if (val == null) { + scheduleDeleteTask(scopeKey, reqKey, false); + } else { + var data = new RecordSet(); + reqKey.addField(FieldKey.DATA_VALUE); + data.put(FieldKey.UNIVERSAL_UUID, uuid); + data.put(FieldKey.DATA_KEY, key); + data.put(FieldKey.DATA_VALUE, DataUtils.blockDataBase64(val)); + scheduleWriteTask(scopeKey, reqKey, data, true); + } + } + void scheduleDelayedChunkDataUpdate(SlimefunChunkData chunkData, String key) { var scopeKey = new ChunkKey(DataScope.NONE, chunkData.getChunk()); var reqKey = new RecordKey(DataScope.CHUNK_DATA); @@ -773,4 +1289,53 @@ private void clearBlockCacheAndTasks(SlimefunBlockData blockData) { removeDelayedBlockDataUpdates(scopeKey); abortScopeTask(scopeKey); } + + /** + * 迁移旧 Slimefun 机器数据至通用数据 + */ + private void migrateUniversalData( + @Nonnull Location l, + @Nonnull String sfId, + @Nonnull List kvData, + @Nonnull List invData) { + try { + if (l == null || sfId == null) { + return; + } + + var universalData = createUniversalBlockData(l, sfId); + + Slimefun.runSync( + () -> Slimefun.getBlockDataService() + .updateUniversalDataUUID(l.getBlock(), String.valueOf(universalData.getUUID())), + 10L); + + kvData.forEach(recordSet -> universalData.setData( + recordSet.get(FieldKey.DATA_KEY), DataUtils.blockDataDebase64(recordSet.get(FieldKey.DATA_VALUE)))); + + var preset = UniversalMenuPreset.getPreset(sfId); + if (preset != null) { + final var inv = new ItemStack[54]; + + invData.forEach(record -> + inv[record.getInt(FieldKey.INVENTORY_SLOT)] = record.getItemStack(FieldKey.INVENTORY_ITEM)); + + universalData.setMenu(new UniversalMenu(preset, universalData.getUUID(), l, inv)); + + var content = universalData.getMenuContents(); + if (content != null) { + invSnapshots.put(universalData.getKey(), InvStorageUtils.getInvSnapshot(content)); + } + } + + removeBlockData(l); + + if (Slimefun.getRegistry().getTickerBlocks().contains(universalData.getSfId())) { + Slimefun.getTickerTask() + .enableTicker(universalData.getLastPresent().toLocation(), universalData.getUUID()); + } + } catch (Exception e) { + Slimefun.logger().log(Level.WARNING, "迁移机器人数据时出现错误", e); + } + } } diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/ControllerHolder.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/ControllerHolder.java index 763c9aabc1..544d4f6627 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/ControllerHolder.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/ControllerHolder.java @@ -9,7 +9,8 @@ public class ControllerHolder { private final Map controllers; public static CT getController(Class clazz, StorageType type) { - return ((ControllerHolder) holders.get(clazz)).get(type); + final var holder = holders.get(clazz); + return holder == null ? null : ((ControllerHolder) holder).get(type); } public static void clearControllers() { diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/SlimefunBlockData.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/SlimefunBlockData.java index 833a17e69c..417f669903 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/SlimefunBlockData.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/SlimefunBlockData.java @@ -12,22 +12,18 @@ public class SlimefunBlockData extends ASlimefunDataContainer { private final Location location; - private final String sfId; private volatile BlockMenu menu; - private volatile boolean pendingRemove = false; @ParametersAreNonnullByDefault SlimefunBlockData(Location location, String sfId) { - super(LocationUtils.getLocKey(location)); + super(LocationUtils.getLocKey(location), sfId); this.location = location; - this.sfId = sfId; } @ParametersAreNonnullByDefault SlimefunBlockData(Location location, SlimefunBlockData other) { - super(LocationUtils.getLocKey(location), other); + super(LocationUtils.getLocKey(location), other, other.getSfId()); this.location = location; - this.sfId = other.sfId; } @Nonnull @@ -37,7 +33,7 @@ public Location getLocation() { @Nonnull public String getSfId() { - return sfId; + return super.getSfId(); } @ParametersAreNonnullByDefault @@ -80,22 +76,14 @@ void setBlockMenu(BlockMenu blockMenu) { return re; } - public void setPendingRemove(boolean pendingRemove) { - this.pendingRemove = pendingRemove; - } - - public boolean isPendingRemove() { - return pendingRemove; - } - @Override public String toString() { return "SlimefunBlockData [sfId=" - + sfId + + getSfId() + ", location=" + location + ", isPendingRemove=" - + pendingRemove + + isPendingRemove() + "]"; } } diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/SlimefunChunkData.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/SlimefunChunkData.java index 9dadc5afd0..174b150a8f 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/SlimefunChunkData.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/SlimefunChunkData.java @@ -15,7 +15,7 @@ import org.bukkit.Chunk; import org.bukkit.Location; -public class SlimefunChunkData extends ASlimefunDataContainer { +public class SlimefunChunkData extends ADataContainer { private static final SlimefunBlockData INVALID_BLOCK_DATA = new SlimefunBlockData( new Location(Bukkit.getWorlds().get(0), Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE), "INVALID_BLOCK_DATA_SF_KEY"); diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/SlimefunUniversalBlockData.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/SlimefunUniversalBlockData.java new file mode 100644 index 0000000000..c1b4355dd2 --- /dev/null +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/SlimefunUniversalBlockData.java @@ -0,0 +1,46 @@ +package com.xzavier0722.mc.plugin.slimefun4.storage.controller; + +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.attributes.UniversalDataTrait; +import com.xzavier0722.mc.plugin.slimefun4.storage.util.LocationUtils; +import io.github.bakedlibs.dough.blocks.BlockPosition; +import java.util.UUID; +import org.bukkit.Location; + +public class SlimefunUniversalBlockData extends SlimefunUniversalData { + private volatile BlockPosition lastPresent; + + public SlimefunUniversalBlockData(UUID uuid, String sfId) { + super(uuid, sfId); + } + + public SlimefunUniversalBlockData(UUID uuid, String sfId, BlockPosition present) { + super(uuid, sfId); + + this.lastPresent = present; + } + + public SlimefunUniversalBlockData(UUID uuid, String sfId, Location present) { + this(uuid, sfId, new BlockPosition(present)); + } + + public void setLastPresent(BlockPosition lastPresent) { + setTraitData(UniversalDataTrait.BLOCK, LocationUtils.locationToString(lastPresent.toLocation())); + this.lastPresent = lastPresent; + } + + public BlockPosition getLastPresent() { + if (lastPresent != null) { + return lastPresent; + } + + var data = getData("location"); + + if (data == null) { + throw new IllegalArgumentException("UniversalBlockData missing location data"); + } + + lastPresent = new BlockPosition(LocationUtils.toLocation(data)); + + return lastPresent; + } +} diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/SlimefunUniversalData.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/SlimefunUniversalData.java new file mode 100644 index 0000000000..baf6d59829 --- /dev/null +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/SlimefunUniversalData.java @@ -0,0 +1,93 @@ +package com.xzavier0722.mc.plugin.slimefun4.storage.controller; + +import city.norain.slimefun4.api.menu.UniversalMenu; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.attributes.UniversalDataTrait; +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Level; +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.inventory.ItemStack; + +@Getter +public class SlimefunUniversalData extends ASlimefunDataContainer { + @Setter + private volatile UniversalMenu menu; + + @Setter + private volatile boolean pendingRemove = false; + + private final Set traits = new HashSet<>(); + + @ParametersAreNonnullByDefault + SlimefunUniversalData(UUID uuid, String sfId) { + super(uuid.toString(), sfId); + } + + @ParametersAreNonnullByDefault + public void setData(String key, String val) { + checkData(); + + if (UniversalDataTrait.isReservedKey(key)) { + Slimefun.logger().log(Level.WARNING, "警告: 有附属正在尝试修改受保护的方块数据, 已取消更改"); + return; + } + + setCacheInternal(key, val, true); + Slimefun.getDatabaseManager().getBlockDataController().scheduleDelayedUniversalDataUpdate(this, key); + } + + @ParametersAreNonnullByDefault + protected void setTraitData(UniversalDataTrait trait, String val) { + checkData(); + + if (!trait.getReservedKey().isBlank()) { + setCacheInternal(trait.getReservedKey(), val, true); + Slimefun.getDatabaseManager() + .getBlockDataController() + .scheduleDelayedUniversalDataUpdate(this, trait.getReservedKey()); + } + } + + @ParametersAreNonnullByDefault + public void removeData(String key) { + if (removeCacheInternal(key) != null || !isDataLoaded()) { + Slimefun.getDatabaseManager().getBlockDataController().scheduleDelayedUniversalDataUpdate(this, key); + } + } + + @Nullable public ItemStack[] getMenuContents() { + if (menu == null) { + return null; + } + var re = new ItemStack[54]; + var presetSlots = menu.getPreset().getPresetSlots(); + var inv = menu.toInventory().getContents(); + for (var i = 0; i < inv.length; i++) { + if (presetSlots.contains(i)) { + continue; + } + re[i] = inv[i]; + } + + return re; + } + + public UUID getUUID() { + return UUID.fromString(getKey()); + } + + public boolean hasTrait(UniversalDataTrait trait) { + return traits.contains(trait); + } + + @Override + public String toString() { + return "SlimefunUniversalData [uuid= " + getUUID() + ", sfId=" + getSfId() + ", isPendingRemove=" + + pendingRemove + "]"; + } +} diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/StorageType.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/StorageType.java index 17852373b3..3e8579ecee 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/StorageType.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/StorageType.java @@ -3,6 +3,5 @@ public enum StorageType { MYSQL, SQLITE, - POSTGRESQL, } diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/attributes/UniversalBlock.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/attributes/UniversalBlock.java new file mode 100644 index 0000000000..0717588fc4 --- /dev/null +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/attributes/UniversalBlock.java @@ -0,0 +1,22 @@ +package com.xzavier0722.mc.plugin.slimefun4.storage.controller.attributes; + +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunUniversalBlockData; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunUniversalData; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; + +/** + * 这个属性用于声明 {@link SlimefunItem} 使用了 {@link SlimefunUniversalData} + *

+ * 当这个 {@link SlimefunItem} 作为机器时, 对应材质需要支持 + * 使用 PDC 存储容器 (用于识别 UUID). + * 否则无法将这个物品/机器绑定到一个通用数据上. + * + * 查看此处了解支持 PDC 的物品材质: + * Paper Doc + * + * @author NoRainCity + * + * @see SlimefunUniversalData + * @see SlimefunUniversalBlockData + */ +public interface UniversalBlock {} diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/attributes/UniversalDataTrait.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/attributes/UniversalDataTrait.java new file mode 100644 index 0000000000..4d402a4118 --- /dev/null +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/attributes/UniversalDataTrait.java @@ -0,0 +1,44 @@ +package com.xzavier0722.mc.plugin.slimefun4.storage.controller.attributes; + +import city.norain.slimefun4.api.menu.UniversalMenu; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunUniversalBlockData; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunUniversalData; +import lombok.Getter; + +/** + * 这个枚举类用于声明 {@link SlimefunUniversalData} 的特征. + * 一个通用数据可以有单个或多个特征. + *

+ * 对于一个通用数据, 它默认拥有作为 k-v 容器的特征. + * + * @see SlimefunUniversalData + * @see SlimefunUniversalBlockData + */ +@Getter +public enum UniversalDataTrait { + /** + * BLOCK 特征标明该通用数据属于 {@link SlimefunUniversalBlockData} + */ + BLOCK("location"), + + /** + * INVENTORY 特征标明该通用数据拥有一个 {@link UniversalMenu} + */ + INVENTORY(""); + + private final String reservedKey; + + UniversalDataTrait(String reservedKey) { + this.reservedKey = reservedKey; + } + + public static boolean isReservedKey(String key) { + for (UniversalDataTrait trait : UniversalDataTrait.values()) { + if (trait.getReservedKey().equals(key)) { + return true; + } + } + + return false; + } +} diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/migrator/BlockStorageMigrator.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/migrator/BlockStorageMigrator.java index 0700d6b20f..79f19083e5 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/migrator/BlockStorageMigrator.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/migrator/BlockStorageMigrator.java @@ -2,6 +2,8 @@ import com.google.common.reflect.TypeToken; import com.google.gson.Gson; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunBlockData; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunUniversalData; import io.github.bakedlibs.dough.config.Config; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; @@ -12,7 +14,7 @@ import java.util.Map; import java.util.logging.Level; import lombok.Getter; -import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; +import me.mrCookieSlime.Slimefun.api.inventory.DirtyChestMenu; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; @@ -135,18 +137,24 @@ private void migrateBlock(World world, String sfId, String locStr, String jsonSt var z = Integer.parseInt(arr[3]); var loc = new Location(world, x, y, z); - var blockData = - Slimefun.getDatabaseManager().getBlockDataController().createBlock(loc, sfId); + var sfData = Slimefun.getDatabaseManager().getBlockDataController().createBlock(loc, sfId); Map data = gson.fromJson(jsonStr, new TypeToken>() {}.getType()); for (var each : data.entrySet()) { var key = each.getKey(); if ("id".equals(key)) { continue; } - blockData.setData(key, each.getValue()); + sfData.setData(key, each.getValue()); + } + + DirtyChestMenu menu = null; + + if (sfData instanceof SlimefunBlockData blockData) { + menu = blockData.getBlockMenu(); + } else if (sfData instanceof SlimefunUniversalData uniData) { + menu = uniData.getMenu(); } - var menu = blockData.getBlockMenu(); if (menu != null) { var f = new File(invFolder, world.getName() + ";" + x + ";" + y + ";" + z + ".sfi"); if (!f.isFile()) { @@ -159,7 +167,7 @@ private void migrateBlock(World world, String sfId, String locStr, String jsonSt } } - private void migrateInv(BlockMenu menu, File f) { + private void migrateInv(DirtyChestMenu menu, File f) { var cfg = new Config(f); var preset = menu.getPreset().getPresetSlots(); for (var key : cfg.getKeys()) { diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/migrator/IMigrator.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/migrator/IMigrator.java index 8048c6d810..20a1543531 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/migrator/IMigrator.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/migrator/IMigrator.java @@ -5,5 +5,7 @@ public interface IMigrator { MigrateStatus migrateData(); - String getName(); + default String getName() { + return this.getClass().getSimpleName(); + } } diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/util/LocationUtils.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/util/LocationUtils.java index 1c01ea947d..ba334a6324 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/util/LocationUtils.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/util/LocationUtils.java @@ -16,6 +16,10 @@ public static String getChunkKey(Chunk chunk) { } public static Location toLocation(String lKey) { + if (lKey == null || lKey.isEmpty()) { + return null; + } + var strArr = lKey.split(";"); var loc = strArr[1].split(":"); return new Location( diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/util/StorageCacheUtils.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/util/StorageCacheUtils.java index f5fc4a6e8f..066f7974d8 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/util/StorageCacheUtils.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/util/StorageCacheUtils.java @@ -1,16 +1,25 @@ package com.xzavier0722.mc.plugin.slimefun4.storage.util; +import city.norain.slimefun4.api.menu.UniversalMenu; import com.google.common.base.Preconditions; import com.xzavier0722.mc.plugin.slimefun4.storage.callback.IAsyncReadCallback; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.ADataContainer; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.ASlimefunDataContainer; import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunBlockData; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunUniversalBlockData; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunUniversalData; +import io.github.bakedlibs.dough.blocks.BlockPosition; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import java.util.HashSet; import java.util.Set; +import java.util.UUID; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; import org.bukkit.Location; +import org.bukkit.block.Block; /** * Utils to access the cached block data. @@ -19,13 +28,18 @@ * {@link com.xzavier0722.mc.plugin.slimefun4.storage.controller.BlockDataController#getBlockData} */ public class StorageCacheUtils { - private static final Set loadingData = new HashSet<>(); + private static final Set loadingData = new HashSet<>(); @ParametersAreNonnullByDefault public static boolean hasBlock(Location l) { return getBlock(l) != null; } + @ParametersAreNonnullByDefault + public static boolean hasUniversalBlock(Location l) { + return Slimefun.getBlockDataService().getUniversalDataUUID(l.getBlock()).isPresent(); + } + @ParametersAreNonnullByDefault @Nullable public static SlimefunBlockData getBlock(Location l) { return Slimefun.getDatabaseManager().getBlockDataController().getBlockDataFromCache(l); @@ -40,26 +54,60 @@ public static boolean isBlock(Location l, String id) { @ParametersAreNonnullByDefault @Nullable public static SlimefunItem getSfItem(Location l) { var blockData = getBlock(l); - return blockData == null ? null : SlimefunItem.getById(blockData.getSfId()); + + if (blockData != null) { + return SlimefunItem.getById(blockData.getSfId()); + } else { + var universalData = getUniversalBlock(l.getBlock()); + return universalData == null ? null : SlimefunItem.getById(universalData.getSfId()); + } } @ParametersAreNonnullByDefault @Nullable public static String getData(Location loc, String key) { var blockData = getBlock(loc); - return blockData == null ? null : blockData.getData(key); + + if (blockData != null) { + return blockData.getData(key); + } else { + var uniData = getUniversalBlock(loc.getBlock()); + + if (uniData == null) { + return null; + } + + return uniData.getData(key); + } + } + + @ParametersAreNonnullByDefault + @Nullable public static String getUniversalBlock(UUID uuid, Location loc, String key) { + var universalData = getUniversalBlock(uuid, loc); + return universalData == null ? null : universalData.getData(key); } @ParametersAreNonnullByDefault public static void setData(Location loc, String key, String val) { var block = getBlock(loc); - Preconditions.checkNotNull(block); - - block.setData(key, val); + if (block != null) { + block.setData(key, val); + } else { + var uni = getUniversalBlock(loc.getBlock()); + Preconditions.checkNotNull(uni); + uni.setData(key, val); + } } @ParametersAreNonnullByDefault public static void removeData(Location loc, String key) { - getBlock(loc).removeData(key); + var block = getBlock(loc); + if (block != null) { + block.removeData(key); + } else { + var uni = getUniversalBlock(loc.getBlock()); + Preconditions.checkNotNull(uni); + uni.removeData(key); + } } @ParametersAreNonnullByDefault @@ -77,30 +125,140 @@ public static void removeData(Location loc, String key) { return blockData.getBlockMenu(); } - public static void requestLoad(SlimefunBlockData blockData) { - if (blockData.isDataLoaded()) { + @ParametersAreNonnullByDefault + @Nullable public static SlimefunUniversalBlockData getUniversalBlock(UUID uuid) { + var uniData = Slimefun.getDatabaseManager().getBlockDataController().getUniversalBlockDataFromCache(uuid); + + if (uniData == null) { + return null; + } + + if (!uniData.isDataLoaded()) { + requestLoad(uniData); + return null; + } + + return uniData; + } + + @ParametersAreNonnullByDefault + @Nullable public static SlimefunUniversalBlockData getUniversalBlock(UUID uuid, Location l) { + var uniData = getUniversalBlock(uuid); + + if (uniData != null) { + uniData.setLastPresent(new BlockPosition(l)); + } + + return uniData; + } + + /** + * Get universal data from block + *

+ * You **must** call this method from sync! + * + * @param block {@link Block} + * @return {@link SlimefunUniversalBlockData} + */ + @ParametersAreNonnullByDefault + @Nullable public static SlimefunUniversalBlockData getUniversalBlock(Block block) { + var uuid = Slimefun.getBlockDataService().getUniversalDataUUID(block); + + return uuid.map(id -> getUniversalBlock(id, block.getLocation())).orElse(null); + } + + /** + * Get universal menu from block + *

+ * You **must** call this method from sync! + * + * @param block {@link Block} + * @return {@link SlimefunUniversalData} + */ + @ParametersAreNonnullByDefault + @Nullable public static UniversalMenu getUniversalMenu(Block block) { + var uniData = getUniversalBlock(block); + + if (uniData == null) { + return null; + } + + return uniData.getMenu(); + } + + @ParametersAreNonnullByDefault + @Nullable public static UniversalMenu getUniversalMenu(UUID uuid, Location l) { + var uniData = Slimefun.getDatabaseManager().getBlockDataController().getUniversalBlockDataFromCache(uuid); + + if (uniData == null) { + return null; + } + + if (!uniData.isDataLoaded()) { + requestLoad(uniData); + return null; + } + + uniData.setLastPresent(new BlockPosition(l)); + + return uniData.getMenu(); + } + + public static boolean isBlockPendingRemove(@Nonnull Block block) { + if (hasBlock(block.getLocation())) { + return getBlock(block.getLocation()).isPendingRemove(); + } + + if (hasUniversalBlock(block.getLocation())) { + return getUniversalBlock(block).isPendingRemove(); + } + + return false; + } + + public static void requestLoad(ADataContainer data) { + if (data.isDataLoaded()) { return; } - if (loadingData.contains(blockData)) { + if (loadingData.contains(data)) { return; } synchronized (loadingData) { - if (loadingData.contains(blockData)) { + if (loadingData.contains(data)) { return; } - loadingData.add(blockData); + loadingData.add(data); } - Slimefun.getDatabaseManager() - .getBlockDataController() - .loadBlockDataAsync(blockData, new IAsyncReadCallback<>() { - @Override - public void onResult(SlimefunBlockData result) { - loadingData.remove(blockData); - } - }); + if (data instanceof SlimefunBlockData blockData) { + Slimefun.getDatabaseManager() + .getBlockDataController() + .loadBlockDataAsync(blockData, new IAsyncReadCallback<>() { + @Override + public void onResult(SlimefunBlockData result) { + loadingData.remove(data); + } + }); + } else if (data instanceof SlimefunUniversalData uniData) { + Slimefun.getDatabaseManager() + .getBlockDataController() + .loadUniversalDataAsync(uniData, new IAsyncReadCallback<>() { + @Override + public void onResult(SlimefunUniversalData result) { + loadingData.remove(data); + } + }); + } + } + + public static void executeAfterLoad(ASlimefunDataContainer data, Runnable execute, boolean runOnMainThread) { + if (data instanceof SlimefunBlockData blockData) { + executeAfterLoad(blockData, execute, runOnMainThread); + } else if (data instanceof SlimefunUniversalData universalData) { + executeAfterLoad(universalData, execute, runOnMainThread); + } } public static void executeAfterLoad(SlimefunBlockData data, Runnable execute, boolean runOnMainThread) { @@ -121,4 +279,23 @@ public void onResult(SlimefunBlockData result) { } }); } + + public static void executeAfterLoad(SlimefunUniversalData data, Runnable execute, boolean runOnMainThread) { + if (data.isDataLoaded()) { + execute.run(); + return; + } + + Slimefun.getDatabaseManager().getBlockDataController().loadUniversalDataAsync(data, new IAsyncReadCallback<>() { + @Override + public boolean runOnMainThread() { + return runOnMainThread; + } + + @Override + public void onResult(SlimefunUniversalData result) { + execute.run(); + } + }); + } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/ErrorReport.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/ErrorReport.java index f8038f069f..60bd92e010 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/ErrorReport.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/ErrorReport.java @@ -81,39 +81,57 @@ public ErrorReport(T throwable, SlimefunAddon addon, Consumer print @ParametersAreNonnullByDefault public ErrorReport(T throwable, Location l, SlimefunItem item) { this(throwable, item.getAddon(), stream -> { - stream.println("Block Info:"); - stream.println(" World: " + l.getWorld().getName()); + stream.println("方块信息:"); + stream.println(" 世界: " + l.getWorld().getName()); stream.println(" X: " + l.getBlockX()); stream.println(" Y: " + l.getBlockY()); stream.println(" Z: " + l.getBlockZ()); - stream.println(" Material: " + l.getBlock().getType()); - stream.println( - " Block Data: " + l.getBlock().getBlockData().getClass().getName()); - stream.println(" State: " + l.getBlock().getState().getClass().getName()); + stream.println(" 方块类型: " + l.getBlock().getType()); + stream.println(" 方块数据: " + l.getBlock().getBlockData().getClass().getName()); + stream.println(" 状态: " + l.getBlock().getState().getClass().getName()); stream.println(); if (item.getBlockTicker() != null) { - stream.println("Ticker-Info:"); - stream.println(" Type: " + (item.getBlockTicker().isSynchronized() ? "Synchronized" : "Asynchronous")); + stream.println("Ticker 信息:"); + stream.println(" 类型: " + (item.getBlockTicker().isSynchronized() ? "同步" : "异步")); stream.println(); } if (item instanceof EnergyNetProvider) { - stream.println("Ticker-Info:"); - stream.println(" Type: Indirect (Energy Network)"); + stream.println("Ticker 信息:"); + stream.println(" 类型: 间接 (由能源网络管理)"); stream.println(); } - stream.println("Slimefun Data:"); + stream.println("Slimefun 数据:"); stream.println(" ID: " + item.getId()); var blockData = Slimefun.getDatabaseManager().getBlockDataController().getBlockData(l); + if (blockData == null) { - stream.println("Block data is not presented."); + Slimefun.getBlockDataService() + .getUniversalDataUUID(l.getBlock()) + .ifPresentOrElse( + uuid -> { + var universalData = Slimefun.getDatabaseManager() + .getBlockDataController() + .getUniversalBlockDataFromCache(uuid); + if (universalData != null) { + stream.println(" 数据加载状态: " + universalData.isDataLoaded()); + stream.println(" 物品栏: " + (universalData.getMenu() != null)); + stream.println(" 数据: "); + universalData + .getAllData() + .forEach((k, v) -> stream.println(" " + k + ": " + v)); + } else { + stream.println("该方块没有任何数据."); + } + }, + () -> stream.println("该方块没有任何数据.")); } else { - stream.println(" Is data loaded: " + blockData.isDataLoaded()); - stream.println(" Inventory: " + (blockData.getBlockMenu() != null)); - stream.println(" Data: "); + stream.println(" 数据加载状态: " + blockData.isDataLoaded()); + stream.println(" 物品栏: " + (blockData.getBlockMenu() != null)); + stream.println(" 数据: "); blockData.getAllData().forEach((k, v) -> stream.println(" " + k + ": " + v)); } stream.println(); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/NotCardinallyRotatable.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/rotations/NotCardinallyRotatable.java similarity index 85% rename from src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/NotCardinallyRotatable.java rename to src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/rotations/NotCardinallyRotatable.java index a4ebd320c8..46b3886140 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/NotCardinallyRotatable.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/rotations/NotCardinallyRotatable.java @@ -1,6 +1,7 @@ -package io.github.thebusybiscuit.slimefun4.core.attributes; +package io.github.thebusybiscuit.slimefun4.core.attributes.rotations; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import io.github.thebusybiscuit.slimefun4.core.attributes.ItemAttribute; import org.bukkit.block.BlockFace; /** diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/NotDiagonallyRotatable.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/rotations/NotDiagonallyRotatable.java similarity index 86% rename from src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/NotDiagonallyRotatable.java rename to src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/rotations/NotDiagonallyRotatable.java index 6dcbb87926..b0ac28fa77 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/NotDiagonallyRotatable.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/rotations/NotDiagonallyRotatable.java @@ -1,6 +1,7 @@ -package io.github.thebusybiscuit.slimefun4.core.attributes; +package io.github.thebusybiscuit.slimefun4.core.attributes.rotations; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import io.github.thebusybiscuit.slimefun4.core.attributes.ItemAttribute; import org.bukkit.block.BlockFace; /** diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/NotRotatable.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/rotations/NotRotatable.java similarity index 73% rename from src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/NotRotatable.java rename to src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/rotations/NotRotatable.java index b8b9f26f89..f3fc095b26 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/NotRotatable.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/rotations/NotRotatable.java @@ -1,6 +1,7 @@ -package io.github.thebusybiscuit.slimefun4.core.attributes; +package io.github.thebusybiscuit.slimefun4.core.attributes.rotations; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import io.github.thebusybiscuit.slimefun4.core.attributes.ItemAttribute; import org.bukkit.block.BlockFace; /** diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/config/SlimefunDatabaseManager.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/config/SlimefunDatabaseManager.java index 805bd4387f..757b0ed886 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/config/SlimefunDatabaseManager.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/config/SlimefunDatabaseManager.java @@ -49,9 +49,6 @@ public SlimefunDatabaseManager(Slimefun plugin) { public void init() { initDefaultVal(); - // Minimise hikari log - System.setProperty("org.slf4j.simpleLogger.log.com.zaxxer.hikari", "error"); - try { blockDataStorageType = StorageType.valueOf(blockStorageConfig.getString("storageType")); var readExecutorThread = blockStorageConfig.getInt("readExecutorThread"); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/AutoSavingService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/AutoSavingService.java index cd80124363..560c6d5510 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/AutoSavingService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/AutoSavingService.java @@ -35,9 +35,15 @@ public void start(@Nonnull Slimefun plugin, int interval) { .getScheduler() .runTaskTimerAsynchronously( plugin, - () -> Slimefun.getDatabaseManager() - .getBlockDataController() - .saveAllBlockInventories(), + () -> { + Slimefun.getDatabaseManager() + .getBlockDataController() + .saveAllBlockInventories(); + + Slimefun.getDatabaseManager() + .getBlockDataController() + .saveAllUniversalInventories(); + }, 2000L, interval * 60L * 20L); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/BlockDataService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/BlockDataService.java index f2bde71bb3..8aeda5a4bf 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/BlockDataService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/BlockDataService.java @@ -4,6 +4,7 @@ import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag; import io.papermc.lib.PaperLib; import java.util.Optional; +import java.util.UUID; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -32,6 +33,7 @@ public class BlockDataService implements Keyed { private final NamespacedKey namespacedKey; + private final NamespacedKey universalDataKey; /** * This creates a new {@link BlockDataService} for the given {@link Plugin}. @@ -45,6 +47,7 @@ public class BlockDataService implements Keyed { */ public BlockDataService(@Nonnull Plugin plugin, @Nonnull String key) { namespacedKey = new NamespacedKey(plugin, key); + universalDataKey = new NamespacedKey(plugin, "slimefun_unidata_uuid"); } @Override @@ -61,6 +64,30 @@ public NamespacedKey getKey() { * The value to store */ public void setBlockData(@Nonnull Block b, @Nonnull String value) { + setBlockData(b, namespacedKey, value); + } + + /** + * This will store the universal data {@link UUID} inside the NBT data of the given {@link Block} + * + * @param b + * The {@link Block} in which to store the given value + * @param uuid + * The uuid linked to certain slimefun item + */ + public void updateUniversalDataUUID(@Nonnull Block b, @Nonnull String uuid) { + setBlockData(b, universalDataKey, uuid); + } + + /** + * This will store the given {@link String} inside the NBT data of the given {@link Block} + * + * @param b + * The {@link Block} in which to store the given value + * @param value + * The value to store + */ + public void setBlockData(@Nonnull Block b, @Nonnull NamespacedKey key, @Nonnull String value) { Validate.notNull(b, "The block cannot be null!"); Validate.notNull(value, "The value cannot be null!"); @@ -73,7 +100,7 @@ public void setBlockData(@Nonnull Block b, @Nonnull String value) { if (state instanceof TileState tileState) { try { PersistentDataContainer container = tileState.getPersistentDataContainer(); - container.set(namespacedKey, PersistentDataType.STRING, value); + container.set(key, PersistentDataType.STRING, value); state.update(); } catch (Exception x) { Slimefun.logger().log(Level.SEVERE, "Please check if your Server Software is up to date!"); @@ -102,13 +129,29 @@ public void setBlockData(@Nonnull Block b, @Nonnull String value) { * @return The stored value */ public Optional getBlockData(@Nonnull Block b) { + return getBlockData(b, namespacedKey); + } + + public Optional getUniversalDataUUID(@Nonnull Block b) { + var uuid = getBlockData(b, universalDataKey); + + return uuid.map(data -> { + try { + return UUID.fromString(data); + } catch (IllegalArgumentException e) { + return null; + } + }); + } + + public Optional getBlockData(@Nonnull Block b, @Nonnull NamespacedKey key) { Validate.notNull(b, "The block cannot be null!"); BlockState state = PaperLib.getBlockState(b, false).getState(); PersistentDataContainer container = getPersistentDataContainer(state); if (container != null) { - return Optional.ofNullable(container.get(namespacedKey, PersistentDataType.STRING)); + return Optional.ofNullable(container.get(key, PersistentDataType.STRING)); } else { return Optional.empty(); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/ticker/TickLocation.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/ticker/TickLocation.java new file mode 100644 index 0000000000..df4e3b5f62 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/ticker/TickLocation.java @@ -0,0 +1,30 @@ +package io.github.thebusybiscuit.slimefun4.core.ticker; + +import io.github.bakedlibs.dough.blocks.BlockPosition; +import java.util.UUID; +import lombok.Getter; +import org.bukkit.Location; + +@Getter +public class TickLocation { + private final BlockPosition position; + private final UUID uuid; + + public TickLocation(BlockPosition position) { + this.position = position; + uuid = null; + } + + public TickLocation(BlockPosition position, UUID uuid) { + this.position = position; + this.uuid = uuid; + } + + public boolean isUniversal() { + return uuid != null; + } + + public Location getLocation() { + return position.toLocation(); + } +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/AndroidAction.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/AndroidAction.java index a7aea4acb5..1a64173d95 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/AndroidAction.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/AndroidAction.java @@ -1,11 +1,11 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.androids; -import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; +import city.norain.slimefun4.api.menu.UniversalMenu; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; @FunctionalInterface interface AndroidAction { - void perform(ProgrammableAndroid android, Block b, BlockMenu inventory, BlockFace face); + void perform(ProgrammableAndroid android, Block b, UniversalMenu inventory, BlockFace face); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/FarmerAndroid.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/FarmerAndroid.java index ec94ac2dc0..7289bed084 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/FarmerAndroid.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/FarmerAndroid.java @@ -1,5 +1,6 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.androids; +import city.norain.slimefun4.api.menu.UniversalMenu; import com.xzavier0722.mc.plugin.slimefun4.storage.util.StorageCacheUtils; import io.github.bakedlibs.dough.protection.Interaction; import io.github.thebusybiscuit.slimefun4.api.events.AndroidFarmEvent; @@ -11,7 +12,6 @@ import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; import javax.annotation.ParametersAreNonnullByDefault; -import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; import org.bukkit.Bukkit; import org.bukkit.Effect; import org.bukkit.Material; @@ -35,10 +35,9 @@ public AndroidType getAndroidType() { } @Override - protected void farm(Block b, BlockMenu menu, Block block, boolean isAdvanced) { - - OfflinePlayer owner = - Bukkit.getOfflinePlayer(UUID.fromString(StorageCacheUtils.getData(b.getLocation(), "owner"))); + protected void farm(Block b, UniversalMenu menu, Block block, boolean isAdvanced) { + OfflinePlayer owner = Bukkit.getOfflinePlayer( + UUID.fromString(StorageCacheUtils.getUniversalBlock(menu.getUuid(), b.getLocation(), "owner"))); if (!Slimefun.getProtectionManager().hasPermission(owner, block, Interaction.BREAK_BLOCK)) { return; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/FishermanAndroid.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/FishermanAndroid.java index 6ea86da447..a40d77df38 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/FishermanAndroid.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/FishermanAndroid.java @@ -1,5 +1,6 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.androids; +import city.norain.slimefun4.api.menu.UniversalMenu; import io.github.bakedlibs.dough.collections.RandomizedSet; import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; @@ -7,7 +8,6 @@ import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundEffect; import java.util.concurrent.ThreadLocalRandom; import javax.annotation.ParametersAreNonnullByDefault; -import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; import org.bukkit.Material; import org.bukkit.Tag; import org.bukkit.block.Block; @@ -50,7 +50,7 @@ public AndroidType getAndroidType() { } @Override - protected void fish(Block b, BlockMenu menu) { + protected void fish(Block b, UniversalMenu menu) { Block water = b.getRelative(BlockFace.DOWN); if (water.getType() == Material.WATER) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Instruction.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Instruction.java index 5a611ccfa6..675f8a0a7b 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Instruction.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Instruction.java @@ -1,5 +1,6 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.androids; +import city.norain.slimefun4.api.menu.UniversalMenu; import io.github.thebusybiscuit.slimefun4.utils.HeadTexture; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; import java.util.HashMap; @@ -8,7 +9,6 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; -import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; import org.apache.commons.lang.Validate; import org.bukkit.Location; import org.bukkit.block.Block; @@ -277,7 +277,7 @@ public AndroidType getRequiredType() { } @ParametersAreNonnullByDefault - public void execute(ProgrammableAndroid android, Block b, BlockMenu inventory, BlockFace face) { + public void execute(ProgrammableAndroid android, Block b, UniversalMenu inventory, BlockFace face) { Validate.notNull(method, "Instruction '" + name() + "' must be executed manually!"); method.perform(android, b, inventory, face); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/MinerAndroid.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/MinerAndroid.java index b0319b96b8..c6f76dc91d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/MinerAndroid.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/MinerAndroid.java @@ -1,5 +1,6 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.androids; +import city.norain.slimefun4.api.menu.UniversalMenu; import com.xzavier0722.mc.plugin.slimefun4.storage.util.StorageCacheUtils; import io.github.bakedlibs.dough.protection.Interaction; import io.github.thebusybiscuit.slimefun4.api.events.AndroidMineEvent; @@ -16,7 +17,6 @@ import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.ParametersAreNonnullByDefault; -import me.mrCookieSlime.Slimefun.api.BlockStorage; import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; import org.bukkit.Bukkit; import org.bukkit.Effect; @@ -71,12 +71,12 @@ public AndroidType getAndroidType() { @Override @ParametersAreNonnullByDefault - protected void dig(Block b, BlockMenu menu, Block block) { + protected void dig(Block b, UniversalMenu menu, Block block) { Collection drops = block.getDrops(effectivePickaxe); if (!SlimefunTag.UNBREAKABLE_MATERIALS.isTagged(block.getType()) && !drops.isEmpty()) { - OfflinePlayer owner = - Bukkit.getOfflinePlayer(UUID.fromString(BlockStorage.getLocationInfo(b.getLocation(), "owner"))); + OfflinePlayer owner = Bukkit.getOfflinePlayer( + UUID.fromString(StorageCacheUtils.getUniversalBlock(menu.getUuid(), b.getLocation(), "owner"))); if (Slimefun.getProtectionManager().hasPermission(owner, block.getLocation(), Interaction.BREAK_BLOCK)) { AndroidMineEvent event = new AndroidMineEvent(block, new AndroidInstance(this, b)); @@ -87,7 +87,8 @@ protected void dig(Block b, BlockMenu menu, Block block) { } // We only want to break non-Slimefun blocks - if (!StorageCacheUtils.hasBlock(block.getLocation())) { + if (!StorageCacheUtils.hasBlock(block.getLocation()) + && !StorageCacheUtils.hasUniversalBlock(block.getLocation())) { breakBlock(menu, drops, block); } } @@ -96,12 +97,12 @@ protected void dig(Block b, BlockMenu menu, Block block) { @Override @ParametersAreNonnullByDefault - protected void moveAndDig(Block b, BlockMenu menu, BlockFace face, Block block) { + protected void moveAndDig(Block b, UniversalMenu menu, BlockFace face, Block block) { Collection drops = block.getDrops(effectivePickaxe); if (!SlimefunTag.UNBREAKABLE_MATERIALS.isTagged(block.getType()) && !drops.isEmpty()) { - OfflinePlayer owner = - Bukkit.getOfflinePlayer(UUID.fromString(BlockStorage.getLocationInfo(b.getLocation(), "owner"))); + OfflinePlayer owner = Bukkit.getOfflinePlayer( + UUID.fromString(StorageCacheUtils.getUniversalBlock(menu.getUuid(), b.getLocation(), "owner"))); if (Slimefun.getProtectionManager().hasPermission(owner, block.getLocation(), Interaction.BREAK_BLOCK)) { AndroidMineEvent event = new AndroidMineEvent(block, new AndroidInstance(this, b)); @@ -112,7 +113,8 @@ protected void moveAndDig(Block b, BlockMenu menu, BlockFace face, Block block) } // We only want to break non-Slimefun blocks - if (!StorageCacheUtils.hasBlock(block.getLocation())) { + if (!StorageCacheUtils.hasBlock(block.getLocation()) + && !StorageCacheUtils.hasUniversalBlock(block.getLocation())) { breakBlock(menu, drops, block); move(b, face, block); } @@ -125,7 +127,7 @@ protected void moveAndDig(Block b, BlockMenu menu, BlockFace face, Block block) } @ParametersAreNonnullByDefault - private void breakBlock(BlockMenu menu, Collection drops, Block block) { + private void breakBlock(UniversalMenu menu, Collection drops, Block block) { if (!block.getWorld().getWorldBorder().isInside(block.getLocation())) { return; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java index c86bb625c2..33e22d6469 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java @@ -1,7 +1,13 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.androids; -import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunBlockData; +import city.norain.slimefun4.api.menu.UniversalMenu; +import city.norain.slimefun4.api.menu.UniversalMenuPreset; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunUniversalBlockData; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunUniversalData; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.attributes.UniversalBlock; +import com.xzavier0722.mc.plugin.slimefun4.storage.util.LocationUtils; import com.xzavier0722.mc.plugin.slimefun4.storage.util.StorageCacheUtils; +import io.github.bakedlibs.dough.blocks.BlockPosition; import io.github.bakedlibs.dough.chat.ChatInput; import io.github.bakedlibs.dough.common.ChatColors; import io.github.bakedlibs.dough.common.CommonPatterns; @@ -14,8 +20,8 @@ import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotDiagonallyRotatable; import io.github.thebusybiscuit.slimefun4.core.attributes.RecipeDisplayItem; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotDiagonallyRotatable; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockBreakHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundEffect; @@ -30,6 +36,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.UUID; import java.util.function.Predicate; @@ -42,13 +49,10 @@ import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.MachineFuel; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.interfaces.InventoryBlock; import me.mrCookieSlime.Slimefun.Objects.handlers.BlockTicker; -import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; -import me.mrCookieSlime.Slimefun.api.inventory.BlockMenuPreset; import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow; import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; -import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.OfflinePlayer; import org.bukkit.Tag; @@ -67,7 +71,7 @@ import org.bukkit.inventory.meta.ItemMeta; public class ProgrammableAndroid extends SlimefunItem - implements InventoryBlock, RecipeDisplayItem, NotDiagonallyRotatable { + implements InventoryBlock, RecipeDisplayItem, NotDiagonallyRotatable, UniversalBlock { private static final List POSSIBLE_ROTATIONS = Arrays.asList(BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST); @@ -91,7 +95,7 @@ public ProgrammableAndroid( texture = item.getSkullTexture().orElse(null); registerDefaultFuelTypes(); - new BlockMenuPreset(getId(), "可编程式机器人") { + new UniversalMenuPreset(getId(), "可编程式机器人") { @Override public void init() { @@ -100,8 +104,16 @@ public void init() { @Override public boolean canOpen(Block b, Player p) { - boolean isOwner = p.getUniqueId().toString().equals(StorageCacheUtils.getData(b.getLocation(), "owner")) - || p.hasPermission("slimefun.android.bypass"); + var uniData = StorageCacheUtils.getUniversalBlock(b); + + Objects.requireNonNull( + uniData, + "Unable to get android's universal data at " + LocationUtils.locationToString(b.getLocation()) + + "!"); + + UUID owner = UUID.fromString(uniData.getData("owner")); + + boolean isOwner = p.getUniqueId().equals(owner) || p.hasPermission("slimefun.android.bypass"); if (isOwner || AndroidShareMenu.isTrustedUser(b, p.getUniqueId())) { return true; @@ -112,19 +124,23 @@ public boolean canOpen(Block b, Player p) { } @Override - public void newInstance(BlockMenu menu, Block b) { + public void newInstance(UniversalMenu menu, Block b) { + var uniData = StorageCacheUtils.getUniversalBlock(menu.getUuid()); + + Objects.requireNonNull(uniData, "Unable to get android's universal data for " + menu.getUuid() + "!"); + menu.replaceExistingItem( 15, new CustomItemStack(HeadTexture.SCRIPT_START.getAsItemStack(), "&a启动/继续运行")); menu.addMenuClickHandler(15, (p, slot, item, action) -> { Slimefun.getLocalization().sendMessage(p, "android.started", true); - StorageCacheUtils.setData(b.getLocation(), "paused", "false"); + uniData.setData("paused", "false"); p.closeInventory(); return false; }); menu.replaceExistingItem(17, new CustomItemStack(HeadTexture.SCRIPT_PAUSE.getAsItemStack(), "&4暂停运行")); menu.addMenuClickHandler(17, (p, slot, item, action) -> { - StorageCacheUtils.setData(b.getLocation(), "paused", "true"); + uniData.setData("paused", "true"); Slimefun.getLocalization().sendMessage(p, "android.stopped", true); return false; }); @@ -134,9 +150,9 @@ public void newInstance(BlockMenu menu, Block b) { new CustomItemStack( HeadTexture.ENERGY_REGULATOR.getAsItemStack(), "&b内存核心", "", "&8\u21E8 &7单击打开脚本编辑器")); menu.addMenuClickHandler(16, (p, slot, item, action) -> { - StorageCacheUtils.setData(b.getLocation(), "paused", "true"); + uniData.setData("paused", "true"); Slimefun.getLocalization().sendMessage(p, "android.stopped", true); - openScriptEditor(p, b); + openScriptEditor(p, uniData); return false; }); @@ -148,7 +164,7 @@ public void newInstance(BlockMenu menu, Block b) { "", Slimefun.getLocalization().getMessage("android.access-manager.subtitle"))); menu.addMenuClickHandler(25, (p, slot, item, action) -> { - StorageCacheUtils.setData(b.getLocation(), "paused", "true"); + uniData.setData("paused", "true"); Slimefun.getLocalization().sendMessage(p, "android.stopped", true); AndroidShareMenu.openShareMenu(p, b); return false; @@ -173,13 +189,15 @@ public void onPlayerPlace(BlockPlaceEvent e) { Player p = e.getPlayer(); Block b = e.getBlock(); - var blockData = StorageCacheUtils.getBlock(b.getLocation()); - blockData.setData("owner", p.getUniqueId().toString()); - blockData.setData("script", DEFAULT_SCRIPT); - blockData.setData("index", "0"); - blockData.setData("fuel", "0"); - blockData.setData("rotation", p.getFacing().getOppositeFace().toString()); - blockData.setData("paused", "true"); + var universalData = StorageCacheUtils.getUniversalBlock(b); + + universalData.setData("owner", p.getUniqueId().toString()); + universalData.setData("script", DEFAULT_SCRIPT); + universalData.setData("index", String.valueOf(0)); + universalData.setData("fuel", String.valueOf(0)); + universalData.setData( + "rotation", p.getFacing().getOppositeFace().toString()); + universalData.setData("paused", String.valueOf(true)); b.setBlockData(Material.PLAYER_HEAD.createBlockData(data -> { if (data instanceof Rotatable rotatable) { @@ -197,21 +215,25 @@ private BlockBreakHandler onBreak() { @Override public void onPlayerBreak(BlockBreakEvent e, ItemStack item, List drops) { Block b = e.getBlock(); - var blockData = StorageCacheUtils.getBlock(b.getLocation()); - String owner = blockData.getData("owner"); - if (!e.getPlayer().hasPermission("slimefun.android.bypass") - && !e.getPlayer().getUniqueId().toString().equals(owner)) { - // The Player is not allowed to break this android - e.setCancelled(true); - return; - } + var uniData = StorageCacheUtils.getUniversalBlock(b); - BlockMenu inv = blockData.getBlockMenu(); + if (uniData != null) { + if (!e.getPlayer().hasPermission("slimefun.android.bypass") + && !e.getPlayer().getUniqueId().equals(uniData.getData("owner"))) { + // The Player is not allowed to break this android + e.setCancelled(true); + return; + } - if (inv != null) { - inv.dropItems(b.getLocation(), 43); - inv.dropItems(b.getLocation(), getOutputSlots()); + var menu = uniData.getMenu(); + if (menu != null) { + menu.dropItems(b.getLocation(), 43); + menu.dropItems(b.getLocation(), getOutputSlots()); + } + } else { + throw new IllegalStateException( + "Missing universal id android @" + LocationUtils.locationToString(b.getLocation())); } } }; @@ -246,10 +268,10 @@ public AndroidFuelSource getFuelSource() { public void preRegister() { super.preRegister(); - addItemHandler(new BlockTicker() { + addItemHandler(new BlockTicker(true) { @Override - public void tick(Block b, SlimefunItem item, SlimefunBlockData data) { + public void tick(Block b, SlimefunItem item, SlimefunUniversalData data) { if (b != null && data != null) { ProgrammableAndroid.this.tick(b, data); } @@ -263,7 +285,7 @@ public boolean isSynchronized() { } @ParametersAreNonnullByDefault - public void openScript(Player p, Block b, String sourceCode) { + public void openScript(Player p, SlimefunUniversalBlockData uniData, String sourceCode) { ChestMenu menu = new ChestMenu(ChatColor.DARK_AQUA + Slimefun.getLocalization().getMessage(p, "android.scripts.editor")); menu.setEmptySlotsClickable(false); @@ -276,7 +298,7 @@ public void openScript(Player p, Block b, String sourceCode) { "", "&7\u21E8 &e左键 &7返回机器人的控制面板")); menu.addMenuClickHandler(0, (pl, slot, item, action) -> { - BlockMenu inv = StorageCacheUtils.getMenu(b.getLocation()); + UniversalMenu inv = uniData.getMenu(); // Fixes #2937 if (inv != null) { inv.open(pl); @@ -297,7 +319,7 @@ public void openScript(Player p, Block b, String sourceCode) { if (hasFreeSlot) { menu.addItem(i, new CustomItemStack(HeadTexture.SCRIPT_NEW.getAsItemStack(), "&7> 添加新命令")); menu.addMenuClickHandler(i, (pl, slot, item, action) -> { - editInstruction(pl, b, script, index); + editInstruction(pl, uniData, script, index); return false; }); } @@ -311,7 +333,7 @@ public void openScript(Player p, Block b, String sourceCode) { "", "&7\u21E8 &e左键 &7返回机器人的控制面板")); menu.addMenuClickHandler(slot, (pl, s, item, action) -> { - BlockMenu inv = StorageCacheUtils.getMenu(b.getLocation()); + UniversalMenu inv = uniData.getMenu(); // Fixes #2937 if (inv != null) { inv.open(pl); @@ -355,14 +377,14 @@ public void openScript(Player p, Block b, String sourceCode) { } String code = duplicateInstruction(script, index); - setScript(b.getLocation(), code); - openScript(pl, b, code); + setScript(uniData, code); + openScript(pl, uniData, code); } else if (action.isRightClicked()) { String code = deleteInstruction(script, index); - setScript(b.getLocation(), code); - openScript(pl, b, code); + setScript(uniData, code); + openScript(pl, uniData, code); } else { - editInstruction(pl, b, script, index); + editInstruction(pl, uniData, script, index); } return false; @@ -428,7 +450,7 @@ private String deleteInstruction(String[] script, int index) { return builder.toString(); } - protected void openScriptDownloader(Player p, Block b, int page) { + protected void openScriptDownloader(Player p, SlimefunUniversalBlockData uniData, int page) { ChestMenu menu = new ChestMenu("机器人脚本"); menu.setEmptySlotsClickable(false); @@ -449,7 +471,7 @@ protected void openScriptDownloader(Player p, Block b, int page) { next = pages; } if (next != page) { - openScriptDownloader(pl, b, next); + openScriptDownloader(pl, uniData, next); } return false; }); @@ -459,7 +481,7 @@ protected void openScriptDownloader(Player p, Block b, int page) { new CustomItemStack( HeadTexture.SCRIPT_UP.getAsItemStack(), "&e上传脚本", "", "&6单击 &7将你正在用的脚本", "&7上传到服务器")); menu.addMenuClickHandler(48, (pl, slot, item, action) -> { - uploadScript(pl, b, page); + uploadScript(pl, uniData, page); return false; }); @@ -470,14 +492,14 @@ protected void openScriptDownloader(Player p, Block b, int page) { next = 1; } if (next != page) { - openScriptDownloader(pl, b, next); + openScriptDownloader(pl, uniData, next); } return false; }); menu.addItem(53, new CustomItemStack(HeadTexture.SCRIPT_LEFT.getAsItemStack(), "&6> 返回", "", "&7返回机器人控制面板")); menu.addMenuClickHandler(53, (pl, slot, item, action) -> { - openScriptEditor(pl, b); + openScriptEditor(pl, uniData); return false; }); @@ -498,14 +520,14 @@ protected void openScriptDownloader(Player p, Block b, int page) { Slimefun.getLocalization().sendMessage(player, "android.scripts.rating.own", true); } else if (script.canRate(player)) { script.rate(player, !action.isRightClicked()); - openScriptDownloader(player, b, page); + openScriptDownloader(player, uniData, page); } else { Slimefun.getLocalization().sendMessage(player, "android.scripts.rating.already", true); } } else if (!action.isRightClicked()) { script.download(); - setScript(b.getLocation(), script.getSourceCode()); - openScriptEditor(player, b); + setScript(uniData, script.getSourceCode()); + openScriptEditor(player, uniData); } } catch (Exception x) { Slimefun.logger() @@ -526,8 +548,8 @@ protected void openScriptDownloader(Player p, Block b, int page) { } @ParametersAreNonnullByDefault - private void uploadScript(Player p, Block b, int page) { - String code = getScript(b.getLocation()); + private void uploadScript(Player p, SlimefunUniversalBlockData uniData, int page) { + String code = getScript(uniData); int nextId = 1; for (Script script : Script.getUploadedScripts(getAndroidType())) { @@ -548,28 +570,23 @@ private void uploadScript(Player p, Block b, int page) { ChatInput.waitForPlayer(Slimefun.instance(), p, msg -> { Script.upload(p, getAndroidType(), id, msg, code); Slimefun.getLocalization().sendMessages(p, "android.scripts.uploaded"); - openScriptDownloader(p, b, page); + openScriptDownloader(p, uniData, page); }); } - public void openScriptEditor(Player p, Block b) { + public void openScriptEditor(Player p, SlimefunUniversalBlockData uniData) { ChestMenu menu = new ChestMenu(ChatColor.DARK_AQUA + Slimefun.getLocalization().getMessage(p, "android.scripts.editor")); menu.setEmptySlotsClickable(false); menu.addItem(1, new CustomItemStack(HeadTexture.SCRIPT_FORWARD.getAsItemStack(), "&2> 编辑脚本", "", "&a修改你现有的脚本")); menu.addMenuClickHandler(1, (pl, slot, item, action) -> { - String script = StorageCacheUtils.getData(b.getLocation(), "script"); - // Fixes #2937 - if (script != null) { - if (CommonPatterns.DASH.split(script).length <= MAX_SCRIPT_LENGTH) { - openScript(pl, b, getScript(b.getLocation())); - } else { - pl.closeInventory(); - Slimefun.getLocalization().sendMessage(pl, "android.scripts.too-long"); - } + String script = getScript(uniData); + if (CommonPatterns.DASH.split(script).length <= MAX_SCRIPT_LENGTH) { + openScript(pl, uniData, script); } else { pl.closeInventory(); + Slimefun.getLocalization().sendMessage(pl, "android.scripts.too-long"); } return false; }); @@ -579,7 +596,7 @@ public void openScriptEditor(Player p, Block b) { new CustomItemStack( HeadTexture.SCRIPT_NEW.getAsItemStack(), "&4> 创建新脚本", "", "&c删除你正在使用的脚本", "&c并创建一个全新的空白脚本")); menu.addMenuClickHandler(3, (pl, slot, item, action) -> { - openScript(pl, b, DEFAULT_SCRIPT); + openScript(pl, uniData, DEFAULT_SCRIPT); return false; }); @@ -592,13 +609,13 @@ public void openScriptEditor(Player p, Block b) { "&e从服务器中下载其他玩家上传的脚本", "&e可以即下即用, 或者修改之后再使用")); menu.addMenuClickHandler(5, (pl, slot, item, action) -> { - openScriptDownloader(pl, b, 1); + openScriptDownloader(pl, uniData, 1); return false; }); menu.addItem(8, new CustomItemStack(HeadTexture.SCRIPT_LEFT.getAsItemStack(), "&6> 返回", "", "&7返回机器人控制面板")); menu.addMenuClickHandler(8, (pl, slot, item, action) -> { - BlockMenu inv = StorageCacheUtils.getMenu(b.getLocation()); + UniversalMenu inv = uniData.getMenu(); // Fixes #2937 if (inv != null) { inv.open(pl); @@ -628,7 +645,7 @@ protected List getValidScriptInstructions() { return list; } - protected void editInstruction(Player p, Block b, String[] script, int index) { + protected void editInstruction(Player p, SlimefunUniversalBlockData uniData, String[] script, int index) { ChestMenu menu = new ChestMenu(ChatColor.DARK_AQUA + Slimefun.getLocalization().getMessage(p, "android.scripts.editor")); ChestMenuUtils.drawBackground(menu, 0, 1, 2, 3, 4, 5, 6, 7, 8); @@ -639,8 +656,8 @@ protected void editInstruction(Player p, Block b, String[] script, int index) { new CustomItemStack(HeadTexture.SCRIPT_PAUSE.getAsItemStack(), "&f什么也不做"), (pl, slot, item, action) -> { String code = deleteInstruction(script, index); - setScript(b.getLocation(), code); - openScript(p, b, code); + setScript(uniData, code); + openScript(p, uniData, code); return false; }); @@ -654,8 +671,8 @@ protected void editInstruction(Player p, Block b, String[] script, int index) { .getMessage(p, "android.scripts.instructions." + instruction.name())), (pl, slot, item, action) -> { String code = addInstruction(script, index, instruction); - setScript(b.getLocation(), code); - openScript(p, b, code); + setScript(uniData, code); + openScript(p, uniData, code); return false; }); @@ -666,14 +683,14 @@ protected void editInstruction(Player p, Block b, String[] script, int index) { } @Nonnull - public String getScript(@Nonnull Location l) { - Validate.notNull(l, "Location for android not specified"); - String script = StorageCacheUtils.getData(l, "script"); + public String getScript(@Nonnull SlimefunUniversalBlockData ubd) { + Validate.notNull(ubd, "SlimefunUniversalBlockData for android not specified"); + String script = ubd.getData("script"); return script != null ? script : DEFAULT_SCRIPT; } - public void setScript(@Nonnull Location l, @Nonnull String script) { - Validate.notNull(l, "Location for android not specified"); + public void setScript(@Nonnull SlimefunUniversalBlockData ubd, @Nonnull String script) { + Validate.notNull(ubd, "SlimefunUniversalBlockData for android not specified"); Validate.notNull(script, "No script given"); Validate.isTrue(script.startsWith(Instruction.START.name() + '-'), "A script must begin with a 'START' token."); Validate.isTrue(script.endsWith('-' + Instruction.REPEAT.name()), "A script must end with a 'REPEAT' token."); @@ -681,7 +698,7 @@ public void setScript(@Nonnull Location l, @Nonnull String script) { CommonPatterns.DASH.split(script).length <= MAX_SCRIPT_LENGTH, "Scripts may not have more than " + MAX_SCRIPT_LENGTH + " segments"); - StorageCacheUtils.setData(l, "script", script); + ubd.setData("script", script); } private void registerDefaultFuelTypes() { @@ -762,14 +779,14 @@ public int getTier() { return tier; } - protected void tick(Block b, SlimefunBlockData data) { + protected void tick(Block b, SlimefunUniversalData data) { if (b.getType() != Material.PLAYER_HEAD) { // The Android was destroyed or moved. return; } if ("false".equals(data.getData("paused"))) { - BlockMenu menu = data.getBlockMenu(); + UniversalMenu menu = data.getMenu(); String fuelData = data.getData("fuel"); float fuel = fuelData == null ? 0 : Float.parseFloat(fuelData); @@ -790,8 +807,7 @@ protected void tick(Block b, SlimefunBlockData data) { Instruction instruction = Instruction.getInstruction(script[index]); if (instruction == null) { - Slimefun.instance() - .getLogger() + Slimefun.logger() .log( Level.WARNING, "Failed to parse Android instruction: {0}, maybe your server is out of date?", @@ -810,7 +826,7 @@ protected void tick(Block b, SlimefunBlockData data) { @ParametersAreNonnullByDefault private void executeInstruction( - Instruction instruction, Block b, BlockMenu inv, SlimefunBlockData data, int index) { + Instruction instruction, Block b, UniversalMenu inv, SlimefunUniversalData data, int index) { if ("true".equals(data.getData("paused"))) { return; } @@ -863,7 +879,7 @@ protected void rotate(Block b, BlockFace current, int mod) { StorageCacheUtils.setData(b.getLocation(), "rotation", rotation.name()); } - protected void depositItems(BlockMenu menu, Block facedBlock) { + protected void depositItems(UniversalMenu menu, Block facedBlock) { if (facedBlock.getType() == Material.DISPENSER && StorageCacheUtils.isBlock(facedBlock.getLocation(), "ANDROID_INTERFACE_ITEMS")) { BlockState state = PaperLib.getBlockState(facedBlock, false).getState(); @@ -887,7 +903,7 @@ protected void depositItems(BlockMenu menu, Block facedBlock) { } } - protected void refuel(BlockMenu menu, Block facedBlock) { + protected void refuel(UniversalMenu menu, Block facedBlock) { if (facedBlock.getType() == Material.DISPENSER && StorageCacheUtils.isBlock(facedBlock.getLocation(), "ANDROID_INTERFACE_FUEL")) { BlockState state = PaperLib.getBlockState(facedBlock, false).getState(); @@ -905,7 +921,7 @@ protected void refuel(BlockMenu menu, Block facedBlock) { } private boolean insertFuel( - BlockMenu menu, Inventory dispenser, int slot, ItemStack currentFuel, ItemStack newFuel) { + UniversalMenu menu, Inventory dispenser, int slot, ItemStack currentFuel, ItemStack newFuel) { if (currentFuel == null) { menu.replaceExistingItem(43, newFuel); dispenser.setItem(slot, null); @@ -926,7 +942,7 @@ private boolean insertFuel( } @ParametersAreNonnullByDefault - private void consumeFuel(Block b, BlockMenu menu) { + private void consumeFuel(Block b, UniversalMenu menu) { ItemStack item = menu.getItemInSlot(43); if (item != null && item.getType() != Material.AIR) { @@ -946,7 +962,7 @@ private void consumeFuel(Block b, BlockMenu menu) { } } - private void constructMenu(@Nonnull BlockMenuPreset preset) { + private void constructMenu(@Nonnull UniversalMenuPreset preset) { preset.drawBackground(BORDER); preset.drawBackground(ChestMenuUtils.getOutputSlotTexture(), OUTPUT_BORDER); @@ -973,7 +989,13 @@ public boolean onClick( public void addItems(Block b, ItemStack... items) { Validate.notNull(b, "The Block cannot be null."); - BlockMenu inv = StorageCacheUtils.getMenu(b.getLocation()); + Optional uuid = Slimefun.getBlockDataService().getUniversalDataUUID(b); + + if (uuid.isEmpty()) { + throw new IllegalStateException("Android missing uuid"); + } + + UniversalMenu inv = StorageCacheUtils.getUniversalMenu(uuid.get(), b.getLocation()); if (inv != null) { for (ItemStack item : items) { @@ -983,49 +1005,63 @@ public void addItems(Block b, ItemStack... items) { } @ParametersAreNonnullByDefault - protected void move(Block b, BlockFace face, Block block) { - var blockData = StorageCacheUtils.getBlock(b.getLocation()); - OfflinePlayer owner = Bukkit.getOfflinePlayer(UUID.fromString(blockData.getData("owner"))); + protected void move(Block from, BlockFace face, Block to) { + var uniData = StorageCacheUtils.getUniversalBlock(from); - if (!Slimefun.getProtectionManager().hasPermission(owner, block.getLocation(), Interaction.PLACE_BLOCK)) { + if (uniData == null) { + throw new IllegalStateException("This android doesn't have universal data! Location at " + + LocationUtils.locationToString(from.getLocation())); + } + + OfflinePlayer owner = Bukkit.getOfflinePlayer(UUID.fromString(uniData.getData("owner"))); + + if (!Slimefun.getProtectionManager().hasPermission(owner, to.getLocation(), Interaction.PLACE_BLOCK)) { return; } - if (block.getY() > block.getWorld().getMinHeight() - && block.getY() < block.getWorld().getMaxHeight() - && block.isEmpty()) { + if (to.getY() > to.getWorld().getMinHeight() + && to.getY() < to.getWorld().getMaxHeight() + && to.isEmpty()) { - if (!block.getWorld().getWorldBorder().isInside(block.getLocation())) { + if (!to.getWorld().getWorldBorder().isInside(to.getLocation())) { return; } + Slimefun.getTickerTask().disableTicker(from.getLocation()); + // Bro encountered a ghost 💀 - if (StorageCacheUtils.hasBlock(block.getLocation())) { - var data = StorageCacheUtils.getBlock(block.getLocation()); + if (StorageCacheUtils.hasBlock(to.getLocation())) { + var data = StorageCacheUtils.getBlock(to.getLocation()); if (data != null && !data.isPendingRemove()) { // Since it's a ghost, we just hunt it. - Slimefun.getDatabaseManager().getBlockDataController().removeBlock(block.getLocation()); + Slimefun.getDatabaseManager().getBlockDataController().removeBlock(to.getLocation()); } return; } - block.setBlockData(Material.PLAYER_HEAD.createBlockData(data -> { + to.setBlockData(Material.PLAYER_HEAD.createBlockData(data -> { if (data instanceof Rotatable rotatable) { rotatable.setRotation(face.getOppositeFace()); } })); + Slimefun.getBlockDataService() + .updateUniversalDataUUID(to, uniData.getUUID().toString()); + Slimefun.runSync(() -> { PlayerSkin skin = PlayerSkin.fromBase64(texture); - Material type = block.getType(); + Material type = to.getType(); // Ensure that this Block is still a Player Head if (type == Material.PLAYER_HEAD || type == Material.PLAYER_WALL_HEAD) { - PlayerHead.setSkin(block, skin, true); + PlayerHead.setSkin(to, skin, true); } }); - b.setType(Material.AIR); - Slimefun.getDatabaseManager().getBlockDataController().setBlockDataLocation(blockData, block.getLocation()); + from.setType(Material.AIR); + uniData.setLastPresent(new BlockPosition(to.getLocation())); + uniData.getMenu().update(to.getLocation()); + + Slimefun.getTickerTask().enableTicker(to.getLocation(), uniData.getUUID()); } } @@ -1033,23 +1069,23 @@ protected void attack(Block b, BlockFace face, Predicate predicate throw new UnsupportedOperationException("Non-butcher Android tried to butcher!"); } - protected void fish(Block b, BlockMenu menu) { + protected void fish(Block b, UniversalMenu menu) { throw new UnsupportedOperationException("Non-fishing Android tried to fish!"); } - protected void dig(Block b, BlockMenu menu, Block block) { + protected void dig(Block b, UniversalMenu menu, Block block) { throw new UnsupportedOperationException("Non-mining Android tried to mine!"); } - protected void moveAndDig(Block b, BlockMenu menu, BlockFace face, Block block) { + protected void moveAndDig(Block b, UniversalMenu menu, BlockFace face, Block block) { throw new UnsupportedOperationException("Non-mining Android tried to mine!"); } - protected boolean chopTree(Block b, BlockMenu menu, BlockFace face) { + protected boolean chopTree(Block b, UniversalMenu menu, BlockFace face) { throw new UnsupportedOperationException("Non-woodcutter Android tried to chop a Tree!"); } - protected void farm(Block b, BlockMenu menu, Block block, boolean isAdvanced) { + protected void farm(Block b, UniversalMenu menu, Block block, boolean isAdvanced) { throw new UnsupportedOperationException("Non-farming Android tried to farm!"); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/WoodcutterAndroid.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/WoodcutterAndroid.java index aa8600d7fd..ec37eb41e4 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/WoodcutterAndroid.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/WoodcutterAndroid.java @@ -1,5 +1,6 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.androids; +import city.norain.slimefun4.api.menu.UniversalMenu; import com.xzavier0722.mc.plugin.slimefun4.storage.util.StorageCacheUtils; import io.github.bakedlibs.dough.blocks.Vein; import io.github.bakedlibs.dough.protection.Interaction; @@ -14,7 +15,6 @@ import java.util.function.Predicate; import javax.annotation.Nonnull; import javax.annotation.ParametersAreNonnullByDefault; -import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; import org.bukkit.Bukkit; import org.bukkit.Effect; import org.bukkit.Material; @@ -40,7 +40,7 @@ public AndroidType getAndroidType() { } @Override - protected boolean chopTree(Block b, BlockMenu menu, BlockFace face) { + protected boolean chopTree(Block b, UniversalMenu menu, BlockFace face) { Block target = b.getRelative(face); if (!target.getWorld().getWorldBorder().isInside(target.getLocation())) { @@ -54,8 +54,8 @@ protected boolean chopTree(Block b, BlockMenu menu, BlockFace face) { Block log = list.get(list.size() - 1); log.getWorld().playEffect(log.getLocation(), Effect.STEP_SOUND, log.getType()); - OfflinePlayer owner = - Bukkit.getOfflinePlayer(UUID.fromString(StorageCacheUtils.getData(b.getLocation(), "owner"))); + OfflinePlayer owner = Bukkit.getOfflinePlayer( + UUID.fromString(StorageCacheUtils.getUniversalBlock(menu.getUuid(), b.getLocation(), "owner"))); if (Slimefun.getProtectionManager().hasPermission(owner, log.getLocation(), Interaction.BREAK_BLOCK)) { breakLog(log, b, menu, face); } @@ -68,7 +68,7 @@ protected boolean chopTree(Block b, BlockMenu menu, BlockFace face) { } @ParametersAreNonnullByDefault - private void breakLog(Block log, Block android, BlockMenu menu, BlockFace face) { + private void breakLog(Block log, Block android, UniversalMenu menu, BlockFace face) { ItemStack drop = new ItemStack(log.getType()); // We try to push the log into the android's inventory, but nothing happens if it does not fit diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/menu/AndroidShareMenu.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/menu/AndroidShareMenu.java index cf7e67925d..c68e136c10 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/menu/AndroidShareMenu.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/menu/AndroidShareMenu.java @@ -7,7 +7,6 @@ import io.github.thebusybiscuit.slimefun4.utils.ChatUtils; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; import io.github.thebusybiscuit.slimefun4.utils.HeadTexture; -import io.papermc.lib.PaperLib; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -248,14 +247,6 @@ private static void setSharedUserData(@Nonnull BlockState state, @Nonnull String container.set(BLOCK_INFO_KEY, PersistentDataType.STRING, value); state.update(); } catch (Exception x) { - Slimefun.logger().log(Level.SEVERE, "Please check if your Server Software is up to date!"); - - String serverSoftware = PaperLib.isSpigot() && !PaperLib.isPaper() ? "Spigot" : Bukkit.getName(); - Slimefun.logger() - .log( - Level.SEVERE, - () -> serverSoftware + " | " + Bukkit.getVersion() + " | " + Bukkit.getBukkitVersion()); - Slimefun.logger() .log(Level.SEVERE, "An Exception was thrown while trying to set Persistent Data for a Android", x); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/ArmorAutoCrafter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/ArmorAutoCrafter.java index 1db215b68e..9b007a5dc9 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/ArmorAutoCrafter.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/ArmorAutoCrafter.java @@ -3,7 +3,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotDiagonallyRotatable; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotDiagonallyRotatable; import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.ArmorForge; import javax.annotation.ParametersAreNonnullByDefault; import org.bukkit.inventory.ItemStack; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/EnhancedAutoCrafter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/EnhancedAutoCrafter.java index 9c8d72a952..b1dbd80658 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/EnhancedAutoCrafter.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/EnhancedAutoCrafter.java @@ -3,7 +3,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotDiagonallyRotatable; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotDiagonallyRotatable; import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.EnhancedCraftingTable; import io.github.thebusybiscuit.slimefun4.implementation.listeners.AutoCrafterListener; import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/SlimefunAutoCrafter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/SlimefunAutoCrafter.java index 3a9210f64a..c1587cb33b 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/SlimefunAutoCrafter.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/SlimefunAutoCrafter.java @@ -116,7 +116,6 @@ protected void updateRecipe(@Nonnull Block b, @Nonnull Player p) { menu.open(p); SoundEffect.AUTO_CRAFTER_UPDATE_RECIPE.playAt(b); - ; if (!task.isEmpty()) { task.start(menu.toInventory()); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/VanillaAutoCrafter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/VanillaAutoCrafter.java index a141837ee0..bf063758a6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/VanillaAutoCrafter.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/VanillaAutoCrafter.java @@ -6,7 +6,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotDiagonallyRotatable; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotDiagonallyRotatable; import io.github.thebusybiscuit.slimefun4.core.services.MinecraftRecipeService; import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundEffect; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/CargoConnectorNode.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/CargoConnectorNode.java index 821214896c..9de124b4c6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/CargoConnectorNode.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/CargoConnectorNode.java @@ -3,7 +3,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotRotatable; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotRotatable; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockUseHandler; import io.github.thebusybiscuit.slimefun4.core.networks.cargo.CargoNet; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/CargoManager.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/CargoManager.java index 967f2e4a27..a53f3d3689 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/CargoManager.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/CargoManager.java @@ -8,7 +8,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; import io.github.thebusybiscuit.slimefun4.core.attributes.HologramOwner; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotRotatable; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotRotatable; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockBreakHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockUseHandler; import io.github.thebusybiscuit.slimefun4.core.networks.cargo.CargoNet; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/TrashCan.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/TrashCan.java index 1b24c6aa63..8a6348acc3 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/TrashCan.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/TrashCan.java @@ -6,7 +6,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotRotatable; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotRotatable; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; import javax.annotation.ParametersAreNonnullByDefault; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.interfaces.InventoryBlock; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/EnergyConnector.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/EnergyConnector.java index 3793dae395..cc9fde2972 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/EnergyConnector.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/EnergyConnector.java @@ -5,7 +5,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotRotatable; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotRotatable; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockUseHandler; import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNet; import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNetComponentType; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/EnergyRegulator.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/EnergyRegulator.java index fd79a39c8e..307ad2f4b2 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/EnergyRegulator.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/EnergyRegulator.java @@ -7,7 +7,7 @@ import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent; import io.github.thebusybiscuit.slimefun4.core.attributes.HologramOwner; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotRotatable; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotRotatable; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockBreakHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNet; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/generators/CoalGenerator.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/generators/CoalGenerator.java index f44b2d1ffa..0d1877edc6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/generators/CoalGenerator.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/generators/CoalGenerator.java @@ -3,7 +3,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotDiagonallyRotatable; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotDiagonallyRotatable; import javax.annotation.Nonnull; import javax.annotation.ParametersAreNonnullByDefault; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AGenerator; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/generators/CombustionGenerator.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/generators/CombustionGenerator.java index e3704158e7..2e4fc2aec2 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/generators/CombustionGenerator.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/generators/CombustionGenerator.java @@ -3,7 +3,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotDiagonallyRotatable; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotDiagonallyRotatable; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import javax.annotation.ParametersAreNonnullByDefault; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AGenerator; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/generators/LavaGenerator.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/generators/LavaGenerator.java index 965ff85d61..df4b97d4a3 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/generators/LavaGenerator.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/generators/LavaGenerator.java @@ -3,7 +3,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotDiagonallyRotatable; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotDiagonallyRotatable; import javax.annotation.ParametersAreNonnullByDefault; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AGenerator; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.MachineFuel; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/generators/MagnesiumGenerator.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/generators/MagnesiumGenerator.java index ea3a4852cc..23dd4a2e5c 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/generators/MagnesiumGenerator.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/generators/MagnesiumGenerator.java @@ -3,7 +3,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotDiagonallyRotatable; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotDiagonallyRotatable; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import javax.annotation.ParametersAreNonnullByDefault; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AGenerator; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/ElectricPress.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/ElectricPress.java index 505d4a1a7a..678e85f9a3 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/ElectricPress.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/ElectricPress.java @@ -5,8 +5,8 @@ import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotDiagonallyRotatable; import io.github.thebusybiscuit.slimefun4.core.attributes.RecipeDisplayItem; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotDiagonallyRotatable; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/ExpCollector.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/ExpCollector.java index 8b9b5c0887..ae6c14e975 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/ExpCollector.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/ExpCollector.java @@ -9,7 +9,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotDiagonallyRotatable; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotDiagonallyRotatable; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNetComponentType; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java index 38e3f193c6..b2f0abbf11 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java @@ -13,6 +13,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; import io.github.thebusybiscuit.slimefun4.core.attributes.*; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotDiagonallyRotatable; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockBreakHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; import io.github.thebusybiscuit.slimefun4.core.machines.MachineProcessor; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOScanner.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOScanner.java index ac5ccaec46..fccb0a776d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOScanner.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOScanner.java @@ -4,7 +4,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotDiagonallyRotatable; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotDiagonallyRotatable; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockUseHandler; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/OilPump.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/OilPump.java index 6b5d9b0545..358e7a7fa3 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/OilPump.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/OilPump.java @@ -5,8 +5,8 @@ import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotDiagonallyRotatable; import io.github.thebusybiscuit.slimefun4.core.attributes.RecipeDisplayItem; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotDiagonallyRotatable; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSControlPanel.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSControlPanel.java index ea0cd06f6a..7b56fa0ecf 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSControlPanel.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSControlPanel.java @@ -4,7 +4,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotDiagonallyRotatable; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotDiagonallyRotatable; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockUseHandler; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSTransmitter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSTransmitter.java index eb8603509c..f0cb60ae89 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSTransmitter.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSTransmitter.java @@ -7,7 +7,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotDiagonallyRotatable; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotDiagonallyRotatable; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockBreakHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNetComponentType; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java index 1a3f7d5c49..2a2588f7c7 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockListener.java @@ -2,6 +2,7 @@ import com.xzavier0722.mc.plugin.slimefun4.storage.callback.IAsyncReadCallback; import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunBlockData; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunUniversalBlockData; import com.xzavier0722.mc.plugin.slimefun4.storage.util.StorageCacheUtils; import io.github.bakedlibs.dough.protection.Interaction; import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; @@ -9,10 +10,10 @@ import io.github.thebusybiscuit.slimefun4.api.events.SlimefunBlockBreakEvent; import io.github.thebusybiscuit.slimefun4.api.events.SlimefunBlockPlaceEvent; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotCardinallyRotatable; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotDiagonallyRotatable; import io.github.thebusybiscuit.slimefun4.core.attributes.NotPlaceable; -import io.github.thebusybiscuit.slimefun4.core.attributes.NotRotatable; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotCardinallyRotatable; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotDiagonallyRotatable; +import io.github.thebusybiscuit.slimefun4.core.attributes.rotations.NotRotatable; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockBreakHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.ToolUseHandler; @@ -121,7 +122,8 @@ public void onBlockPlace(BlockPlaceEvent e) { if (!sfItem.canUse(e.getPlayer(), true)) { e.setCancelled(true); } else { - if (e.getBlock().getBlockData() instanceof Rotatable rotatable + var block = e.getBlock(); + if (block.getBlockData() instanceof Rotatable rotatable && !(rotatable.getRotation() == BlockFace.UP || rotatable.getRotation() == BlockFace.DOWN)) { BlockFace rotation = null; @@ -139,22 +141,32 @@ public void onBlockPlace(BlockPlaceEvent e) { if (rotation != null) { rotatable.setRotation(rotation); - e.getBlock().setBlockData(rotatable); + block.setBlockData(rotatable); } } - var placeEvent = new SlimefunBlockPlaceEvent(e.getPlayer(), item, e.getBlock(), sfItem); + var placeEvent = new SlimefunBlockPlaceEvent(e.getPlayer(), item, block, sfItem); Bukkit.getPluginManager().callEvent(placeEvent); if (placeEvent.isCancelled()) { e.setCancelled(true); } else { - if (Slimefun.getBlockDataService().isTileEntity(e.getBlock().getType())) { - Slimefun.getBlockDataService().setBlockData(e.getBlock(), sfItem.getId()); + if (Slimefun.getBlockDataService().isTileEntity(block.getType())) { + Slimefun.getBlockDataService().setBlockData(block, sfItem.getId()); } - Slimefun.getDatabaseManager() + var data = Slimefun.getDatabaseManager() .getBlockDataController() - .createBlock(e.getBlock().getLocation(), sfItem.getId()); + .createBlock(block.getLocation(), sfItem.getId()); + + if (data instanceof SlimefunUniversalBlockData) { + if (Slimefun.getBlockDataService().isTileEntity(block.getType())) { + Slimefun.getBlockDataService().updateUniversalDataUUID(block, data.getKey()); + } else { + throw new IllegalStateException( + "You must use pdc support material for this Slimefun item!"); + } + } + sfItem.callItemHandler(BlockPlaceHandler.class, handler -> handler.onPlayerPlace(e)); } } @@ -175,7 +187,9 @@ public void onBlockBreak(BlockBreakEvent e) { var heldItem = e.getPlayer().getInventory().getItemInMainHand(); var block = e.getBlock(); - var blockData = StorageCacheUtils.getBlock(block.getLocation()); + var blockData = StorageCacheUtils.hasBlock(block.getLocation()) + ? StorageCacheUtils.getBlock(block.getLocation()) + : StorageCacheUtils.getUniversalBlock(block); var sfItem = blockData == null ? null : SlimefunItem.getById(blockData.getSfId()); // If there is a Slimefun Block here, call our BreakEvent and, if cancelled, cancel this event diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/DebugFishListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/DebugFishListener.java index 965f1d85c9..7de1d5077f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/DebugFishListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/DebugFishListener.java @@ -1,7 +1,9 @@ package io.github.thebusybiscuit.slimefun4.implementation.listeners; import com.xzavier0722.mc.plugin.slimefun4.storage.callback.IAsyncReadCallback; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.ASlimefunDataContainer; import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunBlockData; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunUniversalData; import com.xzavier0722.mc.plugin.slimefun4.storage.util.LocationUtils; import com.xzavier0722.mc.plugin.slimefun4.storage.util.StorageCacheUtils; import io.github.bakedlibs.dough.common.ChatColors; @@ -106,24 +108,45 @@ private void onRightClick(Player p, Block b, BlockFace face) { return; } - var controller = Slimefun.getDatabaseManager().getBlockDataController(); - var blockData = controller.getBlockDataFromCache(b.getLocation()); - if (blockData != null) { + if (StorageCacheUtils.hasBlock(b.getLocation()) || StorageCacheUtils.hasUniversalBlock(b.getLocation())) { + var data = StorageCacheUtils.hasBlock(b.getLocation()) + ? StorageCacheUtils.getBlock(b.getLocation()) + : StorageCacheUtils.getUniversalBlock(b); + try { - if (blockData.isDataLoaded()) { - sendInfo(p, b); + if (data.isDataLoaded()) { + sendInfo(p, b, data); } else { - controller.loadBlockDataAsync(blockData, new IAsyncReadCallback<>() { - @Override - public boolean runOnMainThread() { - return true; - } - - @Override - public void onResult(SlimefunBlockData result) { - sendInfo(p, b); - } - }); + if (data instanceof SlimefunBlockData blockData) { + Slimefun.getDatabaseManager() + .getBlockDataController() + .loadBlockDataAsync(blockData, new IAsyncReadCallback<>() { + @Override + public boolean runOnMainThread() { + return true; + } + + @Override + public void onResult(SlimefunBlockData result) { + sendInfo(p, b, result); + } + }); + } else { + SlimefunUniversalData universalData = (SlimefunUniversalData) data; + Slimefun.getDatabaseManager() + .getBlockDataController() + .loadUniversalDataAsync(universalData, new IAsyncReadCallback<>() { + @Override + public boolean runOnMainThread() { + return true; + } + + @Override + public void onResult(SlimefunUniversalData result) { + sendInfo(p, b, result); + } + }); + } } } catch (Exception x) { Slimefun.logger().log(Level.SEVERE, "An Exception occurred while using a Debug-Fish", x); @@ -153,9 +176,8 @@ public void onResult(SlimefunBlockData result) { } @ParametersAreNonnullByDefault - private void sendInfo(Player p, Block b) { - var blockData = StorageCacheUtils.getBlock(b.getLocation()); - SlimefunItem item = SlimefunItem.getById(blockData.getSfId()); + private void sendInfo(Player p, Block b, ASlimefunDataContainer data) { + SlimefunItem item = SlimefunItem.getById(data.getSfId()); p.sendMessage(" "); p.sendMessage( @@ -176,12 +198,18 @@ private void sendInfo(Player p, Block b) { } } - if (blockData.getBlockMenu() != null) { + if ((data instanceof SlimefunBlockData bd && bd.getBlockMenu() != null) + || (data instanceof SlimefunUniversalData ud && ud.getMenu() != null)) { p.sendMessage(ChatColors.color("&dInventory: " + greenCheckmark)); } else { p.sendMessage(ChatColors.color("&dInventory: " + redCross)); } + if (data instanceof SlimefunUniversalData universalData) { + p.sendMessage(ChatColors.color("&dUniversal Item: " + greenCheckmark)); + p.sendMessage(ChatColors.color(" &dUUID: " + universalData.getUUID())); + } + if (item.isTicking()) { p.sendMessage(ChatColors.color("&dTicker: " + greenCheckmark)); p.sendMessage(ChatColors.color( @@ -192,15 +220,11 @@ private void sendInfo(Player p, Block b) { p.sendMessage(ChatColors.color("&dTicker: " + redCross)); } - var ticker = Slimefun.getTickerTask().getLocations(b.getChunk()).stream() - .filter(l -> l.equals(b.getLocation())) - .findFirst(); - - if (ticker.isPresent()) { - p.sendMessage(ChatColors.color("&dTicking: " + greenCheckmark)); - } else { - p.sendMessage(ChatColors.color("&dTicking: " + redCross)); - } + Slimefun.getTickerTask().getTickLocations(p.getLocation().getChunk()).stream() + .filter(l -> l.getLocation().equals(b.getLocation())) + .findFirst() + .ifPresent(tickLoc -> p.sendMessage(ChatColors.color( + "&dIn Ticker Queue " + (tickLoc.isUniversal() ? "(Universal)" : "") + ": " + greenCheckmark))); if (Slimefun.getProfiler().hasTimings(b)) { p.sendMessage( @@ -243,7 +267,7 @@ private void sendInfo(Player p, Block b) { } } - blockData.getAllData().forEach((k, v) -> p.sendMessage(ChatColors.color("&6" + k + ": " + v))); + data.getAllData().forEach((k, v) -> p.sendMessage(ChatColors.color("&6" + k + ": " + v))); p.sendMessage(" "); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SlimefunItemInteractListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SlimefunItemInteractListener.java index f861c070e3..c71b300770 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SlimefunItemInteractListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SlimefunItemInteractListener.java @@ -2,6 +2,8 @@ import com.xzavier0722.mc.plugin.slimefun4.storage.callback.IAsyncReadCallback; import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunBlockData; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunUniversalData; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.attributes.UniversalBlock; import com.xzavier0722.mc.plugin.slimefun4.storage.util.StorageCacheUtils; import io.github.thebusybiscuit.slimefun4.api.events.PlayerRightClickEvent; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; @@ -13,8 +15,8 @@ import java.util.Optional; import javax.annotation.Nonnull; import javax.annotation.ParametersAreNonnullByDefault; -import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; import me.mrCookieSlime.Slimefun.api.inventory.BlockMenuPreset; +import me.mrCookieSlime.Slimefun.api.inventory.DirtyChestMenu; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.block.Block; @@ -34,11 +36,9 @@ * * @author TheBusyBiscuit * @author Liruxo - * * @see PlayerRightClickEvent * @see ItemUseHandler * @see BlockUseHandler - * */ public class SlimefunItemInteractListener implements Listener { @@ -56,10 +56,7 @@ public void onRightClick(PlayerInteractEvent e) { // Fixes #4087 - Prevents players from interacting with a block that is about to be deleted // We especially don't want to open inventories as that can cause duplication - if (e.getClickedBlock() != null - && StorageCacheUtils.hasBlock(e.getClickedBlock().getLocation()) - && StorageCacheUtils.getBlock(e.getClickedBlock().getLocation()) - .isPendingRemove()) { + if (e.getClickedBlock() != null && StorageCacheUtils.isBlockPendingRemove(e.getClickedBlock())) { e.setCancelled(true); return; } @@ -146,31 +143,62 @@ private void openInventory(Player p, SlimefunItem item, Block clickedBlock, Play if (!p.isSneaking() || event.getItem().getType() == Material.AIR) { event.getInteractEvent().setCancelled(true); - var blockData = StorageCacheUtils.getBlock(clickedBlock.getLocation()); - if (blockData == null) { - return; - } + if (item instanceof UniversalBlock) { + var uniData = StorageCacheUtils.getUniversalBlock(clickedBlock); + + if (uniData == null) { + return; + } + + if (uniData.isDataLoaded()) { + openMenu(uniData.getMenu(), clickedBlock, p); + } else { + Slimefun.getDatabaseManager() + .getBlockDataController() + .loadUniversalDataAsync(uniData, new IAsyncReadCallback<>() { + @Override + public boolean runOnMainThread() { + return true; + } - if (blockData.isDataLoaded()) { - openMenu(blockData.getBlockMenu(), clickedBlock, p); + @Override + public void onResult(SlimefunUniversalData result) { + if (!p.isOnline()) { + return; + } + + openMenu(result.getMenu(), clickedBlock, p); + } + }); + } } else { - Slimefun.getDatabaseManager() - .getBlockDataController() - .loadBlockDataAsync(blockData, new IAsyncReadCallback<>() { - @Override - public boolean runOnMainThread() { - return true; - } - - @Override - public void onResult(SlimefunBlockData result) { - if (!p.isOnline()) { - return; + var blockData = StorageCacheUtils.getBlock(clickedBlock.getLocation()); + + if (blockData == null) { + return; + } + + if (blockData.isDataLoaded()) { + openMenu(blockData.getBlockMenu(), clickedBlock, p); + } else { + Slimefun.getDatabaseManager() + .getBlockDataController() + .loadBlockDataAsync(blockData, new IAsyncReadCallback<>() { + @Override + public boolean runOnMainThread() { + return true; } - openMenu(result.getBlockMenu(), clickedBlock, p); - } - }); + @Override + public void onResult(SlimefunBlockData result) { + if (!p.isOnline()) { + return; + } + + openMenu(result.getBlockMenu(), clickedBlock, p); + } + }); + } } } } catch (Exception | LinkageError x) { @@ -178,7 +206,7 @@ public void onResult(SlimefunBlockData result) { } } - private void openMenu(BlockMenu menu, Block b, Player p) { + private void openMenu(DirtyChestMenu menu, Block b, Player p) { if (menu != null) { if (menu.canOpen(b, p)) { menu.open(p); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/TickerTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/TickerTask.java index 51541f01cf..a9a596d10e 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/TickerTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/TickerTask.java @@ -1,25 +1,32 @@ package io.github.thebusybiscuit.slimefun4.implementation.tasks; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.ASlimefunDataContainer; import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunBlockData; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunUniversalData; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.attributes.UniversalBlock; import com.xzavier0722.mc.plugin.slimefun4.storage.util.StorageCacheUtils; import io.github.bakedlibs.dough.blocks.BlockPosition; import io.github.bakedlibs.dough.blocks.ChunkPosition; import io.github.thebusybiscuit.slimefun4.api.ErrorReport; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import io.github.thebusybiscuit.slimefun4.core.ticker.TickLocation; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; +import java.util.stream.Collectors; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; +import lombok.Setter; import me.mrCookieSlime.Slimefun.Objects.handlers.BlockTicker; import org.apache.commons.lang.Validate; import org.bukkit.Chunk; import org.bukkit.Location; -import org.bukkit.block.Block; import org.bukkit.scheduler.BukkitScheduler; /** @@ -37,7 +44,7 @@ public class TickerTask implements Runnable { * This Map holds all currently actively ticking locations. * The value of this map (Set entries) MUST be thread-safe and mutable. */ - private final Map> tickingLocations = new ConcurrentHashMap<>(); + private final Map> tickingLocations = new ConcurrentHashMap<>(); /** * This Map tracks how many bugs have occurred in a given Location . @@ -49,6 +56,7 @@ public class TickerTask implements Runnable { private boolean halted = false; private boolean running = false; + @Setter private volatile boolean paused = false; /** @@ -89,13 +97,13 @@ public void run() { // Run our ticker code if (!halted) { - Set>> loc; + Set>> loc; synchronized (tickingLocations) { loc = new HashSet<>(tickingLocations.entrySet()); } - for (Map.Entry> entry : loc) { + for (Map.Entry> entry : loc) { tickChunk(entry.getKey(), tickers, new HashSet<>(entry.getValue())); } } @@ -119,12 +127,16 @@ public void run() { } @ParametersAreNonnullByDefault - private void tickChunk(ChunkPosition chunk, Set tickers, Set locations) { + private void tickChunk(ChunkPosition chunk, Set tickers, Set locations) { try { // Only continue if the Chunk is actually loaded if (chunk.isLoaded()) { - for (Location l : locations) { - tickLocation(tickers, l); + for (TickLocation l : locations) { + if (l.isUniversal()) { + tickUniversalLocation(l.getUuid(), l.getLocation(), tickers); + } else { + tickLocation(tickers, l.getLocation()); + } } } } catch (ArrayIndexOutOfBoundsException | NumberFormatException x) { @@ -138,6 +150,7 @@ private void tickLocation(@Nonnull Set tickers, @Nonnull Location l if (blockData == null || !blockData.isDataLoaded() || blockData.isPendingRemove()) { return; } + SlimefunItem item = SlimefunItem.getById(blockData.getSfId()); if (item != null && item.getBlockTicker() != null) { @@ -158,14 +171,50 @@ private void tickLocation(@Nonnull Set tickers, @Nonnull Location l if (blockData.isPendingRemove()) { return; } - Block b = l.getBlock(); - tickBlock(l, b, item, blockData, System.nanoTime()); + tickBlock(l, item, blockData, System.nanoTime()); + }); + } else { + long timestamp = Slimefun.getProfiler().newEntry(); + item.getBlockTicker().update(); + tickBlock(l, item, blockData, timestamp); + } + + tickers.add(item.getBlockTicker()); + } catch (Exception x) { + reportErrors(l, item, x); + } + } + } + + @ParametersAreNonnullByDefault + private void tickUniversalLocation(UUID uuid, Location l, @Nonnull Set tickers) { + var data = StorageCacheUtils.getUniversalBlock(uuid); + var item = SlimefunItem.getById(data.getSfId()); + + if (item != null && item.getBlockTicker() != null) { + if (item.isDisabledIn(l.getWorld())) { + return; + } + + try { + if (item.getBlockTicker().isSynchronized()) { + Slimefun.getProfiler().scheduleEntries(1); + item.getBlockTicker().update(); + + /** + * We are inserting a new timestamp because synchronized actions + * are always ran with a 50ms delay (1 game tick) + */ + Slimefun.runSync(() -> { + if (data.isPendingRemove()) { + return; + } + tickBlock(l, item, data, System.nanoTime()); }); } else { long timestamp = Slimefun.getProfiler().newEntry(); item.getBlockTicker().update(); - Block b = l.getBlock(); - tickBlock(l, b, item, blockData, timestamp); + tickBlock(l, item, data, timestamp); } tickers.add(item.getBlockTicker()); @@ -176,9 +225,21 @@ private void tickLocation(@Nonnull Set tickers, @Nonnull Location l } @ParametersAreNonnullByDefault - private void tickBlock(Location l, Block b, SlimefunItem item, SlimefunBlockData data, long timestamp) { + private void tickBlock(Location l, SlimefunItem item, ASlimefunDataContainer data, long timestamp) { try { - item.getBlockTicker().tick(b, item, data); + if (item.getBlockTicker().isUniversal()) { + if (data instanceof SlimefunUniversalData universalData) { + item.getBlockTicker().tick(l.getBlock(), item, universalData); + } else { + throw new IllegalStateException("BlockTicker is universal but item is non-universal!"); + } + } else { + if (data instanceof SlimefunBlockData blockData) { + item.getBlockTicker().tick(l.getBlock(), item, blockData); + } else { + throw new IllegalStateException("BlockTicker is non-universal but item is universal!"); + } + } } catch (Exception | LinkageError x) { reportErrors(l, item, x); } finally { @@ -227,6 +288,21 @@ public int getTickRate() { return tickRate; } + /** + * BINARY COMPATIBILITY + * + * Use #getTickLocations instead + * + * @return A {@link Map} representation of all ticking {@link Location Locations} + */ + @Nonnull + public Map> getLocations() { + return tickingLocations.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().stream() + .map(TickLocation::getLocation) + .collect(Collectors.toUnmodifiableSet()))); + } + /** * This method returns a read-only {@link Map} * representation of every {@link ChunkPosition} and its corresponding @@ -234,10 +310,10 @@ public int getTickRate() { * * This does include any {@link Location} from an unloaded {@link Chunk} too! * - * @return A {@link Map} representation of all ticking {@link Location Locations} + * @return A {@link Map} representation of all ticking {@link TickLocation Locations} */ @Nonnull - public Map> getLocations() { + public Map> getTickLocations() { return Collections.unmodifiableMap(tickingLocations); } @@ -256,8 +332,27 @@ public Map> getLocations() { public Set getLocations(@Nonnull Chunk chunk) { Validate.notNull(chunk, "The Chunk cannot be null!"); - Set locations = tickingLocations.getOrDefault(new ChunkPosition(chunk), Collections.emptySet()); - return Collections.unmodifiableSet(locations); + Set locations = tickingLocations.getOrDefault(new ChunkPosition(chunk), Collections.emptySet()); + return locations.stream().map(TickLocation::getLocation).collect(Collectors.toUnmodifiableSet()); + } + + /** + * 返回一个给定区块下的 只读 的 {@link Map} + * 代表每个 {@link ChunkPosition} 中有 {@link UniversalBlock} 属性的物品 + * Tick 的 {@link Location 位置}集合. + * + * 其中包含的 {@link Location} 可以是已加载或卸载的 {@link Chunk} + * + * @param chunk + * {@link Chunk} + * + * @return 包含所有机器 Tick {@link TickLocation 位置}的只读 {@link Map} + */ + @Nonnull + public Set getTickLocations(@Nonnull Chunk chunk) { + Validate.notNull(chunk, "The Chunk cannot be null!"); + + return tickingLocations.getOrDefault(new ChunkPosition(chunk), Collections.emptySet()); } /** @@ -267,10 +362,17 @@ public Set getLocations(@Nonnull Chunk chunk) { * The {@link Location} to activate */ public void enableTicker(@Nonnull Location l) { + enableTicker(l, null); + } + + public void enableTicker(@Nonnull Location l, @Nullable UUID uuid) { Validate.notNull(l, "Location cannot be null!"); synchronized (tickingLocations) { ChunkPosition chunk = new ChunkPosition(l.getWorld(), l.getBlockX() >> 4, l.getBlockZ() >> 4); + final var tickPosition = uuid == null + ? new TickLocation(new BlockPosition(l)) + : new TickLocation(new BlockPosition(l), uuid); /* Note that all the values in #tickingLocations must be thread-safe. @@ -278,17 +380,17 @@ public void enableTicker(@Nonnull Location l) { The CHM KeySet was chosen since it at least permits multiple concurrent reads without blocking. */ - Set newValue = ConcurrentHashMap.newKeySet(); - Set oldValue = tickingLocations.putIfAbsent(chunk, newValue); + Set newValue = ConcurrentHashMap.newKeySet(); + Set oldValue = tickingLocations.putIfAbsent(chunk, newValue); /** * This is faster than doing computeIfAbsent(...) * on a ConcurrentHashMap because it won't block the Thread for too long */ if (oldValue != null) { - oldValue.add(l); + oldValue.add(tickPosition); } else { - newValue.add(l); + newValue.add(tickPosition); } } } @@ -305,10 +407,10 @@ public void disableTicker(@Nonnull Location l) { synchronized (tickingLocations) { ChunkPosition chunk = new ChunkPosition(l.getWorld(), l.getBlockX() >> 4, l.getBlockZ() >> 4); - Set locations = tickingLocations.get(chunk); + Set locations = tickingLocations.get(chunk); if (locations != null) { - locations.remove(l); + locations.removeIf(tk -> l.equals(tk.getLocation())); if (locations.isEmpty()) { tickingLocations.remove(chunk); @@ -317,7 +419,21 @@ public void disableTicker(@Nonnull Location l) { } } - public void setPaused(boolean isPaused) { - paused = isPaused; + /** + * This method disables the ticker at the given {@link UUID} and removes it from our internal + * "queue". + * + * DO NOT USE THIS until you cannot disable by location, + * or enjoy extremely slow. + * + * @param uuid + * The {@link UUID} to remove + */ + public void disableTicker(@Nonnull UUID uuid) { + Validate.notNull(uuid, "Universal Data ID cannot be null!"); + + synchronized (tickingLocations) { + tickingLocations.values().forEach(loc -> loc.removeIf(tk -> uuid.equals(tk.getUuid()))); + } } } diff --git a/src/main/java/me/mrCookieSlime/CSCoreLibPlugin/general/Inventory/ChestMenu.java b/src/main/java/me/mrCookieSlime/CSCoreLibPlugin/general/Inventory/ChestMenu.java index 54dba98243..1e3ee2b35f 100644 --- a/src/main/java/me/mrCookieSlime/CSCoreLibPlugin/general/Inventory/ChestMenu.java +++ b/src/main/java/me/mrCookieSlime/CSCoreLibPlugin/general/Inventory/ChestMenu.java @@ -12,6 +12,7 @@ import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.Nonnull; +import lombok.Getter; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.entity.Player; @@ -29,7 +30,10 @@ public class ChestMenu extends SlimefunInventoryHolder { private boolean clickable; private boolean emptyClickable; + + @Getter private String title; + private List items; /** * Size of chestmenu diff --git a/src/main/java/me/mrCookieSlime/Slimefun/Objects/handlers/BlockTicker.java b/src/main/java/me/mrCookieSlime/Slimefun/Objects/handlers/BlockTicker.java index b72020f1a0..fdb1c9eb2a 100644 --- a/src/main/java/me/mrCookieSlime/Slimefun/Objects/handlers/BlockTicker.java +++ b/src/main/java/me/mrCookieSlime/Slimefun/Objects/handlers/BlockTicker.java @@ -2,18 +2,31 @@ import com.xzavier0722.mc.plugin.slimefun4.storage.controller.BlockDataConfigWrapper; import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunBlockData; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunUniversalData; import io.github.thebusybiscuit.slimefun4.api.exceptions.IncompatibleItemHandlerException; import io.github.thebusybiscuit.slimefun4.api.items.ItemHandler; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.core.attributes.NotPlaceable; import java.util.Optional; +import lombok.Getter; import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; import org.bukkit.block.Block; public abstract class BlockTicker implements ItemHandler { + @Getter + private final boolean universal; + protected boolean unique = true; + public BlockTicker() { + this.universal = false; + } + + public BlockTicker(boolean universal) { + this.universal = universal; + } + public void update() { if (unique) { uniqueTick(); @@ -58,6 +71,20 @@ public void tick(Block b, SlimefunItem item, SlimefunBlockData data) { tick(b, item, new BlockDataConfigWrapper(data)); } + /** + * This method is called every tick for every block + * + * @param b + * The {@link Block} that was ticked + * @param item + * The corresponding {@link SlimefunItem} + * @param data + * The data stored in this {@link Block} + */ + public void tick(Block b, SlimefunItem item, SlimefunUniversalData data) { + // Override this method and fill it with content + } + @Deprecated public void tick(Block b, SlimefunItem item, Config data) {} diff --git a/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/BlockMenuPreset.java b/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/BlockMenuPreset.java index 2a12e94208..829d66755c 100644 --- a/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/BlockMenuPreset.java +++ b/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/BlockMenuPreset.java @@ -20,7 +20,7 @@ // This class will be deprecated, relocated and rewritten in a future version. public abstract class BlockMenuPreset extends ChestMenu { - private final Set occupiedSlots = new HashSet<>(); + protected final Set occupiedSlots = new HashSet<>(); private final String inventoryTitle; private final String id; diff --git a/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/DirtyChestMenu.java b/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/DirtyChestMenu.java index baa281af27..0b34c58e5e 100644 --- a/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/DirtyChestMenu.java +++ b/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/DirtyChestMenu.java @@ -1,17 +1,12 @@ package me.mrCookieSlime.Slimefun.api.inventory; -import city.norain.slimefun4.utils.StringUtil; import io.github.bakedlibs.dough.inventory.InvUtils; import io.github.bakedlibs.dough.items.CustomItemStack; import io.github.bakedlibs.dough.items.ItemUtils; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; -import io.github.thebusybiscuit.slimefun4.core.debug.Debug; -import io.github.thebusybiscuit.slimefun4.core.debug.TestCase; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper; import java.util.ArrayList; -import java.util.Arrays; -import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ChestMenu; @@ -84,12 +79,6 @@ public void close() { } public boolean fits(@Nonnull ItemStack item, int... slots) { - Debug.log( - TestCase.UTILS, - "DirtyChestMenu#fits - start check fits | item {} | slots {}", - StringUtil.itemStackToString(item), - Arrays.stream(slots).mapToObj(String::valueOf).collect(Collectors.joining(","))); - var isSfItem = SlimefunItem.getByItem(item) != null; var wrapper = ItemStackWrapper.wrap(item); var remain = item.getAmount(); @@ -101,15 +90,7 @@ public boolean fits(@Nonnull ItemStack item, int... slots) { return true; } - Debug.log( - TestCase.UTILS, - "DirtyChestMenu#fits - Now checking item | Slot {} | Item {}", - slot, - StringUtil.itemStackToString(slotItem)); - if (isSfItem) { - Debug.log(TestCase.UTILS, "DirtyChestMenu#fits - Check slimefun item fits"); - if (!slotItem.hasItemMeta() || item.getType() != slotItem.getType() || !SlimefunUtils.isItemSimilar(slotItem, wrapper, true, false)) { @@ -118,14 +99,9 @@ public boolean fits(@Nonnull ItemStack item, int... slots) { var slotRemain = slotItem.getMaxStackSize() - slotItem.getAmount(); - Debug.log(TestCase.UTILS, "DirtyChestMenu#fits - current slot remain: {}", slotRemain); - remain -= slotRemain; - Debug.log(TestCase.UTILS, "DirtyChestMenu#fits - remaining amount: {}", remain); - if (remain <= 0) { - Debug.log(TestCase.UTILS, "DirtyChestMenu#fits - check fits result (no remain): false"); return true; } } @@ -137,8 +113,6 @@ public boolean fits(@Nonnull ItemStack item, int... slots) { result = InvUtils.fits(toInventory(), wrapper, slots); } - Debug.log(TestCase.UTILS, "DirtyChestMenu#fits - check fits result: {}", result); - return result; }