Skip to content

Commit 078adf5

Browse files
committed
Improved entity serialization
1 parent 49262d9 commit 078adf5

9 files changed

Lines changed: 721 additions & 488 deletions

File tree

src/main/java/net/coreprotect/database/DuckDBDatabase.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ static void createTables(String prefix, Connection forceConnection, boolean purg
8585
statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "entity_interaction (" + rowId(prefix, "entity_interaction") + ", time INTEGER, \"user\" INTEGER, entity_spawn_rowid INTEGER NOT NULL, wid INTEGER, x INTEGER, y INTEGER, z INTEGER, type INTEGER, action TINYINT, metadata BLOB, rolled_back TINYINT)");
8686
statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "item (" + rowId(prefix, "item") + ", time INTEGER, \"user\" INTEGER, wid INTEGER, x INTEGER, y INTEGER, z INTEGER, type INTEGER, data BLOB, amount INTEGER, action TINYINT, rolled_back TINYINT)");
8787
statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "database_lock (rowid INTEGER PRIMARY KEY, status TINYINT, time INTEGER)");
88-
statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "entity (" + rowId(prefix, "entity") + ", time INTEGER, data VARCHAR)");
89-
statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "entity_spawn (" + rowId(prefix, "entity_spawn") + ", time INTEGER, block_rowid BIGINT, kill_rowid INTEGER, uuid VARCHAR UNIQUE, wid INTEGER, current_wid INTEGER, origin_x DOUBLE, origin_y DOUBLE, origin_z DOUBLE, x DOUBLE, y DOUBLE, z DOUBLE, yaw FLOAT, pitch FLOAT, data VARCHAR, removed TINYINT, UNIQUE(kill_rowid))");
88+
statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "entity (" + rowId(prefix, "entity") + ", time INTEGER, data BLOB)");
89+
statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "entity_spawn (" + rowId(prefix, "entity_spawn") + ", time INTEGER, block_rowid BIGINT, kill_rowid INTEGER, uuid VARCHAR UNIQUE, wid INTEGER, current_wid INTEGER, origin_x DOUBLE, origin_y DOUBLE, origin_z DOUBLE, x DOUBLE, y DOUBLE, z DOUBLE, yaw FLOAT, pitch FLOAT, data BLOB, removed TINYINT, UNIQUE(kill_rowid))");
9090
statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "entity_map (" + rowId(prefix, "entity_map") + ", id INTEGER, entity VARCHAR)");
9191
statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "material_map (" + rowId(prefix, "material_map") + ", id INTEGER, material VARCHAR)");
9292
statement.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix + "blockdata_map (" + rowId(prefix, "blockdata_map") + ", id INTEGER, data VARCHAR)");
@@ -129,8 +129,8 @@ private static void validateEntityDataColumns(Connection connection, String pref
129129
}
130130
for (String table : new String[] { entityTable, entitySpawnTable }) {
131131
String type = types.get(table);
132-
if (!"VARCHAR".equalsIgnoreCase(type)) {
133-
throw new SQLException("Unsupported DuckDB " + table + ".data format: " + (type == null ? "missing" : type) + " (expected VARCHAR)");
132+
if (!"BLOB".equalsIgnoreCase(type)) {
133+
throw new SQLException("Unsupported DuckDB " + table + ".data format: " + (type == null ? "missing" : type) + " (expected BLOB)");
134134
}
135135
}
136136
}

src/main/java/net/coreprotect/database/RelationalConsumerWriteBatch.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import net.coreprotect.config.ConfigHandler;
1919
import net.coreprotect.database.statement.EntitySpawnStatement;
2020
import net.coreprotect.utility.ErrorReporter;
21-
import net.coreprotect.utility.serialize.EntityDataCodec;
2221

