Skip to content

Commit

Permalink
Parallelized optimization to allow multi-threading
Browse files Browse the repository at this point in the history
  • Loading branch information
RhysB committed Aug 6, 2024
1 parent 66e39f0 commit b4b80b0
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 31 deletions.
12 changes: 0 additions & 12 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,4 @@
<groupId>org.example</groupId>
<artifactId>Server</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,31 @@


import java.io.File;
import java.util.concurrent.TimeUnit;

public class AnvilConverter {

public static void main(String[] args) {

if (args.length != 2) {
if (args.length < 2) {
printUsageAndExit();
}

// Handle optional third argument for number of threads
int numThreads = 1;
if (args.length == 3) {
try {
numThreads = Integer.parseInt(args[2]);
if (numThreads < 0) {
throw new NumberFormatException();
}
} catch (NumberFormatException e) {
System.err.println("Invalid number of threads: " + args[2]);
System.out.println("");
printUsageAndExit();
return;
}
}

File baseFolder;
try {
baseFolder = new File(args[0]);
Expand All @@ -40,7 +56,40 @@ public static void main(String[] args) {
return;
}

// Remove old .mca region files if they exist (they will be regenerated)
File regionFolder = new File(baseFolder, args[1] + "/region");
int regionCount = 0;
if (regionFolder.exists()) {
File[] regionFiles = regionFolder.listFiles();
if (regionFiles != null) {
for (File regionFile : regionFiles) {
if (regionFile.getName().endsWith(".mca")) {
regionFile.delete();
regionCount++;
}
}
}
}
if (regionCount > 0) {
System.out.println("Deleted " + regionCount + " old mca region files");
}

// Rename level.dat_mcr to level.dat
File levelDatMcr = new File(baseFolder, args[1] + "/level.dat_mcr");
File levelDat = new File(baseFolder, args[1] + "/level.dat");
if (levelDatMcr.exists()) {
if (levelDat.exists()) {
levelDat.delete();
}
levelDatMcr.renameTo(levelDat);
System.out.println("Renamed level.dat_mcr to level.dat");
}

System.out.println("Converting map!");

// Duration duration = new Duration();
long startTime = System.currentTimeMillis();

storage.convertLevel(args[1], new ProgressListener() {
private long timeStamp = System.currentTimeMillis();

Expand All @@ -59,7 +108,25 @@ public void progressStagePercentage(int i) {

public void progressStage(String string) {
}
});
}, numThreads);

long endTime = System.currentTimeMillis();
long durationMillis = endTime - startTime;

// Calculate minutes, seconds, and milliseconds
long hours = TimeUnit.MILLISECONDS.toHours(durationMillis);
long minutes = TimeUnit.MILLISECONDS.toMinutes(durationMillis) -
TimeUnit.HOURS.toMinutes(hours);
long seconds = TimeUnit.MILLISECONDS.toSeconds(durationMillis) -
TimeUnit.MINUTES.toSeconds(minutes);
long millis = durationMillis - TimeUnit.MINUTES.toMillis(minutes) -
TimeUnit.SECONDS.toMillis(seconds);

// Format and print the duration time
String duration = String.format("%02d:%02d:%02d.%03d", hours, minutes, seconds, millis);
System.out.println("Conversion completed in: " + duration);


System.out.println("Done!");
System.out.println("To revert, replace level.dat with level.dat_mcr. Old mcr region files have not been modified.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@
* Don't do evil.
*/

import java.io.*;
import java.util.ArrayList;

import com.mojang.nbt.CompoundTag;
import com.mojang.nbt.NbtIo;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.chunk.storage.*;
import net.minecraft.world.level.chunk.storage.OldChunkStorage;
import net.minecraft.world.level.chunk.storage.OldChunkStorage.OldLevelChunk;
import net.minecraft.world.level.chunk.storage.RegionFile;

import com.mojang.nbt.*;
import java.io.*;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

public class AnvilLevelStorageSource {

Expand Down Expand Up @@ -79,7 +83,7 @@ private void saveDataTag(String levelId, CompoundTag dataTag) {
}
}

public boolean convertLevel(String levelId, ProgressListener progress) {
public boolean convertLevel(String levelId, ProgressListener progress, int threadCount) {

progress.progressStagePercentage(0);

Expand Down Expand Up @@ -109,12 +113,23 @@ public boolean convertLevel(String levelId, ProgressListener progress) {

CompoundTag levelData = getDataTagFor(levelId);

//int threadCount = Runtime.getRuntime().availableProcessors();

// Print in what mode we are running
if (threadCount == 0) {
System.out.println("Running sequentially");
} else {
System.out.println("Running " + threadCount + " threads in parallel");
}

AtomicInteger currentCount = new AtomicInteger(0);

// convert normal world
convertRegions(new File(baseFolder, "region"), normalRegions, null, 0, totalCount, progress);
convertRegions(new File(baseFolder, "region"), normalRegions, null, currentCount, totalCount, progress, threadCount);
// convert hell world
convertRegions(new File(netherFolder, "region"), netherRegions, null, normalRegions.size(), totalCount, progress);
convertRegions(new File(netherFolder, "region"), netherRegions, null, currentCount, totalCount, progress, threadCount);
// convert end world
convertRegions(new File(enderFolder, "region"), enderRegions, null, normalRegions.size() + netherRegions.size(), totalCount, progress);
convertRegions(new File(enderFolder, "region"), enderRegions, null, currentCount, totalCount, progress, threadCount);

makeMcrLevelDatBackup(levelId);

Expand Down Expand Up @@ -143,19 +158,40 @@ private void makeMcrLevelDatBackup(String levelId) {
}
}

private void convertRegions(File baseFolder, ArrayList<File> regionFiles, BiomeSource biomeSource, int currentCount, int totalCount, ProgressListener progress) {
private void convertRegions(File baseFolder, ArrayList<File> regionFiles, BiomeSource biomeSource, AtomicInteger currentCount, int totalCount, ProgressListener progress, int numThreads) {
// Create an ExecutorService with a fixed thread pool

for (File regionFile : regionFiles) {
convertRegion(baseFolder, regionFile, biomeSource, currentCount, totalCount, progress);
if (numThreads <= 0) {
//Program is being run sequentially
for (File regionFile : regionFiles) {
convertRegion(baseFolder, regionFile, biomeSource, currentCount, totalCount, progress);
}
} else {
//Program is being run in parallel
ExecutorService executorService = Executors.newFixedThreadPool(numThreads);

// Submit tasks for each region file
for (File regionFile : regionFiles) {
executorService.submit(() -> {
convertRegion(baseFolder, regionFile, biomeSource, currentCount, totalCount, progress);
});
}

currentCount++;
int percent = (int) Math.round(100.0d * (double) currentCount / (double) totalCount);
progress.progressStagePercentage(percent);
// Shut down the executor service and wait for all tasks to complete
executorService.shutdown();
try {
executorService.awaitTermination(Long.MAX_VALUE, java.util.concurrent.TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

// Update progress after all tasks are completed
int percent = (int) Math.round(100.0d * (double) totalCount / (double) totalCount);
progress.progressStagePercentage(percent);
}

private void convertRegion(File baseFolder, File regionFile, BiomeSource biomeSource, int currentCount, int totalCount, ProgressListener progress) {
private void convertRegion(File baseFolder, File regionFile, BiomeSource biomeSource, AtomicInteger currentCounter, int totalCount, ProgressListener progress) {

try {
String name = regionFile.getName();
Expand Down Expand Up @@ -189,13 +225,20 @@ private void convertRegion(File baseFolder, File regionFile, BiomeSource biomeSo
}
}
}

// Update progress
int currentCount = currentCounter.get();

int basePercent = (int) Math.round(100.0d * (double) (currentCount * 1024) / (double) (totalCount * 1024));
int newPercent = (int) Math.round(100.0d * (double) ((x + 1) * 32 + currentCount * 1024) / (double) (totalCount * 1024));
if (newPercent > basePercent) {
progress.progressStagePercentage(newPercent);
}
}

// Increment the counter
currentCounter.incrementAndGet();

regionSource.close();
regionDest.close();
} catch (IOException e) {
Expand Down

0 comments on commit b4b80b0

Please sign in to comment.