Prevent creating LODs for already processed chunks
This commit is contained in:
@@ -41,10 +41,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
@@ -302,6 +299,17 @@ public class SharedApi
|
||||
|
||||
try
|
||||
{
|
||||
// check if this chunk has been converted into an LOD already
|
||||
int oldChunkHash = dhLevel.getChunkHash(chunkWrapper.getChunkPos()); // shouldn't happen on the render thread since it may take a few moments to run
|
||||
if (oldChunkHash == chunkWrapper.getBlockBiomeHashCode())
|
||||
{
|
||||
// if the chunk hashes are the same then we don't need to bother with lighting the chunk
|
||||
// or creating/updating the LODs
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Save or populate the chunk wrapper's lighting
|
||||
// this is done so we don't have to worry about MC unloading the lighting data for this chunk
|
||||
boolean onlyUseDhLighting = Config.Client.Advanced.LodBuilding.onlyUseDhLightingEngine.get();
|
||||
@@ -336,6 +344,7 @@ public class SharedApi
|
||||
}
|
||||
|
||||
dhLevel.updateChunkAsync(chunkWrapper);
|
||||
dhLevel.setChunkHash(chunkWrapper.getChunkPos(), chunkWrapper.getBlockBiomeHashCode());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
-2
@@ -45,8 +45,6 @@ public abstract class AbstractSaveStructure implements AutoCloseable
|
||||
*/
|
||||
public abstract File getLevelFolder(ILevelWrapper wrapper);
|
||||
|
||||
/** Will return null if no parent folder exists for the given {@link ILevelWrapper}. */
|
||||
public abstract File getRenderCacheFolder(ILevelWrapper world);
|
||||
/** Will return null if no parent folder exists for the given {@link ILevelWrapper}. */
|
||||
public abstract File getFullDataFolder(ILevelWrapper world);
|
||||
|
||||
|
||||
-12
@@ -168,18 +168,6 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public File getRenderCacheFolder(ILevelWrapper level)
|
||||
{
|
||||
File levelFolder = this.levelWrapperToFileMap.get(level);
|
||||
if (levelFolder == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return levelFolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getFullDataFolder(ILevelWrapper level)
|
||||
{
|
||||
|
||||
-8
@@ -51,14 +51,6 @@ public class LocalSaveStructure extends AbstractSaveStructure
|
||||
return serverSide.getSaveFolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getRenderCacheFolder(ILevelWrapper level)
|
||||
{
|
||||
IServerLevelWrapper serverSide = (IServerLevelWrapper) level;
|
||||
this.debugPath = serverSide.getSaveFolder();
|
||||
return serverSide.getSaveFolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getFullDataFolder(ILevelWrapper level)
|
||||
{
|
||||
|
||||
@@ -23,18 +23,30 @@ import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiChunkMo
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.dataObjects.transformers.ChunkToLodBuilder;
|
||||
import com.seibel.distanthorizons.core.file.fullDatafile.DelayedFullDataSourceSaveCache;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.sql.dto.ChunkHashDTO;
|
||||
import com.seibel.distanthorizons.core.sql.repo.ChunkHashRepo;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public abstract class AbstractDhLevel implements IDhLevel
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
public final ChunkToLodBuilder chunkToLodBuilder;
|
||||
|
||||
/** if this is null then the other handler is probably null too, but just in case */
|
||||
@Nullable
|
||||
public ChunkHashRepo chunkHashRepo;
|
||||
|
||||
protected final DelayedFullDataSourceSaveCache delayedFullDataSourceSaveCache = new DelayedFullDataSourceSaveCache(this::onDataSourceSave, 2_000);
|
||||
/** contains the {@link DhChunkPos} for each {@link DhSectionPos} that are queued to save via {@link AbstractDhLevel#delayedFullDataSourceSaveCache} */
|
||||
protected final ConcurrentHashMap<Long, HashSet<DhChunkPos>> updatedChunkPosSetBySectionPos = new ConcurrentHashMap<>();
|
||||
@@ -47,6 +59,20 @@ public abstract class AbstractDhLevel implements IDhLevel
|
||||
|
||||
protected AbstractDhLevel() { this.chunkToLodBuilder = new ChunkToLodBuilder(); }
|
||||
|
||||
protected void createAndSetChunkHashRepo(String databaseFilePath)
|
||||
{
|
||||
ChunkHashRepo newChunkHashRepo = null;
|
||||
try
|
||||
{
|
||||
newChunkHashRepo = new ChunkHashRepo("jdbc:sqlite", databaseFilePath);
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
LOGGER.error("Unable to create [ChunkHashRepo], error: ["+e.getMessage()+"].", e);
|
||||
}
|
||||
this.chunkHashRepo = newChunkHashRepo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
@@ -99,6 +125,32 @@ public abstract class AbstractDhLevel implements IDhLevel
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getChunkHash(DhChunkPos pos)
|
||||
{
|
||||
if (this.chunkHashRepo == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ChunkHashDTO dto = this.chunkHashRepo.getByKey(pos);
|
||||
return (dto != null) ? dto.chunkHash : 0;
|
||||
}
|
||||
@Override
|
||||
public void setChunkHash(DhChunkPos pos, int chunkHash)
|
||||
{
|
||||
if (this.chunkHashRepo != null)
|
||||
{
|
||||
this.chunkHashRepo.save(new ChunkHashDTO(pos, chunkHash));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// base overrides //
|
||||
//================//
|
||||
|
||||
@Override
|
||||
public void close() { this.chunkToLodBuilder.close(); }
|
||||
|
||||
|
||||
@@ -61,6 +61,8 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
|
||||
this.dataFileHandler = new RemoteFullDataSourceProvider(this, saveStructure, fullDataSaveDirOverride);
|
||||
this.clientside = new ClientLevelModule(this);
|
||||
|
||||
this.createAndSetChunkHashRepo(this.dataFileHandler.repo.databaseLocation);
|
||||
|
||||
if (enableRendering)
|
||||
{
|
||||
this.clientside.startRenderer(clientLevelWrapper);
|
||||
|
||||
@@ -53,6 +53,10 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public DhClientServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper)
|
||||
{
|
||||
if (saveStructure.getFullDataFolder(serverLevelWrapper).mkdirs())
|
||||
@@ -62,6 +66,8 @@ 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);
|
||||
|
||||
LOGGER.info("Started " + DhClientServerLevel.class.getSimpleName() + " for " + serverLevelWrapper + " with saves at " + saveStructure);
|
||||
}
|
||||
|
||||
@@ -72,10 +78,7 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev
|
||||
//==============//
|
||||
|
||||
@Override
|
||||
public void clientTick()
|
||||
{
|
||||
clientside.clientTick();
|
||||
}
|
||||
public void clientTick() { this.clientside.clientTick(); }
|
||||
|
||||
@Override
|
||||
public void render(DhApiRenderParam renderEventParam, IProfilerWrapper profiler)
|
||||
@@ -124,6 +127,8 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//========//
|
||||
// render //
|
||||
//========//
|
||||
@@ -132,6 +137,8 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev
|
||||
|
||||
public void stopRenderer() { this.clientside.stopRenderer(); }
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// level handling //
|
||||
//================//
|
||||
@@ -180,7 +187,7 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev
|
||||
public CompletableFuture<Void> updateDataSourcesAsync(FullDataSourceV2 data) { return this.clientside.updateDataSourcesAsync(data); }
|
||||
|
||||
@Override
|
||||
public int getMinY() { return getLevelWrapper().getMinHeight(); }
|
||||
public int getMinY() { return this.getLevelWrapper().getMinHeight(); }
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel
|
||||
public final ServerLevelModule serverside;
|
||||
private final IServerLevelWrapper serverLevelWrapper;
|
||||
|
||||
|
||||
|
||||
public DhServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper)
|
||||
{
|
||||
if (saveStructure.getFullDataFolder(serverLevelWrapper).mkdirs())
|
||||
@@ -44,9 +46,13 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel
|
||||
}
|
||||
this.serverLevelWrapper = serverLevelWrapper;
|
||||
this.serverside = new ServerLevelModule(this, saveStructure);
|
||||
this.createAndSetChunkHashRepo(this.serverside.fullDataFileHandler.repo.databaseLocation);
|
||||
|
||||
LOGGER.info("Started DHLevel for {} with saves at {}", serverLevelWrapper, saveStructure);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void serverTick() { this.chunkToLodBuilder.tick(); }
|
||||
|
||||
@Override
|
||||
|
||||
@@ -22,6 +22,7 @@ package com.seibel.distanthorizons.core.level;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2;
|
||||
import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
|
||||
@@ -37,6 +38,9 @@ public interface IDhLevel extends AutoCloseable
|
||||
*/
|
||||
ILevelWrapper getLevelWrapper();
|
||||
|
||||
/** @return 0 if no hash is known */
|
||||
int getChunkHash(DhChunkPos pos);
|
||||
void setChunkHash(DhChunkPos pos, int chunkHash);
|
||||
void updateChunkAsync(IChunkWrapper chunk);
|
||||
|
||||
FullDataSourceProviderV2 getFullDataProvider();
|
||||
|
||||
@@ -43,6 +43,10 @@ public class ServerLevelModule implements AutoCloseable
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public ServerLevelModule(IDhServerLevel parentServerLevel, AbstractSaveStructure saveStructure)
|
||||
{
|
||||
this.parentServerLevel = parentServerLevel;
|
||||
@@ -54,6 +58,10 @@ public class ServerLevelModule implements AutoCloseable
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// base overrides //
|
||||
//================//
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.core.sql.dto;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiWorldCompressionMode;
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.util.zip.Adler32;
|
||||
import java.util.zip.CheckedOutputStream;
|
||||
|
||||
/** handles storing {@link FullDataSourceV2}'s in the database. */
|
||||
public class ChunkHashDTO implements IBaseDTO<DhChunkPos>
|
||||
{
|
||||
public DhChunkPos pos;
|
||||
public int chunkHash;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public ChunkHashDTO(DhChunkPos pos, int chunkHash)
|
||||
{
|
||||
this.pos = pos;
|
||||
this.chunkHash = chunkHash;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===========//
|
||||
// overrides //
|
||||
//===========//
|
||||
|
||||
@Override
|
||||
public DhChunkPos getKey() { return this.pos; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.core.sql.repo;
|
||||
|
||||
import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.sql.dto.ChunkHashDTO;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
|
||||
public class ChunkHashRepo extends AbstractDhRepo<DhChunkPos, ChunkHashDTO>
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public ChunkHashRepo(String databaseType, String databaseLocation) throws SQLException
|
||||
{
|
||||
super(databaseType, databaseLocation, ChunkHashDTO.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===========//
|
||||
// overrides //
|
||||
//===========//
|
||||
|
||||
@Override
|
||||
public String getTableName() { return "ChunkHash"; }
|
||||
|
||||
@Override
|
||||
public String createWhereStatement(DhChunkPos pos) { return "ChunkPosX = '"+pos.x+"' AND ChunkPosZ = '"+pos.z+"'"; }
|
||||
|
||||
|
||||
|
||||
//=======================//
|
||||
// repo required methods //
|
||||
//=======================//
|
||||
|
||||
@Override
|
||||
public ChunkHashDTO convertDictionaryToDto(Map<String, Object> objectMap) throws ClassCastException
|
||||
{
|
||||
int posX = (Integer) objectMap.get("ChunkPosX");
|
||||
int posZ = (Integer) objectMap.get("ChunkPosZ");
|
||||
|
||||
int chunkHash = (Integer) objectMap.get("ChunkHash");
|
||||
|
||||
|
||||
ChunkHashDTO dto = new ChunkHashDTO(new DhChunkPos(posX, posZ), chunkHash);
|
||||
return dto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement createInsertStatement(ChunkHashDTO dto) throws SQLException
|
||||
{
|
||||
String sql =
|
||||
"INSERT INTO "+this.getTableName() + " (\n" +
|
||||
" ChunkPosX, ChunkPosZ, \n" +
|
||||
" ChunkHash, \n" +
|
||||
" LastModifiedUnixDateTime, CreatedUnixDateTime) \n" +
|
||||
"VALUES( \n" +
|
||||
" ?, ?, \n" +
|
||||
" ?, \n" +
|
||||
" ?, ? \n" +
|
||||
");";
|
||||
PreparedStatement statement = this.createPreparedStatement(sql);
|
||||
|
||||
int i = 1;
|
||||
statement.setObject(i++, dto.pos.x);
|
||||
statement.setObject(i++, dto.pos.z);
|
||||
|
||||
statement.setObject(i++, dto.chunkHash);
|
||||
|
||||
statement.setObject(i++, System.currentTimeMillis()); // last modified unix time
|
||||
statement.setObject(i++, System.currentTimeMillis()); // created unix time
|
||||
|
||||
return statement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement createUpdateStatement(ChunkHashDTO dto) throws SQLException
|
||||
{
|
||||
String sql =
|
||||
"UPDATE "+this.getTableName()+" \n" +
|
||||
"SET \n" +
|
||||
" ChunkHash = ? \n" +
|
||||
" ,LastModifiedUnixDateTime = ? \n" +
|
||||
"WHERE ChunkPosX = ? AND ChunkPosZ = ?";
|
||||
PreparedStatement statement = this.createPreparedStatement(sql);
|
||||
|
||||
int i = 1;
|
||||
statement.setObject(i++, dto.chunkHash);
|
||||
|
||||
statement.setObject(i++, dto.pos.x);
|
||||
statement.setObject(i++, dto.pos.z);
|
||||
|
||||
return statement;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
CREATE TABLE ChunkHash(
|
||||
-- compound primary key
|
||||
ChunkPosX INT NOT NULL
|
||||
,ChunkPosZ INT NOT NULL
|
||||
|
||||
,ChunkHash INT NOT NULL
|
||||
|
||||
,LastModifiedUnixDateTime BIGINT NOT NULL -- in GMT 0
|
||||
,CreatedUnixDateTime BIGINT NOT NULL -- in GMT 0
|
||||
|
||||
,PRIMARY KEY (ChunkPosX, ChunkPosZ)
|
||||
);
|
||||
@@ -5,3 +5,4 @@
|
||||
0031-sqlite-useSqliteWalJournaling.sql
|
||||
0040-sqlite-removeRenderCache.sql
|
||||
0050-sqlite-addApplyToParentIndex.sql
|
||||
0060-sqlite-createChunkHashTable.sql
|
||||
|
||||
Reference in New Issue
Block a user