Made it reloadable, added import command, fixed race conditions and many other things

This commit is contained in:
mcrcortex
2024-01-14 10:14:01 +10:00
parent cf44c2e5b1
commit 10a0db8e04
14 changed files with 150 additions and 99 deletions

View File

@@ -0,0 +1,7 @@
package me.cortex.voxelmon;
import me.cortex.voxelmon.core.VoxelCore;
public interface IGetVoxelCore {
VoxelCore getVoxelCore();
}

View File

@@ -2,13 +2,17 @@ package me.cortex.voxelmon;
import me.cortex.voxelmon.terrain.TestSparseGenCommand;
//import me.cortex.voxelmon.terrain.WorldImportCommand;
import me.cortex.voxelmon.terrain.WorldImportCommand;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
public class Voxelmon implements ClientModInitializer {
@Override
public void onInitializeClient() {
//CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> WorldImportCommand.register(dispatcher));
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> {
dispatcher.register(WorldImportCommand.register());
});
}
}

View File

@@ -29,7 +29,7 @@ public class DistanceTracker {
this.tracker = tracker;
this.scale = scale;
this.rings[0] = new TransitionRing2D(5, (int) Math.ceil(MinecraftClient.getInstance().gameRenderer.getViewDistance()/16)/2, (x, z)->{
this.rings[0] = new TransitionRing2D(5, (int) MinecraftClient.getInstance().options.getClampedViewDistance()/2, (x, z)->{
if (false) {
return;
}

View File

@@ -48,7 +48,7 @@ import static org.lwjgl.opengl.ARBFramebufferObject.glBindFramebuffer;
//Ingest -> world engine -> raw render data -> render data
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.TALL_GRASS, Blocks.LARGE_FERN, Blocks.SHORT_GRASS,
Blocks.SPRUCE_LEAVES,
Blocks.BIRCH_LEAVES,
@@ -58,9 +58,6 @@ public class VoxelCore {
private static final Set<Block> waterTint = new HashSet<>(List.of(Blocks.WATER));
public static VoxelCore INSTANCE = new VoxelCore();
private final WorldEngine world;
private final DistanceTracker distanceTracker;
private final RenderGenerationService renderGen;
@@ -69,6 +66,8 @@ public class VoxelCore {
private final AbstractFarWorldRenderer renderer;
private final PostProcessing postProcessing;
//private final Thread shutdownThread = new Thread(this::shutdown);
public VoxelCore() {
//Trigger the shared index buffer loading
SharedIndexBuffer.INSTANCE.id();
@@ -81,33 +80,13 @@ public class VoxelCore {
this.renderTracker.setRenderGen(this.renderGen);
//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, 16);
this.distanceTracker = new DistanceTracker(this.renderTracker, 5, 16);//20
this.postProcessing = new PostProcessing();
this.world.getMapper().setCallbacks(this::stateUpdate, this::biomeUpdate);
Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));
/*
Random r = new Random();
for (int ring = 0; ring < 5; ring++) {
for (int x = -32; x < 32; x++) {
for (int z = -32; z < 32; z++) {
if ((-16 < x && x < 16) && (-16 < z && z < 16)) {
continue;
}
var b = new MemoryBuffer(1000 * 8);
for (long j = 0; j < b.size; j += 8) {
MemoryUtil.memPutLong(b.address + j, r.nextLong());
}
this.renderer.enqueueResult(new BuiltSectionGeometry(WorldEngine.getWorldSectionId(ring, x, 2>>ring, z), b, null));
}
}
}*/
//Runtime.getRuntime().addShutdownHook(this.shutdownThread);
//WorldImporter importer = new WorldImporter(this.world, MinecraftClient.getInstance().world);
@@ -200,15 +179,21 @@ public class VoxelCore {
// since they are AABBS crossing the normal is impossible without one of the axis being equal
public void shutdown() {
try {this.world.shutdown();} catch (Exception e) {System.err.println(e);}
//if (Thread.currentThread() != this.shutdownThread) {
// Runtime.getRuntime().removeShutdownHook(this.shutdownThread);
//}
//this.world.getMapper().forceResaveStates();
System.out.println("Shutting down voxel core");
try {this.renderGen.shutdown();} catch (Exception e) {System.err.println(e);}
try {this.world.shutdown();} catch (Exception e) {System.err.println(e);}
try {this.renderer.shutdown();} catch (Exception e) {System.err.println(e);}
try {this.postProcessing.shutdown();} catch (Exception e) {System.err.println(e);}
}
public WorldImporter createWorldImporter(World mcWorld, File worldPath) {
var importer = new WorldImporter(this.world, mcWorld);
importer.importWorldAsyncStart(worldPath, 10, null, ()->{
importer.importWorldAsyncStart(worldPath, 5, null, ()->{
System.err.println("DONE IMPORT");
});
return importer;

View File

@@ -32,47 +32,6 @@ public class RenderTracker {
public RenderTracker(WorldEngine world, AbstractFarWorldRenderer renderer) {
this.world = world;
this.renderer = renderer;
var loader = new Thread(()->{
try {
Thread.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int OX = 0;//-27;
int OZ = 0;//276;
int DROP = 48;
//Do ring rendering
for (int i = 0; i < 5; i++) {
for (int x = -DROP; x <= DROP; x++) {
for (int z = -DROP; z <= DROP; z++) {
int d = x*x+z*z;
if (d<(DROP/2-1)*(DROP/2) || d>DROP*DROP)
continue;
for (int y = -3>>i; y < Math.max(1, 10 >> i); y++) {
var sec = this.world.acquire(i, x + (OX>>(1+i)), y, z + (OZ>>(1+i)));
//this.renderGen.enqueueTask(sec);
sec.release();
}
try {
while (this.renderGen.getTaskCount() > 1000) {
Thread.sleep(50);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
});
loader.setDaemon(true);
//loader.start();
}
//Adds a lvl 0 section into the world renderer

View File

@@ -38,7 +38,8 @@ public abstract class TrackedObject {
boolean[] freed = new boolean[1];
var clean = cleaner.register(obj, ()->{
if (!freed[0]) {
System.err.println("Object named: "+ clazz+" was not freed, location at:\n" + trace);
System.err.println("Object named: "+ clazz+" was not freed, location at:\n");
trace.printStackTrace();
System.err.flush();
}
});

View File

@@ -131,6 +131,10 @@ public class ColourResolver {
public static long resolveBiomeColour(String biomeId) {
var biome = MinecraftClient.getInstance().world.getRegistryManager().get(RegistryKeys.BIOME).get(new Identifier(biomeId));
if (biome == null) {
System.err.println("Biome: " + biomeId + " doesnt exist in registry!");
return 0;
}
int ARGBFoliage = biome.getFoliageColor();
int ARGBWater = biome.getWaterColor();
return Integer.toUnsignedLong(((ARGBFoliage&0xFFFFFF)<<8)|(ARGBFoliage>>>24)) | (Integer.toUnsignedLong(((ARGBWater&0xFFFFFF)<<8)|(ARGBWater>>>24))<<32);

View File

@@ -79,6 +79,7 @@ public class Mapper {
List<BiomeEntry> bentries = new ArrayList<>();
List<Pair<byte[], Integer>> sentryErrors = new ArrayList<>();
for (var entry : mappings.int2ObjectEntrySet()) {
int entryType = entry.getIntKey()>>>30;
int id = entry.getIntKey() & ((1<<30)-1);
@@ -126,6 +127,7 @@ public class Mapper {
}
this.blockId2stateEntry.add(entry);
});
bentries.stream().sorted(Comparator.comparing(a->a.id)).forEach(entry -> {
if (this.biomeId2biomeEntry.size() != entry.id) {
throw new IllegalStateException("Biome entry not ordered");
@@ -135,7 +137,7 @@ public class Mapper {
}
private StateEntry registerNewBlockState(BlockState state) {
private synchronized StateEntry registerNewBlockState(BlockState state) {
StateEntry entry = new StateEntry(this.blockId2stateEntry.size(), state);
//this.block2stateEntry.put(state, entry);
this.blockId2stateEntry.add(entry);
@@ -151,7 +153,7 @@ public class Mapper {
return entry;
}
private BiomeEntry registerNewBiome(String biome) {
private synchronized BiomeEntry registerNewBiome(String biome) {
BiomeEntry entry = new BiomeEntry(this.biome2biomeEntry.size(), biome);
//this.biome2biomeEntry.put(biome, entry);
this.biomeId2biomeEntry.add(entry);
@@ -207,6 +209,42 @@ public class Mapper {
return out;
}
public void forceResaveStates() {
var blocks = new ArrayList<>(this.block2stateEntry.values());
var biomes = new ArrayList<>(this.biome2biomeEntry.values());
for (var entry : blocks) {
if (entry.state.isAir() && entry.id == 0) {
continue;
}
if (this.blockId2stateEntry.indexOf(entry) != entry.id) {
throw new IllegalStateException("State Id NOT THE SAME, very critically bad");
}
byte[] serialized = entry.serialize();
ByteBuffer buffer = MemoryUtil.memAlloc(serialized.length);
buffer.put(serialized);
buffer.rewind();
this.storage.putIdMapping(entry.id | (BLOCK_STATE_TYPE<<30), buffer);
MemoryUtil.memFree(buffer);
}
for (var entry : biomes) {
if (this.biomeId2biomeEntry.indexOf(entry) != entry.id) {
throw new IllegalStateException("Biome Id NOT THE SAME, very critically bad");
}
byte[] serialized = entry.serialize();
ByteBuffer buffer = MemoryUtil.memAlloc(serialized.length);
buffer.put(serialized);
buffer.rewind();
this.storage.putIdMapping(entry.id | (BIOME_TYPE<<30), buffer);
MemoryUtil.memFree(buffer);
}
this.storage.flush();
}
public static final class StateEntry {
public final int id;
public final BlockState state;

View File

@@ -115,7 +115,7 @@ public class StorageBackend {
}));
}
public void putIdMapping(int id, ByteBuffer data) {
public synchronized void putIdMapping(int id, ByteBuffer data) {
this.resizingTransaction(()->this.idMappingDatabase.transaction(transaction->{
var keyBuff = transaction.stack.malloc(4);
keyBuff.putInt(0, id);
@@ -135,7 +135,9 @@ public class StorageBackend {
int keyVal = keyPtr.mv_data().getInt(0);
byte[] data = new byte[(int) valPtr.mv_size()];
Objects.requireNonNull(valPtr.mv_data()).get(data);
mapping.put(keyVal, data);
if (mapping.put(keyVal, data) != null) {
throw new IllegalStateException("Multiple mappings to same id");
}
}
}
return null;

View File

@@ -1,6 +1,7 @@
package me.cortex.voxelmon.mixin.minecraft;
import me.cortex.voxelmon.core.VoxelCore;
import me.cortex.voxelmon.IGetVoxelCore;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.world.ClientChunkManager;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.chunk.WorldChunk;
@@ -14,6 +15,9 @@ import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
public class MixinClientChunkManager {
@Inject(method = "unload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/world/ClientChunkManager$ClientChunkMap;compareAndSet(ILnet/minecraft/world/chunk/WorldChunk;Lnet/minecraft/world/chunk/WorldChunk;)Lnet/minecraft/world/chunk/WorldChunk;", shift = At.Shift.BEFORE), locals = LocalCapture.CAPTURE_FAILHARD)
private void injectUnload(ChunkPos pos, CallbackInfo ci, int index, WorldChunk worldChunk) {
VoxelCore.INSTANCE.enqueueIngest(worldChunk);
var core = ((IGetVoxelCore)MinecraftClient.getInstance().worldRenderer).getVoxelCore();
if (core != null) {
core.enqueueIngest(worldChunk);
}
}
}

View File

@@ -1,6 +1,8 @@
package me.cortex.voxelmon.mixin.minecraft;
import me.cortex.voxelmon.IGetVoxelCore;
import me.cortex.voxelmon.core.VoxelCore;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.hud.DebugHud;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -14,6 +16,9 @@ public class MixinDebugHud {
@Inject(method = "getRightText", at = @At("TAIL"))
private void injectDebug(CallbackInfoReturnable<List<String>> cir) {
var ret = cir.getReturnValue();
VoxelCore.INSTANCE.addDebugInfo(ret);
var core = ((IGetVoxelCore) MinecraftClient.getInstance().worldRenderer).getVoxelCore();
if (core != null) {
core.addDebugInfo(ret);
}
}
}

View File

@@ -1,40 +1,80 @@
package me.cortex.voxelmon.mixin.minecraft;
import me.cortex.voxelmon.IGetVoxelCore;
import me.cortex.voxelmon.Voxelmon;
import me.cortex.voxelmon.core.VoxelCore;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.*;
import net.minecraft.client.render.block.entity.BlockEntityRenderDispatcher;
import net.minecraft.client.render.entity.EntityRenderDispatcher;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.world.ClientWorld;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(WorldRenderer.class)
public abstract class MixinWorldRenderer {
public abstract class MixinWorldRenderer implements IGetVoxelCore {
@Shadow protected abstract void renderLayer(RenderLayer renderLayer, MatrixStack matrices, double cameraX, double cameraY, double cameraZ, Matrix4f positionMatrix);
@Shadow protected abstract void setupTerrain(Camera camera, Frustum frustum, boolean hasForcedFrustum, boolean spectator);
@Shadow private Frustum frustum;
@Shadow private @Nullable ClientWorld world;
@Unique private VoxelCore core;
@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) {
//Call our setup method
VoxelCore.INSTANCE.renderSetup(this.frustum, camera);
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();
//Call the actual render method
VoxelCore.INSTANCE.renderOpaque(matrices, cam.x, cam.y, cam.z);
this.core.renderOpaque(matrices, cam.x, cam.y, cam.z);
}
public VoxelCore getVoxelCore() {
return this.core;
}
@Inject(method = "reload()V", at = @At("TAIL"))
private void resetVoxelCore(CallbackInfo ci) {
if (this.world != null && this.core != null) {
this.core.shutdown();
this.core = new VoxelCore();
}
}
@Inject(method = "setWorld", at = @At("TAIL"))
private void initVoxelCore(ClientWorld world, CallbackInfo ci) {
if (world == null) {
if (this.core != null) {
this.core.shutdown();
this.core = null;
}
return;
}
if (this.core != null) {
throw new IllegalStateException("Core is not null");
}
this.core = new VoxelCore();
}
@Inject(method = "close", at = @At("HEAD"))
private void injectClose(CallbackInfo ci) {
if (this.core != null) {
this.core.shutdown();
this.core = null;
}
}

View File

@@ -1,29 +1,31 @@
package me.cortex.voxelmon.terrain;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import me.cortex.voxelmon.core.VoxelCore;
import me.cortex.voxelmon.IGetVoxelCore;
import me.cortex.voxelmon.importers.WorldImporter;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientCommandSource;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.Text;
import org.jetbrains.annotations.NotNull;
import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal;
import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument;
import java.io.File;
/*
public class WorldImportCommand {
public static void register(CommandDispatcher<ClientCommandSource> dispatcher) {
dispatcher.register(literal("zenith")
.then(literal("import").then(literal("world").then(argument("world_name", StringArgumentType.string()).executes(WorldImportCommand::importWorld)))));
public static LiteralArgumentBuilder<FabricClientCommandSource> register() {
return ClientCommandManager.literal("zenith").then(ClientCommandManager.literal("import").then(ClientCommandManager.literal("world").then(ClientCommandManager.argument("world_name", StringArgumentType.string()).executes(WorldImportCommand::importWorld))));
}
public static WorldImporter importerInstance;
private static int importWorld(CommandContext<ClientCommandSource> ctx) {
VoxelCore.INSTANCE.createWorldImporter(MinecraftClient.getInstance().world, ctx.getArgument("world_name", String.class));
private static int importWorld(CommandContext<FabricClientCommandSource> ctx) {
var instance = MinecraftClient.getInstance();
var file = new File(ctx.getArgument("world_name", String.class));
importerInstance = ((IGetVoxelCore)instance.worldRenderer).getVoxelCore().createWorldImporter(MinecraftClient.getInstance().player.clientWorld, file);
return 0;
}
}
*/
}

View File

@@ -81,7 +81,7 @@ void main() {
if (face == 0) {
colour.xyz *= vec3(0.75, 0.75, 0.75);
} else if (face != 1) {
colour.xyz *= vec3((float(face-2)/4)*0.25 + 0.75);
colour.xyz *= vec3((float(face-2)/4)*0.5 + 0.5);
}