diff --git a/common/src/main/java/com/daqem/grieflogger/database/Database.java b/common/src/main/java/com/daqem/grieflogger/database/Database.java index c412ffe..0f48cc2 100644 --- a/common/src/main/java/com/daqem/grieflogger/database/Database.java +++ b/common/src/main/java/com/daqem/grieflogger/database/Database.java @@ -24,11 +24,10 @@ public class Database { @Nullable private Connection connection; - @Nullable - private Statement statement; public final IQueue queue; public final IQueue batchQueue; private IDatabaseDialect dialect; + private final Object lock = new Object(); public Database() { queue = new Queue(this, false); @@ -46,12 +45,6 @@ public boolean createConnection() { } if (connection != null) { GriefLogger.LOGGER.info("Connected to database"); - try { - statement = connection.createStatement(); - } catch (SQLException e) { - GriefLogger.LOGGER.error("Failed to create statement", e); - return false; - } try { connection.setAutoCommit(false); } catch (SQLException e) { @@ -59,7 +52,7 @@ public boolean createConnection() { return false; } } - return connected && connection != null && statement != null; + return connected && connection != null; } public boolean createMysqlConnection() { @@ -98,7 +91,6 @@ public boolean createSqliteConnection() { path.toFile().mkdirs(); } try { - // Construct the JDBC URL with the full path to database.db String dbPath = path.resolve("database.db").toString(); connection = DriverManager.getConnection("jdbc:sqlite:" + dbPath); } catch (SQLException e) { @@ -109,23 +101,25 @@ public boolean createSqliteConnection() { } public void createTable(String sql) { - try { - if (statement != null) { - statement.execute(sql); - } - } catch (SQLException e) { - GriefLogger.LOGGER.error("Failed to create table", e); - } + execute(sql, true); } public void execute(String sql, boolean logError) { - try { - if (statement != null) { + if (connection == null) return; + + synchronized (lock) { + try (Statement statement = connection.createStatement()) { statement.execute(sql); - } - } catch (SQLException e) { - if (logError) { - GriefLogger.LOGGER.error("Failed to execute statement", e); + connection.commit(); + } catch (SQLException e) { + if (logError) { + GriefLogger.LOGGER.error("Failed to execute statement", e); + } + try { + connection.rollback(); + } catch (SQLException ex) { + GriefLogger.LOGGER.error("Failed to rollback", ex); + } } } } @@ -139,30 +133,41 @@ public PreparedStatement prepareStatement(String query) throws SQLException { } public void executeQueue(List items, boolean isBatch) { - try { - for (Object item : items) { - if (item instanceof PreparedStatement preparedStatement) { - if (preparedStatement.isClosed()) { - continue; - } - try (preparedStatement) { - if (isBatch) { - preparedStatement.executeBatch(); - } else { - preparedStatement.executeUpdate(); + if (connection == null) return; + + synchronized (lock) { + try { + for (Object item : items) { + if (item instanceof PreparedStatement preparedStatement) { + if (preparedStatement.isClosed()) { + continue; } + try (preparedStatement) { + if (isBatch) { + preparedStatement.executeBatch(); + } else { + preparedStatement.executeUpdate(); + } + } + } else if (item instanceof SqlTask task) { + task.execute(connection); } - } else if (item instanceof SqlTask task) { - task.execute(connection); } - } - if (!items.isEmpty()) { - if (connection != null) { - connection.commit(); + if (!items.isEmpty()) { + if (connection != null && !connection.isClosed()) { + connection.commit(); + } + } + } catch (SQLException e) { + GriefLogger.LOGGER.error("Failed to execute database queue", e); + try { + if (connection != null && !connection.isClosed()) { + connection.rollback(); + } + } catch (SQLException ex) { + GriefLogger.LOGGER.error("Failed to rollback transaction", ex); } } - } catch (SQLException e) { - GriefLogger.LOGGER.error("Failed to execute database queue", e); } } diff --git a/common/src/main/java/com/daqem/grieflogger/database/cache/UserCache.java b/common/src/main/java/com/daqem/grieflogger/database/cache/UserCache.java index 5ed8e57..b54f8da 100644 --- a/common/src/main/java/com/daqem/grieflogger/database/cache/UserCache.java +++ b/common/src/main/java/com/daqem/grieflogger/database/cache/UserCache.java @@ -15,7 +15,7 @@ public class UserCache implements ICache { private final Map usernames = new HashMap<>(); public Map getAllUsernames() { - if (usernameTime + 1000 < System.currentTimeMillis()) { + if (usernameTime + 300000 < System.currentTimeMillis()) { usernames.clear(); usernames.putAll(userService.getAllUsernames()); usernameTime = System.currentTimeMillis(); diff --git a/common/src/main/java/com/daqem/grieflogger/database/repository/ContainerRepository.java b/common/src/main/java/com/daqem/grieflogger/database/repository/ContainerRepository.java index 3b040ae..d33433e 100644 --- a/common/src/main/java/com/daqem/grieflogger/database/repository/ContainerRepository.java +++ b/common/src/main/java/com/daqem/grieflogger/database/repository/ContainerRepository.java @@ -1,5 +1,6 @@ package com.daqem.grieflogger.database.repository; +import java.io.ByteArrayInputStream; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -9,6 +10,10 @@ import java.util.Map; import com.daqem.grieflogger.database.dialect.MySQLDialect; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtAccounter; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtOps; import org.jetbrains.annotations.Nullable; import com.daqem.grieflogger.GriefLogger; @@ -19,10 +24,7 @@ import com.daqem.grieflogger.model.history.ContainerHistory; import com.daqem.grieflogger.model.history.IHistory; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import net.minecraft.core.component.DataComponentPatch; -import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.resources.Identifier; import net.minecraft.world.level.Level; @@ -34,7 +36,6 @@ public ContainerRepository(Database database) { this.database = database; } - // createTable / createIndexes omitted (same as original) public void createTable() { String sql = "CREATE TABLE IF NOT EXISTS containers (" + "time " + database.getDialect().getDataType("bigint") + " NOT NULL," + @@ -199,7 +200,20 @@ public void insertMap(long time, String userUuid, Level level, int x, int y, int }); } - // ... getHistory, getFilteredContainerHistory (read-only methods, same as original) ... + private DataComponentPatch readPatch(byte[] bytes, Level level) { + if (bytes == null || bytes.length == 0) { + return DataComponentPatch.EMPTY; + } + try { + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + CompoundTag compoundTag = NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap()); + return DataComponentPatch.CODEC.parse(level.registryAccess().createSerializationContext(NbtOps.INSTANCE), compoundTag) + .getOrThrow(IllegalStateException::new); + } catch (Exception e) { + return DataComponentPatch.EMPTY; + } + } + public List getHistory(Level level, int x, int y, int z) { List containerHistory = new ArrayList<>(); String query = """ @@ -222,9 +236,7 @@ public List getHistory(Level level, int x, int y, int z) { ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { - ByteBuf buf1 = Unpooled.wrappedBuffer(resultSet.getBytes(8)); - RegistryFriendlyByteBuf buf = new RegistryFriendlyByteBuf(buf1, level.registryAccess()); - DataComponentPatch patch = DataComponentPatch.STREAM_CODEC.decode(buf); + DataComponentPatch patch = readPatch(resultSet.getBytes(8), level); containerHistory.add(new ContainerHistory( resultSet.getLong(1), resultSet.getString(2), @@ -269,9 +281,7 @@ public List getHistory(Level level, int x, int y, int z, int x2, int y ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { - ByteBuf buf1 = Unpooled.wrappedBuffer(resultSet.getBytes(8)); - RegistryFriendlyByteBuf buf = new RegistryFriendlyByteBuf(buf1, level.registryAccess()); - DataComponentPatch patch = DataComponentPatch.STREAM_CODEC.decode(buf); + DataComponentPatch patch = readPatch(resultSet.getBytes(8), level); containerHistory.add(new ContainerHistory( resultSet.getLong(1), resultSet.getString(2), @@ -374,9 +384,7 @@ public List getFilteredContainerHistory(Level level, FilterList filter List blockHistory = new ArrayList<>(); ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { - ByteBuf buf1 = Unpooled.wrappedBuffer(resultSet.getBytes(8)); - RegistryFriendlyByteBuf buf = new RegistryFriendlyByteBuf(buf1, level.registryAccess()); - DataComponentPatch patch = DataComponentPatch.STREAM_CODEC.decode(buf); + DataComponentPatch patch = readPatch(resultSet.getBytes(8), level); blockHistory.add(new ContainerHistory( resultSet.getLong(1), resultSet.getString(2), diff --git a/common/src/main/java/com/daqem/grieflogger/database/repository/ItemRepository.java b/common/src/main/java/com/daqem/grieflogger/database/repository/ItemRepository.java index 9dc8b5c..665dabb 100644 --- a/common/src/main/java/com/daqem/grieflogger/database/repository/ItemRepository.java +++ b/common/src/main/java/com/daqem/grieflogger/database/repository/ItemRepository.java @@ -1,5 +1,6 @@ package com.daqem.grieflogger.database.repository; +import java.io.ByteArrayInputStream; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -9,6 +10,10 @@ import java.util.Map; import com.daqem.grieflogger.database.dialect.MySQLDialect; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtAccounter; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtOps; import org.jetbrains.annotations.Nullable; import com.daqem.grieflogger.GriefLogger; @@ -18,10 +23,7 @@ import com.daqem.grieflogger.model.action.ItemAction; import com.daqem.grieflogger.model.history.ItemHistory; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import net.minecraft.core.component.DataComponentPatch; -import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.resources.Identifier; import net.minecraft.world.level.Level; @@ -152,6 +154,20 @@ public void insertMap(long time, String userUuid, Level level, int x, int y, int }); } + private DataComponentPatch readPatch(byte[] bytes, Level level) { + if (bytes == null || bytes.length == 0) { + return DataComponentPatch.EMPTY; + } + try { + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + CompoundTag compoundTag = NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap()); + return DataComponentPatch.CODEC.parse(level.registryAccess().createSerializationContext(NbtOps.INSTANCE), compoundTag) + .getOrThrow(IllegalStateException::new); + } catch (Exception e) { + return DataComponentPatch.EMPTY; + } + } + public List getFilteredItemHistory(Level level, FilterList filterList) { @Nullable String actions = filterList.getActionString(); @Nullable String users = filterList.getUserString(); @@ -235,9 +251,7 @@ public List getFilteredItemHistory(Level level, FilterList filterLi List itemHistory = new ArrayList<>(); ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { - ByteBuf buf1 = Unpooled.wrappedBuffer(resultSet.getBytes(8)); - RegistryFriendlyByteBuf buf = new RegistryFriendlyByteBuf(buf1, level.registryAccess()); - DataComponentPatch patch = DataComponentPatch.STREAM_CODEC.decode(buf); + DataComponentPatch patch = readPatch(resultSet.getBytes(8), level); itemHistory.add(new ItemHistory( resultSet.getLong(1), resultSet.getString(2), diff --git a/common/src/main/java/com/daqem/grieflogger/database/repository/UserRepository.java b/common/src/main/java/com/daqem/grieflogger/database/repository/UserRepository.java index 71f2468..a9b3558 100644 --- a/common/src/main/java/com/daqem/grieflogger/database/repository/UserRepository.java +++ b/common/src/main/java/com/daqem/grieflogger/database/repository/UserRepository.java @@ -47,7 +47,7 @@ public void insertOrUpdateName(String name, String uuid) { } public void insertNonPlayer(String name) { - String query = "INSERT INTO users(name) VALUES('%s') " + + String query = "INSERT INTO users(name) VALUES(?) " + database.getDialect().getOnConflictDoNothing("name"); database.queue.add(connection -> { diff --git a/common/src/main/java/com/daqem/grieflogger/model/SimpleItemStack.java b/common/src/main/java/com/daqem/grieflogger/model/SimpleItemStack.java index e7c4424..836ea19 100644 --- a/common/src/main/java/com/daqem/grieflogger/model/SimpleItemStack.java +++ b/common/src/main/java/com/daqem/grieflogger/model/SimpleItemStack.java @@ -1,15 +1,19 @@ package com.daqem.grieflogger.model; -import io.netty.buffer.Unpooled; +import com.daqem.grieflogger.GriefLogger; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtOps; +import net.minecraft.nbt.Tag; import net.minecraft.resources.Identifier; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import org.jetbrains.annotations.Nullable; +import java.io.ByteArrayOutputStream; import java.util.Objects; public class SimpleItemStack { @@ -77,14 +81,20 @@ public void addCount(int count) { } public byte @Nullable [] getTagBytes(Level level) { - if (tag == null) { + if (tag == null || tag.isEmpty()) { + return null; + } + try { + Tag nbtTag = DataComponentPatch.CODEC.encodeStart(level.registryAccess().createSerializationContext(NbtOps.INSTANCE), tag) + .getOrThrow(IllegalStateException::new); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + NbtIo.writeCompressed((CompoundTag) nbtTag, outputStream); + return outputStream.toByteArray(); + } catch (Exception e) { + GriefLogger.LOGGER.error("Failed to serialize item components", e); return null; } - RegistryFriendlyByteBuf buf = new RegistryFriendlyByteBuf(Unpooled.buffer(), level.registryAccess()); - DataComponentPatch.STREAM_CODEC.encode(buf, tag); - byte[] temp = new byte[buf.readableBytes()]; - buf.readBytes(temp); - return temp; } public ItemStack toItemStack() { @@ -96,4 +106,4 @@ public ItemStack toItemStack() { public boolean isEmpty() { return item.equals(ItemStack.EMPTY.getItem()) || count == 0; } -} +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index a153dfa..6a29df5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ maven_group=com.daqem.grieflogger archives_base_name=grieflogger # Project -mod_version=19.0.3 +mod_version=19.0.4 mod_id=grieflogger mod_name=GriefLogger mod_description=A mod that logs all player interactions with blocks and entities and stores them in a database.