Merge branch 'feat/server-updates'
This commit is contained in:
@@ -28,8 +28,10 @@ import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGenerat
|
||||
import com.seibel.distanthorizons.core.config.eventHandlers.QuickRenderToggleConfigEventHandler;
|
||||
import com.seibel.distanthorizons.core.config.eventHandlers.RenderCacheConfigEventHandler;
|
||||
import com.seibel.distanthorizons.core.config.eventHandlers.UnsafeValuesConfigListener;
|
||||
import com.seibel.distanthorizons.core.config.eventHandlers.WorldCurvatureConfigEventHandler;
|
||||
import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPresetConfigEventHandler;
|
||||
import com.seibel.distanthorizons.core.config.eventHandlers.presets.RenderQualityPresetConfigEventHandler;
|
||||
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
|
||||
import com.seibel.distanthorizons.core.config.types.*;
|
||||
import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryAppearance;
|
||||
import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryPerformance;
|
||||
@@ -575,6 +577,7 @@ public class Config
|
||||
+ "Note: Due to current limitations, the min value is 50 \n"
|
||||
+ "and the max value is 5000. Any values outside this range \n"
|
||||
+ "will be set to 0 (disabled).")
|
||||
.addListener(WorldCurvatureConfigEventHandler.INSTANCE)
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Double> lodBias = new ConfigEntry.Builder<Double>()
|
||||
@@ -734,6 +737,7 @@ public class Config
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
// not currently implemented
|
||||
public static ConfigEntry<Boolean> enableMultiverseNetworking = new ConfigEntry.Builder<Boolean>()
|
||||
.set(true)
|
||||
.comment(""
|
||||
@@ -742,6 +746,7 @@ public class Config
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
// not currently implemented
|
||||
public static ConfigEntry<Boolean> enableServerNetworking = new ConfigEntry.Builder<Boolean>()
|
||||
.set(false)
|
||||
.comment(""
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package com.seibel.distanthorizons.core.config.eventHandlers;
|
||||
|
||||
import com.seibel.distanthorizons.api.DhApi;
|
||||
import com.seibel.distanthorizons.api.enums.config.ELodShading;
|
||||
import com.seibel.distanthorizons.api.enums.config.EMaxHorizontalResolution;
|
||||
import com.seibel.distanthorizons.api.enums.config.EVerticalQuality;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* Listens to the config and will automatically
|
||||
* clear the current render cache if certain settings are changed. <br> <br>
|
||||
*
|
||||
* Note: if additional settings should clear the render cache, add those to this listener, don't create a new listener
|
||||
*/
|
||||
public class WorldCurvatureConfigEventHandler implements IConfigListener
|
||||
{
|
||||
public static WorldCurvatureConfigEventHandler INSTANCE = new WorldCurvatureConfigEventHandler();
|
||||
|
||||
private static final int MIN_VALID_CURVE_VALUE = 50;
|
||||
|
||||
|
||||
/** private since we only ever need one handler at a time */
|
||||
private WorldCurvatureConfigEventHandler() { }
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onConfigValueSet()
|
||||
{
|
||||
int curveRatio = Config.Client.Advanced.Graphics.AdvancedGraphics.earthCurveRatio.get();
|
||||
if (curveRatio > 0 && curveRatio < MIN_VALID_CURVE_VALUE)
|
||||
{
|
||||
// shouldn't update the UI, otherwise we may end up fighting the user
|
||||
Config.Client.Advanced.Graphics.AdvancedGraphics.earthCurveRatio.set(MIN_VALID_CURVE_VALUE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUiModify() { /* do nothing, we only care about modified config values */ }
|
||||
|
||||
|
||||
}
|
||||
-2
@@ -1,10 +1,8 @@
|
||||
package com.seibel.distanthorizons.core.config.eventHandlers.presets;
|
||||
|
||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
||||
import com.seibel.distanthorizons.core.config.ConfigEntryWithPresetOptions;
|
||||
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.world.EWorldEnvironment;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui;
|
||||
import com.seibel.distanthorizons.coreapi.interfaces.config.IConfigEntry;
|
||||
import com.seibel.distanthorizons.coreapi.util.StringUtil;
|
||||
|
||||
+170
-1
@@ -1,11 +1,21 @@
|
||||
package com.seibel.distanthorizons.core.dataObjects.fullData.accessor;
|
||||
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IStreamableFullDataSource;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhLodPos;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
|
||||
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.dataStreams.DhDataInputStream;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A more specific version of {@link FullDataArrayAccessor}
|
||||
@@ -15,6 +25,8 @@ import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
*/
|
||||
public class ChunkSizedFullDataAccessor extends FullDataArrayAccessor
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
public final DhChunkPos pos;
|
||||
// TODO replace this var with LodUtil.BLOCK_DETAIL_LEVEL
|
||||
public final byte detailLevel = LodUtil.BLOCK_DETAIL_LEVEL;
|
||||
@@ -31,6 +43,163 @@ public class ChunkSizedFullDataAccessor extends FullDataArrayAccessor
|
||||
}
|
||||
|
||||
|
||||
//=================//
|
||||
// stream handling //
|
||||
//=================//
|
||||
|
||||
|
||||
public void writeSourceSummaryInfo(IDhLevel level, DhDataOutputStream outputStream) throws IOException
|
||||
{
|
||||
outputStream.writeInt(level.getMinY());
|
||||
}
|
||||
|
||||
public void readSourceSummaryInfo(DhDataInputStream inputStream, IDhLevel level) throws IOException
|
||||
{
|
||||
int minY = inputStream.readInt();
|
||||
if (minY != level.getMinY())
|
||||
{
|
||||
LOGGER.warn("Data minY mismatch: " + minY + " != " + level.getMinY() + ". Will ignore data's y level");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean writeDataPoints(DhDataOutputStream outputStream) throws IOException
|
||||
{
|
||||
outputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE);
|
||||
|
||||
|
||||
|
||||
// Data array length
|
||||
for (int x = 0; x < this.width; x++)
|
||||
{
|
||||
for (int z = 0; z < this.width; z++)
|
||||
{
|
||||
outputStream.writeInt(this.get(x, z).getSingleLength());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Data array content (only on non-empty columns)
|
||||
outputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE);
|
||||
for (int x = 0; x < this.width; x++)
|
||||
{
|
||||
for (int z = 0; z < this.width; z++)
|
||||
{
|
||||
SingleColumnFullDataAccessor columnAccessor = this.get(x, z);
|
||||
if (columnAccessor.doesColumnExist())
|
||||
{
|
||||
long[] dataPointArray = columnAccessor.getRaw();
|
||||
for (long dataPoint : dataPointArray)
|
||||
{
|
||||
outputStream.writeLong(dataPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
public long[][] readDataPoints(DhDataInputStream dataInputStream) throws IOException
|
||||
{
|
||||
// Data array length
|
||||
int dataPresentFlag = dataInputStream.readInt();
|
||||
if (dataPresentFlag != IFullDataSource.DATA_GUARD_BYTE)
|
||||
{
|
||||
throw new IOException("Invalid file format. Data Points guard byte expected: (no data) [" + IFullDataSource.NO_DATA_FLAG_BYTE + "] or (data present) [" + IFullDataSource.DATA_GUARD_BYTE + "], but found [" + dataPresentFlag + "].");
|
||||
}
|
||||
|
||||
|
||||
|
||||
long[][] dataPointArray = new long[width * width][];
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int z = 0; z < width; z++)
|
||||
{
|
||||
dataPointArray[x * width + z] = new long[dataInputStream.readInt()];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// check if the array start flag is present
|
||||
int arrayStartFlag = dataInputStream.readInt();
|
||||
if (arrayStartFlag != IFullDataSource.DATA_GUARD_BYTE)
|
||||
{
|
||||
throw new IOException("invalid data length end guard");
|
||||
}
|
||||
|
||||
for (int xz = 0; xz < dataPointArray.length; xz++) // x and z are combined
|
||||
{
|
||||
if (dataPointArray[xz].length != 0)
|
||||
{
|
||||
for (int y = 0; y < dataPointArray[xz].length; y++)
|
||||
{
|
||||
dataPointArray[xz][y] = dataInputStream.readLong();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return dataPointArray;
|
||||
}
|
||||
public void setDataPoints(long[][] dataPoints)
|
||||
{
|
||||
LodUtil.assertTrue(this.dataArrays.length == dataPoints.length, "Data point array length mismatch.");
|
||||
|
||||
System.arraycopy(dataPoints, 0, this.dataArrays, 0, dataPoints.length);
|
||||
}
|
||||
|
||||
|
||||
public void writeIdMappings(DhDataOutputStream outputStream, ILevelWrapper levelWrapper) throws IOException
|
||||
{
|
||||
outputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE);
|
||||
this.mapping.serialize(outputStream, levelWrapper);
|
||||
}
|
||||
public FullDataPointIdMap readIdMappings(DhDataInputStream inputStream, ILevelWrapper levelWrapper) throws IOException, InterruptedException
|
||||
{
|
||||
int guardByte = inputStream.readInt();
|
||||
if (guardByte != IFullDataSource.DATA_GUARD_BYTE)
|
||||
{
|
||||
throw new IOException("Invalid data content end guard for ID mapping");
|
||||
}
|
||||
|
||||
return FullDataPointIdMap.deserialize(inputStream, null, levelWrapper);
|
||||
}
|
||||
public void setIdMapping(FullDataPointIdMap mappings) { this.mapping.mergeAndReturnRemappedEntityIds(mappings); }
|
||||
|
||||
|
||||
public void populateFromStream(DhDataInputStream inputStream, IDhLevel level) throws IOException, InterruptedException
|
||||
{
|
||||
this.readSourceSummaryInfo(inputStream, level);
|
||||
|
||||
long[][] dataPoints = this.readDataPoints(inputStream);
|
||||
if (dataPoints == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
this.setDataPoints(dataPoints);
|
||||
|
||||
|
||||
FullDataPointIdMap mapping = this.readIdMappings(inputStream, level.getLevelWrapper());
|
||||
this.setIdMapping(mapping);
|
||||
|
||||
}
|
||||
|
||||
public void writeToStream(DhDataOutputStream outputStream, IDhLevel level) throws IOException
|
||||
{
|
||||
this.writeSourceSummaryInfo(level, outputStream);
|
||||
|
||||
boolean hasData = this.writeDataPoints(outputStream);
|
||||
if (!hasData)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.writeIdMappings(outputStream, level.getLevelWrapper());
|
||||
}
|
||||
|
||||
|
||||
public void setSingleColumn(long[] data, int xRelative, int zRelative) { this.dataArrays[xRelative * LodUtil.CHUNK_WIDTH + zRelative] = data; }
|
||||
|
||||
|
||||
+23
@@ -10,6 +10,7 @@ import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhLodPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
|
||||
@@ -23,6 +24,7 @@ import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
import java.io.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* This data source contains every datapoint over its given {@link DhSectionPos}.
|
||||
@@ -368,6 +370,27 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu
|
||||
}
|
||||
}
|
||||
|
||||
public void splitIntoChunkSizedAccessors(Consumer<ChunkSizedFullDataAccessor> consumer)
|
||||
{
|
||||
LodUtil.assertTrue(sectionPos.sectionDetailLevel == DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, "Data source detail level must be at block detail level.");
|
||||
|
||||
sectionPos.forEachChildAtLevel(LodUtil.CHUNK_DETAIL_LEVEL, childPos -> {
|
||||
ChunkSizedFullDataAccessor accessor = new ChunkSizedFullDataAccessor(new DhChunkPos(childPos.sectionX, childPos.sectionZ));
|
||||
|
||||
int detailLevelDifference = sectionPos.sectionDetailLevel - childPos.sectionDetailLevel;
|
||||
int childRelativeX = childPos.sectionX - sectionPos.sectionX * BitShiftUtil.powerOfTwo(detailLevelDifference);
|
||||
int childRelativeZ = childPos.sectionZ - sectionPos.sectionZ * BitShiftUtil.powerOfTwo(detailLevelDifference);
|
||||
|
||||
subView(
|
||||
LodUtil.CHUNK_WIDTH,
|
||||
childRelativeX * LodUtil.CHUNK_WIDTH,
|
||||
childRelativeZ * LodUtil.CHUNK_WIDTH
|
||||
).shadowCopyTo(accessor);
|
||||
|
||||
consumer.accept(accessor);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=====================//
|
||||
|
||||
-2
@@ -82,8 +82,6 @@ public class ChunkToLodBuilder implements AutoCloseable
|
||||
}
|
||||
else if (MC != null && !MC.playerExists())
|
||||
{
|
||||
// TODO handle server side properly
|
||||
|
||||
// MC hasn't finished loading (or is currently unloaded)
|
||||
|
||||
// can be uncommented if tasks aren't being cleared correctly
|
||||
|
||||
+10
-7
@@ -215,16 +215,19 @@ public abstract class AbstractMetaDataContainerFile
|
||||
{
|
||||
fileChannel.position(METADATA_SIZE_IN_BYTES);
|
||||
|
||||
try (DhDataOutputStream compressedOut = new DhDataOutputStream(Channels.newOutputStream(fileChannel));
|
||||
CheckedOutputStream checkedOut = new CheckedOutputStream(compressedOut, new Adler32())) // TODO: Is Adler32 ok?
|
||||
{
|
||||
dataWriterFunc.writeBufferToFile(compressedOut);
|
||||
this.baseMetaData.checksum = (int) checkedOut.getChecksum().getValue();
|
||||
}
|
||||
// the order of these streams is important, otherwise the checksum won't be calculated
|
||||
CheckedOutputStream checkedOut = new CheckedOutputStream(Channels.newOutputStream(fileChannel), new Adler32());
|
||||
// normally a DhStream should be the topmost stream to prevent closing the stream accidentally, but since this stream will be closed immediately after writing anyway, it won't be an issue
|
||||
DhDataOutputStream compressedOut = new DhDataOutputStream(checkedOut);
|
||||
|
||||
// write the contained data
|
||||
dataWriterFunc.writeBufferToFile(compressedOut);
|
||||
compressedOut.flush();
|
||||
this.baseMetaData.checksum = (int) checkedOut.getChecksum().getValue();
|
||||
|
||||
|
||||
fileChannel.position(0);
|
||||
// Write metadata
|
||||
fileChannel.position(0);
|
||||
ByteBuffer buffer = ByteBuffer.allocate(METADATA_SIZE_IN_BYTES);
|
||||
buffer.putInt(METADATA_IDENTITY_BYTES);
|
||||
buffer.putInt(this.pos.sectionX);
|
||||
|
||||
+3
@@ -18,6 +18,9 @@ public interface IWorldGenerationQueue extends Closeable
|
||||
|
||||
void runCurrentGenTasksUntilBusy(DhBlockPos2D targetPos);
|
||||
|
||||
int getWaitingTaskCount();
|
||||
int getInProgressTaskCount();
|
||||
|
||||
CompletableFuture<Void> startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning);
|
||||
void close();
|
||||
}
|
||||
|
||||
+23
-2
@@ -18,6 +18,7 @@ import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
|
||||
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
||||
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.DhThreadFactory;
|
||||
import com.seibel.distanthorizons.core.util.objects.RateLimitedThreadPoolExecutor;
|
||||
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||
@@ -34,7 +35,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
public static final DhThreadFactory THREAD_FACTORY = new DhThreadFactory(ThreadUtil.THREAD_NAME_PREFIX + "Gen-Worker-Thread", Thread.MIN_PRIORITY);
|
||||
public static final DhThreadFactory THREAD_FACTORY = new DhThreadFactory(ThreadUtil.THREAD_NAME_PREFIX + "World-Gen-Worker-Thread", Thread.MIN_PRIORITY);
|
||||
|
||||
private final IDhApiWorldGenerator generator;
|
||||
|
||||
@@ -74,7 +75,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender
|
||||
private final HashMap<DhLodPos, StackTraceElement[]> alreadyGeneratedPosHashSet = new HashMap<>(MAX_ALREADY_GENERATED_COUNT);
|
||||
private final Queue<DhLodPos> alreadyGeneratedPosQueue = new LinkedList<>();
|
||||
|
||||
private static ExecutorService worldGeneratorThreadPool;
|
||||
private static RateLimitedThreadPoolExecutor worldGeneratorThreadPool;
|
||||
private static ConfigChangeListener<Integer> configListener;
|
||||
|
||||
|
||||
@@ -533,6 +534,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender
|
||||
}
|
||||
|
||||
worldGeneratorThreadPool = ThreadUtil.makeRateLimitedThreadPool(threadPoolSize, THREAD_FACTORY, Config.Client.Advanced.MultiThreading.runTimeRatioForWorldGenerationThreads);
|
||||
worldGeneratorThreadPool.setOnTerminatedEventHandler(WorldGenerationQueue::onWorldGenThreadPoolTerminated);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -548,6 +550,20 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender
|
||||
}
|
||||
}
|
||||
|
||||
private static void onWorldGenThreadPoolTerminated()
|
||||
{
|
||||
LOGGER.debug("World generator thread pool terminated. Suggesting the JVM runs a garbage collection to clean up any loose world generation objects...");
|
||||
System.gc();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// getters //
|
||||
//=========//
|
||||
|
||||
public int getWaitingTaskCount() { return this.waitingTasks.size(); }
|
||||
public int getInProgressTaskCount() { return this.inProgressGenTasksByLodPos.size(); }
|
||||
|
||||
|
||||
//==========//
|
||||
@@ -681,6 +697,11 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=======//
|
||||
// debug //
|
||||
//=======//
|
||||
|
||||
@Override
|
||||
public void debugRender(DebugRenderer r)
|
||||
{
|
||||
|
||||
+13
-23
@@ -44,7 +44,6 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug
|
||||
|
||||
private final ConcurrentMap<DhSectionPos, WorldGenQueueEntry> waitingTasks = new ConcurrentHashMap<>();
|
||||
private final Semaphore pendingTasksSemaphore = new Semaphore(Short.MAX_VALUE, true);
|
||||
private int pendingTasks() { return Short.MAX_VALUE - pendingTasksSemaphore.availablePermits(); }
|
||||
|
||||
private CompletableFuture<?> genTaskPriorityRequest = CompletableFuture.completedFuture(null);
|
||||
private final Semaphore genTaskPriorityRequestSemaphore = new Semaphore(1, true);
|
||||
@@ -87,8 +86,8 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug
|
||||
{
|
||||
if (generatorClosingFuture != null || !networkState.getClient().isReady()) return;
|
||||
|
||||
while (waitingTasks.size() > pendingTasks()
|
||||
&& pendingTasks() < this.networkState.config.fullDataRequestRateLimit
|
||||
while (getWaitingTaskCount() > getInProgressTaskCount()
|
||||
&& getInProgressTaskCount() < this.networkState.config.fullDataRequestRateLimit
|
||||
&& pendingTasksSemaphore.tryAcquire())
|
||||
{
|
||||
sendNewRequest(targetPos);
|
||||
@@ -195,21 +194,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug
|
||||
if (chunkDataConsumer == null)
|
||||
return entry.future.cancel(false);
|
||||
|
||||
sectionPos.forEachChildAtLevel(LodUtil.CHUNK_DETAIL_LEVEL, childPos -> {
|
||||
ChunkSizedFullDataAccessor accessor = new ChunkSizedFullDataAccessor(new DhChunkPos(childPos.sectionX, childPos.sectionZ));
|
||||
|
||||
int detailLevelDifference = sectionPos.sectionDetailLevel - childPos.sectionDetailLevel;
|
||||
int childRelativeX = childPos.sectionX - sectionPos.sectionX * BitShiftUtil.powerOfTwo(detailLevelDifference);
|
||||
int childRelativeZ = childPos.sectionZ - sectionPos.sectionZ * BitShiftUtil.powerOfTwo(detailLevelDifference);
|
||||
|
||||
fullDataSource.subView(
|
||||
LodUtil.CHUNK_WIDTH,
|
||||
childRelativeX * LodUtil.CHUNK_WIDTH,
|
||||
childRelativeZ * LodUtil.CHUNK_WIDTH
|
||||
).shadowCopyTo(accessor);
|
||||
|
||||
chunkDataConsumer.accept(accessor);
|
||||
});
|
||||
fullDataSource.splitIntoChunkSizedAccessors(chunkDataConsumer);
|
||||
}
|
||||
catch (ChannelException | RateLimitedException e)
|
||||
{
|
||||
@@ -238,18 +223,23 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug
|
||||
{
|
||||
ArrayList<String> lines = new ArrayList<>();
|
||||
lines.add("World Remote Generation Queue ["+level.getClientLevelWrapper().getDimensionType().getDimensionName()+"]");
|
||||
lines.add(" Requests: "+this.finishedRequests+" / "+(this.waitingTasks.size() + this.finishedRequests.get())+" (failed: "+ this.failedRequests+")");
|
||||
lines.add(" Pending: "+this.pendingTasks()+" / "+this.networkState.config.fullDataRequestRateLimit);
|
||||
lines.add("Requests: "+this.finishedRequests+" / "+(this.getWaitingTaskCount() + this.finishedRequests.get())+" (failed: "+ this.failedRequests+", rate limit: "+this.networkState.config.fullDataRequestRateLimit+")");
|
||||
return lines.toArray(new String[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWaitingTaskCount() { return this.waitingTasks.size(); }
|
||||
@Override
|
||||
public int getInProgressTaskCount() { return Short.MAX_VALUE - pendingTasksSemaphore.availablePermits(); }
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning)
|
||||
{
|
||||
return this.generatorClosingFuture = CompletableFuture.runAsync(() -> {
|
||||
while (!genTaskPriorityRequestSemaphore.tryAcquire())
|
||||
{
|
||||
genTaskPriorityRequest.cancel(false);
|
||||
if (genTaskPriorityRequest.cancel(false))
|
||||
genTaskPriorityRequestSemaphore.release();
|
||||
}
|
||||
|
||||
while (!pendingTasksSemaphore.tryAcquire(Short.MAX_VALUE))
|
||||
@@ -257,8 +247,8 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug
|
||||
for (WorldGenQueueEntry entry : this.waitingTasks.values())
|
||||
{
|
||||
entry.future.cancel(alsoInterruptRunning);
|
||||
if (entry.request != null)
|
||||
entry.request.cancel(alsoInterruptRunning);
|
||||
if (entry.request != null && entry.request.cancel(alsoInterruptRunning))
|
||||
pendingTasksSemaphore.release();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
package com.seibel.distanthorizons.core.level;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDebugRendering;
|
||||
import com.seibel.distanthorizons.core.config.AppliedConfigState;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider;
|
||||
import com.seibel.distanthorizons.core.file.renderfile.RenderSourceFileHandler;
|
||||
import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
|
||||
import com.seibel.distanthorizons.core.generation.WorldRemoteGenerationQueue;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
|
||||
import com.seibel.distanthorizons.core.network.NetworkClient;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
|
||||
import com.seibel.distanthorizons.core.pos.DhLodPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
@@ -22,31 +19,31 @@ import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class ClientLevelModule
|
||||
public class ClientLevelModule implements Closeable
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||
private final IDhClientLevel parent;
|
||||
public final AtomicReference<ClientRenderState> ClientRenderStateRef = new AtomicReference<>();
|
||||
public final F3Screen.NestedMessage f3Message;
|
||||
|
||||
public ClientLevelModule(IDhClientLevel parent)
|
||||
{
|
||||
this.parent = parent; this.f3Message = new F3Screen.NestedMessage(this::f3Log);
|
||||
this.parent = parent;
|
||||
this.f3Message = new F3Screen.NestedMessage(this::f3Log);
|
||||
}
|
||||
|
||||
|
||||
//==============//
|
||||
// tick methods //
|
||||
//==============//
|
||||
|
||||
|
||||
private EDebugRendering lastDebugRendering = EDebugRendering.OFF;
|
||||
|
||||
public void clientTick()
|
||||
@@ -56,8 +53,9 @@ public class ClientLevelModule
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ClientRenderState clientRenderState = this.ClientRenderStateRef.get(); if (clientRenderState == null)
|
||||
|
||||
ClientRenderState clientRenderState = this.ClientRenderStateRef.get();
|
||||
if (clientRenderState == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -70,40 +68,50 @@ public class ClientLevelModule
|
||||
return;
|
||||
}
|
||||
|
||||
clientRenderState.close(); clientRenderState = new ClientRenderState(parent, parent.getFileHandler(), parent.getSaveStructure()); if (!this.ClientRenderStateRef.compareAndSet(null, clientRenderState))
|
||||
{
|
||||
//FIXME: How to handle this?
|
||||
LOGGER.warn("Failed to set render state due to concurrency after changing view distance"); clientRenderState.close(); return;
|
||||
clientRenderState.close();
|
||||
clientRenderState = new ClientRenderState(parent, parent.getFileHandler(), parent.getSaveStructure());
|
||||
if (!this.ClientRenderStateRef.compareAndSet(null, clientRenderState))
|
||||
{
|
||||
//FIXME: How to handle this?
|
||||
LOGGER.warn("Failed to set render state due to concurrency after changing view distance");
|
||||
clientRenderState.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} clientRenderState.quadtree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()));
|
||||
|
||||
boolean isBuffersDirty = false; EDebugRendering newDebugRendering = Config.Client.Advanced.Debugging.debugRendering.get();
|
||||
clientRenderState.quadtree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()));
|
||||
|
||||
boolean isBuffersDirty = false;
|
||||
EDebugRendering newDebugRendering = Config.Client.Advanced.Debugging.debugRendering.get();
|
||||
if (newDebugRendering != lastDebugRendering)
|
||||
{
|
||||
lastDebugRendering = newDebugRendering; isBuffersDirty = true;
|
||||
lastDebugRendering = newDebugRendering;
|
||||
isBuffersDirty = true;
|
||||
}
|
||||
if (isBuffersDirty)
|
||||
{
|
||||
clientRenderState.renderer.bufferHandler.MarkAllBuffersDirty();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//========//
|
||||
// render //
|
||||
//========//
|
||||
|
||||
|
||||
/** @return if the {@link ClientRenderState} was successfully swapped */
|
||||
public boolean startRenderer()
|
||||
{
|
||||
ClientRenderState ClientRenderState = new ClientRenderState(parent, parent.getFileHandler(), parent.getSaveStructure()); if (!this.ClientRenderStateRef.compareAndSet(null, ClientRenderState))
|
||||
{
|
||||
LOGGER.warn("Failed to start renderer due to concurrency"); ClientRenderState.close(); return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
ClientRenderState ClientRenderState = new ClientRenderState(parent, parent.getFileHandler(), parent.getSaveStructure());
|
||||
if (!this.ClientRenderStateRef.compareAndSet(null, ClientRenderState))
|
||||
{
|
||||
LOGGER.warn("Failed to start renderer due to concurrency");
|
||||
ClientRenderState.close();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRendering()
|
||||
@@ -113,27 +121,34 @@ public class ClientLevelModule
|
||||
|
||||
public void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler)
|
||||
{
|
||||
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); if (ClientRenderState == null)
|
||||
{
|
||||
// either the renderer hasn't been started yet, or is being reloaded
|
||||
return;
|
||||
} ClientRenderState.renderer.drawLODs(mcModelViewMatrix, mcProjectionMatrix, partialTicks, profiler);
|
||||
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get();
|
||||
if (ClientRenderState == null)
|
||||
{
|
||||
// either the renderer hasn't been started yet, or is being reloaded
|
||||
return;
|
||||
}
|
||||
ClientRenderState.renderer.drawLODs(mcModelViewMatrix, mcProjectionMatrix, partialTicks, profiler);
|
||||
}
|
||||
|
||||
public void stopRenderer()
|
||||
{
|
||||
LOGGER.info("Stopping renderer for " + this); ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); if (ClientRenderState == null)
|
||||
{
|
||||
LOGGER.warn("Tried to stop renderer for " + this + " when it was not started!"); return;
|
||||
}
|
||||
LOGGER.info("Stopping renderer for " + this);
|
||||
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get();
|
||||
if (ClientRenderState == null)
|
||||
{
|
||||
LOGGER.warn("Tried to stop renderer for " + this + " when it was not started!");
|
||||
return;
|
||||
}
|
||||
// stop the render state
|
||||
while (!this.ClientRenderStateRef.compareAndSet(ClientRenderState, null)) // TODO why is there a while loop here?
|
||||
{
|
||||
ClientRenderState = this.ClientRenderStateRef.get(); if (ClientRenderState == null)
|
||||
{
|
||||
return;
|
||||
ClientRenderState = this.ClientRenderStateRef.get();
|
||||
if (ClientRenderState == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
} ClientRenderState.close();
|
||||
ClientRenderState.close();
|
||||
}
|
||||
|
||||
//===============//
|
||||
@@ -141,47 +156,54 @@ public class ClientLevelModule
|
||||
//===============//
|
||||
public void saveWrites(ChunkSizedFullDataAccessor data)
|
||||
{
|
||||
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); DhLodPos pos = data.getLodPos().convertToDetailLevel(CompleteFullDataSource.SECTION_SIZE_OFFSET); if (ClientRenderState != null)
|
||||
{
|
||||
ClientRenderState.renderSourceFileHandler.writeChunkDataToFile(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data);
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.getFileHandler().write(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data);
|
||||
}
|
||||
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get();
|
||||
DhLodPos pos = data.getLodPos().convertToDetailLevel(CompleteFullDataSource.SECTION_SIZE_OFFSET);
|
||||
if (ClientRenderState != null)
|
||||
{
|
||||
ClientRenderState.renderSourceFileHandler.writeChunkDataToFile(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data);
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.getFileHandler().write(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data);
|
||||
}
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> saveAsync()
|
||||
{
|
||||
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); if (ClientRenderState != null)
|
||||
{
|
||||
return ClientRenderState.renderSourceFileHandler.flushAndSaveAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get();
|
||||
if (ClientRenderState != null)
|
||||
{
|
||||
return ClientRenderState.renderSourceFileHandler.flushAndSaveAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void close()
|
||||
{
|
||||
// shutdown the renderer
|
||||
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); if (ClientRenderState != null)
|
||||
{
|
||||
// TODO does this have to be in a while loop, if so why?
|
||||
while (!this.ClientRenderStateRef.compareAndSet(ClientRenderState, null))
|
||||
{
|
||||
ClientRenderState = this.ClientRenderStateRef.get(); if (ClientRenderState == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get();
|
||||
if (ClientRenderState != null)
|
||||
{
|
||||
ClientRenderState.close();
|
||||
// TODO does this have to be in a while loop, if so why?
|
||||
while (!this.ClientRenderStateRef.compareAndSet(ClientRenderState, null))
|
||||
{
|
||||
ClientRenderState = this.ClientRenderStateRef.get();
|
||||
if (ClientRenderState == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ClientRenderState != null)
|
||||
{
|
||||
ClientRenderState.close();
|
||||
}
|
||||
}
|
||||
} f3Message.close();
|
||||
|
||||
this.f3Message.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -199,30 +221,34 @@ public class ClientLevelModule
|
||||
/** Returns what should be displayed in Minecraft's F3 debug menu */
|
||||
protected String[] f3Log()
|
||||
{
|
||||
String dimName = parent.getClientLevelWrapper().getDimensionType().getDimensionName(); ClientRenderState renderState = this.ClientRenderStateRef.get(); if (renderState == null)
|
||||
{
|
||||
return new String[]{"level @ " + dimName + ": Inactive"};
|
||||
}
|
||||
else
|
||||
{
|
||||
return new String[]{"level @ " + dimName + ": Active"};
|
||||
}
|
||||
String dimName = parent.getClientLevelWrapper().getDimensionType().getDimensionName();
|
||||
ClientRenderState renderState = this.ClientRenderStateRef.get();
|
||||
if (renderState == null)
|
||||
{
|
||||
return new String[]{"level @ " + dimName + ": Inactive"};
|
||||
}
|
||||
else
|
||||
{
|
||||
return new String[]{"level @ " + dimName + ": Active"};
|
||||
}
|
||||
}
|
||||
|
||||
public void clearRenderCache()
|
||||
{
|
||||
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); if (ClientRenderState != null && ClientRenderState.quadtree != null)
|
||||
{
|
||||
ClientRenderState.quadtree.clearRenderDataCache();
|
||||
}
|
||||
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get();
|
||||
if (ClientRenderState != null && ClientRenderState.quadtree != null)
|
||||
{
|
||||
ClientRenderState.quadtree.clearRenderDataCache();
|
||||
}
|
||||
}
|
||||
|
||||
public void reloadPos(DhSectionPos pos)
|
||||
{
|
||||
ClientRenderState clientRenderState = this.ClientRenderStateRef.get(); if (clientRenderState != null && clientRenderState.quadtree != null)
|
||||
{
|
||||
clientRenderState.quadtree.reloadPos(pos);
|
||||
}
|
||||
ClientRenderState clientRenderState = this.ClientRenderStateRef.get();
|
||||
if (clientRenderState != null && clientRenderState.quadtree != null)
|
||||
{
|
||||
clientRenderState.quadtree.reloadPos(pos);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ClientRenderState
|
||||
@@ -236,15 +262,19 @@ public class ClientLevelModule
|
||||
public final LodRenderer renderer;
|
||||
|
||||
public ClientRenderState(
|
||||
IDhClientLevel dhClientLevel, IFullDataSourceProvider fullDataSourceProvider, AbstractSaveStructure saveStructure)
|
||||
IDhClientLevel dhClientLevel, IFullDataSourceProvider fullDataSourceProvider,
|
||||
AbstractSaveStructure saveStructure)
|
||||
{
|
||||
this.levelWrapper = dhClientLevel.getLevelWrapper(); this.renderSourceFileHandler = new RenderSourceFileHandler(fullDataSourceProvider, dhClientLevel, saveStructure);
|
||||
this.levelWrapper = dhClientLevel.getLevelWrapper();
|
||||
this.renderSourceFileHandler = new RenderSourceFileHandler(fullDataSourceProvider, dhClientLevel, saveStructure);
|
||||
|
||||
this.quadtree = new LodQuadTree(dhClientLevel, Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH,
|
||||
// initial position is (0,0) just in case the player hasn't loaded in yet, the tree will be moved once the level starts ticking
|
||||
0, 0, this.renderSourceFileHandler);
|
||||
0, 0,
|
||||
this.renderSourceFileHandler);
|
||||
|
||||
RenderBufferHandler renderBufferHandler = new RenderBufferHandler(this.quadtree); this.renderer = new LodRenderer(renderBufferHandler);
|
||||
RenderBufferHandler renderBufferHandler = new RenderBufferHandler(this.quadtree);
|
||||
this.renderer = new LodRenderer(renderBufferHandler);
|
||||
}
|
||||
|
||||
|
||||
@@ -253,7 +283,9 @@ public class ClientLevelModule
|
||||
{
|
||||
LOGGER.info("Shutting down " + ClientRenderState.class.getSimpleName());
|
||||
|
||||
this.renderer.close(); this.quadtree.close(); this.renderSourceFileHandler.close();
|
||||
this.renderer.close();
|
||||
this.quadtree.close();
|
||||
this.renderSourceFileHandler.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.seibel.distanthorizons.core.level;
|
||||
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider;
|
||||
import com.seibel.distanthorizons.core.file.fullDatafile.RemoteFullDataFileHandler;
|
||||
@@ -8,6 +9,9 @@ import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
|
||||
import com.seibel.distanthorizons.core.generation.WorldRemoteGenerationQueue;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.multiplayer.ClientNetworkState;
|
||||
import com.seibel.distanthorizons.core.network.NetworkClient;
|
||||
import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource;
|
||||
import com.seibel.distanthorizons.core.network.messages.FullDataPartialUpdateMessage;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
@@ -47,6 +51,8 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel
|
||||
|
||||
@CheckForNull
|
||||
private final ClientNetworkState networkState;
|
||||
@Nullable
|
||||
private final ScopedNetworkEventSource<NetworkClient> eventSource;
|
||||
public final WorldGenModule worldGenModule;
|
||||
|
||||
|
||||
@@ -63,12 +69,41 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel
|
||||
|
||||
this.networkState = networkState;
|
||||
this.worldGenModule = new WorldGenModule(dataFileHandler, this);
|
||||
if (networkState != null)
|
||||
{
|
||||
this.eventSource = new ScopedNetworkEventSource<>(networkState.getClient());
|
||||
this.registerNetworkHandlers();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.eventSource = null;
|
||||
}
|
||||
|
||||
clientside = new ClientLevelModule(this);
|
||||
clientside.startRenderer();
|
||||
LOGGER.info("Started DHLevel for " + this.levelWrapper + " with saves at " + this.saveStructure);
|
||||
}
|
||||
|
||||
private void registerNetworkHandlers()
|
||||
{
|
||||
assert this.eventSource != null;
|
||||
|
||||
this.eventSource.registerHandler(FullDataPartialUpdateMessage.class, msg ->
|
||||
{
|
||||
try
|
||||
{
|
||||
ChunkSizedFullDataAccessor fullDataAccessor = msg.getFullDataSource(this);
|
||||
if (fullDataAccessor == null) return;
|
||||
|
||||
this.saveWrites(fullDataAccessor);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.error("Error while updating full data source", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//==============//
|
||||
// tick methods //
|
||||
//==============//
|
||||
|
||||
@@ -32,7 +32,8 @@ public class DhClientServerLevel extends DhLevel implements IDhClientLevel, IDhS
|
||||
public final ClientLevelModule clientside;
|
||||
|
||||
private final IServerLevelWrapper serverLevelWrapper;
|
||||
public IClientLevelWrapper clientLevelWrapper;
|
||||
|
||||
|
||||
|
||||
public DhClientServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper)
|
||||
{
|
||||
@@ -41,11 +42,13 @@ public class DhClientServerLevel extends DhLevel implements IDhClientLevel, IDhS
|
||||
LOGGER.warn("unable to create data folder.");
|
||||
}
|
||||
this.serverLevelWrapper = serverLevelWrapper;
|
||||
serverside = new ServerLevelModule(this, serverLevelWrapper, saveStructure);
|
||||
clientside = new ClientLevelModule(this);
|
||||
this.serverside = new ServerLevelModule(this, serverLevelWrapper, saveStructure);
|
||||
this.clientside = new ClientLevelModule(this);
|
||||
LOGGER.info("Started " + DhClientServerLevel.class.getSimpleName() + " for " + serverLevelWrapper + " with saves at " + saveStructure);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// tick methods //
|
||||
//==============//
|
||||
@@ -105,11 +108,7 @@ public class DhClientServerLevel extends DhLevel implements IDhClientLevel, IDhS
|
||||
clientside.startRenderer();
|
||||
}
|
||||
|
||||
public void stopRenderer()
|
||||
{
|
||||
clientside.stopRenderer();
|
||||
clientLevelWrapper = null;
|
||||
}
|
||||
public void stopRenderer() { this.clientside.stopRenderer(); }
|
||||
|
||||
//================//
|
||||
// level handling //
|
||||
|
||||
@@ -25,7 +25,7 @@ public abstract class DhLevel implements IDhLevel
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateChunkAsync(IChunkWrapper chunk)
|
||||
public CompletableFuture<ChunkSizedFullDataAccessor> updateChunkAsync(IChunkWrapper chunk)
|
||||
{
|
||||
CompletableFuture<ChunkSizedFullDataAccessor> future = this.chunkToLodBuilder.tryGenerateData(chunk);
|
||||
if (future != null)
|
||||
@@ -44,6 +44,7 @@ public abstract class DhLevel implements IDhLevel
|
||||
new DhApiChunkModifiedEvent.EventParam(this.getLevelWrapper(), chunk.getChunkPos().x, chunk.getChunkPos().z));
|
||||
});
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,10 +15,12 @@ import com.seibel.distanthorizons.core.network.NetworkServer;
|
||||
import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException;
|
||||
import com.seibel.distanthorizons.core.network.messages.*;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhLodPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||
@@ -26,9 +28,7 @@ import com.seibel.distanthorizons.coreapi.util.math.Vec3d;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class DhServerLevel extends DhLevel implements IDhServerLevel
|
||||
@@ -40,7 +40,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel
|
||||
private final RemotePlayerConnectionHandler remotePlayerConnectionHandler;
|
||||
private final ScopedNetworkEventSource<NetworkServer> eventSource;
|
||||
|
||||
private final ConcurrentLinkedQueue<IServerPlayerWrapper> worldGenLoopingQueue = new ConcurrentLinkedQueue<>();
|
||||
private final LinkedBlockingQueue<IServerPlayerWrapper> worldGenLoopingQueue = new LinkedBlockingQueue<>();
|
||||
private final ConcurrentMap<DhSectionPos, IncompleteDataSourceEntry> incompleteDataSources = new ConcurrentHashMap<>();
|
||||
private final ConcurrentMap<Long, IncompleteDataSourceEntry> fullDataRequests = new ConcurrentHashMap<>();
|
||||
private final AppliedConfigState<Integer> rateLimitConfig = new AppliedConfigState<>(Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit);
|
||||
@@ -66,12 +66,16 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel
|
||||
// TODO implement transparent message handling restriction by level
|
||||
// workaround:
|
||||
// ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg);
|
||||
// if (serverPlayerState == null) return;
|
||||
//
|
||||
// if (serverPlayerState.serverPlayer.getLevel() != this.serverLevelWrapper)
|
||||
// return;
|
||||
|
||||
this.eventSource.registerHandler(FullDataSourceRequestMessage.class, msg ->
|
||||
{
|
||||
ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg);
|
||||
if (serverPlayerState == null) return;
|
||||
|
||||
if (serverPlayerState.serverPlayer.getLevel() != this.serverLevelWrapper)
|
||||
return;
|
||||
|
||||
@@ -106,6 +110,8 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel
|
||||
|
||||
this.eventSource.registerHandler(GenTaskPriorityRequestMessage.class, msg -> {
|
||||
ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg);
|
||||
if (serverPlayerState == null) return;
|
||||
|
||||
if (serverPlayerState.serverPlayer.getLevel() != this.serverLevelWrapper)
|
||||
return;
|
||||
|
||||
@@ -120,7 +126,9 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel
|
||||
if (entry == null) return;
|
||||
FullDataSourceRequestMessage requestMessage = entry.requestMessages.remove(msg.futureId);
|
||||
|
||||
remotePlayerConnectionHandler.getConnectedPlayer(msg).pendingFullDataRequests.decrementAndGet();
|
||||
ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg);
|
||||
if (serverPlayerState != null)
|
||||
serverPlayerState.pendingFullDataRequests.decrementAndGet();
|
||||
|
||||
entry.requestCollectionSemaphore.acquireUninterruptibly(Short.MAX_VALUE);
|
||||
if (entry.requestMessages.isEmpty())
|
||||
@@ -135,18 +143,12 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel
|
||||
|
||||
public void addPlayer(IServerPlayerWrapper serverPlayer)
|
||||
{
|
||||
synchronized (worldGenLoopingQueue)
|
||||
{
|
||||
this.worldGenLoopingQueue.add(serverPlayer);
|
||||
}
|
||||
this.worldGenLoopingQueue.add(serverPlayer);
|
||||
}
|
||||
|
||||
public void removePlayer(IServerPlayerWrapper serverPlayer)
|
||||
{
|
||||
synchronized (worldGenLoopingQueue)
|
||||
{
|
||||
this.worldGenLoopingQueue.remove(serverPlayer);
|
||||
}
|
||||
boolean ignored = this.worldGenLoopingQueue.remove(serverPlayer);
|
||||
}
|
||||
|
||||
public void serverTick()
|
||||
@@ -189,6 +191,28 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChunkSizedFullDataAccessor> updateChunkAsync(IChunkWrapper chunk)
|
||||
{
|
||||
CompletableFuture<ChunkSizedFullDataAccessor> future = super.updateChunkAsync(chunk);
|
||||
if (future == null)
|
||||
return null;
|
||||
|
||||
future.thenAccept(chunkSizedFullDataAccessor ->
|
||||
{
|
||||
for (IServerPlayerWrapper serverPlayer : worldGenLoopingQueue)
|
||||
{
|
||||
ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getPlayer(serverPlayer);
|
||||
if (serverPlayerState == null) continue;
|
||||
|
||||
if (chunk.getChunkPos().distance(new DhChunkPos(serverPlayer.getPosition())) <= serverPlayerState.config.renderDistance)
|
||||
serverPlayerState.channelContext.writeAndFlush(new FullDataPartialUpdateMessage(chunkSizedFullDataAccessor, this));
|
||||
}
|
||||
});
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveWrites(ChunkSizedFullDataAccessor data)
|
||||
{
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.seibel.distanthorizons.core.level;
|
||||
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider;
|
||||
import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@@ -20,7 +22,8 @@ public interface IDhLevel extends AutoCloseable
|
||||
*/
|
||||
ILevelWrapper getLevelWrapper();
|
||||
|
||||
void updateChunkAsync(IChunkWrapper chunk);
|
||||
@Nullable
|
||||
CompletableFuture<ChunkSizedFullDataAccessor> updateChunkAsync(IChunkWrapper chunk);
|
||||
|
||||
IFullDataSourceProvider getFileHandler();
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.seibel.distanthorizons.core.level;
|
||||
import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataFileHandler;
|
||||
import com.seibel.distanthorizons.core.generation.IWorldGenerationQueue;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@@ -16,7 +17,9 @@ public class WorldGenModule implements Closeable
|
||||
|
||||
private final GeneratedFullDataFileHandler dataFileHandler;
|
||||
private final GeneratedFullDataFileHandler.IOnWorldGenCompleteListener onWorldGenCompleteListener;
|
||||
|
||||
private final AtomicReference<WorldGenState> worldGenStateRef = new AtomicReference<>();
|
||||
private final F3Screen.DynamicMessage worldGenF3Message;
|
||||
|
||||
public static abstract class WorldGenState
|
||||
{
|
||||
@@ -48,6 +51,22 @@ public class WorldGenModule implements Closeable
|
||||
{
|
||||
this.dataFileHandler = dataFileHandler;
|
||||
this.onWorldGenCompleteListener = onWorldGenCompleteListener;
|
||||
this.worldGenF3Message = new F3Screen.DynamicMessage(() ->
|
||||
{
|
||||
WorldGenState worldGenState = this.worldGenStateRef.get();
|
||||
if (worldGenState != null)
|
||||
{
|
||||
int waiting = worldGenState.worldGenerationQueue.getWaitingTaskCount();
|
||||
int inProgress = worldGenState.worldGenerationQueue.getInProgressTaskCount();
|
||||
|
||||
return "World Gen Tasks: "+waiting+", (in progress: "+inProgress+")";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "World Gen Disabled";
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void startWorldGen(GeneratedFullDataFileHandler dataFileHandler, WorldGenState newWgs)
|
||||
@@ -121,5 +140,6 @@ public class WorldGenModule implements Closeable
|
||||
}
|
||||
}
|
||||
dataFileHandler.close();
|
||||
this.worldGenF3Message.close();
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -2,7 +2,6 @@ package com.seibel.distanthorizons.core.multiplayer;
|
||||
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.network.IClientRequestHandler;
|
||||
import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource;
|
||||
import com.seibel.distanthorizons.core.network.NetworkClient;
|
||||
import com.seibel.distanthorizons.core.network.messages.AckMessage;
|
||||
@@ -10,6 +9,7 @@ import com.seibel.distanthorizons.core.network.messages.HelloMessage;
|
||||
import com.seibel.distanthorizons.core.network.messages.PlayerUUIDMessage;
|
||||
import com.seibel.distanthorizons.core.network.messages.RemotePlayerConfigMessage;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.UUID;
|
||||
@@ -26,7 +26,7 @@ public class ClientNetworkState implements Closeable
|
||||
* Returns the client used by this instance. <p>
|
||||
* If you need to subscribe to any packet events, create an instance of {@link ScopedNetworkEventSource} using the returned instance.
|
||||
*/
|
||||
public IClientRequestHandler getClient() { return this.client; }
|
||||
public NetworkClient getClient() { return this.client; }
|
||||
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
|
||||
+3
@@ -10,6 +10,7 @@ import com.seibel.distanthorizons.core.network.messages.PlayerUUIDMessage;
|
||||
import com.seibel.distanthorizons.core.network.protocol.NetworkMessage;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.HashMap;
|
||||
@@ -69,11 +70,13 @@ public class RemotePlayerConnectionHandler implements Closeable
|
||||
return playersByConnection.values();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ServerPlayerState getConnectedPlayer(NetworkMessage msg)
|
||||
{
|
||||
return playersByConnection.get(msg.getChannelContext());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ServerPlayerState getPlayer(IServerPlayerWrapper serverPlayer)
|
||||
{
|
||||
return playersByUUID.get(serverPlayer.getUUID());
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
package com.seibel.distanthorizons.core.network;
|
||||
|
||||
import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface IClientRequestHandler
|
||||
{
|
||||
/** Indicates whether the client is initialized and not started connecting yet. */
|
||||
boolean isInitialState();
|
||||
/** Indicates whether the client is closed(-ing) and should not be used. */
|
||||
boolean isClosed();
|
||||
/** Indicates whether the connection is established and first message is sent. */
|
||||
boolean isReady();
|
||||
|
||||
/** Sends a new request. */
|
||||
<TResponse extends FutureTrackableNetworkMessage> CompletableFuture<TResponse> sendRequest(FutureTrackableNetworkMessage msg);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class NetworkClient extends NetworkEventSource implements IClientRequestHandler, AutoCloseable
|
||||
public class NetworkClient extends NetworkEventSource implements AutoCloseable
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
|
||||
@@ -5,8 +5,10 @@ import com.google.common.collect.Table;
|
||||
import com.google.common.collect.Tables;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.network.messages.CancelMessage;
|
||||
import com.seibel.distanthorizons.core.network.messages.CloseEvent;
|
||||
import com.seibel.distanthorizons.core.network.messages.ExceptionMessage;
|
||||
import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage;
|
||||
import com.seibel.distanthorizons.core.network.protocol.MessageRegistry;
|
||||
import com.seibel.distanthorizons.core.network.protocol.NetworkMessage;
|
||||
import io.netty.channel.ChannelException;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
@@ -75,7 +77,13 @@ public abstract class NetworkEventSource
|
||||
|
||||
public <T extends NetworkMessage> void registerHandler(Class<T> handlerClass, Consumer<T> handlerImplementation)
|
||||
{
|
||||
this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> new HashSet<>())
|
||||
this.handlers.computeIfAbsent(handlerClass, missingHandlerClass ->
|
||||
{
|
||||
// Will throw if the handler class is not found
|
||||
if (handlerClass != CloseEvent.class)
|
||||
MessageRegistry.INSTANCE.getMessageId(handlerClass);
|
||||
return new HashSet<>();
|
||||
})
|
||||
.add((Consumer<NetworkMessage>) handlerImplementation);
|
||||
}
|
||||
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package com.seibel.distanthorizons.core.network.messages;
|
||||
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.level.DhServerLevel;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufInputStream;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class FullDataPartialUpdateMessage extends FutureTrackableNetworkMessage
|
||||
{
|
||||
private ChunkSizedFullDataAccessor fullDataAccessor;
|
||||
private DhServerLevel level;
|
||||
|
||||
private int levelHashCode;
|
||||
private DhChunkPos chunkPos;
|
||||
private ByteBuf dataBuffer;
|
||||
|
||||
public FullDataPartialUpdateMessage() {}
|
||||
public FullDataPartialUpdateMessage(ChunkSizedFullDataAccessor fullDataAccessor, DhServerLevel level)
|
||||
{
|
||||
this.fullDataAccessor = fullDataAccessor;
|
||||
this.level = level;
|
||||
|
||||
// TODO Multiverse support
|
||||
this.levelHashCode = level.getLevelWrapper().getDimensionType().getDimensionName().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode0(ByteBuf out) throws IOException
|
||||
{
|
||||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream())
|
||||
{
|
||||
DhDataOutputStream dhOutputStream = new DhDataOutputStream(outputStream);
|
||||
fullDataAccessor.writeToStream(dhOutputStream, level);
|
||||
dhOutputStream.flush();
|
||||
|
||||
out.writeInt(levelHashCode);
|
||||
|
||||
out.writeInt(fullDataAccessor.pos.x);
|
||||
out.writeInt(fullDataAccessor.pos.z);
|
||||
|
||||
out.writeInt(outputStream.size());
|
||||
out.writeBytes(outputStream.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode0(ByteBuf in)
|
||||
{
|
||||
levelHashCode = in.readInt();
|
||||
|
||||
chunkPos = new DhChunkPos(in.readInt(), in.readInt());
|
||||
|
||||
this.dataBuffer = in.readBytes(in.readInt());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ChunkSizedFullDataAccessor getFullDataSource(IDhLevel level) throws IOException, InterruptedException
|
||||
{
|
||||
// TODO Multiverse support
|
||||
if (levelHashCode != level.getLevelWrapper().getDimensionType().getDimensionName().hashCode())
|
||||
return null;
|
||||
|
||||
try (ByteBufInputStream inputStream = new ByteBufInputStream(dataBuffer))
|
||||
{
|
||||
ChunkSizedFullDataAccessor result = new ChunkSizedFullDataAccessor(chunkPos);
|
||||
result.populateFromStream(new DhDataInputStream(inputStream), level);
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
dataBuffer.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
@@ -71,5 +71,9 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage
|
||||
{
|
||||
return fullDataSourceLoader.loadData(pos, new DhDataInputStream(inputStream), level);
|
||||
}
|
||||
finally
|
||||
{
|
||||
dataBuffer.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -35,9 +35,10 @@ public class MessageRegistry
|
||||
this.registerMessage(PlayerUUIDMessage.class, PlayerUUIDMessage::new);
|
||||
this.registerMessage(RemotePlayerConfigMessage.class, RemotePlayerConfigMessage::new);
|
||||
|
||||
// Full data requests
|
||||
// Full data requests & updates
|
||||
this.registerMessage(FullDataSourceRequestMessage.class, FullDataSourceRequestMessage::new);
|
||||
this.registerMessage(FullDataSourceResponseMessage.class, FullDataSourceResponseMessage::new);
|
||||
this.registerMessage(FullDataPartialUpdateMessage.class, FullDataPartialUpdateMessage::new);
|
||||
|
||||
// Generation task prioritization
|
||||
this.registerMessage(GenTaskPriorityRequestMessage.class, GenTaskPriorityRequestMessage::new);
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.pos;
|
||||
|
||||
import com.seibel.distanthorizons.coreapi.util.math.Vec3d;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class DhChunkPos
|
||||
@@ -48,6 +50,10 @@ public class DhChunkPos
|
||||
// >> 4 is the Same as div 16
|
||||
this(blockPos.x >> 4, blockPos.z >> 4);
|
||||
}
|
||||
public DhChunkPos(Vec3d pos)
|
||||
{
|
||||
this(((int)pos.x) >> 4, ((int)pos.z) >> 4);
|
||||
}
|
||||
public DhChunkPos(long packed) { this(getX(packed), getZ(packed)); }
|
||||
|
||||
|
||||
@@ -72,6 +78,11 @@ public class DhChunkPos
|
||||
|
||||
public long getLong() { return toLong(x, z); }
|
||||
|
||||
public double distance(DhChunkPos other)
|
||||
{
|
||||
return Math.sqrt(Math.pow(x - other.x, 2) + Math.pow(z - other.z, 2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
|
||||
@@ -329,8 +329,6 @@ public class DebugRenderer
|
||||
|
||||
public void render(Mat4f transform)
|
||||
{
|
||||
if (!Config.Client.Advanced.Debugging.debugWireframeRendering.get()) return;
|
||||
|
||||
transform_this_frame = transform;
|
||||
Vec3d cam = MC_RENDER.getCameraExactPosition();
|
||||
camf = new Vec3f((float) cam.x, (float) cam.y, (float) cam.z);
|
||||
|
||||
@@ -301,7 +301,15 @@ public class LodRenderer
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
this.shaderProgram.unbind();
|
||||
DebugRenderer.INSTANCE.render(modelViewProjectionMatrix);
|
||||
|
||||
if (Config.Client.Advanced.Debugging.debugWireframeRendering.get())
|
||||
{
|
||||
profiler.popPush("Debug wireframes");
|
||||
// Note: this can be very slow if a lot of boxes are being rendered
|
||||
DebugRenderer.INSTANCE.render(modelViewProjectionMatrix);
|
||||
profiler.popPush("LOD cleanup");
|
||||
}
|
||||
|
||||
GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
minecraftGlState.restore();
|
||||
|
||||
+21
@@ -15,6 +15,8 @@ public class RateLimitedThreadPoolExecutor extends ThreadPoolExecutor
|
||||
/** How long it took this thread to run its last task */
|
||||
private final ThreadLocal<Long> lastRunDurationNanoTimeRef = ThreadLocal.withInitial(() -> -1L);
|
||||
|
||||
private Runnable onTerminatedEventHandler = null;
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
@@ -63,4 +65,23 @@ public class RateLimitedThreadPoolExecutor extends ThreadPoolExecutor
|
||||
this.lastRunDurationNanoTimeRef.set(System.nanoTime() - this.runStartNanoTimeRef.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void terminated()
|
||||
{
|
||||
super.terminated();
|
||||
if (this.onTerminatedEventHandler != null)
|
||||
{
|
||||
this.onTerminatedEventHandler.run();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// custom logic //
|
||||
//==============//
|
||||
|
||||
/** only one event handler can be present at a time */
|
||||
public void setOnTerminatedEventHandler(Runnable runnable) { this.onTerminatedEventHandler = runnable; }
|
||||
|
||||
}
|
||||
@@ -19,9 +19,9 @@ import java.util.concurrent.ExecutorService;
|
||||
|
||||
public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWorld, IDhServerWorld
|
||||
{
|
||||
private final HashMap<ILevelWrapper, DhClientServerLevel> levelObjMap;
|
||||
private final HashSet<DhClientServerLevel> dhLevels;
|
||||
public final LocalSaveStructure saveStructure;
|
||||
private final HashMap<ILevelWrapper, DhClientServerLevel> levelWrapperByDhLevel = new HashMap<>();
|
||||
private final HashSet<DhClientServerLevel> dhLevels = new HashSet<>();
|
||||
public final LocalSaveStructure saveStructure = new LocalSaveStructure();
|
||||
|
||||
public ExecutorService dhTickerThread = ThreadUtil.makeSingleThreadPool("DH Client Server World Ticker Thread", 2);
|
||||
public EventLoop eventLoop = new EventLoop(this.dhTickerThread, this::_clientTick); //TODO: Rate-limit the loop
|
||||
@@ -30,12 +30,13 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public DhClientServerWorld()
|
||||
{
|
||||
super(EWorldEnvironment.Client_Server);
|
||||
this.saveStructure = new LocalSaveStructure();
|
||||
this.levelObjMap = new HashMap<>();
|
||||
this.dhLevels = new HashSet<>();
|
||||
|
||||
LOGGER.info("Started DhWorld of type " + this.environment);
|
||||
|
||||
@@ -44,12 +45,16 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// methods //
|
||||
//=========//
|
||||
|
||||
@Override
|
||||
public DhClientServerLevel getOrLoadLevel(ILevelWrapper wrapper)
|
||||
{
|
||||
if (wrapper instanceof IServerLevelWrapper)
|
||||
{
|
||||
return this.levelObjMap.computeIfAbsent(wrapper, (levelWrapper) ->
|
||||
return this.levelWrapperByDhLevel.computeIfAbsent(wrapper, (levelWrapper) ->
|
||||
{
|
||||
File levelFile = this.saveStructure.getLevelFolder(levelWrapper);
|
||||
LodUtil.assertTrue(levelFile != null);
|
||||
@@ -60,7 +65,7 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.levelObjMap.computeIfAbsent(wrapper, (levelWrapper) ->
|
||||
return this.levelWrapperByDhLevel.computeIfAbsent(wrapper, (levelWrapper) ->
|
||||
{
|
||||
IClientLevelWrapper clientLevelWrapper = (IClientLevelWrapper) levelWrapper;
|
||||
IServerLevelWrapper serverLevelWrapper = clientLevelWrapper.tryGetServerSideWrapper();
|
||||
@@ -68,7 +73,7 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor
|
||||
LodUtil.assertTrue(clientLevelWrapper.getDimensionType().equals(serverLevelWrapper.getDimensionType()), "tryGetServerSideWrapper returned a level for a different dimension. ClientLevelWrapper dim: " + clientLevelWrapper.getDimensionType().getDimensionName() + " ServerLevelWrapper dim: " + serverLevelWrapper.getDimensionType().getDimensionName());
|
||||
|
||||
|
||||
DhClientServerLevel level = this.levelObjMap.get(serverLevelWrapper);
|
||||
DhClientServerLevel level = this.levelWrapperByDhLevel.get(serverLevelWrapper);
|
||||
if (level == null)
|
||||
{
|
||||
return null;
|
||||
@@ -81,7 +86,7 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor
|
||||
}
|
||||
|
||||
@Override
|
||||
public DhClientServerLevel getLevel(ILevelWrapper wrapper) { return this.levelObjMap.get(wrapper); }
|
||||
public DhClientServerLevel getLevel(ILevelWrapper wrapper) { return this.levelWrapperByDhLevel.get(wrapper); }
|
||||
|
||||
@Override
|
||||
public Iterable<? extends IDhLevel> getAllLoadedLevels() { return this.dhLevels; }
|
||||
@@ -89,21 +94,23 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor
|
||||
@Override
|
||||
public void unloadLevel(ILevelWrapper wrapper)
|
||||
{
|
||||
if (this.levelObjMap.containsKey(wrapper))
|
||||
if (this.levelWrapperByDhLevel.containsKey(wrapper))
|
||||
{
|
||||
if (wrapper instanceof IServerLevelWrapper)
|
||||
{
|
||||
LOGGER.info("Unloading level " + this.levelObjMap.get(wrapper));
|
||||
DhClientServerLevel clientServerLevel = this.levelObjMap.remove(wrapper);
|
||||
this.dhLevels.remove(clientServerLevel);
|
||||
LOGGER.info("Unloading level " + this.levelWrapperByDhLevel.get(wrapper));
|
||||
wrapper.onUnload();
|
||||
|
||||
DhClientServerLevel clientServerLevel = this.levelWrapperByDhLevel.remove(wrapper);
|
||||
clientServerLevel.close();
|
||||
this.dhLevels.remove(clientServerLevel);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the level wrapper is a Client Level Wrapper, then that means the client side leaves the level,
|
||||
// but note that the server side still has the level loaded. So, we don't want to unload the level,
|
||||
// we just want to stop rendering it.
|
||||
this.levelObjMap.remove(wrapper).stopRenderer(); // Ignore resource warning. The level obj is referenced elsewhere.
|
||||
this.levelWrapperByDhLevel.remove(wrapper).stopRenderer(); // Ignore resource warning. The level obj is referenced elsewhere.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,10 +144,18 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor
|
||||
for (DhClientServerLevel level : this.dhLevels)
|
||||
{
|
||||
LOGGER.info("Unloading level " + level.getServerLevelWrapper().getDimensionType().getDimensionName());
|
||||
|
||||
// level wrapper shouldn't be null, but just in case
|
||||
IServerLevelWrapper serverLevelWrapper = level.getServerLevelWrapper();
|
||||
if (serverLevelWrapper != null)
|
||||
{
|
||||
serverLevelWrapper.onUnload();
|
||||
}
|
||||
|
||||
level.close();
|
||||
}
|
||||
|
||||
this.levelObjMap.clear();
|
||||
this.levelWrapperByDhLevel.clear();
|
||||
this.eventLoop.close();
|
||||
LOGGER.info("Closed DhWorld of type " + this.environment);
|
||||
}
|
||||
|
||||
@@ -28,8 +28,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
|
||||
@CheckForNull
|
||||
private final ClientNetworkState networkState;
|
||||
|
||||
// TODO why does this executor have 2 threads?
|
||||
public ExecutorService dhTickerThread = ThreadUtil.makeSingleThreadPool("DH Client World Ticker Thread", 2);
|
||||
public ExecutorService dhTickerThread = ThreadUtil.makeSingleThreadPool("DH Client World Ticker Thread");
|
||||
public EventLoop eventLoop = new EventLoop(this.dhTickerThread, this::_clientTick);
|
||||
|
||||
|
||||
@@ -111,6 +110,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
|
||||
if (this.levels.containsKey(wrapper))
|
||||
{
|
||||
LOGGER.info("Unloading level " + this.levels.get(wrapper));
|
||||
wrapper.onUnload();
|
||||
this.levels.remove(wrapper).close();
|
||||
}
|
||||
}
|
||||
@@ -145,6 +145,14 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
|
||||
for (DhClientLevel dhClientLevel : this.levels.values())
|
||||
{
|
||||
LOGGER.info("Unloading level " + dhClientLevel.getLevelWrapper().getDimensionType().getDimensionName());
|
||||
|
||||
// level wrapper shouldn't be null, but just in case
|
||||
IClientLevelWrapper clientLevelWrapper = dhClientLevel.getClientLevelWrapper();
|
||||
if (clientLevelWrapper != null)
|
||||
{
|
||||
clientLevelWrapper.onUnload();
|
||||
}
|
||||
|
||||
dhClientLevel.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,10 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
public DhServerWorld()
|
||||
{
|
||||
super(EWorldEnvironment.Server_Only);
|
||||
@@ -54,6 +58,12 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// methods //
|
||||
//=========//
|
||||
|
||||
public void addPlayer(IServerPlayerWrapper serverPlayer)
|
||||
{
|
||||
this.remotePlayerConnectionHandler.registerJoinedPlayer(serverPlayer);
|
||||
@@ -111,6 +121,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
|
||||
if (this.levels.containsKey(wrapper))
|
||||
{
|
||||
LOGGER.info("Unloading level {} ", this.levels.get(wrapper));
|
||||
wrapper.onUnload();
|
||||
this.levels.remove(wrapper).close();
|
||||
}
|
||||
}
|
||||
@@ -146,6 +157,14 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld
|
||||
for (DhServerLevel level : this.levels.values())
|
||||
{
|
||||
LOGGER.info("Unloading level " + level.getLevelWrapper().getDimensionType().getDimensionName());
|
||||
|
||||
// level wrapper shouldn't be null, but just in case
|
||||
IServerLevelWrapper serverLevelWrapper = level.getServerLevelWrapper();
|
||||
if (serverLevelWrapper != null)
|
||||
{
|
||||
serverLevelWrapper.onUnload();
|
||||
}
|
||||
|
||||
level.close();
|
||||
}
|
||||
|
||||
|
||||
+2
-6
@@ -39,10 +39,6 @@ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable
|
||||
@Override
|
||||
IDhApiDimensionTypeWrapper getDimensionType();
|
||||
|
||||
int getBlockLight(int x, int y, int z);
|
||||
|
||||
int getSkyLight(int x, int y, int z);
|
||||
|
||||
@Override
|
||||
boolean hasCeiling();
|
||||
|
||||
@@ -65,7 +61,7 @@ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable
|
||||
@Deprecated
|
||||
IBiomeWrapper getBiome(DhBlockPos pos);
|
||||
|
||||
// TODO implement onUnload
|
||||
// necessary so ChunkToLodBuilder can have its cache cleared after the level closes
|
||||
/** Fired when the level is being unloaded. Doesn't unload the level. */
|
||||
void onUnload();
|
||||
|
||||
}
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
"distanthorizons.config.client.advanced.graphics.advancedGraphics.earthCurveRatio":
|
||||
"Earth Curve Ratio §6(EXPERIMENTAL)§r",
|
||||
"distanthorizons.config.client.advanced.graphics.advancedGraphics.earthCurveRatio.@tooltip":
|
||||
"A value of 1 is equivalent to the curvature of Earth in real life.",
|
||||
"A value of 1 is equivalent to the curvature of Earth in real life. \nThe minimum accepted value is 50 and the maximum value is 5000. \nEverything between 1 and 49 will be rounded up to 50.",
|
||||
"distanthorizons.config.client.advanced.graphics.advancedGraphics.lodBias":
|
||||
"LOD Bias §6(Affects vanilla terrain)§r",
|
||||
"distanthorizons.config.client.advanced.graphics.advancedGraphics.lodBias.@tooltip":
|
||||
|
||||
@@ -8,6 +8,8 @@ out vec4 vertexColor;
|
||||
out vec3 vertexWorldPos;
|
||||
out float vertexYPos;
|
||||
|
||||
uniform bool whiteWorld;
|
||||
|
||||
uniform mat4 combinedMatrix;
|
||||
uniform vec3 modelOffset;
|
||||
uniform float worldYOffset;
|
||||
@@ -19,6 +21,8 @@ uniform float mircoOffset;
|
||||
uniform float earthRadius;
|
||||
|
||||
/**
|
||||
* TODO in the future this and standard.vert should be merged together to prevent inconsistencies between the two
|
||||
*
|
||||
* Vertex Shader
|
||||
*
|
||||
* author: James Seibel
|
||||
@@ -51,43 +55,25 @@ void main()
|
||||
vertexWorldPos.x += mx;
|
||||
vertexWorldPos.y += my;
|
||||
vertexWorldPos.z += mz;
|
||||
|
||||
#if 0
|
||||
// Old (disabled) vertex transformation logic - Leetom
|
||||
|
||||
// Calculate the vertex pos due to curvature of the earth
|
||||
// We use spherical coordinates to calculate the vertex position
|
||||
//if (vertexWorldPos.x == 0.0 && vertexWorldPos.z == 0.0)
|
||||
//{
|
||||
// // In the center. No curvature needed
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
float theta = atan(vertexWorldPos.z, vertexWorldPos.x); // in radians (-pi, pi)
|
||||
float trueY = earthRadius + vertexWorldPos.y; // true Y position, or height
|
||||
float phi = sqrt(vertexWorldPos.z * vertexWorldPos.z + vertexWorldPos.x * vertexWorldPos.x) / trueY;
|
||||
// Convert spherical coordinates to cartesian coordinates
|
||||
vertexWorldPos.x = trueY * sin(phi) * cos(theta);
|
||||
vertexWorldPos.z = trueY * sin(phi) * sin(theta);
|
||||
vertexWorldPos.y = trueY * cos(phi) - earthRadius;
|
||||
//}
|
||||
|
||||
#else
|
||||
// new vertex transformation logic - stduhpf
|
||||
|
||||
float localRadius = earthRadius + vertexYPos;// vertexWorldPos.y + cameraPosition.y - Center_Y;
|
||||
|
||||
float phi = length(vertexWorldPos.xz) / localRadius;
|
||||
|
||||
vertexWorldPos.y += (cos(phi) - 1.0) * localRadius;
|
||||
vertexWorldPos.xz = vertexWorldPos.xz * sin(phi) / phi;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// vertex transformation logic - stduhpf
|
||||
float localRadius = earthRadius + vertexYPos;
|
||||
float phi = length(vertexWorldPos.xz) / localRadius;
|
||||
vertexWorldPos.y += (cos(phi) - 1.0) * localRadius;
|
||||
vertexWorldPos.xz = vertexWorldPos.xz * sin(phi) / phi;
|
||||
|
||||
|
||||
uint lights = meta & 0xFFu;
|
||||
|
||||
float light2 = (mod(float(lights), 16.0) + 0.5) / 16.0;
|
||||
float light = (float(lights / 16u) + 0.5) / 16.0;
|
||||
vertexColor = color * vec4(texture(lightMap, vec2(light, light2)).xyz, 1.0);
|
||||
|
||||
vertexColor = vec4(texture(lightMap, vec2(light, light2)).xyz, 1.0);
|
||||
|
||||
if (!whiteWorld)
|
||||
{
|
||||
vertexColor *= color;
|
||||
}
|
||||
|
||||
gl_Position = combinedMatrix * vec4(vertexWorldPos, 1.0);
|
||||
}
|
||||
|
||||
@@ -46,16 +46,17 @@ void main()
|
||||
{
|
||||
vec3 samplePos = vec3(0.0) + (TBN * gKernel[i]);
|
||||
samplePos = viewPos + samplePos * gSampleRad;
|
||||
|
||||
|
||||
vec4 offset = gProj * vec4(samplePos + viewPos, 1.0);
|
||||
offset.xy /= offset.w;
|
||||
offset.xy = offset.xy * HALF_2 + HALF_2;
|
||||
|
||||
|
||||
float geometryDepth = calcViewPosition(offset.xy).z;
|
||||
|
||||
|
||||
float rangeCheck = smoothstep(0.0, 1.0, gSampleRad / abs(viewPos.z - geometryDepth));
|
||||
occlusion_factor += float(geometryDepth >= samplePos.z + 0.05) * rangeCheck;
|
||||
|
||||
// the number added to the samplePos.z can be used to reduce noise in the SSAO application at the cost of reducing the overall affect
|
||||
occlusion_factor += float(geometryDepth >= samplePos.z + 1.0) * rangeCheck;
|
||||
|
||||
}
|
||||
|
||||
float visibility_factor = 1.0 - (occlusion_factor / MAX_KERNEL_SIZE);
|
||||
|
||||
@@ -20,6 +20,8 @@ uniform float mircoOffset;
|
||||
|
||||
|
||||
/**
|
||||
* TODO in the future this and curve.vert should be merged together to prevent inconsistencies between the two
|
||||
*
|
||||
* Vertex Shader
|
||||
*
|
||||
* author: James Seibel
|
||||
|
||||
Reference in New Issue
Block a user