diff --git a/.gitignore b/.gitignore index e8f169b..8656ef4 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,7 @@ OUT # Gource my_captions.txt source_visual.ppm -source_visual.mp4 \ No newline at end of file +source_visual.mp4 + +# Leftover from tests +*.tmp.sqlite3 diff --git a/src/main/java/com/cjburkey/claimchunk/ClaimChunk.java b/src/main/java/com/cjburkey/claimchunk/ClaimChunk.java index 2bcb641..3a50b36 100644 --- a/src/main/java/com/cjburkey/claimchunk/ClaimChunk.java +++ b/src/main/java/com/cjburkey/claimchunk/ClaimChunk.java @@ -393,28 +393,22 @@ private boolean initDataHandler() { : createJsonDataHandler(); }*/ if (dataHandler == null) { - File sqliteFile = new File(getDataFolder(), "/data/claimAndPlayerData.sqlite3"); + File dataFolder = new File(getDataFolder(), "/data"); + File sqliteFile = new File(dataFolder, "/claimAndPlayerData.sqlite3"); + File oldClaimedFile = new File(dataFolder, "/claimedChunks.json"); + File oldPlayerFile = new File(dataFolder, "/playerData.json"); + boolean oldUseDb = config.getUseDatabase(); IClaimChunkDataHandler oldDataHandler = null; - // UGLY HACK! - // RANKS ARE INITIALIZED AFTER DATA HANDLER, SO IF RANK FILE - // DOESN'T EXIST, WE CAN ASSUME THIS IS A NEW INSTALL RATHER THAN - // CONVERSION! - // AFTER 0.0.25 releases, 0.0.26 doesn't need to include this (but - // WILL require players to install 0.0.25 FIRST to upgrade from - // pre-0.0.25 if, say, 0.0.26 comes out while they're on 0.0.24). - if (!sqliteFile.exists() && new File(getDataFolder(), "/ranks.json").exists()) { + if (!sqliteFile.exists() + && (oldUseDb || (oldClaimedFile.exists() && oldPlayerFile.exists()))) { oldDataHandler = - (config.getUseDatabase()) + oldUseDb ? ((config.getGroupRequests()) ? new BulkMySQLDataHandler<>( - this, - this::createJsonDataHandler, - ignored -> {} /*JsonDataHandler::deleteFiles*/) + this, this::createJsonDataHandler, ignored -> {}) : new MySQLDataHandler<>( - this, - this::createJsonDataHandler, - ignored -> {} /*JsonDataHandler::deleteFiles*/)) + this, this::createJsonDataHandler, ignored -> {})) : createJsonDataHandler(); } @@ -425,9 +419,6 @@ private boolean initDataHandler() { IDataConverter.copyConvert(oldDataHandler, dataHandler); oldDataHandler.exit(); - File dataFolder = new File(getDataFolder(), "/data"); - File oldClaimedFile = new File(dataFolder, "/claimedChunks.json"); - File oldPlayerFile = new File(dataFolder, "/playerData.json"); if (oldClaimedFile.exists()) { Files.move( oldClaimedFile.toPath(), @@ -435,12 +426,12 @@ private boolean initDataHandler() { } if (oldPlayerFile.exists()) { Files.move( - oldClaimedFile.toPath(), + oldPlayerFile.toPath(), new File(dataFolder, "/OLD_playerData.json").toPath()); } } catch (Exception e) { throw new RuntimeException( - "Failed to initialize previous data handler to convert old data!"); + "Failed to initialize previous data handler to convert old data!", e); } } } diff --git a/src/main/java/com/cjburkey/claimchunk/chunk/ChunkPlayerPermissions.java b/src/main/java/com/cjburkey/claimchunk/chunk/ChunkPlayerPermissions.java index 7d75b08..506db81 100644 --- a/src/main/java/com/cjburkey/claimchunk/chunk/ChunkPlayerPermissions.java +++ b/src/main/java/com/cjburkey/claimchunk/chunk/ChunkPlayerPermissions.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Objects; public class ChunkPlayerPermissions { @@ -141,4 +142,17 @@ public Map toPermissionsMap() { return chunkPlayerPermissions; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ChunkPlayerPermissions that = (ChunkPlayerPermissions) o; + return permissionFlags == that.permissionFlags; + } + + @Override + public int hashCode() { + return Objects.hash(permissionFlags); + } } 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 cf907c4..915ae51 100644 --- a/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqLiteTableMigrationManager.java +++ b/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqLiteTableMigrationManager.java @@ -60,7 +60,6 @@ FOREIGN KEY(other_player_uuid) REFERENCES player_data(player_uuid) } // Use this method to determine if a column exists in a table to perform migrations - // TODO: MAYBE CHECK IF THIS WORKS @SuppressWarnings("unused") public static boolean columnExists(String tableName, String columnName) { return SqlClosure.sqlExecute( 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 e81c93f..b9f7716 100644 --- a/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqLiteWrapper.java +++ b/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqLiteWrapper.java @@ -15,10 +15,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.UUID; +import java.util.*; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -68,6 +65,7 @@ public void close() { public void addClaimedChunk(DataChunk chunk) { SqlClosure.sqlExecute( connection -> { + // Add the chunk try (PreparedStatement statement = connection.prepareStatement( """ @@ -83,8 +81,45 @@ INSERT INTO chunk_data ( int next = setChunkPosParams(statement, 1, chunk.chunk); statement.setString(next, chunk.player.toString()); statement.execute(); - return null; } + + // Add the player permissions + if (!chunk.playerPermissions.isEmpty()) { + String permsInsertPrefixSql = + """ + INSERT INTO chunk_permissions ( + chunk_id, + other_player_uuid, + permission_bits + ) VALUES + """; + + // Better way to do this? + String permsValsSql = + chunk.playerPermissions.entrySet().stream() + .map( + ignored -> + replaceChunkIdQuery( + """ + (%%SELECT_CHUNK_ID_SQL%%, ?, ?) + """)) + .collect(Collectors.joining(",")); + + try (PreparedStatement statement = + connection.prepareStatement(permsInsertPrefixSql + permsValsSql)) { + int currentParam = 1; + for (Map.Entry entry : + chunk.playerPermissions.entrySet()) { + currentParam = + setChunkPosParams(statement, currentParam, chunk.chunk); + statement.setString(currentParam++, entry.getKey().toString()); + statement.setInt(currentParam++, entry.getValue().permissionFlags); + } + statement.execute(); + } + } + + return null; }); } @@ -327,10 +362,10 @@ public Collection getAllChunks() { String otherUuid = resultSet.getString("other_player_uuid"); if (otherUuid != null) { - UUID otherPlayer = - UUID.fromString(otherUuid); + UUID otherPlayer = UUID.fromString(otherUuid); ChunkPlayerPermissions chunkPerms = - new ChunkPlayerPermissions(resultSet.getInt("permission_bits")); + new ChunkPlayerPermissions( + resultSet.getInt("permission_bits")); permissions .computeIfAbsent(pos, ignoredPos -> new HashMap<>()) diff --git a/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqlDataChunk.java b/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqlDataChunk.java index 585d73d..302bc4d 100644 --- a/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqlDataChunk.java +++ b/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqlDataChunk.java @@ -1,7 +1,5 @@ package com.cjburkey.claimchunk.data.sqlite; -import com.cjburkey.claimchunk.chunk.DataChunk; - import javax.persistence.*; @Table(name = "chunk_data") @@ -26,11 +24,4 @@ public class SqlDataChunk { @SuppressWarnings("unused") public SqlDataChunk() {} - - public SqlDataChunk(DataChunk chunk) { - this.world = chunk.chunk.world(); - this.x = chunk.chunk.x(); - this.z = chunk.chunk.z(); - this.uuid = chunk.player.toString(); - } } diff --git a/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqlDataChunkPermission.java b/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqlDataChunkPermission.java deleted file mode 100644 index a11aa49..0000000 --- a/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqlDataChunkPermission.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.cjburkey.claimchunk.data.sqlite; - -import com.cjburkey.claimchunk.chunk.ChunkPlayerPermissions; - -import java.util.UUID; - -import javax.persistence.*; - -@Table(name = "chunk_permissions") -public class SqlDataChunkPermission { - - @Id - @Column(name = "perm_id") - @GeneratedValue(strategy = GenerationType.IDENTITY) - public int permissionId; - - @Column(name = "chunk_id") - public int chunkId; - - @Column(name = "other_player_uuid") - public String otherPlayerUuid; - - @Column(name = "permission_bits") - public int permissionBits; - - @SuppressWarnings("unused") - public SqlDataChunkPermission() {} - - public SqlDataChunkPermission(UUID otherPlayer, ChunkPlayerPermissions permissions) { - this.chunkId = -1; - this.otherPlayerUuid = otherPlayer.toString(); - this.permissionBits = permissions.permissionFlags; - } -} diff --git a/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqlDataPlayer.java b/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqlDataPlayer.java index c8bd299..14df833 100644 --- a/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqlDataPlayer.java +++ b/src/main/java/com/cjburkey/claimchunk/data/sqlite/SqlDataPlayer.java @@ -1,7 +1,5 @@ package com.cjburkey.claimchunk.data.sqlite; -import com.cjburkey.claimchunk.player.FullPlayerData; - import javax.persistence.Column; import javax.persistence.Id; import javax.persistence.Table; @@ -30,13 +28,4 @@ public class SqlDataPlayer { @SuppressWarnings("unused") public SqlDataPlayer() {} - - public SqlDataPlayer(FullPlayerData player) { - this.uuid = player.player.toString(); - this.lastIgn = player.lastIgn; - this.chunkName = player.chunkName; - this.lastOnlineTime = player.lastOnlineTime; - this.alert = player.alert; - this.extraMaxClaims = player.extraMaxClaims; - } } diff --git a/src/test/java/com/cjburkey/claimchunk/TestSQLPlease.java b/src/test/java/com/cjburkey/claimchunk/TestSQLPlease.java index 758843e..a6d83a4 100644 --- a/src/test/java/com/cjburkey/claimchunk/TestSQLPlease.java +++ b/src/test/java/com/cjburkey/claimchunk/TestSQLPlease.java @@ -1,5 +1,6 @@ package com.cjburkey.claimchunk; +import com.cjburkey.claimchunk.chunk.ChunkPlayerPermissions; import com.cjburkey.claimchunk.chunk.ChunkPos; import com.cjburkey.claimchunk.chunk.DataChunk; import com.cjburkey.claimchunk.data.sqlite.SqLiteTableMigrationManager; @@ -9,42 +10,64 @@ import org.junit.jupiter.api.Test; import java.io.File; +import java.util.Collection; import java.util.HashMap; +import java.util.Objects; import java.util.UUID; class TestSQLPlease { @Test - void ensureSomeColumnsExistsAfterInitializing() { + void ensureNoDataLoss() { File dbFile = randomDbFile(); + dbFile.deleteOnExit(); try (SqLiteWrapper ignoredWrapper = new SqLiteWrapper(dbFile, false)) { - // Make sure that instantiating SqLiteWrapper created the tables - assert SqLiteTableMigrationManager.columnExists("player_data", "player_uuid"); - assert SqLiteTableMigrationManager.columnExists("chunk_data", "owner_uuid"); - assert SqLiteTableMigrationManager.columnExists("chunk_permissions", "permission_bits"); - assert !SqLiteTableMigrationManager.columnExists("chunk_hell", "permission_bits"); - assert !SqLiteTableMigrationManager.columnExists("player_data", "fake_col"); - // Add a random player UUID plyUuid = UUID.randomUUID(); ignoredWrapper.addPlayer( new FullPlayerData( plyUuid, "SomeGuysName", null, System.currentTimeMillis(), true, 0)); - // Add a chunk to the player + // 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); - ignoredWrapper.addClaimedChunk( - new DataChunk(chunkPos, plyUuid, new HashMap<>(), false)); + DataChunk chunkData = new DataChunk(chunkPos, plyUuid, new HashMap<>(), false); + chunkData.playerPermissions.put(accessorUuid1, permissions1); + chunkData.playerPermissions.put(accessorUuid2, permissions2); + ignoredWrapper.addClaimedChunk(chunkData); + + // Load the chunk after adding it + Collection loadedChunks = ignoredWrapper.getAllChunks(); + DataChunk loadedChunk = loadedChunks.iterator().next(); + Objects.requireNonNull(loadedChunk); // Make sure the chunk exists when we load from the database - assert ignoredWrapper.getAllChunks().stream() - .anyMatch( - chunk -> chunk.player.equals(plyUuid) && chunk.chunk.equals(chunkPos)); + assert loadedChunk.player.equals(plyUuid) && loadedChunk.chunk.equals(chunkPos); + // Make sure the chunk permission got loaded correctly + assert Objects.equals(permissions1, loadedChunk.playerPermissions.get(accessorUuid1)); + assert Objects.equals(permissions2, loadedChunk.playerPermissions.get(accessorUuid2)); } + } - //noinspection ResultOfMethodCallIgnored - dbFile.delete(); + @Test + void ensureSomeColumnsExistsAfterInitializing() { + File dbFile = randomDbFile(); + dbFile.deleteOnExit(); + + try (SqLiteWrapper ignoredWrapper = new SqLiteWrapper(dbFile, false)) { + // Make sure that instantiating SqLiteWrapper created the tables + assert SqLiteTableMigrationManager.columnExists("player_data", "player_uuid"); + assert SqLiteTableMigrationManager.columnExists("chunk_data", "owner_uuid"); + assert SqLiteTableMigrationManager.columnExists("chunk_permissions", "permission_bits"); + assert !SqLiteTableMigrationManager.columnExists("chunk_hell", "permission_bits"); + assert !SqLiteTableMigrationManager.columnExists("player_data", "fake_col"); + } } protected static File randomDbFile() {