diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java index de67f7f3d..5025e1d68 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java @@ -42,14 +42,14 @@ public final class ModInfo public static final String NAME = "DistantHorizons"; /** Human-readable version of NAME */ public static final String READABLE_NAME = "Distant Horizons"; - public static final String VERSION = "2.1.1-a-dev"; + public static final String VERSION = "2.1.3-a-dev"; /** Returns true if the current build is an unstable developer build, false otherwise. */ public static boolean IS_DEV_BUILD = VERSION.toLowerCase().contains("dev"); /** This version should only be updated when breaking changes are introduced to the DH API */ public static final int API_MAJOR_VERSION = 2; /** This version should be updated whenever new methods are added to the DH API */ - public static final int API_MINOR_VERSION = 0; + public static final int API_MINOR_VERSION = 1; /** This version should be updated whenever non-breaking fixes are added to the DH API */ public static final int API_PATH_VERSION = 0; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index 4ec84046c..b5dfc7c7c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -554,7 +554,7 @@ public class ClientApi MC.sendChatMessage("\u00A76" + "Distant Horizons: Low memory detected." + "\u00A7r"); MC.sendChatMessage("Stuttering or low FPS may occur."); MC.sendChatMessage("Please increase Minecraft's available memory to 4 gigabytes."); - MC.sendChatMessage("This can be disabled in DH's config under Advanced -> Logging."); + MC.sendChatMessage("This warning can be disabled in DH's config under Advanced -> Logging."); MC.sendChatMessage(""); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index ce4231066..2f72f424d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -724,7 +724,7 @@ public class Config public static ConfigEntry worldGenerationTimeoutLengthInSeconds = new ConfigEntry.Builder() .setServersideShortName("worldGenerationTimeout") - .setMinDefaultMax(5, 60, 60 * 10/*10 minutes*/ ) + .setMinDefaultMax(5, 60 * 3, 60 * 10/*10 minutes*/ ) .comment("" + "How long should a world generator thread run for before timing out? \n" + "Note: If you are experiencing timeout errors it is better to lower your CPU usage first \n" diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/AbstractDataSourceHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/AbstractDataSourceHandler.java index f78a8f7a4..90af51ebb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/AbstractDataSourceHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/AbstractDataSourceHandler.java @@ -80,11 +80,6 @@ public abstract class AbstractDataSourceHandler { this.level = level; this.saveDir = (saveDirOverride == null) ? saveStructure.getFullDataFolder(level.getLevelWrapper()) : saveDirOverride; - if (!this.saveDir.exists() && !this.saveDir.mkdirs()) - { - LOGGER.warn("Unable to create full data folder, file saving may fail."); - } - this.repo = this.createRepo(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV1.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV1.java index 4c1e443fe..0311ed9ae 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV1.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV1.java @@ -64,7 +64,7 @@ public class FullDataSourceProviderV1 { try { - return new FullDataSourceV1Repo("jdbc:sqlite", this.saveDir.getPath() + "/" + AbstractSaveStructure.DATABASE_NAME); + return new FullDataSourceV1Repo("jdbc:sqlite", new File(this.saveDir.getPath() + File.separator + AbstractSaveStructure.DATABASE_NAME)); } catch (SQLException e) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java index 61fff431e..d7c220e1b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java @@ -145,7 +145,7 @@ public class FullDataSourceProviderV2 { try { - return new FullDataSourceV2Repo("jdbc:sqlite", this.saveDir.getPath() + "/" + AbstractSaveStructure.DATABASE_NAME); + return new FullDataSourceV2Repo("jdbc:sqlite", new File(this.saveDir.getPath() + File.separator + AbstractSaveStructure.DATABASE_NAME)); } catch (SQLException e) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java index ab0b15ffa..b556bdcff 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java @@ -33,6 +33,7 @@ import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.sql.SQLException; import java.util.HashSet; import java.util.concurrent.ConcurrentHashMap; @@ -59,12 +60,12 @@ public abstract class AbstractDhLevel implements IDhLevel protected AbstractDhLevel() { this.chunkToLodBuilder = new ChunkToLodBuilder(); } - protected void createAndSetChunkHashRepo(String databaseFilePath) + protected void createAndSetChunkHashRepo(File databaseFile) { ChunkHashRepo newChunkHashRepo = null; try { - newChunkHashRepo = new ChunkHashRepo("jdbc:sqlite", databaseFilePath); + newChunkHashRepo = new ChunkHashRepo("jdbc:sqlite", databaseFile); } catch (SQLException e) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 3dd26305a..ae8c7c98c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -49,7 +49,6 @@ import org.jetbrains.annotations.Nullable; import javax.annotation.CheckForNull; import java.awt.*; import java.io.File; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -118,7 +117,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel this.clientside = new ClientLevelModule(this); - this.createAndSetChunkHashRepo(this.dataFileHandler.repo.databaseLocation); + this.createAndSetChunkHashRepo(this.dataFileHandler.repo.databaseFile); if (enableRendering) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java index 94a03fd83..fd946f84f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java @@ -39,7 +39,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapp import org.apache.logging.log4j.Logger; import java.awt.*; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -69,7 +68,7 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev this.serverLevelWrapper = serverLevelWrapper; this.serverside = new ServerLevelModule(this, saveStructure); this.clientside = new ClientLevelModule(this); - this.createAndSetChunkHashRepo(this.serverside.fullDataFileHandler.repo.databaseLocation); + this.createAndSetChunkHashRepo(this.serverside.fullDataFileHandler.repo.databaseFile); LOGGER.info("Started " + DhClientServerLevel.class.getSimpleName() + " for " + serverLevelWrapper + " with saves at " + saveStructure); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index ea985439e..3932deb7e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -46,14 +46,12 @@ import com.seibel.distanthorizons.coreapi.util.math.Vec3d; import org.apache.logging.log4j.Logger; import java.text.MessageFormat; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; import javax.annotation.CheckForNull; import java.util.Map; import java.util.concurrent.*; -import java.util.function.BiConsumer; import java.util.function.Consumer; public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel @@ -79,7 +77,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } this.serverLevelWrapper = serverLevelWrapper; this.serverside = new ServerLevelModule(this, saveStructure); - this.createAndSetChunkHashRepo(this.serverside.fullDataFileHandler.repo.databaseLocation); + this.createAndSetChunkHashRepo(this.serverside.fullDataFileHandler.repo.databaseFile); LOGGER.info("Started DHLevel for {} with saves at {}", serverLevelWrapper, saveStructure); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/DatabaseUpdater.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/DatabaseUpdater.java index 6d5c70b61..b3696d065 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/DatabaseUpdater.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/DatabaseUpdater.java @@ -97,7 +97,7 @@ public class DatabaseUpdater Map scriptAlreadyRunResult = repo.queryDictionaryFirst("SELECT EXISTS(SELECT 1 FROM "+SCHEMA_TABLE_NAME+" WHERE ScriptName='"+resource.name+"') as 'existingCount';"); if (scriptAlreadyRunResult != null && (int) scriptAlreadyRunResult.get("existingCount") == 0) { - LOGGER.info("Running SQL update script: ["+resource.name+"], for repo: ["+repo.databaseLocation+"]"); + LOGGER.info("Running SQL update script: ["+resource.name+"], for repo: ["+repo.databaseFile +"]"); int sqlIndex = 0; @@ -147,7 +147,7 @@ public class DatabaseUpdater catch (RuntimeException e) { // updating needs to stop to prevent data corruption - LOGGER.error("Unexpected error running database update script ["+resource.name+"] on database ["+repo.databaseLocation+"], stopping database update. Database reading/writing may fail if you continue. \n" + + LOGGER.error("Unexpected error running database update script ["+resource.name+"] on database ["+repo.databaseFile +"], stopping database update. Database reading/writing may fail if you continue. \n" + "Error: ["+e.getMessage()+"]. \n" + "Sql Script:["+resource.queryString+"]", e); throw e; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/AbstractDhRepo.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/AbstractDhRepo.java index 74c39f016..34de259dc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/AbstractDhRepo.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/AbstractDhRepo.java @@ -26,6 +26,8 @@ import com.seibel.distanthorizons.core.sql.dto.IBaseDTO; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; +import java.io.File; +import java.io.IOException; import java.sql.*; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -49,7 +51,7 @@ public abstract class AbstractDhRepo> implemen private final Connection connection; public final String databaseType; - public final String databaseLocation; + public final File databaseFile; public final Class dtoClass; @@ -59,16 +61,15 @@ public abstract class AbstractDhRepo> implemen - //=============// // constructor // //=============// /** @throws SQLException if the repo is unable to access the database or has trouble updating said database. */ - public AbstractDhRepo(String databaseType, String databaseLocation, Class dtoClass) throws SQLException + public AbstractDhRepo(String databaseType, File databaseFile, Class dtoClass) throws SQLException { this.databaseType = databaseType; - this.databaseLocation = databaseLocation; + this.databaseFile = databaseFile; this.dtoClass = dtoClass; @@ -93,9 +94,55 @@ public abstract class AbstractDhRepo> implemen } + + //==========================// + // database file validation // + //==========================// + + // check that the database file exists + if (!databaseFile.exists()) + { + // check that the parent folder exists + File parentFolder = databaseFile.getParentFile(); + if (parentFolder != null && !parentFolder.exists()) + { + if (!parentFolder.mkdirs()) + { + throw new RuntimeException("Unable to create the necessary parent folders for the database file at location ["+databaseFile.getPath()+"]."); + } + } + + if (!databaseFile.exists()) + { + try + { + boolean fileCreated = databaseFile.createNewFile(); + } + catch (IOException e) + { + throw new RuntimeException("Unable to create database file at location ["+databaseFile.getPath()+"] due to error: ["+e.getMessage()+"]", e); + } + } + } + + if (!databaseFile.canRead()) + { + throw new RuntimeException("Unable to read database file at location ["+databaseFile.getPath()+"], please make sure the folder and file has the correct permissions."); + } + if (!databaseFile.canWrite()) + { + throw new RuntimeException("Unable to write database file at location ["+databaseFile.getPath()+"], please make sure the folder and file aren't set to read-only."); + } + + + + //==================// + // connection setup // + //==================// + // get or create the connection, // reusing existing connections reduces the chance of locking the database during trivial queries - this.connectionString = this.databaseType+":"+this.databaseLocation; + this.connectionString = this.databaseType+":"+this.databaseFile.getPath(); this.connection = CONNECTIONS_BY_CONNECTION_STRING.computeIfAbsent(this.connectionString, (connectionString) -> diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/ChunkHashRepo.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/ChunkHashRepo.java index ad9152886..4fc545b6a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/ChunkHashRepo.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/ChunkHashRepo.java @@ -26,6 +26,7 @@ import com.seibel.distanthorizons.core.sql.dto.ChunkHashDTO; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import org.apache.logging.log4j.Logger; +import java.io.File; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Map; @@ -40,9 +41,9 @@ public class ChunkHashRepo extends AbstractDhRepo // constructor // //=============// - public ChunkHashRepo(String databaseType, String databaseLocation) throws SQLException + public ChunkHashRepo(String databaseType, File databaseFile) throws SQLException { - super(databaseType, databaseLocation, ChunkHashDTO.class); + super(databaseType, databaseFile, ChunkHashDTO.class); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/FullDataSourceV1Repo.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/FullDataSourceV1Repo.java index 0c7c444b7..a61bc22ab 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/FullDataSourceV1Repo.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/FullDataSourceV1Repo.java @@ -26,6 +26,7 @@ import com.seibel.distanthorizons.coreapi.util.StringUtil; import it.unimi.dsi.fastutil.longs.LongArrayList; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.ArrayList; @@ -42,9 +43,9 @@ public class FullDataSourceV1Repo extends AbstractDhRepo { - public TestCompoundKeyRepo(String databaseType, String databaseLocation) throws SQLException + public TestCompoundKeyRepo(String databaseType, File databaseFile) throws SQLException { - super(databaseType, databaseLocation, TestCompoundKeyDto.class); + super(databaseType, databaseFile, TestCompoundKeyDto.class); // note: this should only ever be done with the test repo. // All long term tables should be created using a sql Script. diff --git a/core/src/test/java/testItems/sql/TestPrimaryKeyRepo.java b/core/src/test/java/testItems/sql/TestPrimaryKeyRepo.java index 347e6d686..9df6a16c8 100644 --- a/core/src/test/java/testItems/sql/TestPrimaryKeyRepo.java +++ b/core/src/test/java/testItems/sql/TestPrimaryKeyRepo.java @@ -21,6 +21,7 @@ package testItems.sql; import com.seibel.distanthorizons.core.sql.repo.AbstractDhRepo; +import java.io.File; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Map; @@ -28,9 +29,9 @@ import java.util.Map; public class TestPrimaryKeyRepo extends AbstractDhRepo { - public TestPrimaryKeyRepo(String databaseType, String databaseLocation) throws SQLException + public TestPrimaryKeyRepo(String databaseType, File databaseFile) throws SQLException { - super(databaseType, databaseLocation, TestSingleKeyDto.class); + super(databaseType, databaseFile, TestSingleKeyDto.class); // note: this should only ever be done with the test repo. // All long term tables should be created using a sql Script. diff --git a/core/src/test/java/tests/CompressionTest.java b/core/src/test/java/tests/CompressionTest.java index d871b58ac..f17af721a 100644 --- a/core/src/test/java/tests/CompressionTest.java +++ b/core/src/test/java/tests/CompressionTest.java @@ -259,7 +259,7 @@ public class CompressionTest File uncompressedDatabaseFile = new File(uncompressedDatabaseFilePath); Assert.assertTrue(uncompressedDatabaseFile.exists()); - FullDataSourceV2Repo uncompressedRepo = new FullDataSourceV2Repo("jdbc:sqlite", uncompressedDatabaseFilePath); + FullDataSourceV2Repo uncompressedRepo = new FullDataSourceV2Repo("jdbc:sqlite", uncompressedDatabaseFile); String compressedDatabaseFilePath = TEST_DIR + "/output/" + DB_FILE_NAME_PREFIX + "_" + compressorName + ".sqlite"; @@ -267,7 +267,7 @@ public class CompressionTest compressedDatabaseFile.mkdirs(); compressedDatabaseFile.delete(); Assert.assertTrue(!compressedDatabaseFile.exists()); - FullDataSourceV2Repo compressedRepo = new FullDataSourceV2Repo("jdbc:sqlite", compressedDatabaseFilePath); + FullDataSourceV2Repo compressedRepo = new FullDataSourceV2Repo("jdbc:sqlite", uncompressedDatabaseFile); diff --git a/core/src/test/java/tests/DhRepoSqliteTest.java b/core/src/test/java/tests/DhRepoSqliteTest.java index 066106110..bb9707b6c 100644 --- a/core/src/test/java/tests/DhRepoSqliteTest.java +++ b/core/src/test/java/tests/DhRepoSqliteTest.java @@ -62,7 +62,7 @@ public class DhRepoSqliteTest TestPrimaryKeyRepo primaryKeyRepo = null; try { - primaryKeyRepo = new TestPrimaryKeyRepo(DATABASE_TYPE, DB_FILE_NAME); + primaryKeyRepo = new TestPrimaryKeyRepo(DATABASE_TYPE, new File(DB_FILE_NAME)); @@ -78,8 +78,8 @@ public class DhRepoSqliteTest } // check that the update scripts aren't run multiple times - TestPrimaryKeyRepo altDataRepoOne = new TestPrimaryKeyRepo(DATABASE_TYPE, DB_FILE_NAME); - TestPrimaryKeyRepo altDataRepoTwo = new TestPrimaryKeyRepo(DATABASE_TYPE, DB_FILE_NAME); + TestPrimaryKeyRepo altDataRepoOne = new TestPrimaryKeyRepo(DATABASE_TYPE, new File(DB_FILE_NAME)); + TestPrimaryKeyRepo altDataRepoTwo = new TestPrimaryKeyRepo(DATABASE_TYPE, new File(DB_FILE_NAME)); @@ -142,7 +142,7 @@ public class DhRepoSqliteTest TestCompoundKeyRepo compoundKeyRepo = null; try { - compoundKeyRepo = new TestCompoundKeyRepo(DATABASE_TYPE, DB_FILE_NAME); + compoundKeyRepo = new TestCompoundKeyRepo(DATABASE_TYPE, new File(DB_FILE_NAME));