Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 85 additions & 29 deletions src/main/java/net/coreprotect/utility/BlockUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.bukkit.Material;
import org.bukkit.block.Banner;
Expand All @@ -21,59 +22,114 @@

import net.coreprotect.CoreProtect;
import net.coreprotect.bukkit.BukkitAdapter;
import net.coreprotect.config.ConfigHandler;
import net.coreprotect.model.PendingBlockChange;
import net.coreprotect.thread.Scheduler;

public class BlockUtils {

private static final int BLOCK_DATA_CACHE_LIMIT = 4096;
private static volatile BlockDataCache blockDataCache;

private BlockUtils() {
throw new IllegalStateException("Utility class");
}

public static byte[] stringToByteData(String string, int type) {
byte[] result = null;
if (string != null) {
Material material = MaterialUtils.getType(type);
String blockKey = MaterialUtils.getBlockName(type);
if ((blockKey == null || blockKey.length() == 0) && material != null) {
blockKey = material.getKey().toString();
}
if (blockKey == null || blockKey.length() == 0) {
return result;
}
if (string == null) {
return null;
}

BlockData defaultBlockData = createBlockData(type);
if (defaultBlockData != null && !defaultBlockData.getAsString().equals(string) && string.startsWith(blockKey + "[") && string.endsWith("]")) {
String substring = string.substring(blockKey.length() + 1, string.length() - 1);
String[] blockDataSplit = substring.split(",");
ArrayList<String> blockDataArray = new ArrayList<>();
for (String data : blockDataSplit) {
int id = MaterialUtils.getBlockdataId(data, true);
if (id > -1) {
blockDataArray.add(Integer.toString(id));
}
}
string = String.join(",", blockDataArray);
// ClickHouse uncaches identifiers when a batch is discarded, so a cached encoding could point at an unpublished id
Map<String, CachedBlockData> cache = ConfigHandler.databaseType.isClickHouse() ? null : blockDataCache();
if (cache != null) {
CachedBlockData cached = cache.get(string);
if (cached != null && cached.type == type) {
return cached.data;
}
else if (material != null && !string.contains(":") && (material == Material.PAINTING || BukkitAdapter.ADAPTER.isItemFrame(material))) {
int id = MaterialUtils.getBlockdataId(string, true);
}

byte[] result = null;
boolean resolved = true;
Material material = MaterialUtils.getType(type);
String blockKey = MaterialUtils.getBlockName(type);
if ((blockKey == null || blockKey.length() == 0) && material != null) {
blockKey = material.getKey().toString();
}
if (blockKey == null || blockKey.length() == 0) {
return null;
}

BlockData defaultBlockData = createBlockData(type);
if (defaultBlockData != null && !defaultBlockData.getAsString().equals(string) && string.startsWith(blockKey + "[") && string.endsWith("]")) {
String substring = string.substring(blockKey.length() + 1, string.length() - 1);
String[] blockDataSplit = substring.split(",");
ArrayList<String> blockDataArray = new ArrayList<>();
for (String data : blockDataSplit) {
int id = MaterialUtils.getBlockdataId(data, true);
if (id > -1) {
string = Integer.toString(id);
blockDataArray.add(Integer.toString(id));
}
else {
return result;
resolved = false;
}
}
result = String.join(",", blockDataArray).getBytes(StandardCharsets.UTF_8);
}
else if (material != null && !string.contains(":") && (material == Material.PAINTING || BukkitAdapter.ADAPTER.isItemFrame(material))) {
int id = MaterialUtils.getBlockdataId(string, true);
if (id > -1) {
result = Integer.toString(id).getBytes(StandardCharsets.UTF_8);
}
else {
return result;
resolved = false;
}

result = string.getBytes(StandardCharsets.UTF_8);
}

if (cache != null && resolved) {
if (cache.size() >= BLOCK_DATA_CACHE_LIMIT) {
cache.clear();
}
cache.put(string, new CachedBlockData(type, result));
}
return result;
}

// Loading the material or blockdata maps assigns new map instances, which starts a new cache
private static Map<String, CachedBlockData> blockDataCache() {
BlockDataCache cache = blockDataCache;
Map<Integer, String> materials = ConfigHandler.materialsReversed;
Map<String, Integer> blockdata = ConfigHandler.blockdata;
if (cache == null || cache.materials != materials || cache.blockdata != blockdata) {
cache = new BlockDataCache(materials, blockdata);
blockDataCache = cache;
}
return cache.entries;
}

private static final class BlockDataCache {

private final Map<Integer, String> materials;
private final Map<String, Integer> blockdata;
private final Map<String, CachedBlockData> entries = new ConcurrentHashMap<>();

private BlockDataCache(Map<Integer, String> materials, Map<String, Integer> blockdata) {
this.materials = materials;
this.blockdata = blockdata;
}
}

private static final class CachedBlockData {

private final int type;
private final byte[] data;

private CachedBlockData(int type, byte[] data) {
this.type = type;
this.data = data;
}
}

public static String byteDataToString(byte[] data, int type) {
String result = "";
if (data != null) {
Expand Down
14 changes: 13 additions & 1 deletion src/main/java/net/coreprotect/utility/MaterialUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,24 @@ public static Material getType(String name) {
}

name = net.coreprotect.bukkit.BukkitAdapter.ADAPTER.parseLegacyName(name);
material = Material.matchMaterial(name);
material = isEnumName(name) ? Material.getMaterial(name) : Material.matchMaterial(name);
}

return material;
}

// matchMaterial only uppercases and strips whitespace and non-word characters before getMaterial, none of which changes an A-Z, 0-9 and underscore name
private static boolean isEnumName(String name) {
for (int index = 0; index < name.length(); index++) {
char character = name.charAt(index);
if ((character < 'A' || character > 'Z') && (character < '0' || character > '9') && character != '_') {
return false;
}
}

return !name.isEmpty();
}

public static int getArtId(String name, boolean internal) {
int id = -1;
name = name.toLowerCase(Locale.ROOT).trim();
Expand Down
Loading