diff --git a/src/main/java/me/cortex/voxy/client/core/VoxelCore.java b/src/main/java/me/cortex/voxy/client/core/VoxelCore.java index bd7e37c0..3e4de223 100644 --- a/src/main/java/me/cortex/voxy/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/voxy/client/core/VoxelCore.java @@ -16,7 +16,10 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.Camera; import net.minecraft.client.render.Frustum; import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.boss.BossBar; +import net.minecraft.entity.boss.ServerBossBar; import net.minecraft.registry.RegistryKeys; +import net.minecraft.text.Text; import net.minecraft.world.World; import net.minecraft.world.chunk.WorldChunk; import org.joml.Matrix4f; @@ -54,7 +57,7 @@ public class VoxelCore { //private final Thread shutdownThread = new Thread(this::shutdown); - + private WorldImporter importer; public VoxelCore(ContextSelectionSystem.Selection worldSelection) { this.world = worldSelection.createEngine(); var cfg = worldSelection.getConfig(); @@ -216,6 +219,10 @@ public class VoxelCore { //} //this.world.getMapper().forceResaveStates(); + if (this.importer != null) { + System.out.println("Shutting down importer"); + try {this.importer.shutdown();this.importer = null;} catch (Exception e) {System.err.println(e);} + } System.out.println("Shutting down voxel core"); try {this.renderGen.shutdown();} catch (Exception e) {System.err.println(e);} System.out.println("Render gen shut down"); @@ -227,12 +234,24 @@ public class VoxelCore { System.out.println("Voxel core shut down"); } - public WorldImporter createWorldImporter(World mcWorld, File worldPath) { + public boolean createWorldImporter(World mcWorld, File worldPath) { + if (this.importer != null) { + return false; + } var importer = new WorldImporter(this.world, mcWorld); - importer.importWorldAsyncStart(worldPath, 4, null, ()->{ - System.err.println("DONE IMPORT"); - }); - return importer; + var bossBar = new ServerBossBar(Text.of("Voxy world importer"), BossBar.Color.GREEN, BossBar.Style.PROGRESS); + bossBar.addPlayer(MinecraftClient.getInstance().getServer().getPlayerManager().getPlayer(MinecraftClient.getInstance().player.getUuid())); + importer.importWorldAsyncStart(worldPath, 4, (a,b)-> + MinecraftClient.getInstance().executeSync(()-> { + bossBar.setPercent(((float) a)/((float) b)); + bossBar.setName(Text.of("Voxy import: "+ a+"/"+b + " region files")); + }), + ()-> { + MinecraftClient.getInstance().executeSync(bossBar::clearPlayers); + this.importer = null; + }); + this.importer = importer; + return true; } public WorldEngine getWorldEngine() { diff --git a/src/main/java/me/cortex/voxy/client/importers/WorldImporter.java b/src/main/java/me/cortex/voxy/client/importers/WorldImporter.java index d87b92c4..ef9d4401 100644 --- a/src/main/java/me/cortex/voxy/client/importers/WorldImporter.java +++ b/src/main/java/me/cortex/voxy/client/importers/WorldImporter.java @@ -36,19 +36,19 @@ import java.util.function.Function; import java.util.function.Predicate; public class WorldImporter { - public record ImportUpdate(){} + public interface UpdateCallback { + void update(int finished, int outof); + } private final WorldEngine world; - private final World mcWorld; private final ReadableContainer> defaultBiomeProvider; private final Codec>> biomeCodec; private final AtomicInteger totalRegions = new AtomicInteger(); private final AtomicInteger regionsProcessed = new AtomicInteger(); - private final AtomicInteger percentMarker = new AtomicInteger(); + private volatile boolean isRunning; public WorldImporter(WorldEngine worldEngine, World mcWorld) { this.world = worldEngine; - this.mcWorld = mcWorld; var biomeRegistry = mcWorld.getRegistryManager().get(RegistryKeys.BIOME); var defaultBiome = biomeRegistry.entryOf(BiomeKeys.PLAINS); @@ -97,9 +97,16 @@ public class WorldImporter { this.biomeCodec = PalettedContainer.createReadableContainerCodec(biomeRegistry.getIndexedEntries(), biomeRegistry.createEntryCodec(), PalettedContainer.PaletteProvider.BIOME, biomeRegistry.entryOf(BiomeKeys.PLAINS)); } + + public void shutdown() { + this.isRunning = false; + try {this.worker.join();} catch (InterruptedException e) {throw new RuntimeException(e);} + } + private Thread worker; - public void importWorldAsyncStart(File directory, int threads, Function updateCallback, Runnable onCompletion) { + public void importWorldAsyncStart(File directory, int threads, UpdateCallback updateCallback, Runnable onCompletion) { this.worker = new Thread(() -> { + this.isRunning = true; var workers = new ForkJoinPool(threads); var files = directory.listFiles(); for (var file : files) { @@ -117,16 +124,12 @@ public class WorldImporter { this.totalRegions.addAndGet(1); workers.submit(() -> { try { + if (!isRunning) { + return; + } this.importRegionFile(file.toPath(), rx, rz); int regionsProcessedCount = this.regionsProcessed.addAndGet(1); - synchronized (this.world) { - int percentMark = this.percentMarker.get(); - int percent = (regionsProcessedCount*100)/this.totalRegions.get(); - if (percent > percentMark) { - System.out.println(regionsProcessedCount + "/" + this.totalRegions.get()); - this.percentMarker.addAndGet(1); - } - } + updateCallback.update(regionsProcessedCount, this.totalRegions.get()); } catch ( Exception e) { e.printStackTrace(); diff --git a/src/main/java/me/cortex/voxy/client/terrain/WorldImportCommand.java b/src/main/java/me/cortex/voxy/client/terrain/WorldImportCommand.java index d8094ef8..1bf7a6da 100644 --- a/src/main/java/me/cortex/voxy/client/terrain/WorldImportCommand.java +++ b/src/main/java/me/cortex/voxy/client/terrain/WorldImportCommand.java @@ -3,13 +3,19 @@ package me.cortex.voxy.client.terrain; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; import me.cortex.voxy.client.core.IGetVoxelCore; import me.cortex.voxy.client.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.command.CommandSource; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.concurrent.CompletableFuture; public class WorldImportCommand { @@ -17,33 +23,55 @@ public class WorldImportCommand { return ClientCommandManager.literal("voxy").then( ClientCommandManager.literal("import") .then(ClientCommandManager.literal("world") - .then(ClientCommandManager.argument("world_name", StringArgumentType.string()).executes(WorldImportCommand::importWorld))) + .then(ClientCommandManager.argument("world_name", StringArgumentType.string()) + .suggests(WorldImportCommand::importWorldSuggester) + .executes(WorldImportCommand::importWorld))) .then(ClientCommandManager.literal("bobby") - .then(ClientCommandManager.argument("world_name", StringArgumentType.string()).executes(WorldImportCommand::importBobby))) + .then(ClientCommandManager.argument("world_name", StringArgumentType.string()) + .executes(WorldImportCommand::importBobby))) .then(ClientCommandManager.literal("raw") - .then(ClientCommandManager.argument("path", StringArgumentType.string()).executes(WorldImportCommand::importRaw)))); + .then(ClientCommandManager.argument("path", StringArgumentType.string()) + .executes(WorldImportCommand::importRaw)))); } - public static WorldImporter importerInstance; private static int importRaw(CommandContext ctx) { var instance = MinecraftClient.getInstance(); var file = new File(ctx.getArgument("path", String.class)); - importerInstance = ((IGetVoxelCore)instance.worldRenderer).getVoxelCore().createWorldImporter(MinecraftClient.getInstance().player.clientWorld, file); + ((IGetVoxelCore)instance.worldRenderer).getVoxelCore().createWorldImporter(MinecraftClient.getInstance().player.clientWorld, file); return 0; } private static int importBobby(CommandContext ctx) { var instance = MinecraftClient.getInstance(); var file = new File(".bobby").toPath().resolve(ctx.getArgument("world_name", String.class)).toFile(); - importerInstance = ((IGetVoxelCore)instance.worldRenderer).getVoxelCore().createWorldImporter(MinecraftClient.getInstance().player.clientWorld, file); + ((IGetVoxelCore)instance.worldRenderer).getVoxelCore().createWorldImporter(MinecraftClient.getInstance().player.clientWorld, file); return 0; } + private static CompletableFuture importWorldSuggester(CommandContext ctx, SuggestionsBuilder sb) { + try { + var worlds = Files.list(MinecraftClient.getInstance().runDirectory.toPath().resolve("saves")).toList(); + for (var world : worlds) { + if (!world.toFile().isDirectory()) { + continue; + } + var wn = world.getFileName().toString(); + if (CommandSource.shouldSuggest(sb.getRemaining(), wn)) { + sb.suggest(wn); + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + return sb.buildFuture(); + } + private static int importWorld(CommandContext ctx) { var instance = MinecraftClient.getInstance(); var file = new File("saves").toPath().resolve(ctx.getArgument("world_name", String.class)).resolve("region").toFile(); - importerInstance = ((IGetVoxelCore)instance.worldRenderer).getVoxelCore().createWorldImporter(MinecraftClient.getInstance().player.clientWorld, file); + ((IGetVoxelCore)instance.worldRenderer).getVoxelCore().createWorldImporter(MinecraftClient.getInstance().player.clientWorld, file); return 0; }