Added experimental TranslocatingStorageAdaptor
This commit is contained in:
@@ -2,6 +2,7 @@ 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.terrain.WorldImportCommand;
|
||||
import me.cortex.voxy.common.storage.config.Serialization;
|
||||
import me.cortex.voxy.common.storage.other.CompressionStorageAdaptor;
|
||||
@@ -14,27 +15,21 @@ import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class Voxy implements ClientModInitializer {
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
//var cfg = new CompressionStorageAdaptor.Config();
|
||||
//cfg.compressor = new ZSTDCompressor.Config();
|
||||
//cfg.backend = new FragmentedStorageBackendAdaptor.Config();
|
||||
//((FragmentedStorageBackendAdaptor.Config)cfg.backend).backends.add(new RocksDBStorageBackend.Config());
|
||||
//System.out.println(Serialization.GSON.toJson(cfg));
|
||||
Serialization.init();
|
||||
|
||||
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> {
|
||||
dispatcher.register(WorldImportCommand.register());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static final WorldSelectionSystem selector = new WorldSelectionSystem();
|
||||
|
||||
public static VoxelCore createVoxelCore(ClientWorld world) {
|
||||
StorageBackend storage = new RocksDBStorageBackend(VoxyConfig.CONFIG.storagePath);
|
||||
//StorageBackend storage = new FragmentedStorageBackendAdaptor(new File(VoxyConfig.CONFIG.storagePath));
|
||||
storage = new CompressionStorageAdaptor(new ZSTDCompressor(VoxyConfig.CONFIG.savingCompressionLevel), storage);
|
||||
var engine = new WorldEngine(storage, VoxyConfig.CONFIG.ingestThreads, VoxyConfig.CONFIG.savingThreads, 5);
|
||||
return new VoxelCore(engine);
|
||||
var selection = selector.getBestSelectionOrCreate(world);
|
||||
return new VoxelCore(selection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ public class DistanceTracker {
|
||||
|
||||
public void init(int x, int z) {
|
||||
//Radius of chunks to enqueue
|
||||
int SIZE = 64;
|
||||
int SIZE = 128;
|
||||
//Insert highest LOD level
|
||||
for (int ox = -SIZE; ox <= SIZE; ox++) {
|
||||
for (int oz = -SIZE; oz <= SIZE; oz++) {
|
||||
@@ -265,8 +265,8 @@ public class DistanceTracker {
|
||||
|
||||
this.currentX = cx;
|
||||
this.currentZ = cz;
|
||||
this.lastUpdateX = x;
|
||||
this.lastUpdateZ = z;
|
||||
this.lastUpdateX = x + (((int)(Math.random()*4))<<(this.shiftSize-4));
|
||||
this.lastUpdateZ = z + (((int)(Math.random()*4))<<(this.shiftSize-4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +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.common.world.WorldEngine;
|
||||
import me.cortex.voxy.client.importers.WorldImporter;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
@@ -46,8 +47,8 @@ public class VoxelCore {
|
||||
|
||||
//private final Thread shutdownThread = new Thread(this::shutdown);
|
||||
|
||||
public VoxelCore(WorldEngine engine) {
|
||||
this.world = engine;
|
||||
public VoxelCore(WorldSelectionSystem.Selection worldSelection) {
|
||||
this.world = worldSelection.createEngine();
|
||||
System.out.println("Initializing voxy core");
|
||||
|
||||
//Trigger the shared index buffer loading
|
||||
@@ -106,6 +107,7 @@ public class VoxelCore {
|
||||
}
|
||||
|
||||
private Matrix4f getProjectionMatrix() {
|
||||
//TODO: use the existing projection matrix use mulLocal by the inverse of the projection and then mulLocal our projection
|
||||
|
||||
var projection = new Matrix4f();
|
||||
var client = MinecraftClient.getInstance();
|
||||
@@ -115,7 +117,7 @@ public class VoxelCore {
|
||||
|
||||
projection.setPerspective(fov * 0.01745329238474369f,
|
||||
(float) client.getWindow().getFramebufferWidth() / (float)client.getWindow().getFramebufferHeight(),
|
||||
64F, 16 * 3000f);
|
||||
16F, 16 * 3000f);
|
||||
var transform = new Matrix4f().identity();
|
||||
transform.translate(gameRenderer.zoomX, -gameRenderer.zoomY, 0.0F);
|
||||
transform.scale(gameRenderer.zoom, gameRenderer.zoom, 1.0F);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package me.cortex.voxy.client.core.rendering;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
|
||||
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
|
||||
import me.cortex.voxy.common.world.WorldEngine;
|
||||
@@ -14,45 +16,77 @@ public class RenderTracker {
|
||||
private final WorldEngine world;
|
||||
private RenderGenerationService renderGen;
|
||||
private final AbstractFarWorldRenderer renderer;
|
||||
private final LongSet[] sets;
|
||||
|
||||
//private final Long2ObjectOpenHashMap<Object> activeSections = new Long2ObjectOpenHashMap<>();
|
||||
private final ConcurrentHashMap<Long,Object> activeSections = new ConcurrentHashMap<>(50000,0.75f, 16);
|
||||
private static final Object O = new Object();
|
||||
|
||||
public RenderTracker(WorldEngine world, AbstractFarWorldRenderer renderer) {
|
||||
this.world = world;
|
||||
this.renderer = renderer;
|
||||
this.sets = new LongSet[1<<4];
|
||||
for (int i = 0; i < this.sets.length; i++) {
|
||||
this.sets[i] = new LongOpenHashSet();
|
||||
}
|
||||
}
|
||||
|
||||
public void setRenderGen(RenderGenerationService renderGen) {
|
||||
this.renderGen = renderGen;
|
||||
}
|
||||
|
||||
public RenderTracker(WorldEngine world, AbstractFarWorldRenderer renderer) {
|
||||
this.world = world;
|
||||
this.renderer = renderer;
|
||||
public static long mixStafford13(long seed) {
|
||||
seed = (seed ^ seed >>> 30) * -4658895280553007687L;
|
||||
seed = (seed ^ seed >>> 27) * -7723592293110705685L;
|
||||
return seed ^ seed >>> 31;
|
||||
}
|
||||
|
||||
private LongSet getSet(long key) {
|
||||
return this.sets[(int) (mixStafford13(key) & (this.sets.length-1))];
|
||||
}
|
||||
|
||||
private void put(long key) {
|
||||
var set = this.getSet(key);
|
||||
synchronized (set) {
|
||||
set.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
private void remove(long key) {
|
||||
var set = this.getSet(key);
|
||||
synchronized (set) {
|
||||
set.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean contains(long key) {
|
||||
var set = this.getSet(key);
|
||||
synchronized (set) {
|
||||
return set.contains(key);
|
||||
}
|
||||
}
|
||||
|
||||
//Adds a lvl 0 section into the world renderer
|
||||
public void addLvl0(int x, int y, int z) {
|
||||
this.activeSections.put(WorldEngine.getWorldSectionId(0, x, y, z), O);
|
||||
this.put(WorldEngine.getWorldSectionId(0, x, y, z));
|
||||
this.renderGen.enqueueTask(0, x, y, z, this::shouldStillBuild);
|
||||
}
|
||||
|
||||
//Removes a lvl 0 section from the world renderer
|
||||
public void remLvl0(int x, int y, int z) {
|
||||
this.activeSections.remove(WorldEngine.getWorldSectionId(0, x, y, z));
|
||||
this.remove(WorldEngine.getWorldSectionId(0, x, y, z));
|
||||
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(0, x, y, z)));
|
||||
this.renderGen.removeTask(0, x, y, z);
|
||||
}
|
||||
|
||||
//Increases from lvl-1 to lvl at the coordinates (which are in lvl space)
|
||||
public void inc(int lvl, int x, int y, int z) {
|
||||
this.activeSections.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)));
|
||||
this.activeSections.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)+1));
|
||||
this.activeSections.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)));
|
||||
this.activeSections.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)+1));
|
||||
this.activeSections.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)));
|
||||
this.activeSections.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)+1));
|
||||
this.activeSections.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)));
|
||||
this.activeSections.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)+1));
|
||||
this.activeSections.put(WorldEngine.getWorldSectionId(lvl, x, y, z), O);
|
||||
this.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)));
|
||||
this.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)+1));
|
||||
this.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)));
|
||||
this.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)+1));
|
||||
this.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)));
|
||||
this.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)+1));
|
||||
this.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)));
|
||||
this.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)+1));
|
||||
this.put(WorldEngine.getWorldSectionId(lvl, x, y, z));
|
||||
|
||||
//TODO: make a seperate object to hold the build data and link it with the location in a
|
||||
// concurrent hashmap or something, this is so that e.g. the build data position
|
||||
@@ -82,15 +116,15 @@ public class RenderTracker {
|
||||
|
||||
//Decreases from lvl to lvl-1 at the coordinates (which are in lvl space)
|
||||
public void dec(int lvl, int x, int y, int z) {
|
||||
this.activeSections.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)), O);
|
||||
this.activeSections.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)+1), O);
|
||||
this.activeSections.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)), O);
|
||||
this.activeSections.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)+1), O);
|
||||
this.activeSections.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)), O);
|
||||
this.activeSections.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)+1), O);
|
||||
this.activeSections.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)), O);
|
||||
this.activeSections.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)+1), O);
|
||||
this.activeSections.remove(WorldEngine.getWorldSectionId(lvl, x, y, z));
|
||||
this.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)));
|
||||
this.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)+1));
|
||||
this.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)));
|
||||
this.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)+1));
|
||||
this.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)));
|
||||
this.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)+1));
|
||||
this.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)));
|
||||
this.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)+1));
|
||||
this.remove(WorldEngine.getWorldSectionId(lvl, x, y, z));
|
||||
|
||||
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl, x, y, z)));
|
||||
this.renderGen.removeTask(lvl, x, y, z);
|
||||
@@ -120,7 +154,7 @@ public class RenderTracker {
|
||||
|
||||
//Called by the world engine when a section gets dirtied
|
||||
public void sectionUpdated(WorldSection section) {
|
||||
if (this.activeSections.containsKey(section.key)) {
|
||||
if (this.contains(section.key)) {
|
||||
//TODO:FIXME: if the section gets updated, that means that its neighbors might need to be updated aswell
|
||||
// (due to block occlusion)
|
||||
|
||||
@@ -144,7 +178,7 @@ public class RenderTracker {
|
||||
// it also batch collects the geometry sections until all the geometry for an operation is collected, then it executes the operation, its removes flickering
|
||||
public void processBuildResult(BuiltSection section) {
|
||||
//Check that we still want the section
|
||||
if (this.activeSections.containsKey(section.position)) {
|
||||
if (this.contains(section.position)) {
|
||||
this.renderer.enqueueResult(section);
|
||||
} else {
|
||||
section.free();
|
||||
@@ -152,6 +186,6 @@ public class RenderTracker {
|
||||
}
|
||||
|
||||
public boolean shouldStillBuild(int lvl, int x, int y, int z) {
|
||||
return this.activeSections.containsKey(WorldEngine.getWorldSectionId(lvl, x, y, z));
|
||||
return this.contains(WorldEngine.getWorldSectionId(lvl, x, y, z));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package me.cortex.voxy.client.saver;
|
||||
|
||||
import me.cortex.voxy.client.config.VoxyConfig;
|
||||
import me.cortex.voxy.common.storage.other.CompressionStorageAdaptor;
|
||||
import me.cortex.voxy.common.storage.other.FragmentedStorageBackendAdaptor;
|
||||
import me.cortex.voxy.common.storage.compressors.ZSTDCompressor;
|
||||
import me.cortex.voxy.common.world.WorldEngine;
|
||||
|
||||
import java.io.File;
|
||||
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 SaveSelectionSystem {
|
||||
|
||||
//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 SaveSelectionSystem(List<Path> storagePaths) {
|
||||
|
||||
}
|
||||
|
||||
public WorldEngine createWorldEngine() {
|
||||
//TODO: have basicly a recursive config tree for StorageBackend
|
||||
// with a .build() method
|
||||
// also have a way for the config to specify and create a config "screen"
|
||||
|
||||
// e.g. CompressionStorageAdaptorConfig(StorageCompressorConfig, StorageBackendConfig)
|
||||
// FragmentedStorageBackendAdaptorConfig(File)
|
||||
// RocksDBStorageBackendConfig(File)
|
||||
// RedisStorageBackendConfig(String, int, String)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
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.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 {
|
||||
public WorldEngine createEngine() {
|
||||
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();
|
||||
var storage = translocator.build(ctx);
|
||||
return new WorldEngine(storage, VoxyConfig.CONFIG.ingestThreads, VoxyConfig.CONFIG.savingThreads, 5);
|
||||
|
||||
//StorageBackend storage = new RocksDBStorageBackend(VoxyConfig.CONFIG.storagePath);
|
||||
////StorageBackend storage = new FragmentedStorageBackendAdaptor(new File(VoxyConfig.CONFIG.storagePath));
|
||||
//storage = new CompressionStorageAdaptor(new ZSTDCompressor(VoxyConfig.CONFIG.savingCompressionLevel), storage);
|
||||
//return new WorldEngine(storage, 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();
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package me.cortex.voxy.common.storage;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class StorageBackend {
|
||||
|
||||
@@ -19,4 +21,17 @@ public abstract class StorageBackend {
|
||||
public abstract void flush();
|
||||
|
||||
public abstract void close();
|
||||
|
||||
public List<StorageBackend> getChildBackends() {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
public final List<StorageBackend> collectAllBackends() {
|
||||
List<StorageBackend> backends = new ArrayList<>();
|
||||
backends.add(this);
|
||||
for (var child : this.getChildBackends()) {
|
||||
backends.addAll(child.collectAllBackends());
|
||||
}
|
||||
return backends;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,27 +3,71 @@ package me.cortex.voxy.common.storage.config;
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Stack;
|
||||
|
||||
public class ConfigBuildCtx {
|
||||
//List of tokens
|
||||
public static final String BASE_LEVEL_PATH = "{base_level_path}";
|
||||
|
||||
//Pushes a path to the BuildCtx path stack so that when resolving with resolvePath it uses the entire path stack
|
||||
private final Stack<String> pathStack = new Stack<>();
|
||||
|
||||
/**
|
||||
* Pushes a path to the build context so that when resolvePath is called it is with respect to the added path
|
||||
* @param path the path to add to the stack
|
||||
* @return the build context
|
||||
*/
|
||||
public ConfigBuildCtx pushPath(String path) {
|
||||
this.pathStack.push(path);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pops a path from the build context path stack
|
||||
* @return the build context
|
||||
*/
|
||||
public ConfigBuildCtx popPath() {
|
||||
this.pathStack.pop();
|
||||
return this;
|
||||
}
|
||||
|
||||
//TODO: FINISH THIS and check and test
|
||||
private static String concatPath(String a, String b) {
|
||||
if (b.contains("..")) {
|
||||
throw new IllegalStateException("Relative resolving not supported");
|
||||
}
|
||||
|
||||
if ((!a.isBlank()) && !a.endsWith("/")) {
|
||||
a += "/";
|
||||
}
|
||||
|
||||
if (b.startsWith("/")) {//Absolute path
|
||||
return b;
|
||||
}
|
||||
|
||||
if (b.startsWith("./")) {
|
||||
b = b.substring(2);
|
||||
}
|
||||
|
||||
if (b.startsWith(":", 1)) {//Drive path
|
||||
return b;
|
||||
}
|
||||
|
||||
return a+b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a path with the current build context path
|
||||
* @param other path to resolve against
|
||||
* @return resolved path
|
||||
*/
|
||||
public String resolvePath(String other) {
|
||||
return null;
|
||||
this.pathStack.push(other);
|
||||
String path = "";
|
||||
for (var part : this.pathStack) {
|
||||
path = concatPath(path, part);
|
||||
}
|
||||
this.pathStack.pop();
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,8 +76,8 @@ public class ConfigBuildCtx {
|
||||
* @return substituted string
|
||||
*/
|
||||
public String substituteString(String string) {
|
||||
//This is e.g. so you can have dbs spread across multiple disks if you want
|
||||
return null;
|
||||
//TODO: this
|
||||
return string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -98,4 +98,6 @@ public class Serialization {
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static void init() {}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,26 @@ package me.cortex.voxy.common.storage.config;
|
||||
|
||||
import me.cortex.voxy.common.storage.StorageBackend;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class StorageConfig {
|
||||
static {
|
||||
Serialization.CONFIG_TYPES.add(StorageConfig.class);
|
||||
}
|
||||
|
||||
public abstract StorageBackend build(ConfigBuildCtx ctx);
|
||||
|
||||
public List<StorageConfig> getChildStorageConfigs() {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
public final List<StorageConfig> collectStorageConfigs() {
|
||||
List<StorageConfig> configs = new ArrayList<>();
|
||||
configs.add(this);
|
||||
for (var child : this.getChildStorageConfigs()) {
|
||||
configs.addAll(child.collectStorageConfigs());
|
||||
}
|
||||
return configs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,19 +9,19 @@ import me.cortex.voxy.common.storage.config.StorageConfig;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
//Compresses the section data
|
||||
public class CompressionStorageAdaptor extends StorageBackend {
|
||||
public class CompressionStorageAdaptor extends DelegatingStorageAdaptor {
|
||||
private final StorageCompressor compressor;
|
||||
private final StorageBackend child;
|
||||
public CompressionStorageAdaptor(StorageCompressor compressor, StorageBackend child) {
|
||||
public CompressionStorageAdaptor(StorageCompressor compressor, StorageBackend delegate) {
|
||||
super(delegate);
|
||||
this.compressor = compressor;
|
||||
this.child = child;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getSectionData(long key) {
|
||||
var data = this.child.getSectionData(key);
|
||||
var data = this.delegate.getSectionData(key);
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -33,43 +33,28 @@ public class CompressionStorageAdaptor extends StorageBackend {
|
||||
@Override
|
||||
public void setSectionData(long key, ByteBuffer data) {
|
||||
var cdata = this.compressor.compress(data);
|
||||
this.child.setSectionData(key, cdata);
|
||||
this.delegate.setSectionData(key, cdata);
|
||||
MemoryUtil.memFree(cdata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSectionData(long key) {
|
||||
this.child.deleteSectionData(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putIdMapping(int id, ByteBuffer data) {
|
||||
this.child.putIdMapping(id, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Int2ObjectOpenHashMap<byte[]> getIdMappingsData() {
|
||||
return this.child.getIdMappingsData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
this.child.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.compressor.close();
|
||||
this.child.close();
|
||||
super.close();
|
||||
}
|
||||
|
||||
public static class Config extends StorageConfig {
|
||||
public CompressorConfig compressor;
|
||||
public StorageConfig backend;
|
||||
public StorageConfig delegate;
|
||||
|
||||
@Override
|
||||
public StorageBackend build(ConfigBuildCtx ctx) {
|
||||
return new CompressionStorageAdaptor(this.compressor.build(ctx), this.backend.build(ctx));
|
||||
return new CompressionStorageAdaptor(this.compressor.build(ctx), this.delegate.build(ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StorageConfig> getChildStorageConfigs() {
|
||||
return List.of(this.delegate);
|
||||
}
|
||||
|
||||
public static String getConfigTypeName() {
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package me.cortex.voxy.common.storage.other;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import me.cortex.voxy.common.storage.StorageBackend;
|
||||
import me.cortex.voxy.common.storage.StorageCompressor;
|
||||
import me.cortex.voxy.common.storage.config.CompressorConfig;
|
||||
import me.cortex.voxy.common.storage.config.ConfigBuildCtx;
|
||||
import me.cortex.voxy.common.storage.config.StorageConfig;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
public class DelegatingStorageAdaptor extends StorageBackend {
|
||||
protected final StorageBackend delegate;
|
||||
public DelegatingStorageAdaptor(StorageBackend delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getSectionData(long key) {
|
||||
return this.delegate.getSectionData(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSectionData(long key, ByteBuffer data) {
|
||||
this.delegate.setSectionData(key, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSectionData(long key) {
|
||||
this.delegate.deleteSectionData(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putIdMapping(int id, ByteBuffer data) {
|
||||
this.delegate.putIdMapping(id, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Int2ObjectOpenHashMap<byte[]> getIdMappingsData() {
|
||||
return this.delegate.getIdMappingsData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
this.delegate.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.delegate.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StorageBackend> getChildBackends() {
|
||||
return List.of(this.delegate);
|
||||
}
|
||||
}
|
||||
@@ -131,9 +131,19 @@ public class FragmentedStorageBackendAdaptor extends StorageBackend {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StorageBackend> getChildBackends() {
|
||||
return List.of(this.backends);
|
||||
}
|
||||
|
||||
public static class Config extends StorageConfig {
|
||||
public List<StorageConfig> backends = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public List<StorageConfig> getChildStorageConfigs() {
|
||||
return new ArrayList<>(this.backends);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageBackend build(ConfigBuildCtx ctx) {
|
||||
StorageBackend[] builtBackends = new StorageBackend[this.backends.size()];
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package me.cortex.voxy.common.storage.other;
|
||||
|
||||
import me.cortex.voxy.common.storage.StorageBackend;
|
||||
import me.cortex.voxy.common.storage.config.ConfigBuildCtx;
|
||||
import me.cortex.voxy.common.storage.config.StorageConfig;
|
||||
import me.cortex.voxy.common.world.WorldEngine;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TranslocatingStorageAdaptor extends DelegatingStorageAdaptor {
|
||||
public record BoxTransform(int x1, int y1, int z1, int x2, int y2, int z2, int dx, int dy, int dz) {
|
||||
public long transformIfInBox(long pos) {
|
||||
int lvl = WorldEngine.getLevel(pos);
|
||||
int x = WorldEngine.getX(pos);
|
||||
int y = WorldEngine.getY(pos);
|
||||
int z = WorldEngine.getZ(pos);
|
||||
|
||||
//TODO: FIXME this might need to be the other way around, as in shift x,y,z instead of x1 etc
|
||||
if (!((this.x1>>lvl) <= x && x <= (this.x2>>lvl) &&
|
||||
(this.y1>>lvl) <= y && y <= (this.y2>>lvl) &&
|
||||
(this.z1>>lvl) <= z && z <= (this.z2>>lvl))) {
|
||||
return -1;
|
||||
}
|
||||
return WorldEngine.getWorldSectionId(lvl,
|
||||
x + (this.dx>>lvl),
|
||||
y + (this.dy>>lvl),
|
||||
z + (this.dz>>lvl)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private final BoxTransform[] transforms;
|
||||
|
||||
public TranslocatingStorageAdaptor(StorageBackend delegate, BoxTransform... transforms) {
|
||||
super(delegate);
|
||||
this.transforms = transforms;
|
||||
}
|
||||
|
||||
private long transformPosition(long pos) {
|
||||
for (var transform : this.transforms) {
|
||||
long tpos = transform.transformIfInBox(pos);
|
||||
if (tpos != -1) {
|
||||
return tpos;
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getSectionData(long key) {
|
||||
return super.getSectionData(this.transformPosition(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSectionData(long key, ByteBuffer data) {
|
||||
super.setSectionData(this.transformPosition(key), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSectionData(long key) {
|
||||
super.deleteSectionData(this.transformPosition(key));
|
||||
}
|
||||
|
||||
public static class Config extends StorageConfig {
|
||||
public StorageConfig delegate;
|
||||
public List<BoxTransform> transforms = new ArrayList<>();
|
||||
|
||||
|
||||
@Override
|
||||
public StorageBackend build(ConfigBuildCtx ctx) {
|
||||
return new TranslocatingStorageAdaptor(this.delegate.build(ctx), this.transforms.toArray(BoxTransform[]::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StorageConfig> getChildStorageConfigs() {
|
||||
return List.of(this.delegate);
|
||||
}
|
||||
|
||||
public static String getConfigTypeName() {
|
||||
return "TranslocatingAdaptor";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,11 +66,11 @@ public class SaveLoadSystem {
|
||||
hash ^= lut[i];
|
||||
}
|
||||
|
||||
if (section.key != key) {
|
||||
//throw new IllegalStateException("Decompressed section not the same as requested. got: " + key + " expected: " + section.key);
|
||||
System.err.println("Decompressed section not the same as requested. got: " + key + " expected: " + section.key);
|
||||
return false;
|
||||
}
|
||||
//if (section.key != key) {
|
||||
// //throw new IllegalStateException("Decompressed section not the same as requested. got: " + key + " expected: " + section.key);
|
||||
// System.err.println("Decompressed section not the same as requested. got: " + key + " expected: " + section.key);
|
||||
// return false;
|
||||
//}
|
||||
|
||||
for (int i = 0; i < section.data.length; i++) {
|
||||
short lutId = data.getShort();
|
||||
|
||||
@@ -49,8 +49,6 @@ public class WorldEngine {
|
||||
//TODO: regenerate the section from children
|
||||
Arrays.fill(into.data, Mapper.AIR);
|
||||
System.err.println("Section " + into.lvl + ", " + into.x + ", " + into.y + ", " + into.z + " was unable to load, removing");
|
||||
|
||||
this.storage.deleteSectionData(into.key);
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
@@ -80,6 +78,23 @@ public class WorldEngine {
|
||||
return ((long)lvl<<60)|((long)(y&0xFF)<<52)|((long)(z&((1<<24)-1))<<28)|((long)(x&((1<<24)-1))<<4);//NOTE: 4 bits spare for whatever
|
||||
}
|
||||
|
||||
public static int getLevel(long id) {
|
||||
return (int) ((id>>60)&0xf);
|
||||
}
|
||||
|
||||
//TODO: check these shifts are correct for all the gets
|
||||
public static int getX(long id) {
|
||||
return (int) ((id<<36)>>40);
|
||||
}
|
||||
|
||||
public static int getY(long id) {
|
||||
return (int) ((id<<4)>>56);
|
||||
}
|
||||
|
||||
public static int getZ(long id) {
|
||||
return (int) ((id<<12)>>40);
|
||||
}
|
||||
|
||||
//Marks a section as dirty, enqueuing it for saving and or render data rebuilding
|
||||
public void markDirty(WorldSection section) {
|
||||
if (this.dirtyCallback != null) {
|
||||
|
||||
@@ -36,7 +36,7 @@ public class Mipper {
|
||||
if (!Mapper.isAir(I000)) {
|
||||
return I000;
|
||||
}
|
||||
//TODO: need to account for different light levels of "air"
|
||||
|
||||
int blockLight = (Mapper.getLightId(I000)&0xF0)+(Mapper.getLightId(I001)&0xF0)+(Mapper.getLightId(I010)&0xF0)+(Mapper.getLightId(I011)&0xF0)+
|
||||
(Mapper.getLightId(I100)&0xF0)+(Mapper.getLightId(I101)&0xF0)+(Mapper.getLightId(I110)&0xF0)+(Mapper.getLightId(I111)&0xF0);
|
||||
int skyLight = (Mapper.getLightId(I000)&0x0F)+(Mapper.getLightId(I001)&0x0F)+(Mapper.getLightId(I010)&0x0F)+(Mapper.getLightId(I011)&0x0F)+
|
||||
|
||||
Reference in New Issue
Block a user