minecraft-source/src/net/minecraft/util/worldupdate/WorldUpgrader.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$");
}
}