Added config
This commit is contained in:
@@ -4,4 +4,6 @@ import me.cortex.zenith.client.core.VoxelCore;
|
||||
|
||||
public interface IGetVoxelCore {
|
||||
VoxelCore getVoxelCore();
|
||||
|
||||
void reloadVoxelCore();
|
||||
}
|
||||
|
||||
@@ -1,15 +1,67 @@
|
||||
package me.cortex.zenith.client.config;
|
||||
|
||||
public class ZenithConfig {
|
||||
int qualityScale;
|
||||
int ingestThreads;
|
||||
int savingThreads;
|
||||
int renderThreads;
|
||||
int savingCompressionLevel;
|
||||
StorageConfig storageConfig;
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class ZenithConfig {
|
||||
private static final Gson GSON = new GsonBuilder()
|
||||
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
||||
.setPrettyPrinting()
|
||||
.excludeFieldsWithModifiers(Modifier.PRIVATE)
|
||||
.create();
|
||||
public static ZenithConfig CONFIG = loadOrCreate();
|
||||
|
||||
public boolean enabled = true;
|
||||
public int qualityScale = 20;
|
||||
public int ingestThreads = 2;
|
||||
public int savingThreads = 10;
|
||||
public int renderThreads = 5;
|
||||
public int savingCompressionLevel = 7;
|
||||
public String storagePath = "zenith_db";
|
||||
|
||||
transient StorageConfig storageConfig;
|
||||
public static abstract class StorageConfig { }
|
||||
public static class FragmentedStorageConfig extends StorageConfig { }
|
||||
public static class LmdbStorageConfig extends StorageConfig { }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static ZenithConfig loadOrCreate() {
|
||||
var path = getConfigPath();
|
||||
if (Files.exists(path)) {
|
||||
try (FileReader reader = new FileReader(path.toFile())) {
|
||||
return GSON.fromJson(reader, ZenithConfig.class);
|
||||
} catch (IOException e) {
|
||||
System.err.println("Could not parse config");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return new ZenithConfig();
|
||||
}
|
||||
public void save() {
|
||||
//Unsafe, todo: fixme! needs to be atomic!
|
||||
try {
|
||||
Files.writeString(getConfigPath(), GSON.toJson(this));
|
||||
} catch (IOException e) {
|
||||
System.err.println("Failed to write config file");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static Path getConfigPath() {
|
||||
return FabricLoader.getInstance()
|
||||
.getConfigDir()
|
||||
.resolve("zenith-config.json");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,11 +2,105 @@ package me.cortex.zenith.client.config;
|
||||
|
||||
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
|
||||
import com.terraformersmc.modmenu.api.ModMenuApi;
|
||||
import me.cortex.zenith.client.IGetVoxelCore;
|
||||
import me.shedaniel.clothconfig2.api.ConfigBuilder;
|
||||
import me.shedaniel.clothconfig2.api.ConfigCategory;
|
||||
import me.shedaniel.clothconfig2.api.ConfigEntryBuilder;
|
||||
import me.shedaniel.clothconfig2.api.Requirement;
|
||||
import me.shedaniel.clothconfig2.gui.entries.BooleanListEntry;
|
||||
import me.shedaniel.clothconfig2.gui.entries.IntegerListEntry;
|
||||
import me.shedaniel.clothconfig2.gui.entries.IntegerSliderEntry;
|
||||
import me.shedaniel.clothconfig2.impl.builders.DropdownMenuBuilder;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
public class ZenithConfigScreenFactory implements ModMenuApi {
|
||||
|
||||
private static final ZenithConfig DEFAULT = new ZenithConfig();
|
||||
@Override
|
||||
public ConfigScreenFactory<?> getModConfigScreenFactory() {
|
||||
return parent -> null;
|
||||
return parent -> buildConfigScreen(parent, ZenithConfig.CONFIG);
|
||||
}
|
||||
|
||||
private static Screen buildConfigScreen(Screen parent, ZenithConfig config) {
|
||||
ConfigBuilder builder = ConfigBuilder.create()
|
||||
.setParentScreen(parent)
|
||||
.setTitle(Text.translatable("title.zenith.config"));
|
||||
|
||||
|
||||
addGeneralCategory(builder, config);
|
||||
addThreadsCategory(builder, config);
|
||||
addStorageCategory(builder, config);
|
||||
|
||||
builder.setSavingRunnable(() -> {
|
||||
//After saving the core should be reloaded/reset
|
||||
var world = (IGetVoxelCore)MinecraftClient.getInstance().worldRenderer;
|
||||
if (world != null) {
|
||||
world.reloadVoxelCore();
|
||||
}
|
||||
ZenithConfig.CONFIG.save();
|
||||
});
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static void addGeneralCategory(ConfigBuilder builder, ZenithConfig config) {
|
||||
ConfigCategory category = builder.getOrCreateCategory(Text.translatable("zenith.config.general"));
|
||||
ConfigEntryBuilder entryBuilder = builder.entryBuilder();
|
||||
|
||||
|
||||
category.addEntry(entryBuilder.startBooleanToggle(Text.translatable("zenith.config.general.enabled"), config.enabled)
|
||||
.setTooltip(Text.translatable("zenith.config.general.enabled.tooltip"))
|
||||
.setSaveConsumer(val -> config.enabled = val)
|
||||
.setDefaultValue(DEFAULT.enabled)
|
||||
.build());
|
||||
|
||||
category.addEntry(entryBuilder.startIntSlider(Text.translatable("zenith.config.general.quality"), config.qualityScale, 16, 64)
|
||||
.setTooltip(Text.translatable("zenith.config.general.quality.tooltip"))
|
||||
.setSaveConsumer(val -> config.qualityScale = val)
|
||||
.setDefaultValue(DEFAULT.qualityScale)
|
||||
.build());
|
||||
|
||||
category.addEntry(entryBuilder.startIntSlider(Text.translatable("zenith.config.general.compression"), config.savingCompressionLevel, 1, 21)
|
||||
.setTooltip(Text.translatable("zenith.config.general.compression.tooltip"))
|
||||
.setSaveConsumer(val -> config.savingCompressionLevel = val)
|
||||
.setDefaultValue(DEFAULT.savingCompressionLevel)
|
||||
.build());
|
||||
}
|
||||
|
||||
private static void addThreadsCategory(ConfigBuilder builder, ZenithConfig config) {
|
||||
ConfigCategory category = builder.getOrCreateCategory(Text.translatable("zenith.config.threads"));
|
||||
ConfigEntryBuilder entryBuilder = builder.entryBuilder();
|
||||
|
||||
category.addEntry(entryBuilder.startIntSlider(Text.translatable("zenith.config.threads.ingest"), config.ingestThreads, 1, Runtime.getRuntime().availableProcessors())
|
||||
.setTooltip(Text.translatable("zenith.config.general.ingest.tooltip"))
|
||||
.setSaveConsumer(val -> config.ingestThreads = val)
|
||||
.setDefaultValue(DEFAULT.ingestThreads)
|
||||
.build());
|
||||
|
||||
category.addEntry(entryBuilder.startIntSlider(Text.translatable("zenith.config.threads.saving"), config.savingThreads, 1, Runtime.getRuntime().availableProcessors())
|
||||
.setTooltip(Text.translatable("zenith.config.general.saving.tooltip"))
|
||||
.setSaveConsumer(val -> config.savingThreads = val)
|
||||
.setDefaultValue(DEFAULT.savingThreads)
|
||||
.build());
|
||||
|
||||
category.addEntry(entryBuilder.startIntSlider(Text.translatable("zenith.config.threads.render"), config.renderThreads, 1, Runtime.getRuntime().availableProcessors())
|
||||
.setTooltip(Text.translatable("zenith.config.general.render.tooltip"))
|
||||
.setSaveConsumer(val -> config.renderThreads = val)
|
||||
.setDefaultValue(DEFAULT.renderThreads)
|
||||
.build());
|
||||
}
|
||||
|
||||
private static void addStorageCategory(ConfigBuilder builder, ZenithConfig config) {
|
||||
ConfigCategory category = builder.getOrCreateCategory(Text.translatable("zenith.config.storage"));
|
||||
ConfigEntryBuilder entryBuilder = builder.entryBuilder();
|
||||
|
||||
//Temporary until i figure out how to do more complex multi layer configuration for storage
|
||||
category.addEntry(entryBuilder.startStrField(Text.translatable("zenith.config.storage.path"), config.storagePath)
|
||||
.setTooltip(Text.translatable("zenith.config.storage.path.tooltip"))
|
||||
.setSaveConsumer(val -> config.storagePath = val)
|
||||
.setDefaultValue(DEFAULT.storagePath)
|
||||
.build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.cortex.zenith.client.core;
|
||||
|
||||
import me.cortex.zenith.client.config.ZenithConfig;
|
||||
import me.cortex.zenith.client.core.rendering.*;
|
||||
import me.cortex.zenith.client.core.rendering.building.RenderGenerationService;
|
||||
import me.cortex.zenith.client.core.util.DebugUtil;
|
||||
@@ -39,7 +40,7 @@ import java.util.*;
|
||||
public class VoxelCore {
|
||||
private static final Set<Block> biomeTintableAllFaces = new HashSet<>(List.of(Blocks.OAK_LEAVES, Blocks.JUNGLE_LEAVES, Blocks.ACACIA_LEAVES, Blocks.DARK_OAK_LEAVES, Blocks.VINE, Blocks.MANGROVE_LEAVES,
|
||||
Blocks.TALL_GRASS, Blocks.LARGE_FERN,
|
||||
//Blocks.SHORT_GRASS,
|
||||
Blocks.SHORT_GRASS,
|
||||
|
||||
Blocks.SPRUCE_LEAVES,
|
||||
Blocks.BIRCH_LEAVES,
|
||||
@@ -66,31 +67,23 @@ public class VoxelCore {
|
||||
SharedIndexBuffer.INSTANCE.id();
|
||||
this.renderer = new Gl46FarWorldRenderer();
|
||||
System.out.println("Renderer initialized");
|
||||
this.world = new WorldEngine(new FragmentedStorageBackendAdaptor(), 4, 20, 5);//"storagefile.db"//"ethoslab.db"
|
||||
this.world = new WorldEngine(new FragmentedStorageBackendAdaptor(new File(ZenithConfig.CONFIG.storagePath)), ZenithConfig.CONFIG.ingestThreads, ZenithConfig.CONFIG.savingThreads, ZenithConfig.CONFIG.savingCompressionLevel, 5);//"storagefile.db"//"ethoslab.db"
|
||||
System.out.println("World engine");
|
||||
|
||||
this.renderTracker = new RenderTracker(this.world, this.renderer);
|
||||
this.renderGen = new RenderGenerationService(this.world,7, this.renderTracker::processBuildResult);
|
||||
this.renderGen = new RenderGenerationService(this.world,ZenithConfig.CONFIG.renderThreads, this.renderTracker::processBuildResult);
|
||||
this.world.setDirtyCallback(this.renderTracker::sectionUpdated);
|
||||
this.renderTracker.setRenderGen(this.renderGen);
|
||||
System.out.println("Render tracker and generator initialized");
|
||||
|
||||
//To get to chunk scale multiply the scale by 2, the scale is after how many chunks does the lods halve
|
||||
this.distanceTracker = new DistanceTracker(this.renderTracker, 5, 64);//16//64
|
||||
this.distanceTracker = new DistanceTracker(this.renderTracker, 5, ZenithConfig.CONFIG.qualityScale);
|
||||
System.out.println("Distance tracker initialized");
|
||||
|
||||
this.postProcessing = null;//new PostProcessing();
|
||||
|
||||
this.world.getMapper().setCallbacks(this::stateUpdate, this::biomeUpdate);
|
||||
|
||||
//Runtime.getRuntime().addShutdownHook(this.shutdownThread);
|
||||
|
||||
|
||||
//WorldImporter importer = new WorldImporter(this.world, MinecraftClient.getInstance().world);
|
||||
//importer.importWorldAsyncStart(new File("saves/New World/region"));
|
||||
|
||||
|
||||
|
||||
for (var state : this.world.getMapper().getStateEntries()) {
|
||||
this.stateUpdate(state);
|
||||
}
|
||||
@@ -100,7 +93,6 @@ public class VoxelCore {
|
||||
}
|
||||
System.out.println("Entry updates applied");
|
||||
|
||||
|
||||
System.out.println("Voxel core initialized");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package me.cortex.zenith.client.mixin.minecraft;
|
||||
|
||||
import me.cortex.zenith.client.IGetVoxelCore;
|
||||
import me.cortex.zenith.client.config.ZenithConfig;
|
||||
import me.cortex.zenith.client.core.VoxelCore;
|
||||
import net.minecraft.client.render.*;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
@@ -28,13 +29,17 @@ public abstract class MixinWorldRenderer implements IGetVoxelCore {
|
||||
|
||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;setupTerrain(Lnet/minecraft/client/render/Camera;Lnet/minecraft/client/render/Frustum;ZZ)V", shift = At.Shift.AFTER))
|
||||
private void injectSetup(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightmapTextureManager lightmapTextureManager, Matrix4f projectionMatrix, CallbackInfo ci) {
|
||||
this.core.renderSetup(this.frustum, camera);
|
||||
if (this.core != null) {
|
||||
this.core.renderSetup(this.frustum, camera);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;renderLayer(Lnet/minecraft/client/render/RenderLayer;Lnet/minecraft/client/util/math/MatrixStack;DDDLorg/joml/Matrix4f;)V", ordinal = 2, shift = At.Shift.AFTER))
|
||||
private void injectOpaqueRender(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightmapTextureManager lightmapTextureManager, Matrix4f projectionMatrix, CallbackInfo ci) {
|
||||
var cam = camera.getPos();
|
||||
this.core.renderOpaque(matrices, cam.x, cam.y, cam.z);
|
||||
if (this.core != null) {
|
||||
var cam = camera.getPos();
|
||||
this.core.renderOpaque(matrices, cam.x, cam.y, cam.z);
|
||||
}
|
||||
}
|
||||
|
||||
public VoxelCore getVoxelCore() {
|
||||
@@ -58,10 +63,25 @@ public abstract class MixinWorldRenderer implements IGetVoxelCore {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.core != null) {
|
||||
throw new IllegalStateException("Core is not null");
|
||||
this.core.shutdown();
|
||||
this.core = null;
|
||||
}
|
||||
if (ZenithConfig.CONFIG.enabled) {
|
||||
this.core = new VoxelCore();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadVoxelCore() {
|
||||
if (this.core != null) {
|
||||
this.core.shutdown();
|
||||
this.core = null;
|
||||
}
|
||||
if (this.world != null && ZenithConfig.CONFIG.enabled) {
|
||||
this.core = new VoxelCore();
|
||||
}
|
||||
this.core = new VoxelCore();
|
||||
}
|
||||
|
||||
@Inject(method = "close", at = @At("HEAD"))
|
||||
@@ -72,8 +92,6 @@ public abstract class MixinWorldRenderer implements IGetVoxelCore {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Redirect(method = "render", at = @At(value = "INVOKE", target = "Ljava/lang/Math;max(FF)F"), require = 0)
|
||||
private float redirectMax(float a, float b) {
|
||||
return a;
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.nio.ByteBuffer;
|
||||
import static org.lwjgl.util.zstd.Zstd.*;
|
||||
|
||||
public class SaveLoadSystem {
|
||||
public static ByteBuffer serialize(WorldSection section) {
|
||||
public static ByteBuffer serialize(WorldSection section, int compressionLevel) {
|
||||
var data = section.copyData();
|
||||
var compressed = new Short[data.length];
|
||||
Long2ShortOpenHashMap LUT = new Long2ShortOpenHashMap();
|
||||
@@ -48,7 +48,7 @@ public class SaveLoadSystem {
|
||||
raw.limit(raw.position());
|
||||
raw.rewind();
|
||||
ByteBuffer compressedData = MemoryUtil.memAlloc((int)ZSTD_COMPRESSBOUND(raw.remaining()));
|
||||
long compressedSize = ZSTD_compress(compressedData, raw, 7);
|
||||
long compressedSize = ZSTD_compress(compressedData, raw, compressionLevel);
|
||||
compressedData.limit((int) compressedSize);
|
||||
compressedData.rewind();
|
||||
MemoryUtil.memFree(raw);
|
||||
|
||||
@@ -24,18 +24,18 @@ public class WorldEngine {
|
||||
|
||||
|
||||
public void setDirtyCallback(Consumer<WorldSection> tracker) {
|
||||
this.dirtyCallback = dirtyCallback;
|
||||
this.dirtyCallback = tracker;
|
||||
}
|
||||
|
||||
public Mapper getMapper() {return this.mapper;}
|
||||
|
||||
public WorldEngine(StorageBackend storageBackend, int ingestWorkers, int savingServiceWorkers, int maxMipLayers) {
|
||||
public WorldEngine(StorageBackend storageBackend, int ingestWorkers, int savingServiceWorkers, int compressionLevel, int maxMipLayers) {
|
||||
this.maxMipLevels = maxMipLayers;
|
||||
this.storage = storageBackend;
|
||||
this.mapper = new Mapper(this.storage);
|
||||
this.sectionTracker = new ActiveSectionTracker(maxMipLayers, this::unsafeLoadSection);
|
||||
|
||||
this.savingService = new SectionSavingService(this, savingServiceWorkers);
|
||||
this.savingService = new SectionSavingService(this, savingServiceWorkers, compressionLevel);
|
||||
this.ingestService = new VoxelIngestService(this, ingestWorkers);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,12 +15,13 @@ public class SectionSavingService {
|
||||
private volatile boolean running = true;
|
||||
private final Thread[] workers;
|
||||
|
||||
private final int compressionLevel;
|
||||
private final ConcurrentLinkedDeque<WorldSection> saveQueue = new ConcurrentLinkedDeque<>();
|
||||
private final Semaphore saveCounter = new Semaphore(0);
|
||||
|
||||
private final WorldEngine world;
|
||||
|
||||
public SectionSavingService(WorldEngine worldEngine, int workers) {
|
||||
public SectionSavingService(WorldEngine worldEngine, int workers, int compressionLevel) {
|
||||
this.workers = new Thread[workers];
|
||||
for (int i = 0; i < workers; i++) {
|
||||
var worker = new Thread(this::saveWorker);
|
||||
@@ -29,7 +30,7 @@ public class SectionSavingService {
|
||||
worker.start();
|
||||
this.workers[i] = worker;
|
||||
}
|
||||
|
||||
this.compressionLevel = compressionLevel;
|
||||
this.world = worldEngine;
|
||||
}
|
||||
|
||||
@@ -41,7 +42,7 @@ public class SectionSavingService {
|
||||
section.assertNotFree();
|
||||
section.inSaveQueue.set(false);
|
||||
|
||||
var saveData = SaveLoadSystem.serialize(section);
|
||||
var saveData = SaveLoadSystem.serialize(section, this.compressionLevel);
|
||||
this.world.storage.setSectionData(section.getKey(), saveData);
|
||||
MemoryUtil.memFree(saveData);
|
||||
|
||||
|
||||
@@ -5,15 +5,22 @@ import me.cortex.zenith.common.world.storage.lmdb.LMDBStorageBackend;
|
||||
import net.minecraft.util.math.random.RandomSeed;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
|
||||
//Segments the section data into multiple dbs
|
||||
public class FragmentedStorageBackendAdaptor extends StorageBackend {
|
||||
private final StorageBackend[] backends = new StorageBackend[32];
|
||||
|
||||
public FragmentedStorageBackendAdaptor() {
|
||||
public FragmentedStorageBackendAdaptor(File directory) {
|
||||
try {
|
||||
Files.createDirectories(directory.toPath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
for (int i = 0; i < this.backends.length; i++) {
|
||||
this.backends[i] = new LMDBStorageBackend(new File("storage-db-"+i+".db"));
|
||||
this.backends[i] = new LMDBStorageBackend(directory.toPath().resolve("storage-db-"+i+".db").toFile());//
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user