diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/ChunkToLodBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/ChunkToLodBuilder.java deleted file mode 100644 index 7336b42bd..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/ChunkToLodBuilder.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.dataObjects.transformers; - -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicInteger; - -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.pos.DhChunkPos; -import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; -import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; -import org.apache.logging.log4j.LogManager; - -public class ChunkToLodBuilder implements AutoCloseable -{ - public static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logLodBuilderEvent.get()); - private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - - public static final long MAX_TICK_TIME_NS = 1000000000L / 20L; - - private final ConcurrentHashMap concurrentChunkToBuildByChunkPos = new ConcurrentHashMap<>(); - private final ConcurrentLinkedDeque concurrentTaskToBuildList = new ConcurrentLinkedDeque<>(); - private final AtomicInteger runningCount = new AtomicInteger(0); - - - - //==============// - // constructors // - //==============// - - public ChunkToLodBuilder() { } - - - - //=================// - // data generation // - //=================// - - public CompletableFuture tryGenerateData(IChunkWrapper chunkWrapper) - { - if (chunkWrapper == null) - { - throw new NullPointerException("ChunkWrapper cannot be null!"); - } - - IChunkWrapper oldChunk = this.concurrentChunkToBuildByChunkPos.put(chunkWrapper.getChunkPos(), chunkWrapper); // an Exchange operation - // If there's old chunk, that means we just replaced an unprocessed old request on generating data on this pos. - // if so, we can just return null to signal this, as the old request's future will instead be the proper one - // that will return the latest generated data. - if (oldChunk != null) - { - return null; - } - - // Otherwise, it means we're the first to do so. Let's submit our task to this entry. - CompletableFuture future = new CompletableFuture<>(); - this.concurrentTaskToBuildList.addLast(new Task(chunkWrapper.getChunkPos(), future)); - return future; - } - - // TODO why on tick? - public void tick() - { - int threadCount = ThreadPoolUtil.getWorkerThreadCount(); - if (this.runningCount.get() >= threadCount) - { - return; - } - else if (this.concurrentTaskToBuildList.isEmpty()) - { - return; - } - 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 - //this.clearCurrentTasks(); - return; - } - - ThreadPoolExecutor lodBuilderExecutor = ThreadPoolUtil.getChunkToLodBuilderExecutor(); - if (lodBuilderExecutor == null) - { - return; - } - - - for (int i = 0; i < threadCount; i++) - { - this.runningCount.incrementAndGet(); - try - { - CompletableFuture.runAsync(() -> - { - try - { - this.tickThreadTask(); - } - finally - { - this.runningCount.decrementAndGet(); - } - }, lodBuilderExecutor); - } - catch (RejectedExecutionException ignore) { /* the thread pool was probably shut down because it's size is being changed, just wait a sec and it should be back */ } - } - } - private void tickThreadTask() - { - long time = System.nanoTime(); - int count = 0; - boolean allDone = false; - while (true) - { - // run until we either run out of time, or all tasks are complete - if (System.nanoTime() - time > MAX_TICK_TIME_NS && !this.concurrentTaskToBuildList.isEmpty()) - { - break; - } - - Task task = this.concurrentTaskToBuildList.pollFirst(); - if (task == null) - { - allDone = true; - break; - } - - count++; - IChunkWrapper latestChunk = this.concurrentChunkToBuildByChunkPos.remove(task.chunkPos); // Basically an Exchange operation - if (latestChunk == null) - { - LOGGER.error("Somehow Task at " + task.chunkPos + " has latestChunk as null. Skipping task."); - task.future.complete(null); - continue; - } - - try - { - if (LodDataBuilder.canGenerateLodFromChunk(latestChunk)) - { - FullDataSourceV2 dataSource = LodDataBuilder.createGeneratedDataSource(latestChunk); - if (dataSource != null) - { - task.future.complete(dataSource); - continue; - } - } - else if (task.generationAttemptExpirationTimeMs < System.currentTimeMillis()) - { - // this task won't be re-queued - //LOGGER.trace("removed chunk "+task.chunkPos); - continue; - } - } - catch (Exception ex) - { - LOGGER.error("Error while processing Task at " + task.chunkPos, ex); - } - - // Failed to build due to chunk not meeting requirement, - // re-add it to the queue so it can be tested next time - IChunkWrapper casChunk = this.concurrentChunkToBuildByChunkPos.putIfAbsent(task.chunkPos, latestChunk); // CAS operation with expected=null - if (casChunk == null || latestChunk.isStillValid()) // That means CAS have been successful - { - this.concurrentTaskToBuildList.addLast(task); // Then add back the same old task. - } - else // Else, it means someone managed to sneak in a new gen request in this pos. Then lets drop this old task. - { - task.future.complete(null); - } - - count--; - } - - long time2 = System.nanoTime(); - if (!allDone) - { - //LOGGER.info("Completed {} tasks in {} in this tick", count, Duration.ofNanos(time2 - time)); - } - else if (count > 0) - { - //LOGGER.info("Completed all {} tasks in {}", count, Duration.ofNanos(time2 - time)); - } - } - - /** - * should be called whenever changing levels/worlds - * to prevent trying to generate LODs for chunk(s) that are no longer loaded - * (which can cause exceptions) - */ - public void clearCurrentTasks() - { - this.concurrentTaskToBuildList.clear(); - this.concurrentChunkToBuildByChunkPos.clear(); - } - - - - - //==============// - // base methods // - //==============// - - @Override - public void close() { this.clearCurrentTasks(); } - - - - //================// - // helper classes // - //================// - - private static class Task - { - public final DhChunkPos chunkPos; - public final CompletableFuture future; - /** This is tracked so impossible tasks can be removed from the queue */ - public long generationAttemptExpirationTimeMs = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(10); - - Task(DhChunkPos chunkPos, CompletableFuture future) - { - this.chunkPos = chunkPos; - this.future = future; - } - - } - -} - diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java index 16f7b6694..523a5a376 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java @@ -19,28 +19,19 @@ package com.seibel.distanthorizons.core.level; -import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiChunkModifiedEvent; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; -import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; -import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; -import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; -import com.seibel.distanthorizons.core.dataObjects.transformers.ChunkToLodBuilder; import com.seibel.distanthorizons.core.file.fullDatafile.DelayedFullDataSourceSaveCache; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.generic.BeaconRenderHandler; import com.seibel.distanthorizons.core.render.renderer.generic.CloudRenderHandler; import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; -import com.seibel.distanthorizons.core.render.renderer.generic.GenericRenderObjectFactory; import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; import com.seibel.distanthorizons.core.sql.dto.ChunkHashDTO; import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo; import com.seibel.distanthorizons.core.sql.repo.ChunkHashRepo; -import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import org.apache.logging.log4j.Logger; @@ -59,8 +50,6 @@ public abstract class AbstractDhLevel implements IDhLevel { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - public final ChunkToLodBuilder chunkToLodBuilder; - /** if this is null then the other handler is probably null too, but just in case */ @Nullable public ChunkHashRepo chunkHashRepo; @@ -83,10 +72,7 @@ public abstract class AbstractDhLevel implements IDhLevel // constructor // //=============// - protected AbstractDhLevel() - { - this.chunkToLodBuilder = new ChunkToLodBuilder(); - } + protected AbstractDhLevel() { } /** * Creating the repos requires access to the level file, which isn't @@ -262,8 +248,6 @@ public abstract class AbstractDhLevel implements IDhLevel @Override public void close() { - this.chunkToLodBuilder.close(); - if (this.chunkHashRepo != null) { this.chunkHashRepo.close(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 0b5555b30..143327d5d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -86,7 +86,6 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel { try { - this.chunkToLodBuilder.tick(); this.clientside.clientTick(); } catch (Exception e) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java index b9357e6ff..9427dce50 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java @@ -95,7 +95,7 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev { this.clientside.renderDeferred(renderEventParam, profiler); } @Override - public void serverTick() { this.chunkToLodBuilder.tick(); } + public void serverTick() { } @Override public void doWorldGen() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 238b50e46..355dae28c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -65,7 +65,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel // methods // //=========// - public void serverTick() { this.chunkToLodBuilder.tick(); } + public void serverTick() { } @Override public CompletableFuture updateDataSourcesAsync(FullDataSourceV2 data) { return this.getFullDataProvider().updateDataSourceAsync(data); }