diff --git a/build.gradle.kts b/build.gradle.kts index 2010550..de28151 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -119,7 +119,7 @@ tasks { } register("cleanTests") { - delete(fileTree(mainDir).include("*.tmp.sqlite3")) + delete(fileTree(mainDir).include("tmp/*.tmp.sqlite3")) } test { @@ -131,7 +131,7 @@ tasks { "junit.jupiter.testinstance.lifecycle.default" to "per_class" ) - finalizedBy("cleanTests") +// finalizedBy("cleanTests") } clean { diff --git a/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqLiteTableMigrationManager.java b/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqLiteTableMigrationManager.java index c7369f0..aae25ff 100644 --- a/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqLiteTableMigrationManager.java +++ b/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqLiteTableMigrationManager.java @@ -25,7 +25,7 @@ CREATE TABLE IF NOT EXISTS player_data ( last_online_time INTEGER NOT NULL, alerts_enabled INTEGER NOT NULL, extra_max_claims INTEGER NOT NULL - ) STRICT + ) WITHOUT ROWID, STRICT """); // Chunk data table @@ -55,18 +55,12 @@ CREATE TABLE IF NOT EXISTS permission_flags ( flag_name TEXT NOT NULL, allow_deny INTEGER NOT NULL, - PRIMARY KEY(player_uuid, other_player_uuid, chunk_id, flag_name) + PRIMARY KEY(player_uuid, other_player_uuid, chunk_id, flag_name), FOREIGN KEY(player_uuid) REFERENCES player_data(player_uuid) - ON DELETE CASCADE, - FOREIGN KEY(other_player_uuid) - REFERENCES player_data(player_uuid) - ON DELETE CASCADE, - FOREIGN KEY(chunk_id) - REFERENCES chunk_data(chunk_id) ON DELETE CASCADE - ) STRICT + ) WITHOUT ROWID, STRICT """); } diff --git a/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqLiteWrapper.java b/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqLiteWrapper.java index 6ee2f1b..06f2afe 100644 --- a/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqLiteWrapper.java +++ b/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqLiteWrapper.java @@ -16,6 +16,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.SQLWarning; import java.util.*; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -253,7 +254,9 @@ public void setPermissionFlags( newFlags.values().stream() .map( ignored -> - "(?,?," + "(?," + + (accessor != null ? "?" : "''") + + "," + (chunk != null ? SELECT_CHUNK_ID_SQL : "-1") + ",?,?)") .collect(Collectors.joining(",")); @@ -282,15 +285,16 @@ ON CONFLICT ( int param = 1; for (Map.Entry entry : newFlags.entrySet()) { statement.setString(param++, owner.toString()); - statement.setString( - param++, accessor == null ? "" : accessor.toString()); + if (accessor != null) { + statement.setString(param++, accessor.toString()); + } if (chunk != null) { param = setChunkPosParams(statement, param, chunk); } statement.setString(param++, entry.getKey()); statement.setBoolean(param++, entry.getValue()); } - statement.execute(); + statement.executeUpdate(); return null; } }); @@ -304,25 +308,25 @@ public void clearPermissionFlags( String clauses = Arrays.stream(flagNames) .map(ignored -> "flag_name=?") - .collect(Collectors.joining(" OR ")); + .collect(Collectors.joining(" OR ", "(", ")")); String where = - "WHERE player_uuid=" + "WHERE player_uuid=? AND other_player_uuid=" + (accessor == null ? "''" : "?") + " AND chunk_id=" + (chunk == null ? "-1" : SELECT_CHUNK_ID_SQL) - + " AND (" - + clauses - + ")"; + + " AND " + + clauses; + + System.out.println(where); SqlClosure.sqlExecute( connection -> { try (PreparedStatement statement = - connection.prepareStatement( - chunkIdQuery("DELETE FROM permission_flags " + where))) { + connection.prepareStatement("DELETE FROM permission_flags " + where)) { int param = 1; statement.setString(param++, owner.toString()); if (accessor != null) { - statement.setString(param, accessor.toString()); + statement.setString(param++, accessor.toString()); } if (chunk != null) { param = setChunkPosParams(statement, param, chunk); @@ -330,7 +334,7 @@ public void clearPermissionFlags( for (String flagName : flagNames) { statement.setString(param++, flagName); } - statement.execute(); + statement.executeUpdate(); return null; } }); @@ -352,12 +356,13 @@ public List getAllPlayers() { try (PreparedStatement statement = connection.prepareStatement( """ -SELECT player_uuid, other_player_uuid, flag_name, allow_deny, chunk_id -FROM permission_flags -WHERE chunk_id=-1 -""")) { + SELECT * + FROM permission_flags + WHERE chunk_id=-1 + """)) { ResultSet resultSet = statement.executeQuery(); while (resultSet.next()) { + UUID playerUuid = UUID.fromString(resultSet.getString("player_uuid")); String otherPlyUuid = resultSet.getString("other_player_uuid"); String flagName = resultSet.getString("flag_name"); @@ -365,8 +370,7 @@ public List getAllPlayers() { FullPlayerData thisPly = playerData.get(playerUuid); if (thisPly == null) { - Utils.err("Failed to load player %s for permission", playerUuid); - continue; + throw new RuntimeException("Failed to load player " + playerUuid + " for permission " + flagName); } UUID otherPly = null; @@ -375,7 +379,7 @@ public List getAllPlayers() { } catch (Exception ignored) { } - if (otherPlyUuid != null) { + if (otherPly != null) { thisPly.playerFlags .computeIfAbsent(otherPly, ignored -> new HashMap<>()) .put(flagName, allowDeny); @@ -399,8 +403,7 @@ public Collection getAllChunks() { try (PreparedStatement statement = connection.prepareStatement( """ - SELECT chunk_world, chunk_x, chunk_z, owner_uuid - FROM chunk_data + SELECT * FROM chunk_data """)) { ResultSet resultSet = statement.executeQuery(); while (resultSet.next()) { @@ -418,13 +421,11 @@ public Collection getAllChunks() { try (PreparedStatement statement = connection.prepareStatement( """ -SELECT player_uuid, owner_uuid, other_player_uuid, - chunk_world, chunk_x, chunk_z, - flag_name, allow_deny -FROM permission_flags -LEFT JOIN chunk_data ON permission_flags.chunk_id=chunk_data.chunk_id -WHERE permission_flags.chunk_id!=-1 -""")) { + SELECT * FROM permission_flags + LEFT JOIN chunk_data + ON permission_flags.chunk_id=chunk_data.chunk_id + WHERE permission_flags.chunk_id!=-1 + """)) { ResultSet resultSet = statement.executeQuery(); while (resultSet.next()) { String playerUuid = resultSet.getString("player_uuid"); @@ -439,13 +440,9 @@ public Collection getAllChunks() { String chunkWorld = resultSet.getString("chunk_world"); int chunkX = resultSet.getInt("chunk_x"); int chunkZ = resultSet.getInt("chunk_z"); - ChunkPos chunkPos = - ignoredChunkOwnerUuid != null && chunkWorld != null - ? new ChunkPos(chunkWorld, chunkX, chunkZ) - : null; + ChunkPos chunkPos = new ChunkPos(chunkWorld, chunkX, chunkZ); // May be empty! String otherPlyUuid = resultSet.getString("other_player_uuid"); - // Never null String flagName = resultSet.getString("flag_name"); boolean allowDeny = resultSet.getBoolean("allow_deny"); diff --git a/src/test/java/com/cjburkey/claimchunk/TestSQLPlease.java b/src/test/java/com/cjburkey/claimchunk/TestSQLPlease.java index c2d16ee..ad7a484 100644 --- a/src/test/java/com/cjburkey/claimchunk/TestSQLPlease.java +++ b/src/test/java/com/cjburkey/claimchunk/TestSQLPlease.java @@ -7,9 +7,7 @@ import com.cjburkey.claimchunk.data.sqlite.SqLiteTableMigrationManager; import com.cjburkey.claimchunk.data.sqlite.SqLiteWrapper; import com.cjburkey.claimchunk.player.FullPlayerData; - import org.junit.jupiter.api.Test; - import java.io.File; import java.util.Collection; import java.util.HashMap; @@ -28,6 +26,8 @@ void ensureColumnExistsMethodWorks() { assertFalse(SqLiteTableMigrationManager.tableExists("bob_the_builder_no_we_cant")); assertFalse(SqLiteTableMigrationManager.columnExists("chunk_hell", "permission_bits")); assertFalse(SqLiteTableMigrationManager.columnExists("player_data", "fake_col")); + + } } @@ -55,27 +55,43 @@ void test0026BasicsWork() { wrapper.sql.addClaimedChunk(exampleChunk1); wrapper.sql.addClaimedChunk(exampleChunk2); - HashMap perms1 = new HashMap<>(); - perms1.put("doThis", true); - perms1.put("doThat", false); - wrapper.sql.setPermissionFlags(examplePlayer1.player, null, null, perms1); - - HashMap perms2 = new HashMap<>(); - perms2.put("dontDoThis", true); - perms2.put("dontDoThat", false); - perms2.put("dontAtAll", false); - wrapper.sql.setPermissionFlags( - examplePlayer1.player, null, exampleChunk2.chunk(), perms2); - - HashMap perms3 = new HashMap<>(); - perms3.put("alpha", true); - wrapper.sql.setPermissionFlags( - examplePlayer1.player, examplePlayer2.player, exampleChunk2.chunk(), perms3); - - HashMap perms4 = new HashMap<>(); - perms4.put("alAsphalt", false); - wrapper.sql.setPermissionFlags( - examplePlayer1.player, examplePlayer2.player, null, perms4); + { + HashMap p = new HashMap<>(); + p.put("doThis", true); + p.put("doThat", false); + p.put("doThatClear", true); + wrapper.sql.setPermissionFlags(examplePlayer1.player, null, null, p); + } + // Try to clear a flag + { + wrapper.sql.clearPermissionFlags( + examplePlayer1.player, null, null, "doThatClear"); + } + { + HashMap p = new HashMap<>(); + p.put("dontDoThat", true); + wrapper.sql.setPermissionFlags( + examplePlayer1.player, null, exampleChunk1.chunk(), p); + } + { + HashMap p = new HashMap<>(); + p.put("alpha", false); + wrapper.sql.setPermissionFlags( + examplePlayer1.player, examplePlayer2.player, exampleChunk1.chunk(), p); + } + { + HashMap p = new HashMap<>(); + p.put("alAsphalt", false); + wrapper.sql.setPermissionFlags( + examplePlayer1.player, examplePlayer2.player, null, p); + } + // Try setting an existing flag assignment + { + HashMap p = new HashMap<>(); + p.put("alAsphalt", true); + wrapper.sql.setPermissionFlags( + examplePlayer1.player, examplePlayer2.player, null, p); + } List loadedPlayers = wrapper.sql.getAllPlayers(); Collection loadedChunks = wrapper.sql.getAllChunks(); @@ -95,153 +111,20 @@ void test0026BasicsWork() { .get(); assertEquals(2, firstPly.globalFlags.size()); - assertEquals(false, firstPly.globalFlags.get("doThat")); - assertEquals(true, firstPly.globalFlags.get("doThis")); - assertEquals(false, firstPlysChunk.defaultFlags().get("dontDoThat")); - assertEquals( - true, firstPlysChunk.specificFlags().get(examplePlayer2.player).get("alpha")); - assertEquals(false, firstPly.playerFlags.get(examplePlayer2.player).get("alAsphalt")); - } - } - - // TODO: - /*@Test - void ensureNoDataLoss() { - try (TestQlWrap wrapper = new TestQlWrap()) { - // Add a random player - UUID ply1Uuid = UUID.randomUUID(); - UUID ply2Uuid = UUID.randomUUID(); - wrapper.sql.addPlayer( - new FullPlayerData( - ply1Uuid, "SomeGuysName", null, System.currentTimeMillis(), true, 0)); - wrapper.sql.addPlayer( - new FullPlayerData( - ply2Uuid, - "OtherPersonsName", - "queenshit", - System.currentTimeMillis(), - false, - 0)); - - // Make fake accessors and permissions - UUID accessorUuid1 = UUID.randomUUID(); - UUID accessorUuid2 = UUID.randomUUID(); - ChunkPlayerPermissions permissions1 = new ChunkPlayerPermissions(0b11111111); - ChunkPlayerPermissions permissions2 = new ChunkPlayerPermissions(0b10101101); - - // Add a chunk to the player and give the permissions to the other players - ChunkPos chunkPos = new ChunkPos("world", 10, -3); - DataChunk chunkData = new DataChunk(chunkPos, ply1Uuid); - chunkData.playerPermissions().put(accessorUuid1, permissions1); - chunkData.playerPermissions().put(accessorUuid2, permissions2); - wrapper.sql.addClaimedChunk(chunkData); - - // Make sure both players get loaded - Collection players = wrapper.sql.getAllPlayers(); - assertEquals(2, players.size()); - assert players.stream() - .allMatch(ply -> ply.player.equals(ply1Uuid) || ply.player.equals(ply2Uuid)); - assert players.stream().anyMatch(ply -> "queenshit".equals(ply.chunkName)); - - // Load the chunk after adding it - //noinspection deprecation - Collection loadedChunks = SqLiteWrapper.getAllChunks(); - DataChunk loadedChunk = loadedChunks.iterator().next(); - assertNotNull(loadedChunk); - - // Make sure the chunk exists when we load from the database - assert loadedChunk.player().equals(ply1Uuid) && loadedChunk.chunk().equals(chunkPos); - // Make sure the chunk permission got loaded correctly - assertEquals(permissions1, loadedChunk.playerPermissions().get(accessorUuid1)); - assertEquals(permissions2, loadedChunk.playerPermissions().get(accessorUuid2)); + // Cleared flag + assertFalse(firstPly.globalFlags.containsKey("doThatClear")); + assertTrue(firstPly.globalFlags.get("doThis")); + assertTrue(firstPlysChunk.defaultFlags().get("dontDoThat")); + assertFalse(firstPlysChunk.specificFlags().get(examplePlayer2.player).get("alpha")); + // Updated flag + assertTrue(firstPly.playerFlags.get(examplePlayer2.player).get("alAsphalt")); } } - @Test - void multiplePermissions() { - try (TestQlWrap wrapper = new TestQlWrap()) { - UUID owner = UUID.randomUUID(); - UUID accessor1 = UUID.randomUUID(); - UUID accessor2 = UUID.randomUUID(); - ChunkPos chunk = new ChunkPos("world", 824, -29); - DataChunk chunkData = new DataChunk(chunk, owner); - chunkData.playerPermissions().put(accessor1, new ChunkPlayerPermissions(0b01)); - chunkData.playerPermissions().put(accessor2, new ChunkPlayerPermissions(0b10)); - - // Add the players - wrapper.sql.addPlayer( - new FullPlayerData( - owner, "PersonHere", null, System.currentTimeMillis(), true, 0)); - wrapper.sql.addPlayer( - new FullPlayerData( - accessor1, "PersonThere", null, System.currentTimeMillis(), true, 0)); - wrapper.sql.addPlayer( - new FullPlayerData( - accessor2, "AnotherOne", null, System.currentTimeMillis(), true, 0)); - - // Add the chunk - wrapper.sql.addClaimedChunk(chunkData); - - // Load the chunk and make sure it contains both accessors - //noinspection deprecation - Map loadedPerms = - SqLiteWrapper.getAllChunks().iterator().next().playerPermissions(); - assert loadedPerms.containsKey(accessor1); - assert loadedPerms.containsKey(accessor2); - } - } - - @Test - void insertOrUpdatePermission() { - try (TestQlWrap wrapper = new TestQlWrap()) { - UUID owner = UUID.randomUUID(); - UUID accessor = UUID.randomUUID(); - ChunkPos chunk = new ChunkPos("world", 824, -29); - int flags1 = 0b10101001; - int flags2 = 0b01010100; - - // Add the players and the chunk - wrapper.sql.addPlayer( - new FullPlayerData( - owner, "PersonHere", null, System.currentTimeMillis(), true, 0)); - wrapper.sql.addPlayer( - new FullPlayerData( - accessor, "PersonThere", null, System.currentTimeMillis(), true, 0)); - wrapper.sql.addClaimedChunk(new DataChunk(chunk, owner)); - - // Insert the permission and check it - wrapper.sql.setPlayerAccess(chunk, accessor, flags1); - //noinspection deprecation - assertEquals( - flags1, - SqLiteWrapper.getAllChunks() - .iterator() - .next() - .playerPermissions() - .get(accessor) - .permissionFlags); - - // Update the permission and check it - wrapper.sql.setPlayerAccess(chunk, accessor, flags2); - //noinspection deprecation - assertEquals( - flags2, - SqLiteWrapper.getAllChunks() - .iterator() - .next() - .playerPermissions() - .get(accessor) - .permissionFlags); - - // Remove the permission and make sure there aren't any permissions now - wrapper.sql.removePlayerAccess(chunk, accessor); - //noinspection deprecation - assert SqLiteWrapper.getAllChunks().iterator().next().playerPermissions().isEmpty(); - } - }*/ - protected static File randomDbFile() { - return new File(UUID.randomUUID() + ".tmp.sqlite3"); + File dir = new File("tmp"); + var ignored = dir.mkdirs(); + return new File(dir, UUID.randomUUID() + ".tmp.sqlite3"); } static class TestQlWrap implements AutoCloseable {