Added basic world seperation
This commit is contained in:
@@ -18,6 +18,8 @@ repositories {
|
||||
includeGroup "maven.modrinth"
|
||||
}
|
||||
}
|
||||
maven { url "https://maven.shedaniel.me/" }
|
||||
maven { url "https://maven.terraformersmc.com/releases/" }
|
||||
}
|
||||
|
||||
processResources {
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
package me.cortex.voxy.client;
|
||||
|
||||
import me.cortex.voxy.client.config.VoxyConfig;
|
||||
import me.cortex.voxy.client.core.VoxelCore;
|
||||
import me.cortex.voxy.client.saver.WorldSelectionSystem;
|
||||
import me.cortex.voxy.client.saver.ContextSelectionSystem;
|
||||
import me.cortex.voxy.client.terrain.WorldImportCommand;
|
||||
import me.cortex.voxy.common.storage.config.Serialization;
|
||||
import me.cortex.voxy.common.storage.other.CompressionStorageAdaptor;
|
||||
import me.cortex.voxy.common.storage.StorageBackend;
|
||||
import me.cortex.voxy.common.storage.compressors.ZSTDCompressor;
|
||||
import me.cortex.voxy.common.storage.other.FragmentedStorageBackendAdaptor;
|
||||
import me.cortex.voxy.common.storage.rocksdb.RocksDBStorageBackend;
|
||||
import me.cortex.voxy.common.world.WorldEngine;
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
@@ -26,7 +19,7 @@ public class Voxy implements ClientModInitializer {
|
||||
}
|
||||
|
||||
|
||||
private static final WorldSelectionSystem selector = new WorldSelectionSystem();
|
||||
private static final ContextSelectionSystem selector = new ContextSelectionSystem();
|
||||
|
||||
public static VoxelCore createVoxelCore(ClientWorld world) {
|
||||
var selection = selector.getBestSelectionOrCreate(world);
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.cortex.voxy.client.config;
|
||||
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
|
||||
import com.terraformersmc.modmenu.api.ModMenuApi;
|
||||
import me.cortex.voxy.client.core.IGetVoxelCore;
|
||||
import me.shedaniel.clothconfig2.ClothConfigDemo;
|
||||
import me.shedaniel.clothconfig2.api.ConfigBuilder;
|
||||
import me.shedaniel.clothconfig2.api.ConfigCategory;
|
||||
import me.shedaniel.clothconfig2.api.ConfigEntryBuilder;
|
||||
@@ -10,6 +11,8 @@ import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class VoxyConfigScreenFactory implements ModMenuApi {
|
||||
private static final VoxyConfig DEFAULT = new VoxyConfig();
|
||||
@Override
|
||||
@@ -36,13 +39,26 @@ public class VoxyConfigScreenFactory implements ModMenuApi {
|
||||
VoxyConfig.CONFIG.save();
|
||||
});
|
||||
|
||||
return builder.build();
|
||||
return ClothConfigDemo.getConfigBuilderWithDemo().build();
|
||||
}
|
||||
|
||||
private static void addGeneralCategory(ConfigBuilder builder, VoxyConfig config) {
|
||||
|
||||
|
||||
ConfigCategory category = builder.getOrCreateCategory(Text.translatable("voxy.config.general"));
|
||||
ConfigEntryBuilder entryBuilder = builder.entryBuilder();
|
||||
|
||||
category.addEntry(entryBuilder.startSubCategory(Text.translatable("aaa"), List.of(entryBuilder.startBooleanToggle(Text.translatable("voxy.config.general.enabled"), config.enabled)
|
||||
.setTooltip(Text.translatable("voxy.config.general.enabled.tooltip"))
|
||||
.setSaveConsumer(val -> config.enabled = val)
|
||||
.setDefaultValue(DEFAULT.enabled)
|
||||
.build(), entryBuilder.startSubCategory(Text.translatable("bbb"), List.of(entryBuilder.startIntSlider(Text.translatable("voxy.config.general.geometryBuffer"), config.geometryBufferSize, (1<<27)/8, ((1<<31)-1)/8)
|
||||
.setTooltip(Text.translatable("voxy.config.general.geometryBuffer.tooltip"))
|
||||
.setSaveConsumer(val -> config.geometryBufferSize = val)
|
||||
.setDefaultValue(DEFAULT.geometryBufferSize)
|
||||
.build())).build()
|
||||
)).build());
|
||||
|
||||
|
||||
category.addEntry(entryBuilder.startBooleanToggle(Text.translatable("voxy.config.general.enabled"), config.enabled)
|
||||
.setTooltip(Text.translatable("voxy.config.general.enabled.tooltip"))
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package me.cortex.voxy.client.config.screens;
|
||||
|
||||
import me.shedaniel.clothconfig2.api.AbstractConfigListEntry;
|
||||
|
||||
public class StorageConfigScreen {
|
||||
public static AbstractConfigListEntry makeScreen() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import me.cortex.voxy.client.core.rendering.*;
|
||||
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
|
||||
import me.cortex.voxy.client.core.rendering.post.PostProcessing;
|
||||
import me.cortex.voxy.client.core.util.DebugUtil;
|
||||
import me.cortex.voxy.client.saver.WorldSelectionSystem;
|
||||
import me.cortex.voxy.client.saver.ContextSelectionSystem;
|
||||
import me.cortex.voxy.common.world.WorldEngine;
|
||||
import me.cortex.voxy.client.importers.WorldImporter;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
@@ -47,7 +47,7 @@ public class VoxelCore {
|
||||
|
||||
//private final Thread shutdownThread = new Thread(this::shutdown);
|
||||
|
||||
public VoxelCore(WorldSelectionSystem.Selection worldSelection) {
|
||||
public VoxelCore(ContextSelectionSystem.Selection worldSelection) {
|
||||
this.world = worldSelection.createEngine();
|
||||
System.out.println("Initializing voxy core");
|
||||
|
||||
|
||||
@@ -159,16 +159,16 @@ public class RenderTracker {
|
||||
// (due to block occlusion)
|
||||
|
||||
//TODO: FIXME: REBUILDING THE ENTIRE NEIGHBORS when probably only the internal layout changed is NOT SMART
|
||||
this.renderGen.enqueueTask(section.lvl, section.x, section.y, section.z, this::shouldStillBuild);
|
||||
this.renderGen.enqueueTask(section.lvl, section.x-1, section.y, section.z, this::shouldStillBuild);
|
||||
this.renderGen.enqueueTask(section.lvl, section.x+1, section.y, section.z, this::shouldStillBuild);
|
||||
this.renderGen.enqueueTask(section.lvl, section.x, section.y, section.z-1, this::shouldStillBuild);
|
||||
this.renderGen.enqueueTask(section.lvl, section.x, section.y, section.z+1, this::shouldStillBuild);
|
||||
this.renderGen.clearCache(section.lvl, section.x, section.y, section.z);
|
||||
this.renderGen.clearCache(section.lvl, section.x-1, section.y, section.z);
|
||||
this.renderGen.clearCache(section.lvl, section.x+1, section.y, section.z);
|
||||
this.renderGen.clearCache(section.lvl, section.x, section.y, section.z-1);
|
||||
this.renderGen.clearCache(section.lvl, section.x, section.y, section.z+1);
|
||||
this.renderGen.enqueueTask(section.lvl, section.x, section.y, section.z, this::shouldStillBuild);
|
||||
this.renderGen.enqueueTask(section.lvl, section.x-1, section.y, section.z, this::shouldStillBuild);
|
||||
this.renderGen.enqueueTask(section.lvl, section.x+1, section.y, section.z, this::shouldStillBuild);
|
||||
this.renderGen.enqueueTask(section.lvl, section.x, section.y, section.z-1, this::shouldStillBuild);
|
||||
this.renderGen.enqueueTask(section.lvl, section.x, section.y, section.z+1, this::shouldStillBuild);
|
||||
}
|
||||
//this.renderGen.enqueueTask(section);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
package me.cortex.voxy.client.saver;
|
||||
|
||||
import me.cortex.voxy.client.config.VoxyConfig;
|
||||
import me.cortex.voxy.common.storage.StorageBackend;
|
||||
import me.cortex.voxy.common.storage.compressors.ZSTDCompressor;
|
||||
import me.cortex.voxy.common.storage.config.ConfigBuildCtx;
|
||||
import me.cortex.voxy.common.storage.config.Serialization;
|
||||
import me.cortex.voxy.common.storage.config.StorageConfig;
|
||||
import me.cortex.voxy.common.storage.other.CompressionStorageAdaptor;
|
||||
import me.cortex.voxy.common.storage.other.TranslocatingStorageAdaptor;
|
||||
import me.cortex.voxy.common.storage.rocksdb.RocksDBStorageBackend;
|
||||
import me.cortex.voxy.common.world.WorldEngine;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.util.WorldSavePath;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
//Sets up a world engine with respect to the world the client is currently loaded into
|
||||
// this is a bit tricky as each world has its own config, e.g. storage configuration
|
||||
public class ContextSelectionSystem {
|
||||
public static class Selection {
|
||||
private final Path selectionFolder;
|
||||
private final String worldId;
|
||||
|
||||
private StorageConfig storageConfig;
|
||||
|
||||
public Selection(Path selectionFolder, String worldId) {
|
||||
this.selectionFolder = selectionFolder;
|
||||
this.worldId = worldId;
|
||||
loadStorageConfigOrDefault();
|
||||
}
|
||||
|
||||
private void loadStorageConfigOrDefault() {
|
||||
var json = this.selectionFolder.resolve("storage_config.json");
|
||||
|
||||
if (Files.exists(json)) {
|
||||
try {
|
||||
this.storageConfig = Serialization.GSON.fromJson(Files.readString(json), StorageConfig.class);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
//Load the default config
|
||||
var baseDB = new RocksDBStorageBackend.Config();
|
||||
|
||||
var compressor = new ZSTDCompressor.Config();
|
||||
compressor.compressionLevel = 7;
|
||||
|
||||
var compression = new CompressionStorageAdaptor.Config();
|
||||
compression.delegate = baseDB;
|
||||
compression.compressor = compressor;
|
||||
|
||||
this.storageConfig = compression;
|
||||
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
|
||||
public StorageBackend createStorageBackend() {
|
||||
var ctx = new ConfigBuildCtx();
|
||||
ctx.setProperty(ConfigBuildCtx.BASE_SAVE_PATH, this.selectionFolder.toString());
|
||||
ctx.setProperty(ConfigBuildCtx.WORLD_IDENTIFIER, this.worldId);
|
||||
ctx.pushPath(ConfigBuildCtx.DEFAULT_STORAGE_PATH);
|
||||
return this.storageConfig.build(ctx);
|
||||
|
||||
/*
|
||||
var translocator = new TranslocatingStorageAdaptor.Config();
|
||||
translocator.delegate = compression;
|
||||
translocator.transforms.add(new TranslocatingStorageAdaptor.BoxTransform(0,5,0, 200, 64, 200, 0, -5, 0));
|
||||
*/
|
||||
|
||||
|
||||
//StorageBackend storage = new RocksDBStorageBackend(VoxyConfig.CONFIG.storagePath);
|
||||
////StorageBackend storage = new FragmentedStorageBackendAdaptor(new File(VoxyConfig.CONFIG.storagePath));
|
||||
//return new CompressionStorageAdaptor(new ZSTDCompressor(VoxyConfig.CONFIG.savingCompressionLevel), storage);
|
||||
}
|
||||
|
||||
public WorldEngine createEngine() {
|
||||
return new WorldEngine(this.createStorageBackend(), VoxyConfig.CONFIG.ingestThreads, VoxyConfig.CONFIG.savingThreads, 5);
|
||||
}
|
||||
|
||||
//Saves the config for the world selection or something, need to figure out how to make it work with dimensional configs maybe?
|
||||
// or just have per world config, cause when creating the world engine doing the string substitution would
|
||||
// make it automatically select the right id
|
||||
public void save() {
|
||||
var file = this.selectionFolder.resolve("storage_config.json");
|
||||
var json = Serialization.GSON.toJson(this.storageConfig);
|
||||
try {
|
||||
Files.writeString(file, json);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Gets dimension independent base world, if singleplayer, its the world name, if multiplayer, its the server ip
|
||||
private static Path getBasePath(ClientWorld world) {
|
||||
//TODO: improve this
|
||||
Path basePath = MinecraftClient.getInstance().runDirectory.toPath().resolve(".voxy").resolve("saves");
|
||||
var iserver = MinecraftClient.getInstance().getServer();
|
||||
if (iserver != null) {
|
||||
basePath = iserver.getSavePath(WorldSavePath.ROOT).resolve("voxy");
|
||||
} else {
|
||||
var netHandle = MinecraftClient.getInstance().getNetworkHandler();
|
||||
if (netHandle == null) {
|
||||
System.err.println("Network handle null");
|
||||
basePath = basePath.resolve("UNKNOWN");
|
||||
} else {
|
||||
var info = netHandle.getServerInfo();
|
||||
if (info == null) {
|
||||
System.err.println("Server info null");
|
||||
basePath = basePath.resolve("UNKNOWN");
|
||||
} else {
|
||||
if (info.isRealm()) {
|
||||
basePath = basePath.resolve("realms");
|
||||
} else {
|
||||
basePath = basePath.resolve(info.address.replace(":", "_"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return basePath;
|
||||
}
|
||||
|
||||
private static String bytesToHex(byte[] hash) {
|
||||
StringBuilder hexString = new StringBuilder(2 * hash.length);
|
||||
for (byte b : hash) {
|
||||
String hex = Integer.toHexString(0xff & b);
|
||||
if (hex.length() == 1) {
|
||||
hexString.append('0');
|
||||
}
|
||||
hexString.append(hex);
|
||||
}
|
||||
return hexString.toString();
|
||||
}
|
||||
|
||||
private static String getWorldId(ClientWorld world) {
|
||||
String data = world.getBiomeAccess().seed + world.getRegistryKey().toString();
|
||||
try {
|
||||
return bytesToHex(MessageDigest.getInstance("SHA-256").digest(data.getBytes())).substring(0, 32);
|
||||
} catch (
|
||||
NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
//The way this works is saves are segmented into base worlds, e.g. server ip, local save etc
|
||||
// these are then segmented into subsaves for different worlds within the parent
|
||||
public ContextSelectionSystem() {
|
||||
}
|
||||
|
||||
|
||||
public Selection getBestSelectionOrCreate(ClientWorld world) {
|
||||
var path = getBasePath(world);
|
||||
try {
|
||||
Files.createDirectories(path);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return new Selection(path, getWorldId(world));
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package me.cortex.voxy.client.saver;
|
||||
|
||||
import me.cortex.voxy.client.config.VoxyConfig;
|
||||
import me.cortex.voxy.common.storage.StorageBackend;
|
||||
import me.cortex.voxy.common.storage.compressors.ZSTDCompressor;
|
||||
import me.cortex.voxy.common.storage.config.ConfigBuildCtx;
|
||||
import me.cortex.voxy.common.storage.config.Serialization;
|
||||
import me.cortex.voxy.common.storage.config.StorageConfig;
|
||||
import me.cortex.voxy.common.storage.other.CompressionStorageAdaptor;
|
||||
import me.cortex.voxy.common.storage.other.TranslocatingStorageAdaptor;
|
||||
import me.cortex.voxy.common.storage.rocksdb.RocksDBStorageBackend;
|
||||
import me.cortex.voxy.common.world.WorldEngine;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
//Sets up a world engine with respect to the world the client is currently loaded into
|
||||
// this is a bit tricky as each world has its own config, e.g. storage configuration
|
||||
public class WorldSelectionSystem {
|
||||
public static class Selection {
|
||||
private VoxyConfig config;
|
||||
|
||||
public StorageBackend createStorageBackend() {
|
||||
var baseDB = new RocksDBStorageBackend.Config();
|
||||
baseDB.path = VoxyConfig.CONFIG.storagePath;
|
||||
|
||||
var compressor = new ZSTDCompressor.Config();
|
||||
compressor.compressionLevel = VoxyConfig.CONFIG.savingCompressionLevel;
|
||||
|
||||
var compression = new CompressionStorageAdaptor.Config();
|
||||
compression.delegate = baseDB;
|
||||
compression.compressor = compressor;
|
||||
|
||||
var translocator = new TranslocatingStorageAdaptor.Config();
|
||||
translocator.delegate = compression;
|
||||
translocator.transforms.add(new TranslocatingStorageAdaptor.BoxTransform(0,5,0, 200, 64, 200, 0, -5, 0));
|
||||
|
||||
var ctx = new ConfigBuildCtx();
|
||||
return translocator.build(ctx);
|
||||
|
||||
//StorageBackend storage = new RocksDBStorageBackend(VoxyConfig.CONFIG.storagePath);
|
||||
////StorageBackend storage = new FragmentedStorageBackendAdaptor(new File(VoxyConfig.CONFIG.storagePath));
|
||||
//return new CompressionStorageAdaptor(new ZSTDCompressor(VoxyConfig.CONFIG.savingCompressionLevel), storage);
|
||||
}
|
||||
|
||||
public WorldEngine createEngine() {
|
||||
return new WorldEngine(this.createStorageBackend(), VoxyConfig.CONFIG.ingestThreads, VoxyConfig.CONFIG.savingThreads, 5);
|
||||
}
|
||||
|
||||
//Saves the config for the world selection or something, need to figure out how to make it work with dimensional configs maybe?
|
||||
// or just have per world config, cause when creating the world engine doing the string substitution would
|
||||
// make it automatically select the right id
|
||||
public void save() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//The way this works is saves are segmented into base worlds, e.g. server ip, local save etc
|
||||
// these are then segmented into subsaves for different worlds within the parent
|
||||
public WorldSelectionSystem() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public Selection getBestSelectionOrCreate(ClientWorld world) {
|
||||
return new Selection();
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,9 @@ import java.util.Stack;
|
||||
|
||||
public class ConfigBuildCtx {
|
||||
//List of tokens
|
||||
public static final String BASE_LEVEL_PATH = "{base_level_path}";
|
||||
public static final String BASE_SAVE_PATH = "{base_save_path}";
|
||||
public static final String WORLD_IDENTIFIER = "{world_identifier}";
|
||||
public static final String DEFAULT_STORAGE_PATH = BASE_SAVE_PATH+"/"+WORLD_IDENTIFIER+"/storage/";
|
||||
|
||||
|
||||
private final Map<String, String> properties = new HashMap<>();
|
||||
@@ -74,17 +76,19 @@ public class ConfigBuildCtx {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a path with the current build context path
|
||||
* @param other path to resolve against
|
||||
* Resolves the current path stack recursively
|
||||
* @return resolved path
|
||||
*/
|
||||
public String resolvePath(String other) {
|
||||
this.pathStack.push(other);
|
||||
public String resolvePath() {
|
||||
String prev = "";
|
||||
String path = "";
|
||||
for (var part : this.pathStack) {
|
||||
path = concatPath(path, part);
|
||||
}
|
||||
this.pathStack.pop();
|
||||
do {
|
||||
prev = path;
|
||||
path = "";
|
||||
for (var part : this.pathStack) {
|
||||
path = concatPath(path, part);
|
||||
}
|
||||
} while (!prev.equals(path));
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
@@ -159,11 +159,9 @@ public class LMDBStorageBackend extends StorageBackend {
|
||||
}
|
||||
|
||||
public static class Config extends StorageConfig {
|
||||
public String path;
|
||||
|
||||
@Override
|
||||
public StorageBackend build(ConfigBuildCtx ctx) {
|
||||
return new LMDBStorageBackend(ctx.ensurePathExists(ctx.substituteString(ctx.resolvePath(this.path))));
|
||||
return new LMDBStorageBackend(ctx.ensurePathExists(ctx.substituteString(ctx.resolvePath())));
|
||||
}
|
||||
|
||||
public static String getConfigTypeName() {
|
||||
|
||||
@@ -166,11 +166,9 @@ public class RocksDBStorageBackend extends StorageBackend {
|
||||
}
|
||||
|
||||
public static class Config extends StorageConfig {
|
||||
public String path;
|
||||
|
||||
@Override
|
||||
public StorageBackend build(ConfigBuildCtx ctx) {
|
||||
return new RocksDBStorageBackend(ctx.ensurePathExists(ctx.substituteString(ctx.resolvePath(this.path))));
|
||||
return new RocksDBStorageBackend(ctx.ensurePathExists(ctx.substituteString(ctx.resolvePath())));
|
||||
}
|
||||
|
||||
public static String getConfigTypeName() {
|
||||
|
||||
@@ -8,5 +8,6 @@ accessible field net/minecraft/client/render/GameRenderer zoomX F
|
||||
accessible field net/minecraft/client/render/GameRenderer zoomY F
|
||||
accessible field net/minecraft/client/render/GameRenderer zoom F
|
||||
accessible field net/minecraft/client/world/ClientWorld worldRenderer Lnet/minecraft/client/render/WorldRenderer;
|
||||
accessible field net/minecraft/world/biome/source/BiomeAccess seed J
|
||||
|
||||
accessible method net/minecraft/client/render/GameRenderer getFov (Lnet/minecraft/client/render/Camera;FZ)D
|
||||
Reference in New Issue
Block a user