2322
public final class RelationalConsumerWriteBatch implements ConsumerWriteBatch {
2423

@@ -612,10 +611,10 @@ private PreparedStatement entitySpawnStatement() throws SQLException {
612611

613612
private static void setDuckDBEntityData(PreparedStatement statement, int index, byte[] data) throws SQLException {
614613
if (data == null) {
615-
statement.setNull(index, Types.VARCHAR);
614+
statement.setNull(index, Types.BLOB);
616615
}
617616
else {
618-
statement.setString(index, EntityDataCodec.toText(data));
617+
statement.setBytes(index, data);
619618
}
620619
}
621620

src/main/java/net/coreprotect/database/clickhouse/ClickHouseEntitySpawnUpdates.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@
2626
import net.coreprotect.model.entity.EntityContainerRollbackUpdate;
2727
import net.coreprotect.model.entity.EntitySpawnData;
2828
import net.coreprotect.model.entity.EntitySpawnIdentity;
29+
import net.coreprotect.utility.DatabaseUtils;
2930
import net.coreprotect.utility.ErrorReporter;
3031
import net.coreprotect.utility.WorldUtils;
31-
import net.coreprotect.utility.serialize.EntityDataCodec;
3232

3333
final class ClickHouseEntitySpawnUpdates implements ConsumerEntitySpawnUpdates {
3434

3535
private static final int SELECT_BATCH_SIZE = 500;
3636
private static final String COLUMNS = "rowid,if(block_rowid_present=1,block_rowid,NULL) AS block_rowid,if(kill_rowid_present=1,kill_rowid,NULL) AS kill_rowid,uuid,current_wid,current_x,current_y,current_z,yaw,pitch,"
37-
+ "if(entity_data_present=1,entity_data,NULL) AS data"
37+
+ ClickHouseSchema.binary("if(entity_data_present=1,entity_data,NULL)", "data")
3838
+ ",removed,producer_id,producer_sequence,batch_ordinal,time,wid,x AS key_x,z AS key_z";
3939

4040
private final ClickHouseConsumerWriteBatch owner;
@@ -535,8 +535,7 @@ private static void rejectDifferentOwner(ClickHouseEntityState owner, Integer al
535535
private ClickHouseEntityState readState(ResultSet resultSet) throws Exception {
536536
int rowId = resultSet.getInt("rowid");
537537
ClickHouseEventPointer pointer = new ClickHouseEventPointer(datasetId, ClickHouseFamily.ENTITY_SPAWN, UUID.fromString(resultSet.getString("producer_id")), resultSet.getLong("producer_sequence"), resultSet.getInt("batch_ordinal"), rowId, resultSet.getInt("time"), resultSet.getInt("wid"), resultSet.getInt("key_x"), resultSet.getInt("key_z"));
538-
String text = resultSet.getString("data");
539-
byte[] data = text == null ? null : EntityDataCodec.fromText(text);
538+
byte[] data = DatabaseUtils.getBytes(resultSet, "data");
540539
return new ClickHouseEntityState(pointer, nullableLong(resultSet, "block_rowid"), nullableInteger(resultSet, "kill_rowid"), UUID.fromString(resultSet.getString("uuid")), resultSet.getInt("current_wid"), resultSet.getDouble("current_x"), resultSet.getDouble("current_y"), resultSet.getDouble("current_z"), resultSet.getFloat("yaw"), resultSet.getFloat("pitch"), data, resultSet.getInt("removed") == 1);
541540
}
542541

src/main/java/net/coreprotect/database/clickhouse/ClickHouseEventBatch.java

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public long addDatabaseLockVersion(long rowId, int time, int status) throws SQLE
106106

107107
public long addEntity(int time, byte[] data) throws SQLException {
108108
long rowId = beginRow(ClickHouseFamily.ENTITY, time);
109-
setEntityText("payload", data);
109+
setEntityData("payload", data);
110110
commitRow(ClickHouseFamily.ENTITY, rowId);
111111
return rowId;
112112
}
@@ -130,7 +130,7 @@ public long addEntitySpawn(int time, Long blockRowId, Integer killRowId, UUID uu
130130
set("current_z", currentZ);
131131
set("yaw", yaw);
132132
set("pitch", pitch);
133-
setEntityText("entity_data", data);
133+
setEntityData("entity_data", data);
134134
set("entity_data_present", data == null ? 0 : 1);
135135
set("removed", removed);
136136
commitRow(ClickHouseFamily.ENTITY_SPAWN, rowId);
@@ -249,7 +249,7 @@ public void addCompatibilityRow(ClickHouseFamily family, long rowId, Map<String,
249249
}
250250
Object value = entry.getValue();
251251
if (canonicalColumn.equals("data") && (family == ClickHouseFamily.ENTITY || family == ClickHouseFamily.ENTITY_SPAWN)) {
252-
value = entityText(value);
252+
value = entityData(value);
253253
}
254254
String physicalColumn = compatibilityColumn(family, canonicalColumn);
255255
set(physicalColumn, value);
@@ -400,18 +400,24 @@ private void setBinary(String column, byte[] value) {
400400
set(column, value);
401401
}
402402

403-
private void setEntityText(String column, byte[] value) {
404-
set(column, value == null ? null : EntityDataCodec.toText(value));
403+
private void setEntityData(String column, byte[] value) {
404+
if (value != null && !EntityDataCodec.isEncoded(value)) {
405+
throw new IllegalArgumentException("Entity data does not use the CoreProtect binary format");
406+
}
407+
set(column, value);
405408
}
406409

407-
private static Object entityText(Object value) {
408-
if (value instanceof byte[]) {
409-
return EntityDataCodec.toText((byte[]) value);
410+
private static Object entityData(Object value) {
411+
if (value == null) {
412+
return null;
410413
}
411-
if (value instanceof String) {
412-
EntityDataCodec.fromText((String) value);
414+
if (value instanceof byte[]) {
415+
if (!EntityDataCodec.isEncoded((byte[]) value)) {
416+
throw new IllegalArgumentException("Entity data does not use the CoreProtect binary format");
417+
}
418+
return value;
413419
}
414-
return value;
420+
throw new IllegalArgumentException("ClickHouse entity data must be binary");
415421
}
416422

417423
private void set(String column, Object value) {

src/main/java/net/coreprotect/database/clickhouse/ClickHouseSchema.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ private static void addCompatibilityViews(List<String> statements, Names names)
285285
statements.add(view(names, ClickHouseFamily.ENTITY_INTERACTION, "e.rowid AS rowid,e.time AS time,e.user_id AS `user`,e.entity_spawn_rowid AS entity_spawn_rowid," + location("wid") + "," + location("x") + ",e.y AS y," + location("z") + ",e.type AS type,e.action AS action," + binary("e.metadata", "metadata") + ",e.rolled_back AS rolled_back"));
286286
statements.add(rollbackView(names, ClickHouseFamily.ITEM, "e.rowid AS rowid,e.time AS time,e.user_id AS `user`," + location("wid") + "," + location("x") + ",e.y AS y," + location("z") + ",e.type AS type," + binary("e.payload", "data") + ",e.amount AS amount,e.action AS action"));
287287
statements.add(currentView(names, ClickHouseFamily.DATABASE_LOCK, "e.rowid AS rowid,e.status AS status,e.database_lock_time AS time"));
288-
statements.add(view(names, ClickHouseFamily.ENTITY, "e.rowid AS rowid,e.time AS time,e.payload AS data"));
288+
statements.add(view(names, ClickHouseFamily.ENTITY, "e.rowid AS rowid,e.time AS time," + binary("e.payload", "data")));
289289
statements.add(entitySpawnView(names));
290290
statements.add(view(names, ClickHouseFamily.ENTITY_MAP, "e.rowid AS rowid,e.id AS id,e.name AS entity"));
291291
statements.add(view(names, ClickHouseFamily.MATERIAL_MAP, "e.rowid AS rowid,e.id AS id,e.name AS material"));
@@ -323,7 +323,7 @@ private static String entitySpawnView(Names names) {
323323
+ ",e.uuid AS uuid," + location("wid") + ",e.current_wid AS current_wid"
324324
+ ",e.origin_x AS origin_x,e.origin_y AS origin_y,e.origin_z AS origin_z"
325325
+ ",e.current_x AS x,e.current_y AS y,e.current_z AS z"
326-
+ ",e.yaw AS yaw,e.pitch AS pitch,if(e.entity_data_present=1,e.entity_data,NULL) AS data,e.removed AS removed"
326+
+ ",e.yaw AS yaw,e.pitch AS pitch," + binary("if(e.entity_data_present=1,e.entity_data,NULL)", "data") + ",e.removed AS removed"
327327
+ " FROM " + currentEvents(names, ClickHouseFamily.ENTITY_SPAWN) + " AS e";
328328
}
329329

src/main/java/net/coreprotect/database/clickhouse/ClickHouseStateBatch.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
import java.util.Map;
77
import java.util.Objects;
88

9-
import net.coreprotect.utility.serialize.EntityDataCodec;
10-
119
final class ClickHouseStateBatch implements AutoCloseable {
1210

1311
private final ClickHouseBatchIdentity identity;
@@ -77,7 +75,7 @@ void appendTo(ClickHouseRowBinaryBuffer rows, int firstOrdinal) throws SQLExcept
7775
rows.set("yaw", state.getYaw());
7876
rows.set("pitch", state.getPitch());
7977
byte[] data = state.getData();
80-
rows.set("entity_data", data == null ? null : EntityDataCodec.toText(data));
78+
rows.set("entity_data", data);
8179
rows.set("entity_data_present", data == null ? 0 : 1);
8280
rows.set("removed", state.isRemoved() ? 1 : 0);
8381
rows.commitRow("entity state update");

src/main/java/net/coreprotect/database/statement/EntitySpawnStatement.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import net.coreprotect.utility.ErrorReporter;
3737
import net.coreprotect.utility.EntitySpawnTracking;
3838
import net.coreprotect.utility.WorldUtils;
39-
import net.coreprotect.utility.serialize.EntityDataCodec;
4039
import net.coreprotect.utility.serialize.EntityDataCodec.Kind;
4140

4241
public final class EntitySpawnStatement {
@@ -672,10 +671,7 @@ private void setLocation(PreparedStatement statement, Location value, int offset
672671

673672
private void setNullableData(PreparedStatement statement, int index, byte[] value) throws Exception {
674673
if (value == null) {
675-
statement.setNull(index, databaseType.isDuckDB() ? Types.VARCHAR : Types.BLOB);
676-
}
677-
else if (databaseType.isDuckDB()) {
678-
statement.setString(index, EntityDataCodec.toText(value));
674+
statement.setNull(index, Types.BLOB);
679675
}
680676
else {
681677
statement.setBytes(index, value);

src/main/java/net/coreprotect/database/statement/EntityStatement.java

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -208,15 +208,7 @@ public static List<Object> deserializeData(byte[] data, Kind kind) {
208208
}
209209

210210
public static List<Object> readData(ResultSet resultSet, String column, Kind kind) throws SQLException {
211-
byte[] data;
212-
if (ConfigHandler.databaseType.isColumnar()) {
213-
String text = resultSet.getString(column);
214-
data = text == null ? null : EntityDataCodec.fromText(text);
215-
}
216-
else {
217-
data = DatabaseUtils.getBytes(resultSet, column);
218-
}
219-
return deserializeData(data, kind);
211+
return deserializeData(DatabaseUtils.getBytes(resultSet, column), kind);
220212
}
221213

222214
private static List<Object> deserializeDataStrict(byte[] data, Kind kind) throws Exception {

0 commit comments

Comments
 (0)