243 lines
11 KiB
Java
243 lines
11 KiB
Java
package net.minecraft.util.worldupdate;
|
|
|
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|
import org.apache.logging.log4j.LogManager;
|
|
import java.util.regex.Matcher;
|
|
import net.minecraft.world.level.chunk.storage.RegionFile;
|
|
import com.google.common.collect.Lists;
|
|
import com.google.common.collect.ImmutableList;
|
|
import com.google.common.collect.UnmodifiableIterator;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import java.util.List;
|
|
import java.util.Iterator;
|
|
import net.minecraft.ReportedException;
|
|
import java.io.IOException;
|
|
import net.minecraft.SharedConstants;
|
|
import net.minecraft.world.level.chunk.storage.ChunkStorage;
|
|
import net.minecraft.world.level.ChunkPos;
|
|
import java.util.ListIterator;
|
|
import com.google.common.collect.ImmutableMap;
|
|
import net.minecraft.server.MinecraftServer;
|
|
import net.minecraft.network.chat.TranslatableComponent;
|
|
import it.unimi.dsi.fastutil.objects.Object2FloatMaps;
|
|
import it.unimi.dsi.fastutil.Hash;
|
|
import it.unimi.dsi.fastutil.objects.Object2FloatOpenCustomHashMap;
|
|
import net.minecraft.Util;
|
|
import net.minecraft.world.level.storage.LevelData;
|
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
|
import net.minecraft.world.level.storage.DimensionDataStorage;
|
|
import java.util.regex.Pattern;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.world.level.dimension.DimensionType;
|
|
import it.unimi.dsi.fastutil.objects.Object2FloatMap;
|
|
import java.io.File;
|
|
import net.minecraft.world.level.storage.LevelStorage;
|
|
import java.util.concurrent.ThreadFactory;
|
|
import org.apache.logging.log4j.Logger;
|
|
|
|
public class WorldUpgrader {
|
|
private static final Logger LOGGER;
|
|
private static final ThreadFactory THREAD_FACTORY;
|
|
private final String levelName;
|
|
private final boolean eraseCache;
|
|
private final LevelStorage levelStorage;
|
|
private final Thread thread;
|
|
private final File pathToWorld;
|
|
private volatile boolean running;
|
|
private volatile boolean finished;
|
|
private volatile float progress;
|
|
private volatile int totalChunks;
|
|
private volatile int converted;
|
|
private volatile int skipped;
|
|
private final Object2FloatMap<DimensionType> progressMap;
|
|
private volatile Component status;
|
|
private static final Pattern REGEX;
|
|
private final DimensionDataStorage overworldDataStorage;
|
|
|
|
public WorldUpgrader(final String string, final LevelStorageSource crm, final LevelData crj, final boolean boolean4) {
|
|
this.running = true;
|
|
this.progressMap = (Object2FloatMap<DimensionType>)Object2FloatMaps.synchronize((Object2FloatMap)new Object2FloatOpenCustomHashMap((Hash.Strategy)Util.identityStrategy()));
|
|
this.status = new TranslatableComponent("optimizeWorld.stage.counting", new Object[0]);
|
|
this.levelName = crj.getLevelName();
|
|
this.eraseCache = boolean4;
|
|
(this.levelStorage = crm.selectLevel(string, null)).saveLevelData(crj);
|
|
this.overworldDataStorage = new DimensionDataStorage(new File(DimensionType.OVERWORLD.getStorageFolder(this.levelStorage.getFolder()), "data"), this.levelStorage.getFixerUpper());
|
|
this.pathToWorld = this.levelStorage.getFolder();
|
|
(this.thread = WorldUpgrader.THREAD_FACTORY.newThread(this::work)).setUncaughtExceptionHandler((thread, throwable) -> {
|
|
WorldUpgrader.LOGGER.error("Error upgrading world", throwable);
|
|
this.status = new TranslatableComponent("optimizeWorld.stage.failed", new Object[0]);
|
|
this.finished = true;
|
|
return;
|
|
});
|
|
this.thread.start();
|
|
}
|
|
|
|
public void cancel() {
|
|
this.running = false;
|
|
try {
|
|
this.thread.join();
|
|
}
|
|
catch (InterruptedException ex) {}
|
|
}
|
|
|
|
private void work() {
|
|
final File file2 = this.levelStorage.getFolder();
|
|
this.totalChunks = 0;
|
|
final ImmutableMap.Builder<DimensionType, ListIterator<ChunkPos>> builder3 = ImmutableMap.<DimensionType, ListIterator<ChunkPos>>builder();
|
|
for (final DimensionType cbf5 : DimensionType.getAllTypes()) {
|
|
final List<ChunkPos> list6 = this.getAllChunkPos(cbf5);
|
|
builder3.put(cbf5, list6.listIterator());
|
|
this.totalChunks += list6.size();
|
|
}
|
|
if (this.totalChunks == 0) {
|
|
this.finished = true;
|
|
return;
|
|
}
|
|
final float float4 = (float)this.totalChunks;
|
|
final ImmutableMap<DimensionType, ListIterator<ChunkPos>> immutableMap5 = builder3.build();
|
|
final ImmutableMap.Builder<DimensionType, ChunkStorage> builder4 = ImmutableMap.<DimensionType, ChunkStorage>builder();
|
|
for (final DimensionType cbf6 : DimensionType.getAllTypes()) {
|
|
final File file3 = cbf6.getStorageFolder(file2);
|
|
builder4.put(cbf6, new ChunkStorage(new File(file3, "region"), this.levelStorage.getFixerUpper()));
|
|
}
|
|
final ImmutableMap<DimensionType, ChunkStorage> immutableMap6 = builder4.build();
|
|
long long8 = Util.getMillis();
|
|
this.status = new TranslatableComponent("optimizeWorld.stage.upgrading", new Object[0]);
|
|
while (this.running) {
|
|
boolean boolean10 = false;
|
|
float float5 = 0.0f;
|
|
for (final DimensionType cbf7 : DimensionType.getAllTypes()) {
|
|
final ListIterator<ChunkPos> listIterator14 = immutableMap5.get(cbf7);
|
|
final ChunkStorage cav15 = immutableMap6.get(cbf7);
|
|
if (listIterator14.hasNext()) {
|
|
final ChunkPos bje16 = listIterator14.next();
|
|
boolean boolean11 = false;
|
|
try {
|
|
final CompoundTag jt18 = cav15.read(bje16);
|
|
if (jt18 != null) {
|
|
final int integer19 = ChunkStorage.getVersion(jt18);
|
|
final CompoundTag jt19 = cav15.upgradeChunkTag(cbf7, () -> this.overworldDataStorage, jt18);
|
|
final CompoundTag jt20 = jt19.getCompound("Level");
|
|
final ChunkPos bje17 = new ChunkPos(jt20.getInt("xPos"), jt20.getInt("zPos"));
|
|
if (!bje17.equals(bje16)) {
|
|
WorldUpgrader.LOGGER.warn("Chunk {} has invalid position {}", bje16, bje17);
|
|
}
|
|
boolean boolean12 = integer19 < SharedConstants.getCurrentVersion().getWorldVersion();
|
|
if (this.eraseCache) {
|
|
boolean12 = (boolean12 || jt20.contains("Heightmaps"));
|
|
jt20.remove("Heightmaps");
|
|
boolean12 = (boolean12 || jt20.contains("isLightOn"));
|
|
jt20.remove("isLightOn");
|
|
}
|
|
if (boolean12) {
|
|
cav15.write(bje16, jt19);
|
|
boolean11 = true;
|
|
}
|
|
}
|
|
}
|
|
catch (ReportedException q18) {
|
|
final Throwable throwable19 = q18.getCause();
|
|
if (!(throwable19 instanceof IOException)) {
|
|
throw q18;
|
|
}
|
|
WorldUpgrader.LOGGER.error("Error upgrading chunk {}", bje16, throwable19);
|
|
}
|
|
catch (IOException iOException18) {
|
|
WorldUpgrader.LOGGER.error("Error upgrading chunk {}", bje16, iOException18);
|
|
}
|
|
if (boolean11) {
|
|
++this.converted;
|
|
}
|
|
else {
|
|
++this.skipped;
|
|
}
|
|
boolean10 = true;
|
|
}
|
|
final float float6 = listIterator14.nextIndex() / float4;
|
|
this.progressMap.put(cbf7, float6);
|
|
float5 += float6;
|
|
}
|
|
this.progress = float5;
|
|
if (!boolean10) {
|
|
this.running = false;
|
|
}
|
|
}
|
|
this.status = new TranslatableComponent("optimizeWorld.stage.finished", new Object[0]);
|
|
for (final ChunkStorage cav16 : immutableMap6.values()) {
|
|
try {
|
|
cav16.close();
|
|
}
|
|
catch (IOException iOException19) {
|
|
WorldUpgrader.LOGGER.error("Error upgrading chunk", (Throwable)iOException19);
|
|
}
|
|
}
|
|
this.overworldDataStorage.save();
|
|
long8 = Util.getMillis() - long8;
|
|
WorldUpgrader.LOGGER.info("World optimizaton finished after {} ms", long8);
|
|
this.finished = true;
|
|
}
|
|
|
|
private List<ChunkPos> getAllChunkPos(final DimensionType cbf) {
|
|
final File file3 = cbf.getStorageFolder(this.pathToWorld);
|
|
final File file4 = new File(file3, "region");
|
|
final File[] arr5 = file4.listFiles((file, string) -> string.endsWith(".mca"));
|
|
if (arr5 == null) {
|
|
return ImmutableList.of();
|
|
}
|
|
final List<ChunkPos> list6 = Lists.newArrayList();
|
|
for (final File file5 : arr5) {
|
|
final Matcher matcher11 = WorldUpgrader.REGEX.matcher(file5.getName());
|
|
if (matcher11.matches()) {
|
|
final int integer12 = Integer.parseInt(matcher11.group(1)) << 5;
|
|
final int integer13 = Integer.parseInt(matcher11.group(2)) << 5;
|
|
try (final RegionFile caz14 = new RegionFile(file5, file4)) {
|
|
for (int integer14 = 0; integer14 < 32; ++integer14) {
|
|
for (int integer15 = 0; integer15 < 32; ++integer15) {
|
|
final ChunkPos bje18 = new ChunkPos(integer14 + integer12, integer15 + integer13);
|
|
if (caz14.doesChunkExist(bje18)) {
|
|
list6.add(bje18);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Throwable t3) {}
|
|
}
|
|
}
|
|
return list6;
|
|
}
|
|
|
|
public boolean isFinished() {
|
|
return this.finished;
|
|
}
|
|
|
|
public float dimensionProgress(final DimensionType cbf) {
|
|
return this.progressMap.getFloat(cbf);
|
|
}
|
|
|
|
public float getProgress() {
|
|
return this.progress;
|
|
}
|
|
|
|
public int getTotalChunks() {
|
|
return this.totalChunks;
|
|
}
|
|
|
|
public int getConverted() {
|
|
return this.converted;
|
|
}
|
|
|
|
public int getSkipped() {
|
|
return this.skipped;
|
|
}
|
|
|
|
public Component getStatus() {
|
|
return this.status;
|
|
}
|
|
|
|
static {
|
|
LOGGER = LogManager.getLogger();
|
|
THREAD_FACTORY = new ThreadFactoryBuilder().setDaemon(true).build();
|
|
REGEX = Pattern.compile("^r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mca$");
|
|
}
|
|
}
|