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 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)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> builder3 = ImmutableMap.>builder(); for (final DimensionType cbf5 : DimensionType.getAllTypes()) { final List 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> immutableMap5 = builder3.build(); final ImmutableMap.Builder builder4 = ImmutableMap.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 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 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 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 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$"); } }