Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into 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 */ }
|
||||
|
||||
|
||||
}
|
||||
+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)
|
||||
{
|
||||
|
||||
+8
-5
@@ -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);
|
||||
@@ -224,11 +223,15 @@ 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)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 //
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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