Fix PlayerBounties-OG head compatibility

This commit is contained in:
NotAlexNoyle committed 2026-03-27 10:53:12 -04:00
1 parent d150f0ddc2
commit 9f5a4065d9
5 files changed
+442 -11

No files matched your search

+1
View File
@@ -186,3 +186,4 @@ docs/dev/
# Extensions
/src/main/java/net/coreprotect/extensions/*.java
!/src/main/java/net/coreprotect/extensions/Extensions.java
!/src/main/java/net/coreprotect/extensions/PlayerBountiesHeadCompatibility.java
@@ -2,6 +2,7 @@ package net.coreprotect.consumer.process;
import java.sql.Statement;
import net.coreprotect.config.ConfigHandler;
import net.coreprotect.database.Database;
import net.coreprotect.database.statement.SkullStatement;
import net.coreprotect.utility.Util;
import org.bukkit.block.BlockState;
@@ -17,9 +18,22 @@ class SkullUpdateProcess {
if (object instanceof BlockState) {
BlockState block = (BlockState) object;
String query = "SELECT owner, skin, metadata FROM " + ConfigHandler.prefix + "skull WHERE rowid='" + rowId
+ "' LIMIT 0, 1";
SkullStatement.getData(statement, block, query);
StringBuilder query = new StringBuilder("SELECT owner");
if (Database.hasSkullSkinColumn()) {
query.append(", skin");
}
if (Database.hasSkullMetadataColumn()) {
query.append(", metadata");
}
query.append(" FROM ").append(ConfigHandler.prefix).append("skull WHERE rowid='").append(rowId)
.append("' LIMIT 0, 1");
SkullStatement.getData(statement, block, query.toString());
Util.updateBlock(block);
}
@@ -1,6 +1,7 @@
package net.coreprotect.database;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@@ -40,6 +41,8 @@ public class Database extends Queue {
public static final int ENTITY_MAP = 11;
public static final int BLOCKDATA = 12;
public static final int ITEM = 13;
private static volatile Boolean skullHasSkinColumn = null;
private static volatile Boolean skullHasMetadataColumn = null;
public static void beginTransaction(Statement statement, boolean isMySQL) {
@@ -142,6 +145,48 @@ public class Database extends Queue {
}
public static boolean hasSkullSkinColumn() {
return Boolean.TRUE.equals(skullHasSkinColumn);
}
public static boolean hasSkullMetadataColumn() {
return Boolean.TRUE.equals(skullHasMetadataColumn);
}
public static void initializeSkullColumnCache(Connection connection) {
if (connection == null) {
return;
}
try {
if (skullHasSkinColumn == null) {
skullHasSkinColumn = hasColumn(connection, ConfigHandler.prefix + "skull", "skin");
}
if (skullHasMetadataColumn == null) {
skullHasMetadataColumn = hasColumn(connection, ConfigHandler.prefix + "skull", "metadata");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void containerBreakCheck(String user, Material type, Object container, ItemStack[] contents,
Location location)
{
@@ -319,8 +364,25 @@ public class Database extends Queue {
+ "sign (time, user, wid, x, y, z, action, color, color_secondary, data, waxed, face, line_1, line_2, line_3, line_4, line_5, line_6, line_7, line_8) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
String blockInsert = "INSERT INTO " + ConfigHandler.prefix
+ "block (time, user, wid, x, y, z, type, data, meta, blockdata, action, rolled_back) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
String skullInsert = "INSERT INTO " + ConfigHandler.prefix
+ "skull (time, owner, skin, metadata) VALUES (?, ?, ?, ?)";
initializeSkullColumnCache(connection);
String skullColumns = "time, owner";
String skullValues = "?, ?";
if (hasSkullSkinColumn()) {
skullColumns = skullColumns + ", skin";
skullValues = skullValues + ", ?";
}
if (hasSkullMetadataColumn()) {
skullColumns = skullColumns + ", metadata";
skullValues = skullValues + ", ?";
}
String skullInsert = "INSERT INTO " + ConfigHandler.prefix + "skull (" + skullColumns + ") VALUES ("
+ skullValues + ")";
String containerInsert = "INSERT INTO " + ConfigHandler.prefix
+ "container (time, user, wid, x, y, z, type, data, amount, metadata, action, rolled_back) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
String itemInsert = "INSERT INTO " + ConfigHandler.prefix
@@ -476,6 +538,9 @@ public class Database extends Queue {
public static void createDatabaseTables(String prefix, Connection forceConnection, boolean mySQL, boolean purge) {
skullHasSkinColumn = null;
skullHasMetadataColumn = null;
ConfigHandler.databaseTables.clear();
ConfigHandler.databaseTables.addAll(Arrays.asList("art_map", "block", "chat", "command", "container", "item",
"database_lock", "entity", "entity_map", "material_map", "blockdata_map", "session", "sign", "skull",
@@ -542,6 +607,7 @@ public class Database extends Queue {
+ index + ") ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4");
statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix
+ "skull(rowid int NOT NULL AUTO_INCREMENT,PRIMARY KEY(rowid), time int, owner varchar(255), skin varchar(255), metadata MEDIUMTEXT) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4");
ensureSkullColumns(connection, statement, prefix);
index = ", INDEX(user), INDEX(uuid)";
statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix
+ "user(rowid int NOT NULL AUTO_INCREMENT,PRIMARY KEY(rowid),time int,user varchar(100),uuid varchar(64)"
@@ -719,6 +785,8 @@ public class Database extends Queue {
}
ensureSkullColumns(connection, statement, prefix);
if (!tableData.contains(prefix + "user")) {
statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix
@@ -987,4 +1055,62 @@ public class Database extends Queue {
}
private static boolean hasColumn(Connection connection, String tableName, String columnName) throws SQLException {
DatabaseMetaData metaData = connection.getMetaData();
try (ResultSet resultSet = metaData.getColumns(connection.getCatalog(), null, tableName, columnName)) {
if (resultSet.next()) {
return true;
}
}
try (ResultSet resultSet = metaData.getColumns(connection.getCatalog(), null,
tableName.toUpperCase(Locale.ROOT), columnName.toUpperCase(Locale.ROOT)))
{
return resultSet.next();
}
}
private static void ensureSkullColumns(Connection connection, Statement statement, String prefix) {
try {
String tableName = prefix + "skull";
boolean hasMetadata = hasColumn(connection, tableName, "metadata");
boolean hasSkin = hasColumn(connection, tableName, "skin");
if (!hasMetadata) {
statement.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN metadata "
+ (Config.getGlobal().MYSQL ? "MEDIUMTEXT" : "TEXT") + ";");
hasMetadata = true;
}
if (!hasSkin) {
statement.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN skin "
+ (Config.getGlobal().MYSQL ? "VARCHAR(255)" : "TEXT") + ";");
hasSkin = true;
}
skullHasMetadataColumn = hasMetadata;
skullHasSkinColumn = hasSkin;
} catch (Exception e) {
initializeSkullColumnCache(connection);
}
}
}
@@ -25,8 +25,19 @@ public class SkullStatement {
preparedStmt.setInt(1, time);
preparedStmt.setString(2, owner);
preparedStmt.setString(3, skin);
preparedStmt.setString(4, metadata);
int parameterIndex = 3;
if (Database.hasSkullSkinColumn()) {
preparedStmt.setString(parameterIndex++, skin);
}
if (Database.hasSkullMetadataColumn()) {
preparedStmt.setString(parameterIndex, metadata);
}
if (Database.hasReturningKeys()) {
return preparedStmt.executeQuery();
@@ -69,14 +80,22 @@ public class SkullStatement {
}
String skin = resultSet.getString("skin");
if (owner != null && skin != null && skin.length() > 0) {
if (Database.hasSkullSkinColumn()) {
PaperAdapter.ADAPTER.setSkullSkin(skull, skin);
String skin = resultSet.getString("skin");
if (owner != null && skin != null && skin.length() > 0) {
PaperAdapter.ADAPTER.setSkullSkin(skull, skin);
}
}
PlayerBountiesHeadCompatibility.applySkullMetadata(skull, resultSet.getString("metadata"));
if (Database.hasSkullMetadataColumn()) {
PlayerBountiesHeadCompatibility.applySkullMetadata(skull, resultSet.getString("metadata"));
}
}
@@ -0,0 +1,271 @@
package net.coreprotect.extensions;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Skull;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
public final class PlayerBountiesHeadCompatibility {
private static final String TARGET_KEY = "bounty_head_target";
private static final String UUID_KEY = "bounty_head_target_uuid";
private static final String AMOUNT_KEY = "bounty_head_diamonds";
private static final String ITEM_MARKER = "playerbounties_head";
private static final String SKULL_MARKER = "pbh1";
private PlayerBountiesHeadCompatibility() {
throw new IllegalStateException("Utility class");
}
public static String serializeSkullMetadata(Skull skull) {
if (skull == null) {
return null;
}
SkullData skullData = readSkullData(skull.getPersistentDataContainer());
if (skullData == null) {
return null;
}
return String.join("|", SKULL_MARKER, encode(skullData.namespace), encode(skullData.targetName),
encode(skullData.targetUuid), Double.toString(skullData.bountyAmount));
}
public static void applySkullMetadata(Skull skull, String metadata) {
if (skull == null || metadata == null || metadata.isBlank()) {
return;
}
SkullData skullData = parseMetadata(metadata);
if (skullData == null) {
return;
}
applyPersistentData(skull.getPersistentDataContainer(), skullData);
}
public static void appendItemMetadata(ItemStack item, ItemMeta itemMeta, List<List<Map<String, Object>>> metadata) {
if (item == null || itemMeta == null || metadata == null || item.getType() != Material.PLAYER_HEAD) {
return;
}
SkullData skullData = readSkullData(itemMeta.getPersistentDataContainer());
if (skullData == null) {
return;
}
Map<String, Object> data = new HashMap<>();
data.put("plugin", ITEM_MARKER);
data.put("namespace", skullData.namespace);
data.put("target", skullData.targetName);
data.put("uuid", skullData.targetUuid);
data.put("amount", skullData.bountyAmount);
List<Map<String, Object>> list = new ArrayList<>();
list.add(data);
metadata.add(list);
}
public static boolean applyItemMetadata(ItemStack itemstack, Map<String, Object> mapData) {
if (itemstack == null || mapData == null || itemstack.getType() != Material.PLAYER_HEAD) {
return false;
}
Object marker = mapData.get("plugin");
if (!ITEM_MARKER.equals(marker)) {
return false;
}
String namespace = asString(mapData.get("namespace"));
String target = asString(mapData.get("target"));
String uuid = asString(mapData.get("uuid"));
Double amount = asDouble(mapData.get("amount"));
if (namespace == null || target == null || uuid == null || amount == null) {
return true;
}
ItemMeta itemMeta = itemstack.getItemMeta();
if (itemMeta == null) {
return true;
}
applyPersistentData(itemMeta.getPersistentDataContainer(), new SkullData(namespace, target, uuid, amount));
itemstack.setItemMeta(itemMeta);
return true;
}
private static SkullData readSkullData(PersistentDataContainer container) {
if (container == null) {
return null;
}
NamespacedKey targetKey = null;
NamespacedKey uuidKey = null;
NamespacedKey amountKey = null;
for (NamespacedKey key : container.getKeys()) {
if (TARGET_KEY.equals(key.getKey())) {
targetKey = key;
} else if (UUID_KEY.equals(key.getKey())) {
uuidKey = key;
} else if (AMOUNT_KEY.equals(key.getKey())) {
amountKey = key;
}
}
if (targetKey == null || uuidKey == null || amountKey == null) {
return null;
}
String target = container.get(targetKey, PersistentDataType.STRING);
String uuid = container.get(uuidKey, PersistentDataType.STRING);
Double amount = container.get(amountKey, PersistentDataType.DOUBLE);
if (target == null || uuid == null || amount == null) {
return null;
}
return new SkullData(targetKey.getNamespace(), target, uuid, amount);
}
private static void applyPersistentData(PersistentDataContainer container, SkullData skullData) {
NamespacedKey targetKey = new NamespacedKey(skullData.namespace, TARGET_KEY);
NamespacedKey uuidKey = new NamespacedKey(skullData.namespace, UUID_KEY);
NamespacedKey amountKey = new NamespacedKey(skullData.namespace, AMOUNT_KEY);
container.set(targetKey, PersistentDataType.STRING, skullData.targetName);
container.set(uuidKey, PersistentDataType.STRING, skullData.targetUuid);
container.set(amountKey, PersistentDataType.DOUBLE, skullData.bountyAmount);
}
private static SkullData parseMetadata(String metadata) {
String[] parts = metadata.split("\\|", -1);
if (parts.length != 5 || !SKULL_MARKER.equals(parts[0])) {
return null;
}
try {
return new SkullData(decode(parts[1]), decode(parts[2]), decode(parts[3]), Double.parseDouble(parts[4]));
} catch (Exception e) {
return null;
}
}
private static String encode(String value) {
return Base64.getEncoder().encodeToString(value.getBytes(StandardCharsets.UTF_8));
}
private static String decode(String value) {
return new String(Base64.getDecoder().decode(value), StandardCharsets.UTF_8);
}
private static String asString(Object value) {
if (value == null) {
return null;
}
return String.valueOf(value);
}
private static Double asDouble(Object value) {
if (value instanceof Number) {
return ((Number) value).doubleValue();
}
if (value == null) {
return null;
}
try {
return Double.parseDouble(String.valueOf(value));
} catch (NumberFormatException e) {
return null;
}
}
private record SkullData(String namespace, String targetName, String targetUuid, double bountyAmount) {
}
}