diff --git a/pom.xml b/pom.xml index 4b9271a600..6b54035f62 100644 --- a/pom.xml +++ b/pom.xml @@ -269,6 +269,13 @@ provided + + org.apache.logging.log4j + log4j-core + 2.24.1 + provided + + com.github.mcchampions.dough 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..317e25e2c9 --- /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 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. + */ +@Getter +public class UniversalMenu extends DirtyChestMenu { + private final UUID uuid; + + public UniversalMenu(UniversalMenuPreset preset, UUID uuid) { + this(preset, uuid, (Location) null); + } + + public UniversalMenu(UniversalMenuPreset preset, UUID uuid, Location lastPresent) { + super(preset); + this.uuid = uuid; + + preset.clone(this, lastPresent); + this.getContents(); + } + + public UniversalMenu( + UniversalMenuPreset preset, 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(UniversalMenuPreset preset, UUID uuid, ItemStack[] contents) { + this(preset, uuid, null, contents); + } + + public void update(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..608eaf175e --- /dev/null +++ b/src/main/java/city/norain/slimefun4/api/menu/UniversalMenuPreset.java @@ -0,0 +1,77 @@ +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.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(String id, String title) { + super(id, title); + } + + public void newInstance(UniversalMenu menu, Block b) { + // This method can optionally be overridden by implementations + } + + @Override + protected void clone(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(UniversalMenu menu, 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 583f6d1d46..7f5ab50fd0 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; @@ -21,6 +23,8 @@ import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.Set; public class MysqlAdapter extends SqlCommonAdapter { @Override @@ -38,6 +42,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(); } } @@ -45,9 +50,9 @@ public void initStorage(DataType type) { @Override public void setData(RecordKey key, RecordSet item) { - var data = item.getAll(); - var fields = data.keySet(); - var fieldStr = SqlUtils.buildFieldStr(fields); + Map data = item.getAll(); + Set fields = data.keySet(); + Optional fieldStr = SqlUtils.buildFieldStr(fields); if (fieldStr.isEmpty()) { throw new IllegalArgumentException("No data provided in RecordSet."); } @@ -70,40 +75,40 @@ public void setData(RecordKey key, RecordSet item) { } executeSql("INSERT INTO " - + mapTable(key.getScope()) - + " (" - + fieldStr.get() - + ") " - + "VALUES (" - + valStr - + ")" - + (updateFields.isEmpty() - ? "" - : " ON DUPLICATE KEY UPDATE " - + String.join( - ", ", - updateFields.stream() - .map(field -> { - var val = item.get(field); - if (val == null) { - throw new IllegalArgumentException( - "Cannot find value in RecordSet for the specific key: " - + field); - } - return SqlUtils.buildKvStr(field, val); - }) - .toList())) - + ";"); + + mapTable(key.getScope()) + + " (" + + fieldStr.get() + + ") " + + "VALUES (" + + valStr + + ")" + + (updateFields.isEmpty() + ? "" + : " ON DUPLICATE KEY UPDATE " + + String.join( + ", ", + updateFields.stream() + .map(field -> { + var val = item.get(field); + if (val == null) { + throw new IllegalArgumentException( + "Cannot find value in RecordSet for the specific key: " + + field); + } + return SqlUtils.buildKvStr(field, val); + }) + .toList())) + + ";"); } @Override public List getData(RecordKey key, boolean distinct) { return executeQuery((distinct ? "SELECT DISTINCT " : "SELECT ") - + SqlUtils.buildFieldStr(key.getFields()).orElse("*") - + " FROM " - + mapTable(key.getScope()) - + SqlUtils.buildConditionStr(key.getConditions()) - + ";"); + + SqlUtils.buildFieldStr(key.getFields()).orElse("*") + + " FROM " + + mapTable(key.getScope()) + + SqlUtils.buildConditionStr(key.getConditions()) + + ";"); } @Override @@ -123,192 +128,256 @@ private void createBlockStorageTables() { createBlockDataTable(); createBlockInvTable(); createChunkDataTable(); + createUniversalInventoryTable(); + createUniversalRecordTable(); + createUniversalDataTable(); } private void createProfileTable() { executeSql("CREATE TABLE IF NOT EXISTS " - + profileTable - + "(" - + FIELD_PLAYER_UUID - + " CHAR(64) PRIMARY KEY NOT NULL, " - + FIELD_PLAYER_NAME - + " CHAR(64) NOT NULL, " - + FIELD_BACKPACK_NUM - + " INT UNSIGNED DEFAULT 0, " - + "INDEX index_player_name (" - + FIELD_PLAYER_NAME - + ")" - + ");"); + + profileTable + + "(" + + FIELD_PLAYER_UUID + + " CHAR(64) PRIMARY KEY NOT NULL, " + + FIELD_PLAYER_NAME + + " CHAR(64) NOT NULL, " + + FIELD_BACKPACK_NUM + + " INT UNSIGNED DEFAULT 0, " + + "INDEX index_player_name (" + + FIELD_PLAYER_NAME + + ")" + + ");"); } private void createResearchTable() { executeSql("CREATE TABLE IF NOT EXISTS " - + researchTable - + "(" - + FIELD_PLAYER_UUID - + " CHAR(64) NOT NULL, " - + FIELD_RESEARCH_KEY - + " CHAR(64) NOT NULL, " - + "FOREIGN KEY (" - + FIELD_PLAYER_UUID - + ") " - + "REFERENCES " - + profileTable - + "(" - + FIELD_PLAYER_UUID - + ") " - + "ON UPDATE CASCADE ON DELETE CASCADE, " - + "INDEX index_player_research (" - + FIELD_PLAYER_UUID - + ", " - + FIELD_RESEARCH_KEY - + ")" - + ");"); + + researchTable + + "(" + + FIELD_PLAYER_UUID + + " CHAR(64) NOT NULL, " + + FIELD_RESEARCH_KEY + + " CHAR(64) NOT NULL, " + + "FOREIGN KEY (" + + FIELD_PLAYER_UUID + + ") " + + "REFERENCES " + + profileTable + + "(" + + FIELD_PLAYER_UUID + + ") " + + "ON UPDATE CASCADE ON DELETE CASCADE, " + + "INDEX index_player_research (" + + FIELD_PLAYER_UUID + + ", " + + FIELD_RESEARCH_KEY + + ")" + + ");"); } private void createBackpackTable() { executeSql("CREATE TABLE IF NOT EXISTS " - + backpackTable - + "(" - + FIELD_BACKPACK_ID - + " CHAR(64) PRIMARY KEY NOT NULL, " - + FIELD_PLAYER_UUID - + " CHAR(64) NOT NULL, " - + FIELD_BACKPACK_NUM - + " INT UNSIGNED NOT NULL, " - + FIELD_BACKPACK_NAME - + " CHAR(64) NULL, " - + FIELD_BACKPACK_SIZE - + " TINYINT UNSIGNED NOT NULL, " - + "FOREIGN KEY (" - + FIELD_PLAYER_UUID - + ") " - + "REFERENCES " - + profileTable - + "(" - + FIELD_PLAYER_UUID - + ") " - + "ON UPDATE CASCADE ON DELETE CASCADE, " - + "INDEX index_player_backpack (" - + FIELD_PLAYER_UUID - + ", " - + FIELD_BACKPACK_NUM - + ")" - + ");"); + + backpackTable + + "(" + + FIELD_BACKPACK_ID + + " CHAR(64) PRIMARY KEY NOT NULL, " + + FIELD_PLAYER_UUID + + " CHAR(64) NOT NULL, " + + FIELD_BACKPACK_NUM + + " INT UNSIGNED NOT NULL, " + + FIELD_BACKPACK_NAME + + " CHAR(64) NULL, " + + FIELD_BACKPACK_SIZE + + " TINYINT UNSIGNED NOT NULL, " + + "FOREIGN KEY (" + + FIELD_PLAYER_UUID + + ") " + + "REFERENCES " + + profileTable + + "(" + + FIELD_PLAYER_UUID + + ") " + + "ON UPDATE CASCADE ON DELETE CASCADE, " + + "INDEX index_player_backpack (" + + FIELD_PLAYER_UUID + + ", " + + FIELD_BACKPACK_NUM + + ")" + + ");"); } private void createBackpackInventoryTable() { executeSql("CREATE TABLE IF NOT EXISTS " - + bpInvTable - + "(" - + FIELD_BACKPACK_ID - + " CHAR(64) NOT NULL, " - + FIELD_INVENTORY_SLOT - + " TINYINT UNSIGNED NOT NULL, " - + FIELD_INVENTORY_ITEM - + " TEXT NOT NULL, " - + "FOREIGN KEY (" - + FIELD_BACKPACK_ID - + ") " - + "REFERENCES " - + backpackTable - + "(" - + FIELD_BACKPACK_ID - + ") " - + "ON UPDATE CASCADE ON DELETE CASCADE, " - + "PRIMARY KEY (" - + FIELD_BACKPACK_ID - + ", " - + FIELD_INVENTORY_SLOT - + ")" - + ");"); + + bpInvTable + + "(" + + FIELD_BACKPACK_ID + + " CHAR(64) NOT NULL, " + + FIELD_INVENTORY_SLOT + + " TINYINT UNSIGNED NOT NULL, " + + FIELD_INVENTORY_ITEM + + " TEXT NOT NULL, " + + "FOREIGN KEY (" + + FIELD_BACKPACK_ID + + ") " + + "REFERENCES " + + backpackTable + + "(" + + FIELD_BACKPACK_ID + + ") " + + "ON UPDATE CASCADE ON DELETE CASCADE, " + + "PRIMARY KEY (" + + FIELD_BACKPACK_ID + + ", " + + FIELD_INVENTORY_SLOT + + ")" + + ");"); } private void createBlockRecordTable() { executeSql("CREATE TABLE IF NOT EXISTS " - + blockRecordTable - + "(" - + FIELD_LOCATION - + " CHAR(64) PRIMARY KEY NOT NULL, " - + FIELD_CHUNK - + " CHAR(64) NOT NULL, " - + FIELD_SLIMEFUN_ID - + " CHAR(64) NOT NULL, " - + "INDEX index_ticking (" - + FIELD_CHUNK - + ")" - + ");"); + + blockRecordTable + + "(" + + FIELD_LOCATION + + " CHAR(64) PRIMARY KEY NOT NULL, " + + FIELD_CHUNK + + " CHAR(64) NOT NULL, " + + FIELD_SLIMEFUN_ID + + " CHAR(64) NOT NULL, " + + "INDEX index_ticking (" + + FIELD_CHUNK + + ")" + + ");"); } private void createBlockDataTable() { executeSql("CREATE TABLE IF NOT EXISTS " - + blockDataTable - + "(" - + FIELD_LOCATION - + " CHAR(64) NOT NULL, " - + FIELD_DATA_KEY - + " CHAR(64) NOT NULL, " - + FIELD_DATA_VALUE - + " TEXT NOT NULL, " - + "FOREIGN KEY (" - + FIELD_LOCATION - + ") " - + "REFERENCES " - + blockRecordTable - + "(" - + FIELD_LOCATION - + ") " - + "ON UPDATE CASCADE ON DELETE CASCADE, " - + "PRIMARY KEY (" - + FIELD_LOCATION - + ", " - + FIELD_DATA_KEY - + ")" - + ");"); + + blockDataTable + + "(" + + FIELD_LOCATION + + " CHAR(64) NOT NULL, " + + FIELD_DATA_KEY + + " CHAR(64) NOT NULL, " + + FIELD_DATA_VALUE + + " TEXT NOT NULL, " + + "FOREIGN KEY (" + + FIELD_LOCATION + + ") " + + "REFERENCES " + + blockRecordTable + + "(" + + FIELD_LOCATION + + ") " + + "ON UPDATE CASCADE ON DELETE CASCADE, " + + "PRIMARY KEY (" + + FIELD_LOCATION + + ", " + + FIELD_DATA_KEY + + ")" + + ");"); } private void createChunkDataTable() { executeSql("CREATE TABLE IF NOT EXISTS " - + chunkDataTable - + "(" - + FIELD_CHUNK - + " CHAR(64) NOT NULL, " - + FIELD_DATA_KEY - + " CHAR(64) NOT NULL, " - + FIELD_DATA_VALUE - + " TEXT NOT NULL, " - + "PRIMARY KEY (" - + FIELD_CHUNK - + ", " - + FIELD_DATA_KEY - + ")" - + ");"); + + chunkDataTable + + "(" + + FIELD_CHUNK + + " CHAR(64) NOT NULL, " + + FIELD_DATA_KEY + + " CHAR(64) NOT NULL, " + + FIELD_DATA_VALUE + + " TEXT NOT NULL, " + + "PRIMARY KEY (" + + FIELD_CHUNK + + ", " + + FIELD_DATA_KEY + + ")" + + ");"); } private void createBlockInvTable() { executeSql("CREATE TABLE IF NOT EXISTS " - + blockInvTable - + "(" - + FIELD_LOCATION - + " CHAR(64) NOT NULL, " - + FIELD_INVENTORY_SLOT - + " TINYINT UNSIGNED NOT NULL, " - + FIELD_INVENTORY_ITEM - + " MEDIUMTEXT NOT NULL, " - + "FOREIGN KEY (" - + FIELD_LOCATION - + ") " - + "REFERENCES " - + blockRecordTable - + "(" - + FIELD_LOCATION - + ") " - + "ON UPDATE CASCADE ON DELETE CASCADE, " - + "PRIMARY KEY (" - + FIELD_LOCATION - + ", " - + FIELD_INVENTORY_SLOT - + ")" - + ");"); + + blockInvTable + + "(" + + FIELD_LOCATION + + " CHAR(64) NOT NULL, " + + FIELD_INVENTORY_SLOT + + " TINYINT UNSIGNED NOT NULL, " + + FIELD_INVENTORY_ITEM + + " MEDIUMTEXT NOT NULL, " + + "FOREIGN KEY (" + + FIELD_LOCATION + + ") " + + "REFERENCES " + + blockRecordTable + + "(" + + FIELD_LOCATION + + ") " + + "ON UPDATE CASCADE ON DELETE CASCADE, " + + "PRIMARY KEY (" + + FIELD_LOCATION + + ", " + + FIELD_INVENTORY_SLOT + + ")" + + ");"); + } + + 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 bd0946ddc9..215b512422 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; @@ -40,6 +42,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(); } } @@ -76,46 +81,46 @@ public void setData(RecordKey key, RecordSet item) { .collect(Collectors.joining(", ")); executeSql("INSERT INTO " - + mapTable(key.getScope()) - + " (" - + fieldStr.get() - + ") " - + "VALUES (" - + valStr - + ")" - + (contrastStr.isEmpty() - ? "" - : " ON CONFLICT (" - + contrastStr - + ") " - + (updateFields.isEmpty() - ? "DO NOTHING" - : "DO UPDATE SET " - + String.join( - ", ", - updateFields.stream() - .map(field -> { - var val = item.get(field); - if (val == null) { - throw new IllegalArgumentException( - "Cannot find value in RecordSet for the specific" - + " key: " - + field); - } - return SqlUtils.buildKvStr(field, val); - }) - .toList()))) - + ";"); + + mapTable(key.getScope()) + + " (" + + fieldStr.get() + + ") " + + "VALUES (" + + valStr + + ")" + + (contrastStr.isEmpty() + ? "" + : " ON CONFLICT (" + + contrastStr + + ") " + + (updateFields.isEmpty() + ? "DO NOTHING" + : "DO UPDATE SET " + + String.join( + ", ", + updateFields.stream() + .map(field -> { + var val = item.get(field); + if (val == null) { + throw new IllegalArgumentException( + "Cannot find value in RecordSet for the specific" + + " key: " + + field); + } + return SqlUtils.buildKvStr(field, val); + }) + .toList()))) + + ";"); } @Override public List getData(RecordKey key, boolean distinct) { return executeQuery((distinct ? "SELECT DISTINCT " : "SELECT ") - + SqlUtils.buildFieldStr(key.getFields()).orElse("*") - + " FROM " - + mapTable(key.getScope()) - + SqlUtils.buildConditionStr(key.getConditions()) - + ";"); + + SqlUtils.buildFieldStr(key.getFields()).orElse("*") + + " FROM " + + mapTable(key.getScope()) + + SqlUtils.buildConditionStr(key.getConditions()) + + ";"); } @Override @@ -135,196 +140,260 @@ private void createBlockStorageTables() { createBlockDataTable(); createBlockInvTable(); createChunkDataTable(); + createUniversalInventoryTable(); + createUniversalRecordTable(); + createUniversalDataTable(); } private void createProfileTable() { executeSql("CREATE TABLE IF NOT EXISTS " - + profileTable - + "(" - + FIELD_PLAYER_UUID - + " VARCHAR(64) PRIMARY KEY NOT NULL, " - + FIELD_PLAYER_NAME - + " VARCHAR(64) NOT NULL, " - + FIELD_BACKPACK_NUM - + " INT DEFAULT 0" - + ");"); + + profileTable + + "(" + + FIELD_PLAYER_UUID + + " VARCHAR(64) PRIMARY KEY NOT NULL, " + + FIELD_PLAYER_NAME + + " VARCHAR(64) NOT NULL, " + + FIELD_BACKPACK_NUM + + " INT DEFAULT 0" + + ");"); executeSql("CREATE INDEX IF NOT EXISTS index_player_name ON " + profileTable + " (" + FIELD_PLAYER_NAME + ");"); } private void createResearchTable() { executeSql("CREATE TABLE IF NOT EXISTS " - + researchTable - + "(" - + FIELD_PLAYER_UUID - + " VARCHAR(64) NOT NULL, " - + FIELD_RESEARCH_KEY - + " VARCHAR(64) NOT NULL, " - + "FOREIGN KEY (" - + FIELD_PLAYER_UUID - + ") " - + "REFERENCES " - + profileTable - + "(" - + FIELD_PLAYER_UUID - + ") " - + "ON UPDATE CASCADE ON DELETE CASCADE" - + ");"); + + researchTable + + "(" + + FIELD_PLAYER_UUID + + " VARCHAR(64) NOT NULL, " + + FIELD_RESEARCH_KEY + + " VARCHAR(64) NOT NULL, " + + "FOREIGN KEY (" + + FIELD_PLAYER_UUID + + ") " + + "REFERENCES " + + profileTable + + "(" + + FIELD_PLAYER_UUID + + ") " + + "ON UPDATE CASCADE ON DELETE CASCADE" + + ");"); executeSql("CREATE INDEX IF NOT EXISTS index_player_research ON " - + researchTable - + " (" - + FIELD_PLAYER_UUID - + ", " - + FIELD_RESEARCH_KEY - + ");"); + + researchTable + + " (" + + FIELD_PLAYER_UUID + + ", " + + FIELD_RESEARCH_KEY + + ");"); } private void createBackpackTable() { executeSql("CREATE TABLE IF NOT EXISTS " - + backpackTable - + "(" - + FIELD_BACKPACK_ID - + " VARCHAR(64) PRIMARY KEY NOT NULL, " - + FIELD_PLAYER_UUID - + " VARCHAR(64) NOT NULL, " - + FIELD_BACKPACK_NUM - + " INT NOT NULL, " - + FIELD_BACKPACK_NAME - + " VARCHAR(64) NULL, " - + FIELD_BACKPACK_SIZE - + " SMALLINT NOT NULL, " - + "FOREIGN KEY (" - + FIELD_PLAYER_UUID - + ") " - + "REFERENCES " - + profileTable - + "(" - + FIELD_PLAYER_UUID - + ") " - + "ON UPDATE CASCADE ON DELETE CASCADE" - + ");"); + + backpackTable + + "(" + + FIELD_BACKPACK_ID + + " VARCHAR(64) PRIMARY KEY NOT NULL, " + + FIELD_PLAYER_UUID + + " VARCHAR(64) NOT NULL, " + + FIELD_BACKPACK_NUM + + " INT NOT NULL, " + + FIELD_BACKPACK_NAME + + " VARCHAR(64) NULL, " + + FIELD_BACKPACK_SIZE + + " SMALLINT NOT NULL, " + + "FOREIGN KEY (" + + FIELD_PLAYER_UUID + + ") " + + "REFERENCES " + + profileTable + + "(" + + FIELD_PLAYER_UUID + + ") " + + "ON UPDATE CASCADE ON DELETE CASCADE" + + ");"); executeSql("CREATE INDEX IF NOT EXISTS index_player_backpack ON " - + backpackTable - + " (" - + FIELD_PLAYER_UUID - + ", " - + FIELD_BACKPACK_NUM - + ");"); + + backpackTable + + " (" + + FIELD_PLAYER_UUID + + ", " + + FIELD_BACKPACK_NUM + + ");"); } private void createBackpackInventoryTable() { executeSql("CREATE TABLE IF NOT EXISTS " - + bpInvTable - + "(" - + FIELD_BACKPACK_ID - + " VARCHAR(64) NOT NULL, " - + FIELD_INVENTORY_SLOT - + " SMALLINT NOT NULL, " - + FIELD_INVENTORY_ITEM - + " TEXT NOT NULL, " - + "FOREIGN KEY (" - + FIELD_BACKPACK_ID - + ") " - + "REFERENCES " - + backpackTable - + "(" - + FIELD_BACKPACK_ID - + ") " - + "ON UPDATE CASCADE ON DELETE CASCADE, " - + "PRIMARY KEY (" - + FIELD_BACKPACK_ID - + ", " - + FIELD_INVENTORY_SLOT - + ")" - + ");"); + + bpInvTable + + "(" + + FIELD_BACKPACK_ID + + " VARCHAR(64) NOT NULL, " + + FIELD_INVENTORY_SLOT + + " SMALLINT NOT NULL, " + + FIELD_INVENTORY_ITEM + + " TEXT NOT NULL, " + + "FOREIGN KEY (" + + FIELD_BACKPACK_ID + + ") " + + "REFERENCES " + + backpackTable + + "(" + + FIELD_BACKPACK_ID + + ") " + + "ON UPDATE CASCADE ON DELETE CASCADE, " + + "PRIMARY KEY (" + + FIELD_BACKPACK_ID + + ", " + + FIELD_INVENTORY_SLOT + + ")" + + ");"); } private void createBlockRecordTable() { executeSql("CREATE TABLE IF NOT EXISTS " - + blockRecordTable - + "(" - + FIELD_LOCATION - + " VARCHAR(64) PRIMARY KEY NOT NULL, " - + FIELD_CHUNK - + " VARCHAR(64) NOT NULL, " - + FIELD_SLIMEFUN_ID - + " VARCHAR(64) NOT NULL" - + ");"); + + blockRecordTable + + "(" + + FIELD_LOCATION + + " VARCHAR(64) PRIMARY KEY NOT NULL, " + + FIELD_CHUNK + + " VARCHAR(64) NOT NULL, " + + FIELD_SLIMEFUN_ID + + " VARCHAR(64) NOT NULL" + + ");"); executeSql("CREATE INDEX IF NOT EXISTS index_ticking ON " + blockRecordTable + " (" + FIELD_CHUNK + ");"); } private void createBlockDataTable() { executeSql("CREATE TABLE IF NOT EXISTS " - + blockDataTable - + "(" - + FIELD_LOCATION - + " VARCHAR(64) NOT NULL, " - + FIELD_DATA_KEY - + " VARCHAR(64) NOT NULL, " - + FIELD_DATA_VALUE - + " TEXT NOT NULL, " - + "FOREIGN KEY (" - + FIELD_LOCATION - + ") " - + "REFERENCES " - + blockRecordTable - + "(" - + FIELD_LOCATION - + ") " - + "ON UPDATE CASCADE ON DELETE CASCADE, " - + "PRIMARY KEY (" - + FIELD_LOCATION - + ", " - + FIELD_DATA_KEY - + ")" - + ");"); + + blockDataTable + + "(" + + FIELD_LOCATION + + " VARCHAR(64) NOT NULL, " + + FIELD_DATA_KEY + + " VARCHAR(64) NOT NULL, " + + FIELD_DATA_VALUE + + " TEXT NOT NULL, " + + "FOREIGN KEY (" + + FIELD_LOCATION + + ") " + + "REFERENCES " + + blockRecordTable + + "(" + + FIELD_LOCATION + + ") " + + "ON UPDATE CASCADE ON DELETE CASCADE, " + + "PRIMARY KEY (" + + FIELD_LOCATION + + ", " + + FIELD_DATA_KEY + + ")" + + ");"); } private void createChunkDataTable() { executeSql("CREATE TABLE IF NOT EXISTS " - + chunkDataTable - + "(" - + FIELD_CHUNK - + " VARCHAR(64) NOT NULL, " - + FIELD_DATA_KEY - + " VARCHAR(64) NOT NULL, " - + FIELD_DATA_VALUE - + " TEXT NOT NULL, " - + "PRIMARY KEY (" - + FIELD_CHUNK - + ", " - + FIELD_DATA_KEY - + ")" - + ");"); + + chunkDataTable + + "(" + + FIELD_CHUNK + + " VARCHAR(64) NOT NULL, " + + FIELD_DATA_KEY + + " VARCHAR(64) NOT NULL, " + + FIELD_DATA_VALUE + + " TEXT NOT NULL, " + + "PRIMARY KEY (" + + FIELD_CHUNK + + ", " + + FIELD_DATA_KEY + + ")" + + ");"); } private void createBlockInvTable() { executeSql("CREATE TABLE IF NOT EXISTS " - + blockInvTable - + "(" - + FIELD_LOCATION - + " VARCHAR(64) NOT NULL, " - + FIELD_INVENTORY_SLOT - + " SMALLINT NOT NULL, " - + FIELD_INVENTORY_ITEM - + " TEXT NOT NULL, " - + "FOREIGN KEY (" - + FIELD_LOCATION - + ") " - + "REFERENCES " - + blockRecordTable - + "(" - + FIELD_LOCATION - + ") " - + "ON UPDATE CASCADE ON DELETE CASCADE, " - + "PRIMARY KEY (" - + FIELD_LOCATION - + ", " - + FIELD_INVENTORY_SLOT - + ")" - + ");"); + + blockInvTable + + "(" + + FIELD_LOCATION + + " VARCHAR(64) NOT NULL, " + + FIELD_INVENTORY_SLOT + + " SMALLINT NOT NULL, " + + FIELD_INVENTORY_ITEM + + " TEXT NOT NULL, " + + "FOREIGN KEY (" + + FIELD_LOCATION + + ") " + + "REFERENCES " + + blockRecordTable + + "(" + + FIELD_LOCATION + + ") " + + "ON UPDATE CASCADE ON DELETE CASCADE, " + + "PRIMARY KEY (" + + FIELD_LOCATION + + ", " + + FIELD_INVENTORY_SLOT + + ")" + + ");"); + } + + 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 22d5ea1558..d5fb2eae44 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 @@ -11,7 +11,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 @@ -46,6 +52,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!"); }; } @@ -62,5 +71,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 da69b3105e..afa9b3f2fd 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,12 +24,16 @@ 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; import com.xzavier0722.mc.plugin.slimefun4.storage.common.FieldMapper; import com.xzavier0722.mc.plugin.slimefun4.storage.common.RecordSet; import io.github.bakedlibs.dough.collections.Pair; + import java.sql.Connection; import java.sql.ResultSetMetaData; import java.sql.SQLException; @@ -57,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); } @@ -70,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!"); }; } @@ -103,11 +114,11 @@ public static String buildConditionStr(List> conditions) } return " WHERE " - + String.join( - " AND ", - conditions.stream() - .map(condition -> buildKvStr(condition.getFirstValue(), condition.getSecondValue())) - .toList()); + + String.join( + " AND ", + conditions.stream() + .map(condition -> buildKvStr(condition.getFirstValue(), condition.getSecondValue())) + .toList()); } public static String buildKvStr(FieldKey key, String val) { 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 e0022b9368..5ea8350656 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 com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlCommonAdapter; import com.xzavier0722.mc.plugin.slimefun4.storage.adapter.sqlcommon.SqlUtils; @@ -61,22 +63,22 @@ public void setData(RecordKey key, RecordSet item) { } var row = executeUpdate("UPDATE " - + table - + " SET " - + String.join( - ", ", - updateFields.stream() - .map(field -> { - var val = item.get(field); - if (val == null) { - throw new IllegalArgumentException( - "Cannot find value in RecordSet for the specific key: " + field); - } - return SqlUtils.buildKvStr(field, val); - }) - .toList()) - + SqlUtils.buildConditionStr(key.getConditions()) - + ";"); + + table + + " SET " + + String.join( + ", ", + updateFields.stream() + .map(field -> { + var val = item.get(field); + if (val == null) { + throw new IllegalArgumentException( + "Cannot find value in RecordSet for the specific key: " + field); + } + return SqlUtils.buildKvStr(field, val); + }) + .toList()) + + SqlUtils.buildConditionStr(key.getConditions()) + + ";"); if (row > 0) { return; } @@ -88,19 +90,19 @@ public void setData(RecordKey key, RecordSet item) { @Override public List getData(RecordKey key, boolean distinct) { return executeQuery((distinct ? "SELECT DISTINCT " : "SELECT ") - + SqlUtils.buildFieldStr(key.getFields()).orElse("*") - + " FROM " - + SqlUtils.mapTable(key.getScope()) - + SqlUtils.buildConditionStr(key.getConditions()) - + ";"); + + SqlUtils.buildFieldStr(key.getFields()).orElse("*") + + " FROM " + + SqlUtils.mapTable(key.getScope()) + + SqlUtils.buildConditionStr(key.getConditions()) + + ";"); } @Override public void deleteData(RecordKey key) { executeSql("DELETE FROM " - + SqlUtils.mapTable(key.getScope()) - + SqlUtils.buildConditionStr(key.getConditions()) - + ";"); + + SqlUtils.mapTable(key.getScope()) + + SqlUtils.buildConditionStr(key.getConditions()) + + ";"); } private void createProfileTables() { @@ -115,20 +117,23 @@ private void createBlockStorageTables() { createBlockDataTable(); createBlockInvTable(); createChunkDataTable(); + createUniversalInventoryTable(); + createUniversalRecordTable(); + createUniversalDataTable(); } private void createProfileTable() { var table = SqlUtils.mapTable(DataScope.PLAYER_PROFILE); executeSql("CREATE TABLE IF NOT EXISTS " - + table - + "(" - + FIELD_PLAYER_UUID - + " TEXT PRIMARY KEY NOT NULL, " - + FIELD_PLAYER_NAME - + " TEXT NOT NULL, " - + FIELD_BACKPACK_NUM - + " INTEGER DEFAULT 0" - + ");"); + + table + + "(" + + FIELD_PLAYER_UUID + + " TEXT PRIMARY KEY NOT NULL, " + + FIELD_PLAYER_NAME + + " TEXT NOT NULL, " + + FIELD_BACKPACK_NUM + + " INTEGER DEFAULT 0" + + ");"); executeSql("CREATE INDEX IF NOT EXISTS index_player_name ON " + table + " (" + FIELD_PLAYER_NAME + ");"); } @@ -136,178 +141,239 @@ private void createProfileTable() { private void createResearchTable() { var table = SqlUtils.mapTable(DataScope.PLAYER_RESEARCH); executeSql("CREATE TABLE IF NOT EXISTS " - + table - + "(" - + FIELD_PLAYER_UUID - + " TEXT NOT NULL, " - + FIELD_RESEARCH_KEY - + " TEXT NOT NULL, " - + "FOREIGN KEY (" - + FIELD_PLAYER_UUID - + ") " - + "REFERENCES " - + SqlUtils.mapTable(DataScope.PLAYER_PROFILE) - + "(" - + FIELD_PLAYER_UUID - + ") " - + "ON UPDATE CASCADE ON DELETE CASCADE);"); + + table + + "(" + + FIELD_PLAYER_UUID + + " TEXT NOT NULL, " + + FIELD_RESEARCH_KEY + + " TEXT NOT NULL, " + + "FOREIGN KEY (" + + FIELD_PLAYER_UUID + + ") " + + "REFERENCES " + + SqlUtils.mapTable(DataScope.PLAYER_PROFILE) + + "(" + + FIELD_PLAYER_UUID + + ") " + + "ON UPDATE CASCADE ON DELETE CASCADE);"); executeSql("CREATE INDEX IF NOT EXISTS index_player_researches ON " - + table - + " (" - + FIELD_PLAYER_UUID - + ", " - + FIELD_RESEARCH_KEY - + ");"); + + table + + " (" + + FIELD_PLAYER_UUID + + ", " + + FIELD_RESEARCH_KEY + + ");"); } private void createBackpackTable() { var table = SqlUtils.mapTable(DataScope.BACKPACK_PROFILE); executeSql("CREATE TABLE IF NOT EXISTS " - + table - + "(" - + FIELD_BACKPACK_ID - + " TEXT PRIMARY KEY NOT NULL, " - + FIELD_PLAYER_UUID - + " TEXT NOT NULL, " - + FIELD_BACKPACK_NUM - + " INTEGER NOT NULL, " - + FIELD_BACKPACK_NAME - + " TEXT NULL, " - + FIELD_BACKPACK_SIZE - + " INTEGER NOT NULL, " - + "FOREIGN KEY (" - + FIELD_PLAYER_UUID - + ") " - + "REFERENCES " - + SqlUtils.mapTable(DataScope.PLAYER_PROFILE) - + "(" - + FIELD_PLAYER_UUID - + ") " - + "ON UPDATE CASCADE ON DELETE CASCADE);"); + + table + + "(" + + FIELD_BACKPACK_ID + + " TEXT PRIMARY KEY NOT NULL, " + + FIELD_PLAYER_UUID + + " TEXT NOT NULL, " + + FIELD_BACKPACK_NUM + + " INTEGER NOT NULL, " + + FIELD_BACKPACK_NAME + + " TEXT NULL, " + + FIELD_BACKPACK_SIZE + + " INTEGER NOT NULL, " + + "FOREIGN KEY (" + + FIELD_PLAYER_UUID + + ") " + + "REFERENCES " + + SqlUtils.mapTable(DataScope.PLAYER_PROFILE) + + "(" + + FIELD_PLAYER_UUID + + ") " + + "ON UPDATE CASCADE ON DELETE CASCADE);"); executeSql("CREATE INDEX IF NOT EXISTS index_player_backpack ON " - + table - + " (" - + FIELD_PLAYER_UUID - + ", " - + FIELD_BACKPACK_NUM - + ");"); + + table + + " (" + + FIELD_PLAYER_UUID + + ", " + + FIELD_BACKPACK_NUM + + ");"); } private void createBackpackInvTable() { executeSql("CREATE TABLE IF NOT EXISTS " - + SqlUtils.mapTable(DataScope.BACKPACK_INVENTORY) - + "(" - + FIELD_BACKPACK_ID - + " TEXT NOT NULL, " - + FIELD_INVENTORY_SLOT - + " INTEGER NOT NULL, " - + FIELD_INVENTORY_ITEM - + " TEXT NOT NULL, " - + "FOREIGN KEY (" - + FIELD_BACKPACK_ID - + ") " - + "REFERENCES " - + SqlUtils.mapTable(DataScope.BACKPACK_PROFILE) - + "(" - + FIELD_BACKPACK_ID - + ") " - + "ON UPDATE CASCADE ON DELETE CASCADE, " - + "PRIMARY KEY (" - + FIELD_BACKPACK_ID - + ", " - + FIELD_INVENTORY_SLOT - + ")" - + ");"); + + SqlUtils.mapTable(DataScope.BACKPACK_INVENTORY) + + "(" + + FIELD_BACKPACK_ID + + " TEXT NOT NULL, " + + FIELD_INVENTORY_SLOT + + " INTEGER NOT NULL, " + + FIELD_INVENTORY_ITEM + + " TEXT NOT NULL, " + + "FOREIGN KEY (" + + FIELD_BACKPACK_ID + + ") " + + "REFERENCES " + + SqlUtils.mapTable(DataScope.BACKPACK_PROFILE) + + "(" + + FIELD_BACKPACK_ID + + ") " + + "ON UPDATE CASCADE ON DELETE CASCADE, " + + "PRIMARY KEY (" + + FIELD_BACKPACK_ID + + ", " + + FIELD_INVENTORY_SLOT + + ")" + + ");"); } private void createBlockRecordTable() { var table = SqlUtils.mapTable(DataScope.BLOCK_RECORD); executeSql("CREATE TABLE IF NOT EXISTS " - + table - + "(" - + FIELD_LOCATION - + " TEXT PRIMARY KEY NOT NULL, " - + FIELD_CHUNK - + " TEXT NOT NULL, " - + FIELD_SLIMEFUN_ID - + " TEXT NOT NULL" - + ");"); + + table + + "(" + + FIELD_LOCATION + + " TEXT PRIMARY KEY NOT NULL, " + + FIELD_CHUNK + + " TEXT NOT NULL, " + + FIELD_SLIMEFUN_ID + + " TEXT NOT NULL" + + ");"); executeSql("CREATE INDEX IF NOT EXISTS index_chunk ON " + table + "(" + FIELD_CHUNK + ");"); } private void createBlockDataTable() { executeSql("CREATE TABLE IF NOT EXISTS " - + SqlUtils.mapTable(DataScope.BLOCK_DATA) - + "(" - + FIELD_LOCATION - + " TEXT NOT NULL, " - + FIELD_DATA_KEY - + " TEXT NOT NULL, " - + FIELD_DATA_VALUE - + " TEXT NOT NULL, " - + "FOREIGN KEY (" - + FIELD_LOCATION - + ") " - + "REFERENCES " - + SqlUtils.mapTable(DataScope.BLOCK_RECORD) - + "(" - + FIELD_LOCATION - + ") " - + "ON UPDATE CASCADE ON DELETE CASCADE, " - + "PRIMARY KEY (" - + FIELD_LOCATION - + ", " - + FIELD_DATA_KEY - + ")" - + ");"); + + SqlUtils.mapTable(DataScope.BLOCK_DATA) + + "(" + + FIELD_LOCATION + + " TEXT NOT NULL, " + + FIELD_DATA_KEY + + " TEXT NOT NULL, " + + FIELD_DATA_VALUE + + " TEXT NOT NULL, " + + "FOREIGN KEY (" + + FIELD_LOCATION + + ") " + + "REFERENCES " + + SqlUtils.mapTable(DataScope.BLOCK_RECORD) + + "(" + + FIELD_LOCATION + + ") " + + "ON UPDATE CASCADE ON DELETE CASCADE, " + + "PRIMARY KEY (" + + FIELD_LOCATION + + ", " + + FIELD_DATA_KEY + + ")" + + ");"); } private void createChunkDataTable() { executeSql("CREATE TABLE IF NOT EXISTS " - + SqlUtils.mapTable(DataScope.CHUNK_DATA) - + "(" - + FIELD_CHUNK - + " TEXT NOT NULL, " - + FIELD_DATA_KEY - + " TEXT NOT NULL, " - + FIELD_DATA_VALUE - + " TEXT NOT NULL, " - + "PRIMARY KEY (" - + FIELD_CHUNK - + ", " - + FIELD_DATA_KEY - + ")" - + ");"); + + SqlUtils.mapTable(DataScope.CHUNK_DATA) + + "(" + + FIELD_CHUNK + + " TEXT NOT NULL, " + + FIELD_DATA_KEY + + " TEXT NOT NULL, " + + FIELD_DATA_VALUE + + " TEXT NOT NULL, " + + "PRIMARY KEY (" + + FIELD_CHUNK + + ", " + + FIELD_DATA_KEY + + ")" + + ");"); } private void createBlockInvTable() { executeSql("CREATE TABLE IF NOT EXISTS " - + SqlUtils.mapTable(DataScope.BLOCK_INVENTORY) - + "(" - + FIELD_LOCATION - + " TEXT NOT NULL, " - + FIELD_INVENTORY_SLOT - + " INTEGER NOT NULL, " - + FIELD_INVENTORY_ITEM - + " TEXT NOT NULL, " - + "FOREIGN KEY (" - + FIELD_LOCATION - + ") " - + "REFERENCES " - + SqlUtils.mapTable(DataScope.BLOCK_RECORD) - + "(" - + FIELD_LOCATION - + ") " - + "ON UPDATE CASCADE ON DELETE CASCADE, " - + "PRIMARY KEY (" - + FIELD_LOCATION - + ", " - + FIELD_INVENTORY_SLOT - + ")" - + ");"); + + SqlUtils.mapTable(DataScope.BLOCK_INVENTORY) + + "(" + + FIELD_LOCATION + + " TEXT NOT NULL, " + + FIELD_INVENTORY_SLOT + + " INTEGER NOT NULL, " + + FIELD_INVENTORY_ITEM + + " TEXT NOT NULL, " + + "FOREIGN KEY (" + + FIELD_LOCATION + + ") " + + "REFERENCES " + + SqlUtils.mapTable(DataScope.BLOCK_RECORD) + + "(" + + FIELD_LOCATION + + ") " + + "ON UPDATE CASCADE ON DELETE CASCADE, " + + "PRIMARY KEY (" + + FIELD_LOCATION + + ", " + + FIELD_INVENTORY_SLOT + + ")" + + ");"); + } + + 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) { 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 f2fd50c6be..820eeefc51 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 @@ -6,13 +6,16 @@ public enum DataScope { NONE, PLAYER_RESEARCH, - PLAYER_PROFILE(new FieldKey[] {FieldKey.PLAYER_UUID}), - BACKPACK_PROFILE(new FieldKey[] {FieldKey.BACKPACK_ID}), - BACKPACK_INVENTORY(new FieldKey[] {FieldKey.BACKPACK_ID, FieldKey.INVENTORY_SLOT}), - 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}); + PLAYER_PROFILE(new FieldKey[]{FieldKey.PLAYER_UUID}), + BACKPACK_PROFILE(new FieldKey[]{FieldKey.BACKPACK_ID}), + BACKPACK_INVENTORY(new FieldKey[]{FieldKey.BACKPACK_ID, FieldKey.INVENTORY_SLOT}), + 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}), + 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 29ddef088c..ff085e4dd1 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.HashMap; import java.util.Map; +import java.util.UUID; +import lombok.ToString; import org.bukkit.inventory.ItemStack; +@ToString public class RecordSet { private final Map data; private boolean readonly = false; @@ -49,6 +52,10 @@ public ItemStack getItemStack(FieldKey key) { return DataUtils.string2ItemStack(data.get(key)); } + 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..316be20cdc --- /dev/null +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/ADataContainer.java @@ -0,0 +1,68 @@ +package com.xzavier0722.mc.plugin.slimefun4.storage.controller; + +import lombok.Getter; +import me.qscbm.slimefun4.utils.CacheMap; + +import java.util.Map; +import java.util.Set; +import javax.annotation.Nullable; + +public abstract class ADataContainer { + @Getter + private final String key; + private final Map data; + private volatile boolean isDataLoaded = false; + + public ADataContainer(String key) { + this.key = key; + data = new CacheMap(); + } + + 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() { + } + + public Map getAllData() { + return data; + } + + public Set getDataKeys() { + return data.keySet(); + } + + @Nullable + public String getData(String key) { + return getCacheInternal(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 44671de460..c431a49d9e 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,73 +1,22 @@ package com.xzavier0722.mc.plugin.slimefun4.storage.controller; import lombok.Getter; -import me.qscbm.slimefun4.utils.CacheMap; +import lombok.Setter; -import java.util.Map; -import java.util.Set; -import javax.annotation.Nullable; +@Getter +public abstract class ASlimefunDataContainer extends ADataContainer { + private final String sfId; -public abstract class ASlimefunDataContainer { - @Getter - private final String key; - private final Map data; - private volatile boolean isDataLoaded = false; + @Setter + private volatile boolean pendingRemove = false; - public ASlimefunDataContainer(String key) { - this.key = key; - data = new CacheMap(); + public ASlimefunDataContainer(String key, String sfId) { + super(key); + this.sfId = sfId; } - public ASlimefunDataContainer(String key, ASlimefunDataContainer other) { - this.key = key; - this.data = other.data; - this.isDataLoaded = other.isDataLoaded; + public ASlimefunDataContainer(String key, ADataContainer other, String sfId) { + super(key, other); + this.sfId = sfId; } - - 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!"); - } - } - - public Map getAllData() { - checkData(); - return data; - } - - public Set getDataKeys() { - checkData(); - return data.keySet(); - } - - @Nullable public String getData(String key) { - checkData(); - return getCacheInternal(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 99cb7305a6..3a772ba4d7 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; @@ -18,11 +23,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.logging.Level; @@ -38,25 +39,70 @@ 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); @@ -64,13 +110,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( @@ -85,6 +147,9 @@ private void loadLoadedWorlds() { 1); } + /** + * 加载所有服务器已加载的世界区块中的数据 + */ private void loadLoadedChunks() { Bukkit.getScheduler() .runTaskLater( @@ -101,10 +166,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; @@ -136,24 +208,53 @@ 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} */ - - 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)) { - if (sfId.equalsIgnoreCase("CARGO_MANAGER")) { - Slimefun.instance().getCargoTickerTask().enableTicker(l); - } else { + var sfItem = SlimefunItem.getById(sfId); + + if (sfItem instanceof UniversalBlock) { + SlimefunUniversalData 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; + } + + 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) { @@ -172,6 +273,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 * @@ -182,6 +304,15 @@ public void removeBlock(Location l) { SlimefunBlockData 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; } @@ -203,6 +334,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); @@ -213,6 +393,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 * @@ -252,7 +442,7 @@ 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 callback operation when block data fetched {@link IAsyncReadCallback} @@ -271,6 +461,79 @@ 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(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(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(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(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 *

@@ -378,6 +641,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); @@ -399,6 +664,7 @@ public void loadChunk(Chunk chunk, boolean isNewChunk) { scheduleReadTask(() -> loadBlockData(blockData)); } }); + Bukkit.getPluginManager().callEvent(new SlimefunChunkDataLoadEvent(chunkData)); } @@ -422,6 +688,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; @@ -462,33 +765,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)); + } } } - SlimefunItem sfItem = SlimefunItem.getById(blockData.getSfId()); if (sfItem != null && sfItem.isTicking()) { sfItem.getTickerTask().enableTicker(blockData.getLocation()); } @@ -510,6 +825,87 @@ public void loadBlockDataAsync( invokeCallback(callback, blockDataList); } + 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)) { + SlimefunItem sfItem = SlimefunItem.getById(uniData.getSfId()); + + if (sfItem != null && sfItem.isTicking()) { + Slimefun.getTickerTask() + .enableTicker( + ((SlimefunUniversalBlockData) uniData) + .getLastPresent() + .toLocation(), + uniData.getUUID()); + } + } + } finally { + lock.unlock(key); + } + } + + public void loadUniversalDataAsync( + SlimefunUniversalData uniData, IAsyncReadCallback callback) { + scheduleReadTask(() -> { + loadUniversalData(uniData); + invokeCallback(callback, uniData); + }); + } + public SlimefunChunkData getChunkData(Chunk chunk) { checkDestroy(); loadChunk(chunk, false); @@ -535,6 +931,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; @@ -606,6 +1017,29 @@ public void removeAllDataInWorldAsync(World world, Runnable onFinishedCallback) }); } + public void saveUniversalInventory(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(); @@ -663,9 +1097,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(); @@ -687,6 +1159,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 @@ -708,6 +1195,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); @@ -792,4 +1292,53 @@ private void clearBlockCacheAndTasks(SlimefunBlockData blockData) { removeDelayedBlockDataUpdates(scopeKey); abortScopeTask(scopeKey); } + + /** + * 迁移旧 Slimefun 机器数据至通用数据 + */ + private void migrateUniversalData( + Location l, + String sfId, + List kvData, + 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)))); + + UniversalMenuPreset preset = UniversalMenuPreset.getPreset(sfId); + if (preset != null) { + ItemStack[] 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)); + + ItemStack[] 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 9c69a62a56..ff31481589 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,8 +9,9 @@ public class ControllerHolder { private final Map controllers; public static CT getController(Class clazz, StorageType type) { + final ControllerHolder holder = holders.get(clazz); //noinspection unchecked - return ((ControllerHolder) holders.get(clazz)).get(type); + 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 dbea4888a5..64877aab68 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 @@ -2,10 +2,10 @@ import com.xzavier0722.mc.plugin.slimefun4.storage.util.LocationUtils; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; + import javax.annotation.Nullable; import lombok.Getter; -import lombok.Setter; import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; import org.bukkit.Location; import org.bukkit.inventory.ItemStack; @@ -13,23 +13,21 @@ public class SlimefunBlockData extends ASlimefunDataContainer { @Getter private final Location location; - @Getter - private final String sfId; + private volatile BlockMenu menu; - @Setter - @Getter - private volatile boolean pendingRemove = false; SlimefunBlockData(Location location, String sfId) { - super(LocationUtils.getLocKey(location)); + super(LocationUtils.getLocKey(location), sfId); this.location = location; - this.sfId = sfId; } SlimefunBlockData(Location location, SlimefunBlockData other) { - super(LocationUtils.getLocKey(location), other); + super(LocationUtils.getLocKey(location), other, other.getSfId()); this.location = location; - this.sfId = other.sfId; + } + + public String getSfId() { + return super.getSfId(); } public void setData(String key, String val) { @@ -48,11 +46,13 @@ void setBlockMenu(BlockMenu blockMenu) { menu = blockMenu; } - @Nullable public BlockMenu getBlockMenu() { + @Nullable + public BlockMenu getBlockMenu() { return menu; } - @Nullable public ItemStack[] getMenuContents() { + @Nullable + public ItemStack[] getMenuContents() { if (menu == null) { return null; } @@ -72,11 +72,11 @@ void setBlockMenu(BlockMenu blockMenu) { @Override public String toString() { return "SlimefunBlockData [sfId=" - + sfId - + ", location=" - + location - + ", isPendingRemove=" - + pendingRemove - + "]"; + + getSfId() + + ", location=" + + location + + ", isPendingRemove=" + + 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 d6e2d1bcec..bc70cf5166 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 @@ -16,7 +16,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"); @@ -31,15 +31,12 @@ public class SlimefunChunkData extends ASlimefunDataContainer { } public SlimefunBlockData createBlockData(Location l, String sfId) { - var lKey = LocationUtils.getLocKey(l); - if (getBlockCacheInternal(lKey) != null) { - throw new IllegalStateException("There already a block in this location: " + lKey); - } - var re = new SlimefunBlockData(l, sfId); + String lKey = LocationUtils.getLocKey(l); + SlimefunBlockData re = new SlimefunBlockData(l, sfId); re.setIsDataLoaded(true); sfBlocks.put(lKey, re); - var preset = BlockMenuPreset.getPreset(sfId); + BlockMenuPreset preset = BlockMenuPreset.getPreset(sfId); if (preset != null) { re.setBlockMenu(new BlockMenu(preset, l)); } @@ -56,8 +53,8 @@ public SlimefunBlockData getBlockData(Location l) { @Nullable public SlimefunBlockData removeBlockData(Location l) { - var lKey = LocationUtils.getLocKey(l); - var re = removeBlockDataCacheInternal(lKey); + String lKey = LocationUtils.getLocKey(l); + SlimefunBlockData re = removeBlockDataCacheInternal(lKey); if (re == null) { if (isDataLoaded()) { return null; @@ -77,12 +74,12 @@ void addBlockCacheInternal(SlimefunBlockData data, boolean override) { } SlimefunBlockData getBlockCacheInternal(String lKey) { - var re = sfBlocks.get(lKey); + SlimefunBlockData re = sfBlocks.get(lKey); return re == INVALID_BLOCK_DATA ? null : re; } Set getAllCacheInternal() { - var re = new HashSet<>(sfBlocks.values()); + Set re = new HashSet<>(sfBlocks.values()); re.removeIf(v -> v == INVALID_BLOCK_DATA); return re; } 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..6fe51ec6fb --- /dev/null +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/SlimefunUniversalBlockData.java @@ -0,0 +1,48 @@ +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..a71770d665 --- /dev/null +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/SlimefunUniversalData.java @@ -0,0 +1,91 @@ +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 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<>(); + + SlimefunUniversalData(UUID uuid, String sfId) { + super(uuid.toString(), sfId); + } + + 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); + } + + protected void setTraitData(UniversalDataTrait trait, String val) { + checkData(); + + if (!trait.getReservedKey().isBlank()) { + setCacheInternal(trait.getReservedKey(), val, true); + Slimefun.getDatabaseManager() + .getBlockDataController() + .scheduleDelayedUniversalDataUpdate(this, trait.getReservedKey()); + } + } + + 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/attributes/UniversalBlock.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/attributes/UniversalBlock.java new file mode 100644 index 0000000000..6b47577c93 --- /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/IMigrator.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/migrator/IMigrator.java index a594789b2a..fdffbd8587 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 @@ -6,5 +6,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 272da92628..8ca9e9803c 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 @@ -15,6 +15,10 @@ public static String getChunkKey(Chunk chunk) { } public static Location toLocation(String lKey) { + if (lKey == null || lKey.isEmpty()) { + return null; + } + String[] strArr = lKey.split(";"); String[] 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 3217a9c8be..4ba0995788 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,31 +1,44 @@ 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 javax.annotation.Nullable; +import java.util.UUID; import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; import org.bukkit.Location; +import org.bukkit.block.Block; /** * Utils to access the cached block data. - * It is safe to use when the target block is in a loaded chunk (such as in block events). + * It is safe to use when the target block is in a loaded chunk(such as in block events). * By default, please use * {@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<>(); public static boolean hasBlock(Location l) { return getBlock(l) != null; } - @Nullable + + public static boolean hasUniversalBlock(Location l) { + return Slimefun.getBlockDataService().getUniversalDataUUID(l.getBlock()).isPresent(); + } + + public static SlimefunBlockData getBlock(Location l) { return Slimefun.getDatabaseManager().getBlockDataController().getBlockDataFromCache(l); } @@ -35,31 +48,60 @@ public static boolean isBlock(Location l, String id) { return blockData != null && id.equals(blockData.getSfId()); } - @Nullable public static SlimefunItem getSfItem(Location l) { SlimefunBlockData blockData = getBlock(l); - return blockData == null ? null : SlimefunItem.getById(blockData.getSfId()); + + if (blockData != null) { + return SlimefunItem.getById(blockData.getSfId()); + } else { + SlimefunUniversalData universalData = getUniversalBlock(l.getBlock()); + return universalData == null ? null : SlimefunItem.getById(universalData.getSfId()); + } } - @Nullable public static String getData(Location loc, String key) { SlimefunBlockData blockData = getBlock(loc); - return blockData == null ? null : blockData.getData(key); + + if (blockData != null) { + return blockData.getData(key); + } else { + SlimefunUniversalData uniData = getUniversalBlock(loc.getBlock()); + + if (uniData == null) { + return null; + } + + return uniData.getData(key); + } + } + + public static String getUniversalBlock(UUID uuid, Location loc, String key) { + SlimefunUniversalBlockData universalData = getUniversalBlock(uuid, loc); + return universalData == null ? null : universalData.getData(key); } public static void setData(Location loc, String key, String val) { SlimefunBlockData block = getBlock(loc); - if (block == null) { - return; + if (block != null) { + block.setData(key, val); + } else { + var uni = getUniversalBlock(loc.getBlock()); + Preconditions.checkNotNull(uni); + uni.setData(key, val); } - block.setData(key, val); } 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); + } } - @Nullable public static BlockMenu getMenu(Location loc) { SlimefunBlockData blockData = getBlock(loc); if (blockData == null) { @@ -74,30 +116,135 @@ public static BlockMenu getMenu(Location loc) { return blockData.getBlockMenu(); } - public static void requestLoad(SlimefunBlockData blockData) { - if (blockData.isDataLoaded()) { + 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; + } + + 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} + */ + 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} + */ + public static UniversalMenu getUniversalMenu(Block block) { + var uniData = getUniversalBlock(block); + + if (uniData == null) { + return null; + } + + return uniData.getMenu(); + } + + 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(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) { @@ -118,4 +265,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 cff43c1323..5d9938462d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/ErrorReport.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/ErrorReport.java @@ -79,39 +79,57 @@ public ErrorReport(T throwable, SlimefunAddon addon, Consumer print */ 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/api/items/SlimefunItem.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/SlimefunItem.java index fb3a8dbcc8..69dc448ff4 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/SlimefunItem.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/SlimefunItem.java @@ -5,7 +5,6 @@ import io.github.bakedlibs.dough.items.ItemUtils; import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon; import io.github.thebusybiscuit.slimefun4.api.exceptions.IdConflictException; -import io.github.thebusybiscuit.slimefun4.api.exceptions.IncompatibleItemHandlerException; import io.github.thebusybiscuit.slimefun4.api.exceptions.MissingDependencyException; import io.github.thebusybiscuit.slimefun4.api.exceptions.UnregisteredItemException; import io.github.thebusybiscuit.slimefun4.api.exceptions.WrongItemStackException; @@ -188,7 +187,7 @@ public SlimefunItem( this.recipeType = recipeType; this.recipe = recipe; this.recipeOutput = recipeOutput; - normalItemName = TextUtils.toPlainText(itemStackTemplate.getItemMeta().getDisplayName()).replaceAll(" ","_"); + normalItemName = TextUtils.toPlainText(itemStackTemplate.getItemMeta().getDisplayName()).replaceAll(" ", "_"); } // Previously deprecated constructor, now only for internal purposes @@ -198,7 +197,7 @@ protected SlimefunItem(ItemGroup itemGroup, ItemStack item, String id, RecipeTyp this.id = id; this.recipeType = recipeType; this.recipe = recipe; - normalItemName = TextUtils.toPlainText(itemStackTemplate.getItemMeta().getDisplayName()).replaceAll(" ","_"); + normalItemName = TextUtils.toPlainText(itemStackTemplate.getItemMeta().getDisplayName()).replaceAll(" ", "_"); } /** @@ -505,13 +504,6 @@ private void onEnable() { private void loadItemHandlers() { for (ItemHandler handler : itemHandlers.values()) { - Optional exception = handler.validate(this); - - // Check if the validation caused an exception. - if (exception.isPresent()) { - throw exception.get(); - } - /* * If this ItemHandler is "global" (not bound to this SlimefunItem), * we add it to the list of global Item handlers 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..6dcc9acffe 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; /** @@ -12,7 +13,6 @@ * {@link BlockFace}.WEST * * @author Ddggdd135 - * */ public interface NotCardinallyRotatable extends ItemAttribute { default BlockFace getRotation(double angle) { 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..f992533df7 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; /** @@ -12,7 +13,6 @@ * {@link BlockFace}.SOUTH_WEST * * @author Ddggdd135 - * */ public interface NotDiagonallyRotatable extends ItemAttribute { default BlockFace getRotation(double angle) { 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 72% 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..92d236cbdd 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; /** @@ -8,7 +9,6 @@ * that {@link SlimefunItem} from being rotated. * * @author Ddggdd135 - * */ public interface NotRotatable extends ItemAttribute { default BlockFace getRotation() { 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 b501a0536a..39bd177e83 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 @@ -53,9 +53,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"); @@ -153,7 +150,8 @@ private void initAdapter(StorageType storageType, DataType dataType, Config data } } - @Nullable public ProfileDataController getProfileDataController() { + @Nullable + public ProfileDataController getProfileDataController() { return ControllerHolder.getController(ProfileDataController.class, profileStorageType); } 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 4aab97df04..a30bea02d0 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 @@ -2,8 +2,10 @@ import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; + import java.util.Iterator; import java.util.logging.Level; + import org.bukkit.block.Block; import org.bukkit.entity.Player; @@ -12,16 +14,13 @@ * data. * * @author TheBusyBiscuit - * */ public class AutoSavingService { /** * This method starts the {@link AutoSavingService} with the given interval. * - * @param plugin - * The current instance of Slimefun - * @param interval - * The interval in which to run this task + * @param plugin The current instance of Slimefun + * @param interval The interval in which to run this task */ public void start(Slimefun plugin, int interval) { plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, this::saveAllPlayers, 2000L, interval * 60L * 20L); @@ -29,9 +28,15 @@ public void start(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 2354acf2be..b216f68bc5 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 @@ -2,9 +2,12 @@ import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag; + import java.util.Optional; +import java.util.UUID; import java.util.logging.Level; import javax.annotation.Nullable; + import org.bukkit.Bukkit; import org.bukkit.Keyed; import org.bukkit.Material; @@ -24,23 +27,22 @@ * This is used to speed up performance and prevent * * @author TheBusyBiscuit - * */ public class BlockDataService implements Keyed { private final NamespacedKey namespacedKey; + private final NamespacedKey universalDataKey; /** * This creates a new {@link BlockDataService} for the given {@link Plugin}. * The {@link Plugin} and key will together form a {@link NamespacedKey} used to store * data on a {@link TileState}. * - * @param plugin - * The {@link Plugin} responsible for this service - * @param key - * The key under which to store data + * @param plugin The {@link Plugin} responsible for this service + * @param key The key under which to store data */ public BlockDataService(Plugin plugin, String key) { namespacedKey = new NamespacedKey(plugin, key); + universalDataKey = new NamespacedKey(plugin, "slimefun_unidata_uuid"); } @Override @@ -51,33 +53,44 @@ public NamespacedKey getKey() { /** * 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 + * @param b The {@link Block} in which to store the given value + * @param value The value to store */ public void setBlockData(Block b, 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(Block b, 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(Block b, NamespacedKey key, String value) { BlockState state = b.getState(); 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 (RuntimeException x) { Slimefun.logger().log(Level.SEVERE, "Please check if your Server Software is up to date!"); String serverSoftware = 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 Block", - x); + 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 Block", x); } } } @@ -85,23 +98,38 @@ public void setBlockData(Block b, String value) { /** * This method returns the NBT data previously stored inside this {@link Block}. * - * @param b - * The {@link Block} to retrieve data from - * + * @param b The {@link Block} to retrieve data from * @return The stored value */ public Optional getBlockData(Block b) { + return getBlockData(b, namespacedKey); + } + + public Optional getUniversalDataUUID(Block b) { + var uuid = getBlockData(b, universalDataKey); + + return uuid.map(data -> { + try { + return UUID.fromString(data); + } catch (IllegalArgumentException e) { + return null; + } + }); + } + + public Optional getBlockData(Block b, NamespacedKey key) { BlockState state = b.getState(false); 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(); } } - @Nullable private PersistentDataContainer getPersistentDataContainer(BlockState state) { + @Nullable + private PersistentDataContainer getPersistentDataContainer(BlockState state) { if (state instanceof TileState tileState) { return tileState.getPersistentDataContainer(); } else { @@ -117,9 +145,7 @@ public Optional getBlockData(Block b) { * Due to {@link Block#getState()} being a very expensive call performance-wise though, * this simple lookup method is used instead. * - * @param type - * The {@link Material} to check for - * + * @param type The {@link Material} to check for * @return Whether the given {@link Material} is considered a Tile Entity */ public boolean isTileEntity(@Nullable Material type) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MinecraftRecipeService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MinecraftRecipeService.java index ad1e706791..7cdfef791e 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MinecraftRecipeService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MinecraftRecipeService.java @@ -129,7 +129,7 @@ public RecipeChoice[] getRecipeShape(Recipe recipe) { List choices = new LinkedList<>(); for (String row : shapedRecipe.getShape()) { - int columns = row.toCharArray().length; + int columns = row.length(); for (char key : row.toCharArray()) { choices.add(shapedRecipe.getChoiceMap().get(key)); 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..c7d5ef9693 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/ticker/TickLocation.java @@ -0,0 +1,32 @@ +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/Slimefun.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java index 7301e7b778..7e697bdb08 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java @@ -45,7 +45,6 @@ import io.github.thebusybiscuit.slimefun4.utils.NumberUtils; import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag; import lombok.Getter; -import lombok.NonNull; import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.MenuListener; import me.qscbm.slimefun4.helper.ItemHelper; import me.qscbm.slimefun4.services.LanguageService; @@ -596,7 +595,7 @@ private void loadResearches() { * * @return The {@link Slimefun} instance */ - public static @NonNull Slimefun instance() { + public static Slimefun instance() { return instance; } 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 06a6e5f135..43ce01c23d 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,10 +1,10 @@ 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 35146c1778..3654a96a9e 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; @@ -7,11 +8,11 @@ import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; + import java.util.Random; import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; -import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; import org.bukkit.Bukkit; import org.bukkit.Effect; import org.bukkit.Material; @@ -33,9 +34,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 3d7e186f8d..fc75bdc537 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,13 +1,14 @@ 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; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundEffect; + import java.util.concurrent.ThreadLocalRandom; -import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; import org.bukkit.Material; import org.bukkit.Tag; import org.bukkit.block.Block; @@ -48,7 +49,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 d804dca242..4577b81c83 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,14 +1,15 @@ 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; import java.util.Map; import java.util.function.Predicate; -import javax.annotation.Nullable; import lombok.Getter; -import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; + import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.entity.Ageable; @@ -22,7 +23,6 @@ * added by Slimefun itself. * * @author TheBusyBiscuit - * */ public enum Instruction { /** @@ -248,7 +248,7 @@ public enum Instruction { private final AndroidType type; private final AndroidAction method; - Instruction(AndroidType type, HeadTexture head, @Nullable AndroidAction method) { + Instruction(AndroidType type, HeadTexture head, AndroidAction method) { this.type = type; this.item = SlimefunUtils.getCustomHead(head.getTexture()); this.method = method; @@ -262,8 +262,8 @@ public AndroidType getRequiredType() { return type; } - public void execute(ProgrammableAndroid android, Block b, BlockMenu inventory, BlockFace face) { - method.perform(android, b, inventory, face); + public void execute(ProgrammableAndroid android, Block b, UniversalMenu inventory, BlockFace face) { + method.perform(android, b, inventory, face); } /** @@ -272,12 +272,10 @@ public void execute(ProgrammableAndroid android, Block b, BlockMenu inventory, B * your Java version. It also means that you can avoid an IllegalArgumentException which let's * face it is always good. * - * @param value - * The value which you would like to look up. - * + * @param value The value which you would like to look up. * @return The {@link Instruction} or null if it does not exist. */ - @Nullable public static Instruction getInstruction(String value) { + public static Instruction getInstruction(String value) { return nameLookup.get(value); } } 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 b5354a86f9..056046845c 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; @@ -12,10 +13,10 @@ import io.github.thebusybiscuit.slimefun4.utils.InfiniteBlockGenerator; import io.github.thebusybiscuit.slimefun4.utils.compatibility.VersionedParticle; import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag; + import java.util.Collection; import java.util.UUID; -import me.mrCookieSlime.Slimefun.api.BlockStorage; import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; import org.bukkit.Bukkit; import org.bukkit.Effect; @@ -29,8 +30,6 @@ /** * The {@link MinerAndroid} is a variant of the {@link ProgrammableAndroid} which * is able to break blocks. - * The core functionalities boil down to {@link #dig(Block, BlockMenu, Block)} and - * {@link #moveAndDig(Block, BlockMenu, BlockFace, Block)}. * Otherwise the functionality is similar to a regular android. *

* The {@link MinerAndroid} will also fire an {@link AndroidMineEvent} when breaking a {@link Block}. @@ -42,9 +41,7 @@ * @author CyberPatriot * @author Redemption198 * @author Poslovitch - * * @see AndroidMineEvent - * */ public class MinerAndroid extends ProgrammableAndroid { // Determines the drops a miner android will get @@ -67,12 +64,12 @@ public AndroidType getAndroidType() { } @Override - 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)); @@ -83,7 +80,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); } } @@ -91,12 +89,12 @@ protected void dig(Block b, BlockMenu menu, Block block) { } @Override - 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)); @@ -107,7 +105,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); } @@ -119,7 +118,7 @@ protected void moveAndDig(Block b, BlockMenu menu, BlockFace face, Block block) } } - 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 3020eaa378..047da3384c 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,12 @@ 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.StorageCacheUtils; +import io.github.bakedlibs.dough.blocks.BlockPosition; import io.github.bakedlibs.dough.chat.ChatInput; import io.github.bakedlibs.dough.common.CommonPatterns; import io.github.bakedlibs.dough.items.CustomItemStack; @@ -13,8 +18,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; @@ -41,12 +46,9 @@ 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.bukkit.Bukkit; import org.bukkit.ChatColor; -import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.OfflinePlayer; import org.bukkit.Tag; @@ -65,7 +67,8 @@ 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); private static final int[] BORDER = { @@ -88,7 +91,8 @@ public ProgrammableAndroid( texture = item.getSkullTexture().orElse(null); registerDefaultFuelTypes(); - new BlockMenuPreset(getId(), "可编程式机器人") { + new UniversalMenuPreset(getId(), "可编程式机器人") { + @Override public void init() { constructMenu(this); @@ -96,8 +100,11 @@ 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); + + 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; @@ -108,19 +115,21 @@ 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()); + 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; }); @@ -130,9 +139,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; }); @@ -144,7 +153,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; @@ -167,13 +176,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) { @@ -189,21 +200,19 @@ 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"); + var uniData = StorageCacheUtils.getUniversalBlock(b); if (!e.getPlayer().hasPermission("slimefun.android.bypass") - && !e.getPlayer().getUniqueId().toString().equals(owner)) { + && !e.getPlayer().getUniqueId().equals(uniData.getData("owner"))) { // The Player is not allowed to break this android e.setCancelled(true); return; } - BlockMenu inv = blockData.getBlockMenu(); - - 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()); } } }; @@ -238,9 +247,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); } @@ -253,7 +263,8 @@ public boolean isSynchronized() { }); } - 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); @@ -266,7 +277,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); @@ -287,7 +298,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; }); } @@ -301,7 +312,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); @@ -313,16 +324,6 @@ public void openScript(Player p, Block b, String sourceCode) { } else { Instruction instruction = Instruction.getInstruction(script[i]); - if (instruction == null) { - Slimefun.instance() - .getLogger() - .log( - Level.WARNING, - "Failed to parse Android instruction: {0}, maybe your server is out of date?", - script[i]); - return; - } - ItemStack stack = instruction.getItem(); menu.addItem( i, @@ -345,14 +346,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; @@ -417,7 +418,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); @@ -438,7 +439,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; }); @@ -448,7 +449,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; }); @@ -459,14 +460,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; }); @@ -487,14 +488,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 (RuntimeException x) { Slimefun.logger() @@ -514,8 +515,8 @@ protected void openScriptDownloader(Player p, Block b, int page) { menu.open(p); } - 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())) { @@ -536,28 +537,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; }); @@ -567,7 +563,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; }); @@ -580,13 +576,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); @@ -615,7 +611,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); @@ -626,8 +622,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; }); @@ -641,8 +637,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; }); @@ -652,13 +648,13 @@ protected void editInstruction(Player p, Block b, String[] script, int index) { menu.open(p); } - public String getScript(Location l) { - String script = StorageCacheUtils.getData(l, "script"); + public String getScript(SlimefunUniversalBlockData ubd) { + String script = ubd.getData("script"); return script != null ? script : DEFAULT_SCRIPT; } - public void setScript(Location l, String script) { - StorageCacheUtils.setData(l, "script", script); + public void setScript(SlimefunUniversalBlockData ubd, String script) { + ubd.setData("script", script); } private void registerDefaultFuelTypes() { @@ -733,14 +729,14 @@ public int[] getOutputSlots() { return new int[]{20, 21, 22, 29, 30, 31}; } - 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); @@ -760,16 +756,6 @@ protected void tick(Block b, SlimefunBlockData data) { Instruction instruction = Instruction.getInstruction(script[index]); - if (instruction == null) { - Slimefun.instance() - .getLogger() - .log( - Level.WARNING, - "Failed to parse Android instruction: {0}, maybe your server is out of date?", - script[index]); - return; - } - if ("false".equals(data.getData("paused"))) { executeInstruction(instruction, b, menu, data, index); @@ -780,7 +766,7 @@ protected void tick(Block b, SlimefunBlockData data) { } 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; } @@ -833,7 +819,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 = facedBlock.getState(false); @@ -857,7 +843,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 = facedBlock.getState(false); @@ -875,7 +861,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); @@ -895,7 +881,7 @@ private boolean insertFuel( return false; } - 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) { @@ -915,7 +901,7 @@ private void consumeFuel(Block b, BlockMenu menu) { } } - private void constructMenu(BlockMenuPreset preset) { + private void constructMenu(UniversalMenuPreset preset) { preset.drawBackground(BORDER); preset.drawBackground(ChestMenuUtils.getOutputSlotTexture(), OUTPUT_BORDER); @@ -938,7 +924,13 @@ public boolean onClick( } public void addItems(Block b, ItemStack... items) { - 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) { @@ -947,48 +939,57 @@ public void addItems(Block b, ItemStack... items) { } } - 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); + + OfflinePlayer owner = Bukkit.getOfflinePlayer(UUID.fromString(uniData.getData("owner"))); - if (!Slimefun.getProtectionManager().hasPermission(owner, block.getLocation(), Interaction.PLACE_BLOCK)) { + if (!Slimefun.getProtectionManager().hasPermission(owner, to.getLocation(), Interaction.PLACE_BLOCK)) { return; } + if (to.getY() > to.getWorld().getMinHeight() + && to.getY() < to.getWorld().getMaxHeight() + && to.isEmpty()) { - if (block.getY() > block.getWorld().getMinHeight() - && block.getY() < block.getWorld().getMaxHeight() - && block.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()); } } @@ -996,23 +997,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 f12afa964b..83a997032f 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; @@ -9,11 +10,11 @@ import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag; + import java.util.List; import java.util.UUID; import java.util.function.Predicate; -import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; import org.bukkit.Bukkit; import org.bukkit.Effect; import org.bukkit.Material; @@ -37,7 +38,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())) { @@ -51,8 +52,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); } @@ -64,7 +65,7 @@ protected boolean chopTree(Block b, BlockMenu menu, BlockFace face) { return true; } - 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 @@ -118,7 +119,8 @@ private void replant(Block block) { saplingType = Material.WARPED_FUNGUS; soilRequirement = SlimefunTag.FUNGUS_SOIL::isTagged; } - default -> {} + default -> { + } } if (Slimefun.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_19)) { @@ -127,7 +129,8 @@ private void replant(Block block) { saplingType = Material.MANGROVE_PROPAGULE; soilRequirement = SlimefunTag.MANGROVE_BASE_BLOCKS::isTagged; } - default -> {} + default -> { + } } } @@ -137,7 +140,8 @@ private void replant(Block block) { saplingType = Material.CHERRY_SAPLING; soilRequirement = SlimefunTag.DIRT_VARIANTS::isTagged; } - default -> {} + default -> { + } } } @@ -147,7 +151,8 @@ private void replant(Block block) { saplingType = Material.CHERRY_SAPLING; soilRequirement = SlimefunTag.DIRT_VARIANTS::isTagged; } - default -> {} + default -> { + } } } 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 0e3a9d37c5..2d1c0d44a2 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 @@ -34,7 +34,8 @@ public final class AndroidShareMenu { private static final NamespacedKey BLOCK_INFO_KEY = new NamespacedKey(Slimefun.instance(), "share-users"); private static final int SHARED_USERS_LIMIT = 15; - private AndroidShareMenu() {} + private AndroidShareMenu() { + } /** * Open a share menu for player. @@ -195,7 +196,7 @@ public static List getTrustedUsers(Block b) { /** * Checks user is in trusted users list. * - * @param b the block of Android + * @param b the block of Android * @param uuid user's UUID * @return user trusted status */ @@ -214,15 +215,7 @@ private static void setSharedUserData(BlockState state, String value) { PersistentDataContainer container = ((TileState) state).getPersistentDataContainer(); container.set(BLOCK_INFO_KEY, PersistentDataType.STRING, value); state.update(); - } catch (RuntimeException x) { - Slimefun.logger().log(Level.SEVERE, "Please check if your Server Software is up to date!"); - - String serverSoftware = Bukkit.getName(); - Slimefun.logger() - .log( - Level.SEVERE, - () -> serverSoftware + " | " + Bukkit.getVersion() + " | " + Bukkit.getBukkitVersion()); - + } catch (Exception x) { 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 ae4de80016..ab9ce1f9fe 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 org.bukkit.inventory.ItemStack; @@ -12,12 +12,10 @@ * It can craft items that are crafted using the {@link ArmorForge}. * * @author TheBusyBiscuit - * * @see ArmorForge * @see AbstractAutoCrafter * @see SlimefunAutoCrafter * @see SlimefunItemRecipe - * */ public class ArmorAutoCrafter extends SlimefunAutoCrafter implements NotDiagonallyRotatable { public ArmorAutoCrafter(ItemGroup itemGroup, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { 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 80703f8b7f..694ba27130 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 org.bukkit.inventory.ItemStack; @@ -13,12 +13,10 @@ * It can craft items that are crafted using the {@link EnhancedCraftingTable}. * * @author TheBusyBiscuit - * * @see AbstractAutoCrafter * @see VanillaAutoCrafter * @see SlimefunItemRecipe * @see AutoCrafterListener - * */ public class EnhancedAutoCrafter extends SlimefunAutoCrafter implements NotDiagonallyRotatable { public EnhancedAutoCrafter(ItemGroup itemGroup, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { 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 1abcf1690c..1fa50137c6 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; @@ -40,11 +40,9 @@ * Only {@link ShapedRecipe} and {@link ShapelessRecipe} are therefore supported. * * @author TheBusyBiscuit - * * @see AbstractAutoCrafter * @see EnhancedAutoCrafter * @see VanillaRecipe - * */ public class VanillaAutoCrafter extends AbstractAutoCrafter implements NotDiagonallyRotatable { public VanillaAutoCrafter(ItemGroup itemGroup, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { 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 c807bb8aac..8c68a0e653 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; @@ -17,10 +17,8 @@ * It has no further functionality. * * @author TheBusyBiscuit - * * @see CargoNode * @see CargoNet - * */ public class CargoConnectorNode extends SimpleSlimefunItem implements NotRotatable { public CargoConnectorNode( 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 e1f99a364e..0cfa4df246 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 @@ -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.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; @@ -80,6 +80,7 @@ public boolean isSynchronized() { } }); } + @Override public BaseTickerTask getTickerTask() { return Slimefun.instance().getCargoTickerTask(); 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 eed409040a..155f7ff15d 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 me.mrCookieSlime.Slimefun.Objects.SlimefunItem.interfaces.InventoryBlock; import me.mrCookieSlime.Slimefun.Objects.handlers.BlockTicker; @@ -21,7 +21,6 @@ * items that enter it. * * @author TheBusyBiscuit - * */ public class TrashCan extends SlimefunItem implements InventoryBlock, NotRotatable { private final int[] border = {0, 1, 2, 3, 5, 4, 6, 7, 8, 9, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}; @@ -41,7 +40,7 @@ private void constructMenu(BlockMenuPreset preset) { @Override public int[] getInputSlots() { - return new int[] {10, 11, 12, 13, 14, 15, 16}; + return new int[]{10, 11, 12, 13, 14, 15, 16}; } @Override 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 3f95172adc..d425e4316c 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 @@ -4,7 +4,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; @@ -18,10 +18,8 @@ * They work similar to {@link Capacitor capacitors}. * * @author Linox - * * @see EnergyNet * @see EnergyNetComponent - * */ public class EnergyConnector extends SimpleSlimefunItem implements EnergyNetComponent, NotRotatable { public EnergyConnector( 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 6787a96ee1..7816741046 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; @@ -22,10 +22,8 @@ * {@link EnergyNet}. * * @author TheBusyBiscuit - * * @see EnergyNet * @see EnergyNetComponent - * */ public class EnergyRegulator extends SlimefunItem implements HologramOwner, NotRotatable { public EnergyRegulator(ItemGroup itemGroup, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { 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 65494b09a9..9885199c9c 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 me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AGenerator; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.MachineFuel; import org.bukkit.Material; 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 6b70d378af..235ecd2495 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 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 ddcc630e6b..ef2be7f995 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 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 9922664b1a..b77942ab99 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 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 4b54dbeb17..99a3ae7fb2 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 @@ -4,10 +4,9 @@ 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.SlimefunItems; - import me.qscbm.slimefun4.items.machines.ASpeedableContainer; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; @@ -17,7 +16,6 @@ * It allows you to compact items into their block variant, e.g. 9 diamonds into a diamond block. * * @author TheBusyBiscuit - * */ public class ElectricPress extends ASpeedableContainer implements RecipeDisplayItem, NotDiagonallyRotatable { public ElectricPress(ItemGroup itemGroup, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { @@ -80,7 +78,7 @@ protected void registerDefaultRecipes() { } private void addRecipe(int seconds, ItemStack input, ItemStack output) { - registerRecipe(seconds, new ItemStack[] {input}, new ItemStack[] {output}); + registerRecipe(seconds, new ItemStack[]{input}, new ItemStack[]{output}); } @Override 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 29816ffa38..e9b705f375 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,12 +9,13 @@ 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; import io.github.thebusybiscuit.slimefun4.implementation.handlers.SimpleBlockBreakHandler; import io.github.thebusybiscuit.slimefun4.implementation.items.magical.KnowledgeFlask; + import java.util.Iterator; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.interfaces.InventoryBlock; @@ -34,7 +35,6 @@ * and produces a {@link KnowledgeFlask}. * * @author TheBusyBiscuit - * */ public class ExpCollector extends SlimefunItem implements InventoryBlock, EnergyNetComponent, NotDiagonallyRotatable { private final int[] border = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}; @@ -82,7 +82,7 @@ public int[] getInputSlots() { @Override public int[] getOutputSlots() { - return new int[] {12, 13, 14}; + return new int[]{12, 13, 14}; } @Override @@ -143,10 +143,8 @@ protected void tick(Block block) { * Produces Flasks of Knowledge for the given block until it either uses all stored * experience or runs out of room. * - * @param location - * The {@link Location} of the {@link ExpCollector} to produce flasks in. - * @param experiencePoints - * The number of experience points to use during production. + * @param location The {@link Location} of the {@link ExpCollector} to produce flasks in. + * @param experiencePoints The number of experience points to use during production. */ private void produceFlasks(Location location, int experiencePoints) { int withdrawn = 0; 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 d3ee4e0103..39083f139e 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; @@ -21,6 +22,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.handlers.SimpleBlockBreakHandler; import io.github.thebusybiscuit.slimefun4.implementation.operations.GEOMiningOperation; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; + import java.util.LinkedList; import java.util.List; import java.util.OptionalInt; @@ -43,18 +45,17 @@ * The {@link GEOMiner} is an electrical machine that allows you to obtain a {@link GEOResource}. * * @author TheBusyBiscuit - * * @see GEOResource */ public class GEOMiner extends SlimefunItem implements RecipeDisplayItem, - EnergyNetComponent, - InventoryBlock, - HologramOwner, - MachineProcessHolder, - NotDiagonallyRotatable { + EnergyNetComponent, + InventoryBlock, + HologramOwner, + MachineProcessHolder, + NotDiagonallyRotatable { private static final int[] BORDER = { - 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 26, 27, 35, 36, 44, 45, 53 + 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 26, 27, 35, 36, 44, 45, 53 }; private static final int[] OUTPUT_BORDER = {19, 20, 21, 22, 23, 24, 25, 28, 34, 37, 43, 46, 47, 48, 49, 50, 51, 52}; private static final int[] OUTPUT_SLOTS = {29, 30, 31, 32, 33, 38, 39, 40, 41, 42}; @@ -115,9 +116,7 @@ public int getSpeed() { * This method must be called before registering the item * and only before registering. * - * @param capacity - * The amount of energy this machine can store - * + * @param capacity The amount of energy this machine can store * @return This method will return the current instance of {@link GEOMiner}, so that can be chained. */ public final GEOMiner setCapacity(int capacity) { @@ -132,9 +131,7 @@ public final GEOMiner setCapacity(int capacity) { /** * This sets the speed of this machine. * - * @param speed - * The speed multiplier for this machine, must be above zero - * + * @param speed The speed multiplier for this machine, must be above zero * @return This method will return the current instance of {@link GEOMiner}, so that can be chained. */ public final GEOMiner setProcessingSpeed(int speed) { @@ -145,9 +142,7 @@ public final GEOMiner setProcessingSpeed(int speed) { /** * This method sets the energy consumed by this machine per tick. * - * @param energyConsumption - * The energy consumed per tick - * + * @param energyConsumption The energy consumed per tick * @return This method will return the current instance of {@link GEOMiner}, so that can be chained. */ public final GEOMiner setEnergyConsumption(int energyConsumption) { @@ -167,8 +162,8 @@ public void register(SlimefunAddon addon) { if (getEnergyConsumption() <= 0) { warn("The energy consumption has not been configured correctly. The Item was disabled."); warn("Make sure to call '" - + getClass().getSimpleName() - + "#setEnergyConsumption(...)' before registering!"); + + getClass().getSimpleName() + + "#setEnergyConsumption(...)' before registering!"); } if (getSpeed() <= 0) { 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 07d472b98f..2e087372fe 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,10 +4,11 @@ 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; + import java.util.Optional; import org.bukkit.Location; @@ -42,6 +43,6 @@ public BlockUseHandler getItemHandler() { private boolean hasAccess(Player p, Location l) { return p.hasPermission("slimefun.gps.bypass") - || (Slimefun.getProtectionManager().hasPermission(p, l, Interaction.INTERACT_BLOCK)); + || (Slimefun.getProtectionManager().hasPermission(p, l, Interaction.INTERACT_BLOCK)); } } 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 f8d51008e6..a212daaf6f 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,11 +5,12 @@ 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; + import java.util.Arrays; import java.util.List; import java.util.OptionalInt; @@ -47,8 +48,8 @@ public void init() { @Override public boolean canOpen(Block b, Player p) { if (!(p.hasPermission("slimefun.inventory.bypass") - || Slimefun.getProtectionManager() - .hasPermission(p, b.getLocation(), Interaction.INTERACT_BLOCK))) { + || Slimefun.getProtectionManager() + .hasPermission(p, b.getLocation(), Interaction.INTERACT_BLOCK))) { return false; } @@ -102,7 +103,7 @@ protected MachineRecipe findNextRecipe(BlockMenu inv) { if (supplies.isPresent() && supplies.getAsInt() > 0) { MachineRecipe recipe = new MachineRecipe( - 26, new ItemStack[] {emptyBucket}, new ItemStack[] {SlimefunItems.OIL_BUCKET}); + 26, new ItemStack[]{emptyBucket}, new ItemStack[]{SlimefunItems.OIL_BUCKET}); inv.consumeItem(slot); Slimefun.getGPSNetwork() 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 4a9b7d54fa..78d2bcf52a 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,10 +4,11 @@ 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; + import java.util.Optional; import org.bukkit.Location; @@ -40,6 +41,6 @@ public BlockUseHandler getItemHandler() { private boolean hasAccess(Player p, Location l) { return p.hasPermission("slimefun.gps.bypass") - || (Slimefun.getProtectionManager().hasPermission(p, l, Interaction.INTERACT_BLOCK)); + || (Slimefun.getProtectionManager().hasPermission(p, l, Interaction.INTERACT_BLOCK)); } } 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 9fe4deff8e..5e281e270d 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,12 +7,13 @@ 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; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; + import java.util.List; import java.util.UUID; 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 776c1d0b27..6d597bb352 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,15 +2,16 @@ 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.thebusybiscuit.slimefun4.api.events.ExplosiveToolBreakBlocksEvent; 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; @@ -58,7 +59,7 @@ public BlockListener(Slimefun plugin) { @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onBlockPlaceExisting(BlockPlaceEvent e) { Block block = e.getBlock(); - var loc = block.getLocation(); + Location loc = block.getLocation(); // Fixes #2636 - This will solve the "ghost blocks" issue if (e.getBlockReplacedState().getType().isAir()) { @@ -106,7 +107,8 @@ public void onBlockPlace(BlockPlaceEvent e) { if (!sfItem.canUse(e.getPlayer(), true)) { e.setCancelled(true); } else { - if (e.getBlock().getBlockData() instanceof Rotatable rotatable + Block block = e.getBlock(); + if (block.getBlockData() instanceof Rotatable rotatable && !(rotatable.getRotation() == BlockFace.UP || rotatable.getRotation() == BlockFace.DOWN)) { BlockFace rotation = null; @@ -124,22 +126,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)); } } @@ -160,7 +172,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/SlimefunItemInteractListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SlimefunItemInteractListener.java index c30dc7aff3..60fbcbbbe9 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; @@ -11,8 +13,8 @@ import java.util.Optional; -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; @@ -32,11 +34,9 @@ * * @author TheBusyBiscuit * @author Liruxo - * * @see PlayerRightClickEvent * @see ItemUseHandler * @see BlockUseHandler - * */ public class SlimefunItemInteractListener implements Listener { public SlimefunItemInteractListener(Slimefun plugin) { @@ -48,10 +48,7 @@ public void onRightClick(PlayerInteractEvent e) { if (e.getAction() == Action.RIGHT_CLICK_AIR || e.getAction() == Action.RIGHT_CLICK_BLOCK) { // 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; } @@ -135,31 +132,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 (RuntimeException | LinkageError x) { @@ -167,7 +195,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/listeners/crafting/VanillaCrafterListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/crafting/VanillaCrafterListener.java index d1df2bced8..4d26acf78c 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/crafting/VanillaCrafterListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/crafting/VanillaCrafterListener.java @@ -5,7 +5,6 @@ import io.github.bakedlibs.dough.versions.MinecraftVersion; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; -import javax.annotation.Nonnull; import org.bukkit.block.Crafter; import org.bukkit.entity.Player; import org.bukkit.event.Event; @@ -17,7 +16,7 @@ import org.bukkit.inventory.Inventory; public class VanillaCrafterListener implements SlimefunCraftingListener { - public VanillaCrafterListener(@Nonnull Slimefun plugin) { + public VanillaCrafterListener(Slimefun plugin) { if (SlimefunExtended.getMinecraftVersion().isAtLeast(MinecraftVersion.parse("1.20.3"))) plugin.getServer().getPluginManager().registerEvents(this, plugin); } @@ -28,9 +27,9 @@ public void onCrafter(InventoryClickEvent e) { Inventory topInventory = VersionedEvent.getTopInventory(e); if (clickedInventory != null - && topInventory.getType() == InventoryType.CRAFTER - && topInventory.getHolder() instanceof Crafter - && e.getWhoClicked() instanceof Player) { + && topInventory.getType() == InventoryType.CRAFTER + && topInventory.getHolder() instanceof Crafter + && e.getWhoClicked() instanceof Player) { if (e.getAction() == InventoryAction.HOTBAR_SWAP) { e.setCancelled(true); 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 8abdc66f36..f2857023ed 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,16 +1,21 @@ 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.SlimefunUniversalBlockData; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunUniversalData; 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.HashSet; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; @@ -20,7 +25,6 @@ import me.qscbm.slimefun4.tasks.BaseTickerTask; import org.bukkit.Chunk; import org.bukkit.Location; -import org.bukkit.block.Block; import org.bukkit.scheduler.BukkitScheduler; /** @@ -35,7 +39,7 @@ public class TickerTask extends BaseTickerTask { * 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 . @@ -93,7 +97,7 @@ public void run() { // Run our ticker code if (!halted) { - for (Map.Entry> entry : tickingLocations.entrySet()) { + for (Map.Entry> entry : tickingLocations.entrySet()) { tickChunk(entry.getKey(), tickers, entry.getValue()); } } @@ -115,12 +119,16 @@ public void run() { } } - 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) { @@ -134,6 +142,7 @@ private void tickLocation(Set tickers, Location l) { if (blockData == null || !blockData.isDataLoaded() || blockData.isPendingRemove()) { return; } + SlimefunItem item = SlimefunItem.getById(blockData.getSfId()); if (item != null && item.getBlockTicker() != null) { if (item.isDisabledIn(l.getWorld())) { @@ -152,13 +161,11 @@ private void tickLocation(Set tickers, Location l) { if (blockData.isPendingRemove()) { return; } - Block b = l.getBlock(); - tickBlock(l, b, item, blockData); + tickBlock(l, item, blockData); }); } else { item.getBlockTicker().update(); - Block b = l.getBlock(); - tickBlock(l, b, item, blockData); + tickBlock(l, item, blockData); } tickers.add(item.getBlockTicker()); @@ -170,10 +177,57 @@ private void tickLocation(Set tickers, Location l) { disableTicker(l); } - private void tickBlock(Location l, Block b, SlimefunItem item, SlimefunBlockData data) { + private void tickUniversalLocation(UUID uuid, Location l, Set tickers) { + SlimefunUniversalBlockData data = StorageCacheUtils.getUniversalBlock(uuid); + SlimefunItem item = SlimefunItem.getById(data.getSfId()); + + if (item != null && item.getBlockTicker() != null) { + if (item.isDisabledIn(l.getWorld())) { + return; + } + + try { + if (item.getBlockTicker().isSynchronized()) { + 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); + }); + } else { + item.getBlockTicker().update(); + tickBlock(l, item, data); + } + + tickers.add(item.getBlockTicker()); + } catch (Exception x) { + reportErrors(l, item, x); + } + } + } + + private void tickBlock(Location l, SlimefunItem item, ASlimefunDataContainer data) { try { - item.getBlockTicker().tick(b, item, data); - } catch (RuntimeException | LinkageError x) { + 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); } } @@ -206,7 +260,21 @@ public void halt() { halted = true; } - public Map> getLocations() { + public Map> getLocations() { + return tickingLocations; + } + + + /** + * This method returns a read-only {@link Map} + * representation of every {@link ChunkPosition} and its corresponding + * {@link Set} of ticking {@link Location Locations}. + *

+ * This does include any {@link Location} from an unloaded {@link Chunk} too! + * + * @return A {@link Map} representation of all ticking {@link TickLocation Locations} + */ + public Map> getTickLocations() { return tickingLocations; } @@ -219,7 +287,7 @@ public Map> getLocations() { * @param chunk The {@link Chunk} * @return A {@link Set} of all ticking {@link Location Locations} */ - public Set getLocations(Chunk chunk) { + public Set getLocations(Chunk chunk) { return tickingLocations.getOrDefault(new ChunkPosition(chunk), new HashSet<>()); } @@ -228,28 +296,30 @@ public Set getLocations(Chunk chunk) { * * @param l The {@link Location} to activate */ - @Override public void enableTicker(Location l) { + enableTicker(l, null); + } + + public void enableTicker(Location l, UUID uuid) { ChunkPosition chunk = new ChunkPosition(l.getWorld(), l.getBlockX() >> 4, l.getBlockZ() >> 4); + final TickLocation 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. + Thus, the choice is between the CHM KeySet or a synchronized set. + 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); - /* - Note that all the values in #tickingLocations must be thread-safe. - Thus, the choice is between the CHM KeySet or a synchronized set. - 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); - - /* - This is faster than doing computeIfAbsent(...) - on a ConcurrentHashMap because it won't block the Thread for too long - */ //noinspection ReplaceNullCheck if (oldValue != null) { - oldValue.add(l); + oldValue.add(tickPosition); } else { - newValue.add(l); + newValue.add(tickPosition); } } @@ -262,14 +332,27 @@ This is faster than doing computeIfAbsent(...) @Override public void disableTicker(Location l) { 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); } } } + + /** + * 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(UUID uuid) { + tickingLocations.values().forEach(loc -> loc.removeIf(tk -> uuid.equals(tk.getUuid()))); + } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java index 01c7a7e163..8e2ad07dd7 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java @@ -246,7 +246,7 @@ public static boolean isItemSimilar(@Nullable ItemStack item, @Nullable ItemStac return isItemSimilar(item, sfitem, checkLore, checkAmount, checkDistinctiveItem, true); } - public static boolean isSlimefunItemSimilar(SlimefunItemStack sfItem, ItemStack item,boolean checkLore) { + public static boolean isSlimefunItemSimilar(SlimefunItemStack sfItem, ItemStack item, boolean checkLore) { SlimefunItem sfI = SlimefunItem.getByItem(item); if (sfI == null) { return false; @@ -262,7 +262,7 @@ public static boolean isSlimefunItemSimilar(SlimefunItemStack sfItem, ItemStack if (lores1.size() != lores2.size()) { return false; } - for (int i = 0;i> itemLore = itemMetaSnapshot.getLore(); - - //noinspection DataFlowIssue - if (itemMeta.hasLore() && itemLore.isPresent() && !equalsLore(itemMeta.getLore(), itemLore.get())) { - return false; - } else if (itemMeta.hasLore() != itemLore.isPresent()) { return false; + } else if (checkLore) { + Optional> itemLore = itemMetaSnapshot.getLore(); + + //noinspection DataFlowIssue + if (itemMeta.hasLore() && itemLore.isPresent() && !equalsLore(itemMeta.getLore(), itemLore.get())) { + return false; + } else if (itemMeta.hasLore() != itemLore.isPresent()) { + return false; + } } - } // Fixes #3133: name and lore are not enough OptionalInt itemCustomModelData = itemMetaSnapshot.getCustomModelData(); @@ -387,20 +387,20 @@ private static boolean equalsItemMeta(ItemMeta itemMeta, ItemMeta sfitemMeta, bo return false; } else //noinspection DataFlowIssue if (itemMeta.hasDisplayName() && sfitemMeta.hasDisplayName() && !itemMeta.displayName().equals(sfitemMeta.displayName())) { - return false; - } else if (checkLore) { - boolean hasItemMetaLore = itemMeta.hasLore(); - boolean hasSfItemMetaLore = sfitemMeta.hasLore(); + return false; + } else if (checkLore) { + boolean hasItemMetaLore = itemMeta.hasLore(); + boolean hasSfItemMetaLore = sfitemMeta.hasLore(); - if (hasItemMetaLore && hasSfItemMetaLore) { - //noinspection DataFlowIssue - if (!equalsLoreNew(itemMeta.lore(), sfitemMeta.lore())) { + if (hasItemMetaLore && hasSfItemMetaLore) { + //noinspection DataFlowIssue + if (!equalsLoreNew(itemMeta.lore(), sfitemMeta.lore())) { + return false; + } + } else if (hasItemMetaLore != hasSfItemMetaLore) { return false; } - } else if (hasItemMetaLore != hasSfItemMetaLore) { - return false; } - } if (checkCustomModelCheck) { // Fixes #3133: name and lore are not enough @@ -422,8 +422,8 @@ private static boolean equalsItemMeta(ItemMeta itemMeta, ItemMeta sfitemMeta, bo //noinspection ConstantValue return potionMeta.getBasePotionType() != null - && sfPotionMeta.getBasePotionType() != null - && potionMeta.getBasePotionType().equals(sfPotionMeta.getBasePotionType()); + && sfPotionMeta.getBasePotionType() != null + && potionMeta.getBasePotionType().equals(sfPotionMeta.getBasePotionType()); } else if (SlimefunExtended.getMinecraftVersion().isAtLeast(1, 20, 2)) { return potionMeta.getBasePotionType().equals(sfPotionMeta.getBasePotionType()); } else { @@ -602,7 +602,7 @@ public static boolean canPlayerUseItem(Player p, @Nullable ItemStack item, boole * @param reason The {@link ItemSpawnReason} why the item is being dropped * @return The dropped {@link Item} (or null if the {@link SlimefunItemSpawnEvent} was cancelled) */ - public static @Nullable Item spawnItem(Location loc, ItemStack item, ItemSpawnReason reason) { + public static Item spawnItem(Location loc, ItemStack item, ItemSpawnReason reason) { return spawnItem(loc, item, reason, false); } 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 f41d270bde..1437f53482 100644 --- a/src/main/java/me/mrCookieSlime/CSCoreLibPlugin/general/Inventory/ChestMenu.java +++ b/src/main/java/me/mrCookieSlime/CSCoreLibPlugin/general/Inventory/ChestMenu.java @@ -3,6 +3,14 @@ import city.norain.slimefun4.holder.SlimefunInventoryHolder; import city.norain.slimefun4.utils.InventoryUtil; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; +import lombok.Getter; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -12,12 +20,6 @@ import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicBoolean; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; /** * An old remnant of CS-CoreLib. @@ -27,7 +29,10 @@ public class ChestMenu extends SlimefunInventoryHolder { private boolean clickable; private boolean emptyClickable; + + @Getter private final String title; + private final List items; /** * Size of chestmenu @@ -56,8 +61,10 @@ public ChestMenu(String title) { this.items = new ArrayList<>(); this.handlers = new HashMap<>(); - this.open = p -> {}; - this.close = p -> {}; + this.open = p -> { + }; + this.close = p -> { + }; this.playerclick = (p, slot, item, action) -> isPlayerInventoryClickable(); } @@ -375,8 +382,8 @@ public ChestMenu setSize(int size) { } else { throw new IllegalArgumentException( "The size of a ChestMenu must be a multiple of 9 and within the bounds 0-54," - + " received: " - + size); + + " received: " + + size); } } 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 35a3cbdd63..0e1a6f9fe5 100644 --- a/src/main/java/me/mrCookieSlime/Slimefun/Objects/handlers/BlockTicker.java +++ b/src/main/java/me/mrCookieSlime/Slimefun/Objects/handlers/BlockTicker.java @@ -2,17 +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(); @@ -22,17 +36,7 @@ public void update() { @Override public Optional validate(SlimefunItem item) { - if (!item.getItem().getType().isBlock()) { - return Optional.of(new IncompatibleItemHandlerException( - "Only Materials that are blocks can have a BlockTicker.", item, this)); - } - - if (item instanceof NotPlaceable) { - return Optional.of(new IncompatibleItemHandlerException( - "Only Slimefun items that are not marked as 'NotPlaceable' can have a BlockTicker.", item, this)); - } - - return Optional.empty(); + return ItemHandler.super.validate(item); } /** @@ -46,19 +50,28 @@ public Optional validate(SlimefunItem item) { /** * 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} + * @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, 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) {} + public void tick(Block b, SlimefunItem item, Config data) { + } /** * This method is called every tick but not per-block and only once. 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 56f66c15c9..758a60d513 100644 --- a/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/BlockMenuPreset.java +++ b/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/BlockMenuPreset.java @@ -3,9 +3,11 @@ import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; + import java.util.HashSet; import java.util.Set; import javax.annotation.Nullable; + import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ChestMenu; import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow; import org.bukkit.Location; @@ -16,12 +18,10 @@ // 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; - private boolean locked; - protected BlockMenuPreset(String id, String title) { super(title); @@ -33,10 +33,6 @@ protected BlockMenuPreset(String id, String title) { } private void checkIfLocked() { - if (locked) { - throw new UnsupportedOperationException( - "You cannot modify the BlockMenuPreset anymore, modify the individual instances" + " instead."); - } } public abstract void init(); @@ -46,11 +42,8 @@ private void checkIfLocked() { * {@link BlockMenu} of that {@link Block}. * Override this as necessary. * - * @param b - * The {@link Block} trying to be opened - * @param p - * The {@link Player} who wants to open the {@link BlockMenu} - * + * @param b The {@link Block} trying to be opened + * @param p The {@link Player} who wants to open the {@link BlockMenu} * @return Whether that {@link Player} is allowed */ public abstract boolean canOpen(Block b, Player p); @@ -61,12 +54,11 @@ private void checkIfLocked() { * This method is called whenever an {@link ItemStack} changes. * You can override this as necessary if you need to listen to these events * - * @param next - * The {@link ItemStack} that it changes to - * + * @param next The {@link ItemStack} that it changes to * @return The new outcome of this operation */ - @Nullable protected ItemStack onItemStackChange( + @Nullable + protected ItemStack onItemStackChange( @Nullable ItemStack next) { // Override this as necessary return next; @@ -89,10 +81,8 @@ public void replaceExistingItem(int slot, ItemStack item) { /** * This method will draw unclickable background items into this {@link BlockMenuPreset}. * - * @param item - * The {@link ItemStack} that should be used as background - * @param slots - * The slots which should be treated as background + * @param item The {@link ItemStack} that should be used as background + * @param slots The slots which should be treated as background */ public void drawBackground(ItemStack item, int[] slots) { checkIfLocked(); @@ -105,8 +95,7 @@ public void drawBackground(ItemStack item, int[] slots) { /** * This method will draw unclickable background items into this {@link BlockMenuPreset}. * - * @param slots - * The slots which should be treated as background + * @param slots The slots which should be treated as background */ public void drawBackground(int[] slots) { drawBackground(ChestMenuUtils.getBackground(), slots); @@ -193,8 +182,6 @@ protected void clone(DirtyChestMenu menu) { public void newInstance(BlockMenu menu, Location l) { Slimefun.runSync(() -> { - locked = true; - try { newInstance(menu, l.getBlock()); } catch (RuntimeException | LinkageError x) { @@ -224,7 +211,8 @@ public SlimefunItem getSlimefunItem() { return SlimefunItem.getById(id); } - @Nullable public static BlockMenuPreset getPreset(@Nullable String id) { + @Nullable + public static BlockMenuPreset getPreset(@Nullable String id) { return id == null ? null : Slimefun.getRegistry().getMenuPresets().get(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 301fa275fc..b184625e56 100644 --- a/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/DirtyChestMenu.java +++ b/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/DirtyChestMenu.java @@ -8,6 +8,7 @@ import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper; import lombok.Getter; + import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ChestMenu; import org.bukkit.Material; import org.bukkit.block.Block; @@ -80,9 +81,9 @@ public void close() { } public boolean fits(ItemStack item, int... slots) { - var isSfItem = SlimefunItem.getByItem(item) != null; - var wrapper = ItemStackWrapper.wrap(item); - var remain = item.getAmount(); + boolean isSfItem = SlimefunItem.getByItem(item) != null; + ItemStackWrapper wrapper = ItemStackWrapper.wrap(item); + int remain = item.getAmount(); for (int slot : slots) { // A small optimization for empty slots @@ -234,36 +235,20 @@ public void consumeItem(int slot) { } public void consumeItem(int slot, int amount) { - if (locked()) { - throw new IllegalStateException("Cannot consume item when menu is locked"); - } - consumeItem(slot, amount, false); } public void consumeItem(int slot, int amount, boolean replaceConsumables) { - if (locked()) { - throw new IllegalStateException("Cannot consume item when menu is locked"); - } - ItemUtils.consumeItem(getItemInSlot(slot), amount, replaceConsumables); markDirty(); } @Override public void replaceExistingItem(int slot, ItemStack item) { - if (locked()) { - throw new IllegalStateException("Cannot consume item when menu is locked"); - } - replaceExistingItem(slot, item, true); } public void replaceExistingItem(int slot, ItemStack item, boolean event) { - if (locked()) { - throw new IllegalStateException("Cannot consume item when menu is locked"); - } - if (event) { item = preset.onItemStackChange(item); } diff --git a/src/main/java/me/qscbm/slimefun4/message/QsTextComponentImpl.java b/src/main/java/me/qscbm/slimefun4/message/QsTextComponentImpl.java index 8cd496cf97..7c5418f7f9 100644 --- a/src/main/java/me/qscbm/slimefun4/message/QsTextComponentImpl.java +++ b/src/main/java/me/qscbm/slimefun4/message/QsTextComponentImpl.java @@ -7,7 +7,6 @@ import net.kyori.adventure.text.format.Style; import net.kyori.adventure.text.format.TextColor; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -44,7 +43,7 @@ public String content() { } @Override - public QsTextComponentImpl append(final @NotNull ComponentLike like) { + public QsTextComponentImpl append(final ComponentLike like) { final Component component = like.asComponent(); if (component == Component.empty()) return this; if (children.equals(Collections.emptyList())) { @@ -54,7 +53,7 @@ public QsTextComponentImpl append(final @NotNull ComponentLike like) { return this; } - public QsTextComponentImpl append(final @NotNull QsTextComponentImpl like) { + public QsTextComponentImpl append(final QsTextComponentImpl like) { if (children.equals(Collections.emptyList())) { children = new ArrayList<>(); } diff --git a/src/main/java/me/qscbm/slimefun4/utils/CacheMap.java b/src/main/java/me/qscbm/slimefun4/utils/CacheMap.java index 7de2c39213..3dc06d1fd5 100644 --- a/src/main/java/me/qscbm/slimefun4/utils/CacheMap.java +++ b/src/main/java/me/qscbm/slimefun4/utils/CacheMap.java @@ -1,11 +1,10 @@ package me.qscbm.slimefun4.utils; -import javax.annotation.Nonnull; import java.util.concurrent.ConcurrentHashMap; -public class CacheMap extends ConcurrentHashMap { +public class CacheMap extends ConcurrentHashMap { @Override - public String put(@Nonnull String key, String value) { + public String put(String key, String value) { if (value == null) { value = ""; } @@ -13,7 +12,7 @@ public String put(@Nonnull String key, String value) { } @Override - public String putIfAbsent(@Nonnull String key, String value) { + public String putIfAbsent(String key, String value) { if (value == null) { value = ""; }